Setting up shared image gallery cross-tenant using the Azure CLI

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.

Summary

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.

Leave a Reply