Für die Bereitstellung von Session Hosts kann es Sinnvoll sein, Dateien von einem Storage Account zu kopieren die während der Bereitstellung oder dem Betrieb benötigt werden. Diese Dateien sollen aber nicht öffentlich verfügbar sein, und ein Storage Access Key soll auch nicht verwendet werden.
Damit vom Betriebssystem aus eine Datei heruntergeladen werden kann, ohne Storage Access Key, kann mein eine kurzfristig gültige, Shared Access Signature (SAS) erstellen. Damit lassen sich die Dateien dann von einem Container herunterladen. Dies lässt sich mit einer User-Assigned Managed Identity bewerkstelligen.
Um das realisieren zu können, brauchen wir als erstes eine User-Assigned Managed Identity und ein Stroage Account. Die Informationen werden innerhalb Nerdio in Variablen gespeichert. Um nun die Identity an einem Session Host hinzuzufügen verwenden wir eine Scripted Action.
Sobald die User-Assigned Managed Identity und der Storage Account erstellt sind, die User-Assigned Managed Identity berechtigt ist, können wir mit der Scripted Action die User-Assigned Managed Identity einer VM zuweisen. Mit einer weiteren Scripted Action können wir nun die Datein vom Storage Account herunterladen und ausführen.
Voraussetzungen
Als generelle Voraussetzung gillt, dass auf der VM PowerShell aktuell ist und folgende Module installiert sind:
- az.accounts
- az.resources
- az.storage
- az.keyvault
Für alles andere wird eine Nerdio Instanz und eine Resource Group benötigt, wo der Storage Account und die User-Assigned Managed Identity gespeichert werden sollen. Diese sollte ebenfalls in Nerdio hinzugefügt werden, damit die Berechtigungen Stimmen.
Storage Account erstellen
Ein Storage Account wird ganz klassisch erstellt. Dabei können unterschiedliche Konfigurationen vorgenommen werden. Für einen Storage Account für ein Deployiment reicht in der Regel einen Standard Account mit der Verfügbarkeit ZRS oder GRS aus, welcher über einen Private Endpoint verfügen kann. Nachfolgend eine Scripted Action, die dies automatisiert:
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" |
Beim ausführen werden dann die Parameter konfiguriert:

Der Output kann verwendet werden, um die Variable in Nerdio anzulegen:

User-Assigned Managed Identity erstellen
Eine User-Assigned Managed Identity ist ganz einfach erstellt. Wenn diese über das Azure Portal erstellt werden soll, kann dies wie folgt aussehen:

Später werden wir die Informationen aus den Eigenschaften benötigen, um die Variable in Nerdio anzulegen

Alternativ kann folgende Scripted Action verwendet werden:
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" |
Beim ausführen werden dann die Parameter konfiguriert:

Der Output kann verwendet werden, um die Variable in Nerdio anzulegen:

Variablen erstellen
Wir erstellen nun zwei Secure Variablen in Nerdio, welche alle relevanten Informationen zum Storage Account und zu der User-Assigned Managed Identity beinhaltet.
DeployStorageAccount
Der Storage Account wird fürs Deployment verwendet, weshalb ich die Variable DeployStorageAccount nenne:

Die Variable binhaltet ein JSON formatierten String, mit allen relevanten Informationen:
1 2 3 4 5 6 |
{ "name": "stbestavdssprdwedeploy01", "resourceGroup": "rg-deployment-avdss-prd-we-01", "subscriptionid": "", "container": "deployment" } |
DeploymentIdentity
Da es um die User Assigened Managed Identity für das Deplyoment handelt, heist diese DeploymentIdentity:

Die Variable binhaltet ein JSON formatierten String, mit allen relevanten Informationen:
1 2 3 4 5 6 7 |
{ "name": "uami-nerdio-scripted-actions", "client_id": "", "object_id": "", "subscriptionid": "", "resourcegroup": "rg-deployment-avdss-prd-we-01" } |
Um später die User Assigned Managed Idnetity nutzen zu können, speichern wir nicht nur der Name, sondern alle relevanten Informationen, um diese mit dem CmdLet Get-AzUserAssignedIdentity zu finden.
User-Assigned Managed Identity auf dem Storage Account berechtigen
Nach dem nun der Storage Account und die User-Assigned Managed Identity erstellt wurde, muss die Identity noch auf dem Storage Account berechtigt werden.

Auch dies kann wieder mit einer Scripted Action konfiguriert werden. Voraussetzung ist dazu, dass die beiden Variablen konfiguriert sind.
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 $_ } |
Beim ausführen werden dann die Parameter konfiguriert:
Scripted Action um User-Assigned Managed Identity einer VM hinzuzufügen
Mit dem folgenden Script wird die User-Assigned Managed Identity der VM hinzugefügt. Dabei wird berücksichtigt, ob bereits schon eine System-Assigned Managed Identity oder User-Assigned Managed Identity vorhanden ist.
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 $_ } |
Was das Script für das Hinzufügen der User-Assigned Managed Identity noch nicht unterstützt, ist die Möglichkeit gleichzeitig mehrere User Assigned Managed Identites an einer VM zuzuordnen. Da dieses Script aber beim Erstellen der VM ausgeführt wird, macht das übergeben einer Variable als Parameter keinen Sinn. Mann müsste für unterschiedliche Identities kopien des Scriptes anlegen.
Das Script wird nun in der Regel beim VM Deployment als Step nach dem erstellend er VM hinzugefügt:

Praktisches Beispiel
Nach dem wir nun den Storage Account erstellt, die User-Assigned Managed Identity erstellt, berechtigt und zugewiesen haben, können wir nun Scripte vom Container herunterladen und auf der VM ausführen. Dazu speichern wir zwei Scripte im Container deplyoment. Diese Scripte werden in der Regel während dem erstellen des Images genutzt.
Das erste Script nutze ich, um ein par Informationen in das Betriebssystem zu speichern, um später noch zu wissen, wann das Image erstellt wurde:
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) |
Mit dem zweiten Script deaktivieren wir die Möglichkeit, eine VM aus dem Startmenü nur Herunterzufahren. Dies soll verhindern, dass aus Versehen die VM ausgeschalten, aber nicht Dealloziert werden.
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) |
Da nun die Scripte auf dem Storage Account erstellt sind, kann man mit der folgenden Scripted Action diese herunterladen und ausführen:
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 } |
Abschluss
Nerdio bietet in der Zwischenzeit Möglichkeiten Pakete oder Scripte auch über andere Wege zu installieren oder auszuführen. Jedoch habe ich mir diesen Weg vor Jahren erstellt, welcher immer noch funktioniert, und ich mir auch weiterhin einrichte, um mehr Flexibilität zu haben.
Gerade der Deployment Storage Account hat mir schon geholfen, PowerShell Module zwischenzuspeichern, und dann ohne Internet Access installieren zu können. Das wäre aber ein weiterer Blog Post wert.
Ich hoffe das eine oder andere Script hilft dir auch, ein oder mehrere Herausforderungen in deinem Deployment zu lösen.