In my previous posts I wrote about automating Windows and Linux VM Image creation using Powershell and HashiCorp Packer :
- Create Azure Windows VM Image with Packer and Powershell
- Create Azure Linux VM Image with Packer and Powershell
OS image helps you quickly deploy one or many Azure VMs with same configuration and baseline defined in the image. If you have all your workloads in a single region, it is fine to just keep one copy of the image and use it from there. However, if you have workloads distributed in different Geographic or Azure regions, you need copy or create the same image to those regions. Secondly, if you have a Support model where different IT units supporting different regions, it is very important that everyone using same set of images. Otherwise you will have VMs with inconsistent configuration and could cause Compliance issues.
To solve this problem, Microsoft has introduced a feature called Shared Image Gallery. This feature is quite similar to Azure Marketplace images, but it is secured within your Organization and Tenants. As per Microsoft Documentation, here are some benefits of using shared image gallery :
- Managed global replication of images.
- Versioning and grouping of images for easier management.
- Highly available images with Zone Redundant Storage (ZRS) accounts in regions that support Availability Zones. ZRS offers better resilience against zonal failures.
- Sharing across subscriptions, and even between Active Directory (AD) tenants, using RBAC.
- Scaling your deployments with image replicas in each region.
In this post I will share a script to automate this image gallery sharing process with image definition and versioning. We will also, set up image replication to multiple Azure regions based on workload.
Before we start, I am assuming you have followed Azure managed image creation process posted in my previous blogs and you have created images to share. I will be using an existing managed image to set up the versioning and sharing. This post also assumes, you have Powershell Az Modules already installed and imported in the Powershell session and logged in to Azure using Login-AzAccount command. With that, let’s start!
Let’s first setup the context to the correct subscription you are working on with following script block:
# Setting Azure Context
$context = Set-AzContext -SubscriptionId $subscriptionId
if([String]::IsNullOrEmpty($context)){
Write-Host "Error: Azure Context not set.Check Subscription Id and your access!" -ForegroundColor Yellow
Exit
}
Next, I will get the managed image details I have created previously. I will add this image to Gallery with my own definition and versioning system and will replicate it to other region:
$managedImageInfo = Get-AzImage -ImageName $managedImageName -ResourceGroupName $managedImageRGName
if([String]::IsNullOrEmpty($managedImageInfo)){
Write-Host "Error: Wrong Image Name or Resource Group Provided. Please try again!" -ForegroundColor Yellow
Exit
}
Now, the following step is not so important. If you pass the correct region display name. However, I kept it as I was using short name for Azure region and the Hash Table we build to set the image replication, require display name of azure regions. Example, I am passing “northcentralus” instead of “North Central US” in my script and checking if region name is valid and get the display name from the short name. After that, setting up the Hash table and array of hash objects for replication. Feel free to change region and replica count based on your requirements.
# Create an array of Target Region to replicate the Managed Image in the Image Gallery
$sourceLocation = Get-AzLocation | Where-Object{$_.Location -eq $location} | Select-Object -ExpandProperty DisplayName
$targetLocationName = Get-AzLocation | Where-Object{$_.Location -eq $targetLocation} | Select-Object -ExpandProperty DisplayName
if([String]::IsNullOrEmpty($targetLocationName)){
Write-Host "Error: Wrong Replica location name provided. Provide a valid Azure Region short name!" -ForegroundColor Yellow
Exit
}
$sourceRegion = @{Name="$($sourceLocation)";ReplicaCount=1} # You can change the Replica Count based on your requirements
$targetRegion = @{Name="$($targetLocationName)";ReplicaCount=1} # You can change the Replica Count based on your requirements
$targetRegions = @($sourceRegion,$targetRegion)
Here, I will check if my resource group to store shared image gallery and other components already exists. If not, will create one(for first run).
if(!(Get-AzResourceGroup -Name $azImageGalleryRGName -ErrorAction SilentlyContinue)){
Write-Host "Information: Creating new Resource Group"
New-AzResourceGroup -Name $azImageGalleryRGName -Location $location
}
Get-AzResourceGroup -Name $azImageGalleryRGName
Let’s create the image gallery now if it is not already exists. Provide a short description based on what you feel.
$imageGallery = Get-AzResource -ResourceType Microsoft.Compute/galleries -Name $imageGalleryName
if([String]::IsNullOrEmpty($imageGallery)){
Write-Host "Information: Creating new Image Gallery"
$imageGallery = New-AzGallery -GalleryName $imageGalleryName -ResourceGroupName $azImageGalleryRGName -Location $location -Description 'Shared Image Gallery'
}
else{
Write-Host "Information: Using existing Image Gallery - $($imageGalleryName)"
}
Next, we will create a definition for your image. Important note, Image definition has to be unique as a combination of Image Publisher, Image Offer and Sku. You need to provide those information based on the purpose of and OS etc. of your image. For example, My Image publisher could be my company name “MyDreamCompany”. Offer could be “Windows2016” and Sku could be “BackedServer”. Also note, there are some limitations on the allowed characters and length for image gallery and definition.
$imageDefName = "$($managedImagePublisher)-$($managedImageOffer)-$($managedImageSku)"
$imageGalleryDef = Get-AzResource -ResourceType Microsoft.Compute/galleries/images -Name $imageDefName
if(!([String]::IsNullOrEmpty($imageGalleryDef))){
Write-Host "Information: An Image Definition already exists. Generating a new Name."
$imageDefName = "$($managedImagePublisher)-$($managedImageOffer)-$($managedImageSku)-$(-join ((65..90) + (97..122) | Get-Random -Count 6 | ForEach-Object {[char]$_}))"
}
Write-Host "Information: Creating a new Image Definition"
$imageGalleryDef = New-AzGalleryImageDefinition -Name $imageDefName -GalleryName $imageGallery.Name -ResourceGroupName $azImageGalleryRGName -Location $location `
-OsState generalized -OsType Windows -Publisher $managedImagePublisher -Offer $managedImageOffer -Sku $managedImageSku
Finally, we will define the versioning of the image – it could be <major version>.<minor version>.<patch> etc. , example, 1.0.1. Feel free to use your own standard. Azure will start replicating image (your managed image) to provided regions. The source region is also considered as a target for this replication. Important note – This replication process takes several minutes based on number of replica and size.So, be patient while this replication happenes. You can monitor the state in the console.
Write-Host "Information: Setting up Image Version in the Image Gallery and replicating image to Target region(s) including Source region."
New-AzGalleryImageVersion -GalleryImageDefinitionName $imageGalleryDef.Name -GalleryImageVersionName $imageVersion `
-GalleryName $imageGalleryName -ResourceGroupName $azImageGalleryRGName -Location $location `
-TargetRegion $targetRegions -Source $managedImageInfo.Id.ToString() `
-PublishingProfileEndOfLifeDate (Get-Date).AddYears(3).ToString('yyyy-MM-dd') -AsJob
Write-Host "Information: Image Replication started. It may take several minutes based on number of replica and size of Image." -ForegroundColor Green
Once it completes, you should be ready with your shared image gallery. You can use it to deploy Azure VMs. You can also, set up RBAC to provide authorization to access and use gallery to designated persons only.
Here is the full script that we just went through!
Param(
[Parameter(Mandatory=$true)]
[String]$subscriptionId, # Subscription ID
[Parameter(Mandatory=$true)]
[String]$azImageGalleryRGName, # Image Gallery Resource Group Name
[Parameter(Mandatory=$true)]
[String]$imageGalleryName, # Image Gallery Name
[Parameter(Mandatory=$false)]
[String]$location = "northcentralus", # Image/Resource location. Here I am defaulting to 'northcentralus'
[Parameter(Mandatory=$true)]
[String]$managedImageName, # Existing managed Image Name
[Parameter(Mandatory=$true)]
[String]$managedImageRGName, # Resource Group storing existing managed Image
[Parameter(Mandatory=$true)]
[String]$managedImagePublisher, # Managed Image Publisher.Example, ArindamProd. It should be unique in combination with Offer and Sku
[Parameter(Mandatory=$true)]
[String]$managedImageOffer, # Managed Image Offer.Example, Windows2016. It should be unique in combination with Publisher and Sku
[Parameter(Mandatory=$true)]
[String]$managedImageSku, # Managed Image Sku.Example, FrontEnd,. It should be unique in combination with Publisher and Offer
[Parameter(Mandatory=$true)]
[String]$targetLocation, # Image Gallery replication location short name.Example, 'southcentralus'
[Parameter(Mandatory=$true)]
[String]$imageVersion # Image Version. Example, 1.0.1
)
# Setting Azure Context
$context = Set-AzContext -SubscriptionId $subscriptionId
if([String]::IsNullOrEmpty($context)){
Write-Host "Error: Azure Context not set.Check Subscription Id and your access!" -ForegroundColor Yellow
Exit
}
# Retrieve existing managed Image id to be included in the Image Gallery
$managedImageInfo = Get-AzImage -ImageName $managedImageName -ResourceGroupName $managedImageRGName
if([String]::IsNullOrEmpty($managedImageInfo)){
Write-Host "Error: Wrong Image Name or Resource Group Provided. Please try again!" -ForegroundColor Yellow
Exit
}
# Create an array of Target Regiosn to replicate the Managed Image in the Image Gallery
$sourceLocation = Get-AzLocation | Where-Object{$_.Location -eq $location} | Select-Object -ExpandProperty DisplayName
$targetLocationName = Get-AzLocation | Where-Object{$_.Location -eq $targetLocation} | Select-Object -ExpandProperty DisplayName
if([String]::IsNullOrEmpty($targetLocationName)){
Write-Host "Error: Wrong Replica location name provided. Provide a valid Azure Region short name!" -ForegroundColor Yellow
Exit
}
$sourceRegion = @{Name="$($sourceLocation)";ReplicaCount=1} # You can change the Replica Count based on your requirements
$targetRegion = @{Name="$($targetLocationName)";ReplicaCount=1} # You can change the Replica Count based on your requirements
$targetRegions = @($sourceRegion,$targetRegion)
# Create a Resource Group if it is not already there
if(!(Get-AzResourceGroup -Name $azImageGalleryRGName -ErrorAction SilentlyContinue)){
Write-Host "Information: Creating new Resource Group"
New-AzResourceGroup -Name $azImageGalleryRGName -Location $location
}
Get-AzResourceGroup -Name $azImageGalleryRGName
#Create Image Gallery if it does not exist.
$imageGallery = Get-AzResource -ResourceType Microsoft.Compute/galleries -Name $imageGalleryName
if([String]::IsNullOrEmpty($imageGallery)){
Write-Host "Information: Creating new Image Gallery"
$imageGallery = New-AzGallery -GalleryName $imageGalleryName -ResourceGroupName $azImageGalleryRGName -Location $location -Description 'Shared Image Gallery'
}
else{
Write-Host "Information: Using existing Image Gallery - $($imageGalleryName)"
}
#Define an Image definition Name and create Image Definition
$imageDefName = "$($managedImagePublisher)-$($managedImageOffer)-$($managedImageSku)"
$imageGalleryDef = Get-AzResource -ResourceType Microsoft.Compute/galleries/images -Name $imageDefName
if(!([String]::IsNullOrEmpty($imageGalleryDef))){
Write-Host "Information: An Image Definition already exists. Generating a new Name."
$imageDefName = "$($managedImagePublisher)-$($managedImageOffer)-$($managedImageSku)-$(-join ((65..90) + (97..122) | Get-Random -Count 6 | ForEach-Object {[char]$_}))"
}
Write-Host "Information: Creating a new Image Definition"
$imageGalleryDef = New-AzGalleryImageDefinition -Name $imageDefName -GalleryName $imageGallery.Name -ResourceGroupName $azImageGalleryRGName -Location $location `
-OsState generalized -OsType Windows -Publisher $managedImagePublisher -Offer $managedImageOffer -Sku $managedImageSku
# Create an Image version of my existisng Managed Image for Image Gallery
Write-Host "Information: Setting up Image Version in the Image Gallery and replicating image to Target region(s) including Source region."
New-AzGalleryImageVersion -GalleryImageDefinitionName $imageGalleryDef.Name -GalleryImageVersionName $imageVersion `
-GalleryName $imageGalleryName -ResourceGroupName $azImageGalleryRGName -Location $location `
-TargetRegion $targetRegions -Source $managedImageInfo.Id.ToString() `
-PublishingProfileEndOfLifeDate (Get-Date).AddYears(3).ToString('yyyy-MM-dd') -AsJob
Write-Host "Information: Image Replication started. It may take several minutes based on number of replica and size of Image." -ForegroundColor Green
Here are some screenshot from my last run:
Start Powershell session and run the script with required arguments as shown below
Script execution begins:
A new resource group has been created and resources including image gallery, image definition have been created so far :
Finally, the image version replication started as per below screenshot: