55
66<#
77 . SYNOPSIS
8- Contains 3 functions.
8+ Contains 5 functions.
99 Add-VMImage: Uploads a VM Image to your Azure Stack and creates a Marketplace item for it.
1010 Remove-VMImage: Removes an existing VM Image from your Azure Stack. Does not delete any
1111 maketplace items created by Add-VMImage.
1212 New-Server2016VMImage: Creates and Uploads a new Server 2016 Core and / or Full Image and
1313 creates a Marketplace item for it.
14+ Add-VMExtension: Uploads a VM extension to your Azure Stack.
15+ Remove-VMExtension: Removes an existing VM extension from your Azure Stack.
1416#>
1517
1618Function Add-VMImage {
@@ -660,5 +662,185 @@ Function CreateGalleyItem{
660662 Remove-Item " $workdir \MarketplaceItem.zip"
661663 $azpkg = ' {0}\{1}' -f $workdir , $galleryItemName
662664 return Get-Item - LiteralPath $azpkg
665+ }
666+
667+ Function Add-VMExtension {
668+
669+ [CmdletBinding (DefaultParameterSetName = ' VMExtensionFromLocal' )]
670+ Param (
671+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtensionFromLocal' )]
672+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtesionFromAzure' )]
673+ [ValidatePattern (“ [a-zA-Z0-9-]{3,}” )]
674+ [String ] $publisher ,
675+
676+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtensionFromLocal' )]
677+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtesionFromAzure' )]
678+ [ValidatePattern (“ \d+\.\d+\.\d+” )]
679+ [String ] $version ,
680+
681+ [Parameter (ParameterSetName = ' VMExtensionFromLocal' )]
682+ [Parameter (ParameterSetName = ' VMExtesionFromAzure' )]
683+ [String ] $type ,
684+
685+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtensionFromLocal' )]
686+ [ValidateNotNullorEmpty ()]
687+ [String ] $extensionLocalPath ,
688+
689+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtesionFromAzure' )]
690+ [ValidateNotNullorEmpty ()]
691+ [String ] $extensionBlobURI ,
692+
693+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtensionFromLocal' )]
694+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtesionFromAzure' )]
695+ [ValidateSet (' Windows' , ' Linux' )]
696+ [String ] $osType ,
697+
698+ [Parameter (ParameterSetName = ' VMExtensionFromLocal' )]
699+ [Parameter (ParameterSetName = ' VMExtesionFromAzure' )]
700+ [String ] $vmScaleSetEnabled = " false" ,
701+
702+ [Parameter (ParameterSetName = ' VMExtensionFromLocal' )]
703+ [Parameter (ParameterSetName = ' VMExtesionFromAzure' )]
704+ [String ] $supportMultipleExtensions = " false" ,
705+
706+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtensionFromLocal' )]
707+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtesionFromAzure' )]
708+ [ValidateNotNullorEmpty ()]
709+ [String ] $tenantID ,
710+
711+ [Parameter (ParameterSetName = ' VMExtensionFromLocal' )]
712+ [Parameter (ParameterSetName = ' VMExtesionFromAzure' )]
713+ [String ] $location = ' local' ,
714+
715+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtensionFromLocal' )]
716+ [Parameter (Mandatory = $true , ParameterSetName = ' VMExtesionFromAzure' )]
717+ [System.Management.Automation.PSCredential ] $azureStackCredentials ,
718+
719+ [Parameter (ParameterSetName = ' VMExtensionFromLocal' )]
720+ [Parameter (ParameterSetName = ' VMExtesionFromAzure' )]
721+ [string ] $ArmEndpoint = ' https://adminmanagement.local.azurestack.external'
722+ )
723+
724+ $resourceGroupName = " addvmextresourcegroup"
725+ $storageAccountName = " addvmextstorageaccount"
726+ $containerName = " addvmextensioncontainer"
727+
728+ $subscription , $headers = (Get-AzureStackAdminSubTokenHeader - TenantId $tenantId - AzureStackCredentials $azureStackCredentials - ArmEndpoint $ArmEndpoint )
729+
730+ # potentially the RG was not cleaned up when exception happened in previous run. Test for exist
731+ if (-not (Get-AzureRmResourceGroup - Name $resourceGroupName - Location $location - ErrorAction SilentlyContinue)) {
732+ New-AzureRmResourceGroup - Name $resourceGroupName - Location $location
663733 }
664734
735+ # same for storage
736+ $storageAccount = Get-AzureRmStorageAccount - Name $storageAccountName - ResourceGroupName $resourceGroupName - ErrorAction SilentlyContinue
737+ if (-not ($storageAccount )) {
738+ $storageAccount = New-AzureRmStorageAccount - Name $storageAccountName - Location $location - ResourceGroupName $resourceGroupName - Type Standard_LRS
739+ }
740+
741+ Set-AzureRmCurrentStorageAccount - StorageAccountName $storageAccountName - ResourceGroupName $resourceGroupName
742+
743+ # same for container
744+ $container = Get-AzureStorageContainer - Name $containerName - ErrorAction SilentlyContinue
745+ if (-not ($container )) {
746+ $container = New-AzureStorageContainer - Name $containerName - Permission Blob
747+ }
748+
749+ if ($pscmdlet.ParameterSetName -eq " VMExtensionFromLocal" )
750+ {
751+ $storageAccount.PrimaryEndpoints.Blob
752+ $script :extensionName = Split-Path $extensionLocalPath - Leaf
753+ $script :extensionBlobURIFromLocal = ' {0}{1}/{2}' -f $storageAccount.PrimaryEndpoints.Blob.AbsoluteUri , $containerName , $extensionName
754+ Set-AzureStorageBlobContent - File $extensionLocalPath - Container $containerName - Blob $extensionName
755+ }
756+
757+ $ArmEndpoint = $ArmEndpoint.TrimEnd (" /" )
758+ $uri = $armEndpoint + ' /subscriptions/' + $subscription + ' /providers/Microsoft.Compute.Admin/locations/' + $location + ' /artifactTypes/VMExtension/publishers/' + $publisher
759+ $uri = $uri + ' /types/' + $type + ' /versions/' + $version + ' ?api-version=2015-12-01-preview'
760+
761+ # building request body JSON
762+ if ($pscmdlet.ParameterSetName -eq " VMExtensionFromLocal" ) {
763+ $sourceBlobJSON = ' "SourceBlob" : {"Uri" :"' + $extensionBlobURIFromLocal + ' "}'
764+ }
765+ else {
766+ $sourceBlobJSON = ' "SourceBlob" : {"Uri" :"' + $extensionBlobURI + ' "}'
767+ }
768+
769+ $osTypeJSON = ' "VmOsType" : "' + $osType + ' "'
770+ $ComputeRoleJSON = ' "ComputeRole" : "N/A"'
771+ $VMScaleSetEnabledJSON = ' "VMScaleSetEnabled" : "' + $vmScaleSetEnabled + ' "'
772+ $SupportMultipleExtensionsJSON = ' "SupportMultipleExtensions" : "' + $supportMultipleExtensions + ' "'
773+ $IsSystemExtensionJSON = ' "IsSystemExtension" : "false"'
774+
775+ $propertyBody = $sourceBlobJSON + " ," + $osTypeJSON + ' ,' + $ComputeRoleJSON + " ," + $VMScaleSetEnabledJSON + " ," + $SupportMultipleExtensionsJSON + " ," + $IsSystemExtensionJSON
776+
777+ # building ARMResource
778+
779+ $RequestBody = ' {"Properties":{' + $propertyBody + ' }}'
780+
781+ Invoke-RestMethod - Method PUT - Uri $uri - Body $RequestBody - ContentType ' application/json' - Headers $Headers
782+
783+ $extensionHandler = Invoke-RestMethod - Method GET - Uri $uri - ContentType ' application/json' - Headers $Headers
784+
785+ while ($extensionHandler.Properties.ProvisioningState -ne ' Succeeded' )
786+ {
787+ if ($extensionHandler.Properties.ProvisioningState -eq ' Failed' )
788+ {
789+ Write-Error - Message " VM extension download failed." - ErrorAction Stop
790+ }
791+
792+ if ($extensionHandler.Properties.ProvisioningState -eq ' Canceled' )
793+ {
794+ Write-Error - Message " VM extension download was canceled." - ErrorAction Stop
795+ }
796+
797+ Write-Host " Downloading" ;
798+ Start-Sleep - Seconds 4
799+ $extensionHandler = Invoke-RestMethod - Method GET - Uri $uri - ContentType ' application/json' - Headers $Headers
800+ }
801+
802+ Remove-AzureStorageContainer –Name $containerName - Force
803+ Remove-AzureRmStorageAccount - ResourceGroupName $resourceGroupName - AccountName $storageAccountName
804+ Remove-AzureRmResourceGroup - Name $resourceGroupName - Force
805+ }
806+
807+ Function Remove-VMExtension {
808+ Param (
809+ [ValidatePattern (“ [a-zA-Z0-9-]{3,}” )]
810+ [String ] $publisher ,
811+
812+ [Parameter (Mandatory = $true )]
813+ [ValidatePattern (“ \d+\.\d+\.\d+” )]
814+ [String ] $version ,
815+
816+ [String ] $type = " CustomScriptExtension" ,
817+
818+ [Parameter (Mandatory = $true )]
819+ [ValidateSet (' Windows' , ' Linux' )]
820+ [String ] $osType ,
821+
822+ [Parameter (Mandatory = $true )]
823+ [ValidateNotNullorEmpty ()]
824+ [String ] $tenantID ,
825+
826+ [String ] $location = ' local' ,
827+
828+ [Parameter (Mandatory = $true )]
829+ [System.Management.Automation.PSCredential ] $azureStackCredentials ,
830+
831+ [string ] $ArmEndpoint = ' https://adminmanagement.local.azurestack.external'
832+ )
833+
834+ $subscription , $headers = (Get-AzureStackAdminSubTokenHeader - TenantId $tenantId - AzureStackCredentials $azureStackCredentials - ArmEndpoint $ArmEndpoint )
835+
836+ $ArmEndpoint = $ArmEndpoint.TrimEnd (" /" )
837+ $uri = $armEndpoint + ' /subscriptions/' + $subscription + ' /providers/Microsoft.Compute.Admin/locations/' + $location + ' /artifactTypes/VMExtension/publishers/' + $publisher
838+ $uri = $uri + ' /types/' + $type + ' /versions/' + $version + ' ?api-version=2015-12-01-preview'
839+
840+ try {
841+ Invoke-RestMethod - Method DELETE - Uri $uri - ContentType ' application/json' - Headers $headers
842+ }
843+ catch {
844+ Write-Error - Message (' Deletion of VM extension with publisher "{0}" failed with Error:"{1}.' -f $publisher , $Error ) - ErrorAction Stop
845+ }
846+ }
0 commit comments