As a follow-on to my previous post that covered Creating a Cloud Service (Extended Support) Deployment, this post covers doing the same thing but using a PowerShell script to automate it.
To follow along, first, create a Cloud Service project that you can deploy to Azure. You can follow along until just before the heading Deploying the new Cloud Service (extended support) resource to Azure using the portal in my other post to see how to do this, and then carry on here.
You will need to ensure that you have installed Powershell 7+ and the Az PowerShell Module before the script can be run. Restart all command prompt windows after you have installed these dependencies. Make sure to use the PowerShell 7 terminal and not the Windows PowerShell or Command Prompt terminal windows.
You will also have to set your PowerShell session to trust running scripts locally by setting the Execution Policy to RemoteSigned.
Set-ExecutionPolicy RemoteSigned
As usual, all the code can be found on my GitHub profile.
The script
Firstly, create a new PowerShell file called DeployCloudService.ps1 in the same folder as the solution file. All the PowerShell code will be added to this file. I also recommend using Visual Studio Code with the PowerShell extension installed while developing this – it makes things much easier.
Create a variable to set the location where you want your resources to be deployed. This makes it easier to change in one place later. You can find a list of all Azure locations here.
# Set the location
$location = "southafricanorth";
Next, the script needs to sign into Azure and set the correct tenant to use. You can find your tenant by following these instructions. When running the script, you will need to sign into your Azure account with a web browser for the script to continue.
# Sign In To Azure
Connect-AzAccount
# Set Context
Set-AzContext -Tenant "YOUR TENANT ID"
Once signed in, the script needs to start provisioning resources. First, begin with the Resource Group, Storage Account and Virtual Network. Note that the storage account’s name must be lowercase, and the Virtual Network requires the subnet to first be defined before it can be created.
# Create the Resource Group
$resourceGroup = New-AzResourceGroup -Name "MzansiBytes" -Location $location
# Create the Storage Account
$storageAccount = New-AzStorageAccount -ResourceGroupName $resourceGroup.ResourceGroupName -AccountName "mzansibytes" -Location $location -SkuName "Standard_LRS" -Kind "StorageV2" -AccessTier "Hot" -AllowBlobPublicAccess $true
# Create the Virtual Network
$subnet = New-AzVirtualNetworkSubnetConfig -Name "MzansiBytes" -AddressPrefix "10.1.0.0/24"
New-AzVirtualNetwork -Name "MzansiBytes" -ResourceGroupName $resourceGroup.ResourceGroupName -Location $location -AddressPrefix "10.1.0.0/16" -Subnet $subnet
When creating the Public IP Address, make sure that the DomainNameLabel is lowercase.
New-AzPublicIpAddress -ResourceGroupName $resourceGroup.ResourceGroupName -Name "MzansiBytes" -AllocationMethod Static -DomainNameLabel "mzansibytes" -Location $location -Sku "Basic"
This IP address also needs to be defined in the ServiceConfiguration.Cloud.cscfg file inside of <AddressAssignments></AddressAssignments>.
<ReservedIPs>
<ReservedIP name="mzansibytes-staging" />
</ReservedIPs>
Cloud Services (Extended Support) requires a Key Vault, and the Powershell tooling requires there to be a certificate in the Key Vault, even if it is not used. If there is no certificate, you will get the following error when using the New-CloudService command.
New-AzCloudService.ps1:330
Line |
330 | Az.CloudService\New-AzCloudService @PSBoundParameters
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| The List vaultCertificates must not be empty.
To get around this, create a dummy certificate, put this in the same folder as the script file, and update the .cscfg file with the certificate details.
Use the following code to generate this dummy certificate (Run this directly, do not add this to the script file).
$certificate = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine -DnsName "MzansiBytes"
$password = ConvertTo-SecureString -String "Password123!" -Force -AsPlainText
Export-PfxCertificate -Cert $certificate -FilePath "MzansiBytes.pfx" -Password $password
Then use $certificate.Thumbprint to get the thumbprint.
In my case, the .cscfg will have the following added inside the <Role> element.
<Certificates>
<Certificate name="MzansiBytes" thumbprint="F813AD9576BA40A4862D3CAEF1E4197B0FB697B4" thumbprintAlgorithm="sha1" />
</Certificates>
You will also need to add this under the WebRole and WorkerRole elements in the .csdef files.
<Certificates>
<Certificate name="MzansiBytes" storeLocation="LocalMachine" storeName="My" permissionLevel="limitedOrElevated" />
</Certificates>
Add the following code to the script file to create the Key Vault and upload the certificate.
# Create the Key Vault
$keyVault = New-AzKeyVault -ResourceGroupName $resourceGroup.resourceGroupName -VaultName "MzansiBytes" -Location $location -enabledForDeployment -enabledForTemplateDeployment
$password = ConvertTo-SecureString -String "Password123!" -Force -AsPlainText
Import-AzKeyVaultCertificate -VaultName $keyVault.VaultName -Name "MzansiBytes" -FilePath "$PSScriptRoot\MzansiBytes.pfx" -Password $password
Next, the script needs to call MSBuild.exe to package the project. Change the MSBuild.exe location if you have a different version of Visual Studio installed, or if it is not installed in the default location.
# Package the project
$msBuildFilePath = "C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe
.$msBuildFilePath "$PSScriptRoot\MzansiBytes\MzansiBytes.ccproj" `
/p:Configuration=Release `
/p:PublishDir="$PSScriptRoot\Temp\CloudPackage" `
/p:TargetProfile="Cloud" `
/p:Platform=AnyCpu `
/t:Publish
The package needs to be uploaded to Azure and the Cloud Service Resource must be created. NOTE: .csdef file path is in the solution, not the CloudPackage folder.
# Create the Cloud Service Resource
New-AzCloudService `
-Name "MzansiBytes-1" `
-ResourceGroupName $resourceGroup.ResourceGroupName
-Location $location `
-DefinitionFile "$PSScriptRoot\MzansiBytes\ServiceDefinition.csdef" `
-ConfigurationFile "$PSScriptRoot\Temp\CloudPackage\ServiceConfiguration.Cloud.cscfg" `
-PackageFile "$PSScriptRoot\Temp\CloudPackage\MzansiBytes.cspkg" `
-KeyVaultName $keyVault.VaultName `
-StorageAccount $storageAccount.StorageAccountName
Finally, clean the Temp folder.
# Cleanup
Remove-Item "$PSScriptRoot\Temp" -Recurse -Force
That’s it. Your new Cloud Service should be up and running.