The Azure shared image gallery is a service in Azure that helps you manage and store images in a central location. It’s then easy to share those images with other subscriptions.
It is however not that easy to share those images with other subscriptions if those subscriptions are tied to a different AAD tenant. There’s a doc in the Microsoft documentation describing how to do this using the portal, but not how to do this using the AZ CLI.
The goal of this post is to briefly explain how you can share an image in the shared image gallery with another subscription in a different tenant.
The end-to-end script I’ll be describing here is hosted on my GitHub.
How to share an image with another tenant using the Azure CLI
In order to share an image with another tenant, you’ll need the following:
- A shared image gallery in a subscription linked to the primary AAD tenant
- An image in that shared image gallery
- A cross-tenant Azure AD application
- A service principal derived from that AAD application in both tenants.
- Those service principals will need reader access on the shared image gallery, and the necessary permissions to create VMs in the target subscription linked to the tenant.
To learn more about Azure AD applications and the link to service principals check out this earlier blog post.
Important to understand about this setup is that the VM will be created by the service principal, who in turn has access to the SIG in the source tenant and the ability to create the VM in the target tenant.
Let’s start at the beginning:
Setting up some variables
Throughout the script, I’ll be using a set of variables in bash. If you’re going to be running this yourself, make sure to change them to your values:
RGNAME="sig-cross-tenant" LOC="WESTUS2" GALLERY="nfGallery" VMNAME="sigsource" #used to create an image from IMAGENAME="sigtestimage" TENANT1="72f988bf-86f1-41af-91ab-2d7cd011db47" #MSFT TENANT2="42949aa4-09db-4624-9a32-83e2e02758c5" #MSDN TARGETSUBID="ea122fb3-39a8-411c-995c-3724e344095f"
Creating a VM and prepping it for image creation
If you have an existing image in a shared image gallery, feel free to skip these steps. You can use these steps to create a VM from which you’ll create an image later.
##### # All of this executed in primary tenant ##### echo "Creating RG and VM" # Create RG az group create -n $RGNAME -l $LOC # Create a VM to create an image from vmip=$(az vm create -n $VMNAME -g $RGNAME --admin-username nilfranadmin \ --ssh-key-values ~/.ssh/id_rsa.pub --image ubuntults --query publicIpAddress -o tsv) # Sleep 30 to make sure VM is available to run command echo "Sleeping 30 seconds to wait for VM to be fully ready" sleep 30 # Generalize VM echo "Generalizing VM" ssh -o StrictHostKeyChecking=no nilfranadmin@$vmip 'sudo waagent -deprovision+user -force' # If you're copy pasting, the SSH might kill the stream of commands. Make sure to also execute the commands below. echo "sleeping to ensure deprovision succeeded" sleep 10 az vm deallocate -g $RGNAME -n $VMNAME az vm generalize -g $RGNAME -n $VMNAME vmid=$(az vm show -g $RGNAME -n $VMNAME -o tsv --query id)
Now that you have a generalized VM, you can create the SIG and the image version:
Creating the shared image gallery and image
So now, you can create the SIG and the image. Again, if you already have an image in a SIG, feel free to skip these steps. In the final command, you’ll be creating the image based on the generalized VM:
# Create image gallery echo "Creating sig" az sig create --resource-group $RGNAME --gallery-name $GALLERY sigid=$(az sig show \ --resource-group $RGNAME \ --gallery-name $GALLERY \ --query id -o tsv) echo "Creating image in sig" az sig image-definition create \ --resource-group $RGNAME \ --gallery-name $GALLERY \ --gallery-image-definition $GALLERY \ --publisher $GALLERY \ --offer $GALLERY \ --sku $GALLERY \ --os-type Linux \ --os-state generalized echo "This next step will take a few minutes." az sig image-version create \ --resource-group $RGNAME \ --gallery-name $GALLERY \ --gallery-image-definition $GALLERY \ --gallery-image-version 1.0.0 \ --target-regions $LOC \ --replica-count 1 \ --managed-image $vmid # Get image ID from sig sigimageid=$(az sig image-version show --gallery-image-definition $GALLERY \ --gallery-image-version 1.0.1 \ --gallery-name $GALLERY \ --resource-group $RGNAME \ --query id -o tsv)
Setting up applications and service principal in primary tenant
The way SIG shared cross tenant works is using a cross-tenant Azure AD application, that has rights on the subscriptions in both tenants. In the primary tenant, you’ll create the app in AD allowing cross-tenant registrations and then create a service principal.
Finally, you’ll give that SP permissions to read from the SIG.
# Get image ID from sig sigimageid=$(az sig image-version show --gallery-image-definition $GALLERY \ --gallery-image-version 1.0.1 \ --gallery-name $GALLERY \ --resource-group $RGNAME \ --query id -o tsv) # Create app appid=$(az ad app create --display-name $GALLERY \ --available-to-other-tenants true \ --reply-urls "https://www.microsoft.com" \ --query appId -o tsv) # Create SP az ad sp create --id $appid pw=$(az ad sp credential reset \ --name $appid \ --credential-description "gallery-PW" \ --query password -o tsv) echo "Password below:" echo pw # Create role assignment az role assignment create --assignee $appid --role "Reader" --scope $sigid
Now that this has been setup, you’re ready to move to the secondary tenant to give the service principal permissions to create the VM.
Creating role assignment in secondary tenant
Now, you need to create a service principal based on the application in the secondary tenant and then assign the right role to that service principal to allow it to create VMs in the target subscription. To do this, you need login to the second tenant and execute the following:
az ad sp create --id $appid # Give this SP permissions to create VMs az role assignment create --assignee $appid --role "Contributor"
And now, you are ready to create a VM using an image in the other tenant’s SIG:
Creating the VM based on an image in another tenant’s SIG
To create the VM based on the image, you’ll need to login using the service principal in both tenants, and then create the VM in the target subscription. To ensure you’re creating it in the target subscription, you can add the –subscription option in the az cli, as shown below:
az account clear az login --service-principal -u $appid -p $pw --tenant $TENANT1 az login --service-principal -u $appid -p $pw --tenant $TENANT2 # Create RG in target az group create -n $RGNAME -l $LOC --subscription $TARGETSUBID # Create VM in target tenant az vm create \ -g $RGNAME \ -n $VMNAME \ --image $sigimageid \ --admin-username nilfranadmin \ --ssh-key-values ~/.ssh/id_rsa.pub \ --subscription $TARGETSUBID
And this will create the VM, based on an image in another AAD tenant.
This blog post explains how to use the Azure CLI rather than the Azure portal to share an image in a shared image gallery with subscriptions in another AAD tenant. You have to use service principals to get this to work, and the code in the example walks you through the process end-to-end.