For the provisioning of session hosts, it can be useful to copy files from a storage account that are required during provisioning or operation. However, these files should not be publicly available and a storage access key should not be used.
I can create a short-term shared access signature (SAS) so that a file can be downloaded from the operating system without a storage access key. This allows the files to be downloaded from a container. This can be achieved with a user-assigned managed identity.
In order to realize this, we first need a user-assigned managed identity and a Stroage account. The information is stored in variables within Nerdio. To add the identity to a session host, we use a scripted action.
As soon as the user-assigned managed identity and the Storage Account are created and the user-assigned managed identity is authorized, we can use the Scripted Action to assign the user-assigned managed identity to a VM. With another scripted action, we can now download and execute the files from the storage account.
Prerequisites
As a general requirement, PowerShell must be up to date on the VM and the following modules must be installed:
- az.accounts
- az.resources
- az.storage
- az.keyvault
For everything else, a Nerdio instance and a resource group are required, where the storage account and the user-assigned managed identity should be stored. This should also be added in Nerdio so that the authorizations are correct.
Create storage account
A storage account is created in the classic way. Different configurations can be made. A standard account with the availability ZRS or GRS, which can have a private endpoint, is usually sufficient for a storage account for a deployment. Below is a scripted action that automates this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
#name: Create Storage Account for deployment #description: Create a Storage Account for deployment over Scripted Actions #execution mode: Combined #tags: beckmann.ch <# Notes: Use this script to create a Storage Account for deployment over Scripted Actions. #> <# Variables: { "StorageAccountName": { "Description": "Name of the storage account to be created.", "IsRequired": true }, "SkuName": { "Description": "SKU of the storage account. (Standard_ZRS, Standard_GRS, Standard_LRS)", "IsRequired": true, "DefaultValue": "Standard_ZRS" }, "DeploymentContainerName": { "Description": "Name of the container to be created for deployment scripts.", "IsRequired": true, "DefaultValue": "deployment" }, "PrerequisiteName": { "Description": "Name of the container to be created for prerequisites.", "IsRequired": true, "DefaultValue": "prereq" }, "ResourceGroupName": { "Description": "Name of the resource group where the storage account will be created.", "IsRequired": true } } #> $ErrorActionPreference = 'Stop' $Prefix = ($KeyVaultName -split '-')[0] $NMEIdString = ($KeyVaultName -split '-')[3] $KeyVault = Get-AzKeyVault -VaultName $KeyVaultName $Context = Get-AzContext $NMEResourceGroupName = $KeyVault.ResourceGroupName $NMELocation = $KeyVault.Location ##### Script Logic ##### $SubscriptionId = $Context.Subscription.Id $DeploymentContainerName = $DeploymentContainerName.ToLower() $PrerequisiteName = $PrerequisiteName.ToLower() try { # Creating the storage account Write-Output "Create storage account" $storageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -Location $NMELocation -SkuName $SkuName -Kind StorageV2 -EnableHttpsTrafficOnly $true -AllowBlobPublicAccess $true -PublicNetworkAccess Enabled -ErrorAction Stop # Create a container for the deloyment scripts Write-Output "Create container for deployment scripts" $deploymentContainer = New-AzStorageContainer -Name $DeploymentContainerName -Permission Off -Context $storageAccount.Context -ErrorAction Stop # Create a container for the prerequisites Write-Output "Create container for prerequisites" $prerequisiteContainer = New-AzStorageContainer -Name $PrerequisiteName -Permission Blob -Context $storageAccount.Context -ErrorAction Stop # Create Output for export the information $Output = "{`"name`":`"$StorageAccountName`",`"resourceGroup`":`"$ResourceGroupName`",`"subscriptionid`":`"$SubscriptionId`",`"container`":`"$DeploymentContainerName`"}" } catch { $ErrorActionPreference = 'Continue' Write-Output "Encountered error. $_" Write-Output "Rolling back changes" if ($storageAccount) { Write-Output "Removing storage account $StorageAccountName" Remove-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -Force } Throw $_ } Write-Output "Storage Account created successfully." Write-Output "Storage Account Name: $StorageAccountName" Write-Output "Resource Group: $ResourceGroupName" Write-Output "Subscription ID: $SubscriptionId" Write-Output "Depoyment Container: $DeploymentContainerName" Write-Output "Conten of Secute Varaible 'DeployStorageAccount' = $Output" |
The parameters are then configured during execution:
The output can be used to create the variable in Nerdio:
Create user-assigned managed identity
A user-assigned managed identity is very easy to create. If this is to be created via the Azure portal, it can look like this:
Later we will need the information from the properties to create the variable in Nerdio
Alternatively, the following scripted action can be used:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
#name: Create User-Assigned Managed Identity #description: Create a user-assigned managed identity to use in scripted actions. #execution mode: Combined #tags: beckmann.ch <# Notes: Use this script to create a user-assigned managed identity in the resource group of Nerdio. #> <# Variables: { "UserManagedIdentityName": { "Description": "Name of the user-assigned managed identity.", "IsRequired": true, "DefaultValue": "uami-nerdio-scripted-actions" }, "ResourceGroupName": { "Description": "Name of the resource group where the user assigned identity will be created.", "IsRequired": true } } #> $ErrorActionPreference = 'Stop' $Prefix = ($KeyVaultName -split '-')[0] $NMEIdString = ($KeyVaultName -split '-')[3] $KeyVault = Get-AzKeyVault -VaultName $KeyVaultName $Context = Get-AzContext $NMEResourceGroupName = $KeyVault.ResourceGroupName $NMELocation = $KeyVault.Location ##### Script Logic ##### try { #Creating the user-assigned managed identity. Write-Output "Create user-assigned managed identity" $identity = New-AzUserAssignedIdentity -ResourceGroupName $ResourceGroupName -Name $UserManagedIdentityName -Location $NMELocation $clientId = $identity.ClientId $objectId = $identity.PrincipalId $subscriptionId = $Context.Subscription.Id # Create Output for export the information $Output = "{`"name`":`"$UserManagedIdentityName`", `"client_id`":`"$clientId`", `"object_id`":`"$objectId`", `"subscriptionid`":`"$subscriptionId`", `"resourcegroup`":`"$ResourceGroupName`"}" } catch { $ErrorActionPreference = 'Continue' Write-Output "Encountered error. $_" Write-Output "Rolling back changes" if ($identity) { Write-Output "Removing user-assigned managed identity $UserManagedIdentityName" Remove-AzUserAssignedIdentity -ResourceGroupName $NMEResourceGroupName -Name $UserManagedIdentityName -Force -ErrorAction Continue } Throw $_ } Write-Output "User-assigned managed identity created successfully." Write-Output "User-assigned managed identity name: $UserManagedIdentityName" Write-Output "Resource Group: $ResourceGroupName" Write-Output "Subscription ID: $subscriptionId" Write-Output "Client ID: $clientId" Write-Output "Object ID: $objectId" Write-Output "Conten of Secute Varaible 'DeployIdentity' = $Output" |
The parameters are then configured during execution:
The output can be used to create the variable in Nerdio:
Create variables
We now create two secure variables in Nerdio, which contain all relevant information about the storage account and the user-assigned managed identity.
DeployStorageAccount
The storage account is used for deployment, which is why I call the variable DeployStorageAccount:
The variable bin contains a JSON formatted string with all relevant information:
1 2 3 4 5 6 |
{ "name": "stbestavdssprdwedeploy01", "resourceGroup": "rg-deployment-avdss-prd-we-01", "subscriptionid": "", "container": "deployment" } |
DeploymentIdentity
As this is the User Assigned Managed Identity for the deployment, it is called DeploymentIdentity:
The variable bin contains a JSON formatted string with all relevant information:
1 2 3 4 5 6 7 |
{ "name": "uami-nerdio-scripted-actions", "client_id": "", "object_id": "", "subscriptionid": "", "resourcegroup": "rg-deployment-avdss-prd-we-01" } |
In order to be able to use the User-Assigned Managed Identity later, we save not only the name, but all relevant information in order to find it with the CmdLet Get-AzUserAssignedIdentity.
Authorize user-assigned managed identity on the storage account
Now that the storage account and the User-Assigned Managed Identity have been created, the identity still needs to be authorized on the storage account.
This can also be configured with a scripted action. The prerequisite for this is that the two variables are configured.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
#name: Assign User-Assigned Managed Identity to Storage Account #description: Assign a user-assigned managed identity to a Storage Account to get a SAS Token. #execution mode: Combined #tags: beckmann.ch <# Notes: Use this script to authorize a user-assigned managed identity on a storage account to create a SAS token. Requires: - A variable with the name DeployIdentity and the value for the User-Assigned Managed Identity used for the deployment. - A variable with the name DeployStorageAccount and the value for the storage account used for the deployment. #> <# Variables: { "ManagedIdentityVariable": { "Description": "Name of the secure variable or variable for the managed identity.", "IsRequired": true, "DefaultValue": "DeployIdentity" }, "StorageAccountVariable": { "Description": "Name of the secure variable or variable for the storage account.", "IsRequired": true, "DefaultValue": "DeployStorageAccount" } } #> $ErrorActionPreference = 'Stop' $Prefix = ($KeyVaultName -split '-')[0] $NMEIdString = ($KeyVaultName -split '-')[3] $KeyVault = Get-AzKeyVault -VaultName $KeyVaultName $Context = Get-AzContext $NMEResourceGroupName = $KeyVault.ResourceGroupName # Get secure variables for the managed identity and storage account # Check if the variable is a valid JSON object and convert it to a PowerShell object # If not, try to get the variable from the secure variables and convert it to a PowerShell object # If the variable is not a valid JSON object, exit the script # If the variable is a valid JSON object, continue with the script Write-Output "Get secure variables" If ($ManagedIdentityVariable -like "{*}") { Write-Output "Convert ManagedIdentityVariable to JSON object" $ManagedIdentity = $ManagedIdentityVariable | ConvertFrom-Json } Else { Write-Output "Get Secure Variable for ManagedIdentityVariable and convert to JSON object" $ManagedIdentity = $SecureVars.$ManagedIdentityVariable | ConvertFrom-Json } If ([string]::IsNullOrEmpty($ManagedIdentity.name)) { Write-Output "ManagedIdentityVariable is not a valid JSON object" Exit } Else { Write-Output ("Managed Identity Name: " + $ManagedIdentity.name) } If ($StorageAccountVariable -like "{*}") { Write-Output "Convert StorageAccountVariable to JSON object" $StorageAccount = $StorageAccountVariable | ConvertFrom-Json } Else { Write-Output "Get Secure Variable for StorageAccountVariable and convert to JSON object" $StorageAccount = $SecureVars.$StorageAccountVariable | ConvertFrom-Json } If ([string]::IsNullOrEmpty($StorageAccount.name)) { Write-Output "StorageAccountVariable is not a valid JSON object" Exit } Else { Write-Output ("Storage Account Name : " + $StorageAccount.name | Out-String) } ##### Script Logic ##### try { #Assign the user-assigned managed identity. $actContext = Get-AzContext If ($actContext.Subscription.Id -ne $ManagedIdentity.subscriptionid) { Set-AzContext -SubscriptionId $ManagedIdentity.subscriptionid } $objIdentity = Get-AzUserAssignedIdentity -ResourceGroupName $ManagedIdentity.ResourceGroup -Name $ManagedIdentity.Name $actContext = Get-AzContext If ($actContext.Subscription.Id -ne $StorageAccount.subscriptionid) { Set-AzContext -SubscriptionId $StorageAccount.subscriptionid } $objStorageAccount = Get-AzStorageAccount -ResourceGroupName $StorageAccount.ResourceGroup -Name $StorageAccount.Name # $objIdentity.ObjectId does not work, use $objIdentity.PrincipalId Write-Output ("Assign user-assigned managed identity " + $objIdentity.Name + " to storage account " + $objStorageAccount.StorageAccountName) New-AzRoleAssignment -ObjectId $objIdentity.PrincipalId -Scope $objStorageAccount.Id -RoleDefinitionName "Storage Account Contributor" } catch { $ErrorActionPreference = 'Continue' Write-Output "Encountered error. $_" Throw $_ } |
The parameters are then configured during execution:
Scripted Action to add user-assigned managed identity to a VM
The following script is used to add the user-assigned managed identity to the VM. It takes into account whether a system-assigned managed identity or user-assigned managed identity already exists.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
#name: Assign User-Assigned Managed Identity to VM #description: Assign a user-assigned managed identity to a VM. #execution mode: Individual #tags: beckmann.ch <# Notes: Use this script to assign a user-assigned managed identity to a VM. Requires: - A variable with the name DeployIdentity and the value for the User-Assigned Managed Identity used for the deployment. #> $ManagedIdentityVariable = 'DeployIdentity' $ErrorActionPreference = 'Stop' Write-Output "AzureSubscriptionId = $AzureSubscriptionId" Write-Output "AzureSubscriptionName = $AzureSubscriptionName" Write-Output "AzureResourceGroupName = $AzureResourceGroupName" Write-Output "AzureVMName = $AzureVMName" Write-Output "Get Secure Variable for ManagedIdentityVariable and convert to JSON object" $ManagedIdentity = $SecureVars.$ManagedIdentityVariable | ConvertFrom-Json If ([string]::IsNullOrEmpty($ManagedIdentity.name)) { Write-Output "ManagedIdentityVariable is not a valid JSON object" Exit } Else { Write-Output ("Managed Identity Name: " + $ManagedIdentity.name) } ##### Script Logic ##### try { #Assign the user-assigned managed identity. Write-Output "Assign user-assigned managed identity" $umidentity = Get-AzUserAssignedIdentity -ResourceGroupName $ManagedIdentity.resourcegroup -Name $ManagedIdentity.name -SubscriptionId $ManagedIdentity.subscriptionid Write-Output ("umidentity = {0}" -f ($umidentity | Out-String)) $vm = Get-AzVM -ResourceGroupName $AzureResourceGroupName -VM $AzureVMName Write-Output ("VM = {0}" -f ($vm | Out-String)) If ($vm.Identity.Type -eq 'SystemAssigned') { Write-Output ('System Assigned Identeity exists, add the User Assigned Identity.') Update-AzVM -ResourceGroupName $AzureResourceGroupName -VM $vm -IdentityType "SystemAssignedUserAssigned" -IdentityId $umidentity.Id } ElseIf ($vm.Identity.Type -eq 'SystemAssignedUserAssigned') { Write-Output ("System Assigned Identeity and User Assigned Identity exists, add an additional User Assigned Identity.") # Get the existing User Assigned Identities [array]$umidentities = $vm.Identity.UserAssignedIdentities.Keys if ($umidentities -notcontains $umidentity.Id) { # Add the User Assigned Identity to the existing User Assigned Identities $umidentities += $umidentity.Id Update-AzVM -ResourceGroupName $AzureResourceGroupName -VM $vm -IdentityType "SystemAssignedUserAssigned" -IdentityId $umidentities } } ElseIf ($vm.Identity.Type -eq 'UserAssigned') { Write-Output ("User Assigned Identity exists, add an additional User Assigned Identity.") # Get the existing User Assigned Identities [array]$umidentities = $vm.Identity.UserAssignedIdentities.Keys if ($umidentities -notcontains $umidentity.Id) { # Add the User Assigned Identity to the existing User Assigned Identities $umidentities += $umidentity.Id Update-AzVM -ResourceGroupName $AzureResourceGroupName -VM $vm -IdentityType "UserAssigned" -IdentityId $umidentities } } Else { Write-Output ("System Assigned Identeity doesn't exists, add only the User Assigned Identity.") Update-AzVM -ResourceGroupName $AzureResourceGroupName -VM $vm -IdentityType "UserAssigned" -IdentityId $umidentity.Id } } catch { $ErrorActionPreference = 'Continue' Write-Output "Encountered error. $_" Throw $_ } |
What the script for adding the user-assigned managed identity does not yet support is the possibility of assigning several user-assigned managed identities to a VM at the same time. However, as this script is executed when the VM is created, it makes no sense to pass a variable as a parameter. You would have to create copies of the script for different identities.
The script is now usually added as a step during VM deployment after the VM has been created:
Practical example
Now that we have created the storage account and created, authorized and assigned the user-assigned managed identity, we can download scripts from the container and execute them on the VM. To do this, we save two scripts in the deplyoment container. These scripts are usually used during the creation of the image.
I use the first script to save some information in the operating system so that I know later when the image was created:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$runingScriptName = 'bes_TattooingReferenceImage_1.0' # Default Parameters Write-Output ("Start " + $runingScriptName) Write-Output ("Tattooing Windows Reference Image") $RefImageBuild = Get-Date -Format "yyMM" $Name = "RefImageBuild" $Value = $RefImageBuild Write-Output ("Create Variable: $Name = $Value") [System.Environment]::SetEnvironmentVariable($Name, $Value, [System.EnvironmentVariableTarget]::Machine) $DateOfCreation = Get-Date -Format "MM/dd/yyyy HH:mm K" $Name = "DateOfCreation" $Value = $DateOfCreation Write-Output ("Create Variable: $Name = $Value") [System.Environment]::SetEnvironmentVariable($Name, $Value, [System.EnvironmentVariableTarget]::Machine) Write-Output ("Stop " + $runingScriptName) |
With the second script, we deactivate the option to only shut down a VM from the start menu. This is to prevent the VM from being accidentally switched off but not deallocated.
1 2 3 4 5 6 7 8 9 10 11 |
$runingScriptName = 'bes_DisabelShutdown_1.0' # Default Parameters Write-Output ("Start " + $runingScriptName) Write-Output ("Disable Shutdown in start menu") # Disable Shutdown in start menu $ShutdownPath = 'HKLM:\SOFTWARE\Microsoft\PolicyManager\default\Start\HideShutDown' New-ItemProperty -Path $ShutdownPath -Name 'value' -Value 3 -PropertyType DWord -Force Write-Output ("Stop " + $runingScriptName) |
Now that the scripts have been created on the storage account, you can download and execute them with the following scripted action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
#name: Install Basic Packages via Blob #description: Deploy Basic packages to the Desktop Image via Blob. #execution mode: Individual with restart #tags: beckmann.ch <# Notes: Use this script to prepare a Desktop Image and install Basic Packages via Blob. #> $ErrorActionPreference = 'Stop' $StorageAccountVariable = 'DeployStorageAccount' $ManagedIdentityVariable = 'DeployIdentity' $StorageAccount = $SecureVars.$StorageAccountVariable | ConvertFrom-Json $ManagedIdentity = $SecureVars.$ManagedIdentityVariable | ConvertFrom-Json $storageAccountName = $StorageAccount.name $resourceGroupName = $StorageAccount.resourceGroup $containerName = $StorageAccount.container $subscriptionId = $StorageAccount.subscriptionid $DownloadDirectory = 'C:\Windows\Temp' $Scripts = [System.Collections.ArrayList]@() $null = $Scripts.Add(@{Script = ('bes_TattooingReferenceImage_1.0.ps1'); Arguments = '' }) $null = $Scripts.Add(@{Script = ('bes_DisabelShutdown_1.0.ps1'); Arguments = '' }) $ZipFiles = [System.Collections.ArrayList]@() #$null = $ZipFiles.Add(@{Name = 'bes_CreateSysDir_1.0'; Script = 'Deploy-Application.exe'; Arguments = '' }) function Get-BesSasToken { [CmdletBinding()] param ( [Parameter( Mandatory = $true, HelpMessage = "The Subscription Id of the subscription on which the storage account is located.", ValueFromPipeline = $true )] [string] $SubscriptionId, [Parameter( Mandatory = $true, HelpMessage = "The name of the resource group in which the storage account resides.", ValueFromPipeline = $true )] [string] $ResourceGroupName, [Parameter( Mandatory = $true, HelpMessage = "The name of the storage account.", ValueFromPipeline = $true )] [string] $StorageAccountName, [Parameter( Mandatory = $true, HelpMessage = "Name of the blob container.", ValueFromPipeline = $true )] [string] $ContainerName, [Parameter( Mandatory = $false, HelpMessage = "The Identity to use to acces the storage account.", ValueFromPipeline = $true )] [PScustomObject] $Identity, [Parameter( Mandatory = $false, HelpMessage = "How long the token should be valid, in minutes.", ValueFromPipeline = $true )] [int] $TokenLifeTime = 60 ) begin { $date = Get-Date $actDate = $date.ToUniversalTime() $expiringDate = $actDate.AddMinutes($TokenLifeTime ) $expiringDate = (Get-Date $expiringDate -Format 'yyyy-MM-ddTHH:mm:ssZ') $api = 'http://169.254.169.254/metadata/identity/oauth2/token' $apiVersion = '2018-02-01' $resource = 'https://management.azure.com/' $webUri = "$api`?api-version=$apiVersion&resource=$resource" if ($Identity) { if ($Identity.client_id) { $webUri = $webUri + '&client_id=' + $Identity.client_id } elseif ($Identity.object_id) { $webUri = $webUri + '&object_id=' + $Identity.object_id } } } process { $response = Invoke-WebRequest -Uri $webUri -Method GET -Headers @{ Metadata = "true" } -UseBasicParsing $content = $response.Content | ConvertFrom-Json $armToken = $content.access_token # Convert the parameters to JSON, then call the storage listServiceSas endpoint to create the SAS credential: $params = @{canonicalizedResource = "/blob/$StorageAccountName/$ContainerName"; signedResource = "c"; signedPermission = "r"; signedProtocol = "https"; signedExpiry = "$expiringDate" } $jsonParams = $params | ConvertTo-Json $sasResponse = Invoke-WebRequest ` -Uri "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Storage/storageAccounts/$StorageAccountName/listServiceSas/?api-version=2017-06-01" ` -Method POST ` -Body $jsonParams ` -Headers @{Authorization = "Bearer $armToken" } ` -UseBasicParsing # Extract the SAS credential from the response: $sasContent = $sasResponse.Content | ConvertFrom-Json $sasCred = $sasContent.serviceSasToken } end { return $sasCred } } Function Start-Unzip { param([string]$zipfile, [string]$outpath) $Null = Add-Type -AssemblyName System.IO.Compression.FileSystem $Null = [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath) } $sasCred = Get-BesSasToken -SubscriptionId $subscriptionId -ResourceGroupName $resourceGroupName -StorageAccountName $storageAccountName -ContainerName $containerName -Identity $ManagedIdentity -TokenLifeTime 60 $ctx = New-AzStorageContext -StorageAccountName $storageAccountName -SasToken $sasCred ForEach ($Script in $Scripts) { $ScriptName = $Script.Script $ScriptArguemts = $Script.Arguments Write-Output ("Download Script: " + $ScriptName) Get-AzStorageBlobContent -Blob $ScriptName -Container $containerName -Destination "$DownloadDirectory\$ScriptName" -Context $ctx -Force -ErrorAction SilentlyContinue Write-Output ("Execute Script: " + "$DownloadDirectory\$ScriptName") & "$DownloadDirectory\$ScriptName" @ScriptArguemts } ForEach ($ZipFile in $ZipFiles) { $ScriptArchiv = $ZipFile.Name $ScriptName = $ZipFile.Script $ScriptArguemts = $ZipFile.Arguments $FolderName = $ScriptArchiv $File = $ScriptArchiv + '.zip' Write-Output ("Download File: " + $File) Get-AzStorageBlobContent -Blob $File -Container $containerName -Destination "$InstallDirectory\$File" -Context $ctx -Force -ErrorAction SilentlyContinue Start-Unzip "$InstallDirectory\$File" "$InstallDirectory\$FolderName" Write-Output ("Execute Script: " + "$InstallDirectory\$FolderName\$ScriptName") & "$InstallDirectory\$FolderName\$ScriptName" @ScriptArguemts } |
Conclusion
In the meantime, Nerdio offers the possibility to install or execute packages or scripts in other ways. However, I created this way years ago, which still works, and I continue to set it up to have more flexibility.
The Deployment Storage Account in particular has already helped me to temporarily store PowerShell modules and then install them without Internet access. But that would be worth another blog post.
I hope the one or other script will also help you to solve one or more challenges in your deployment.