{"id":1576,"date":"2021-04-02T14:11:10","date_gmt":"2021-04-02T21:11:10","guid":{"rendered":"http:\/\/blog.nillsf.com\/?p=1576"},"modified":"2021-04-02T14:11:18","modified_gmt":"2021-04-02T21:11:18","slug":"setting-up-shared-image-gallery-cross-tenant-using-the-azure-cli","status":"publish","type":"post","link":"https:\/\/nillsf.com\/index.php\/2021\/04\/02\/setting-up-shared-image-gallery-cross-tenant-using-the-azure-cli\/","title":{"rendered":"Setting up shared image gallery cross-tenant using the Azure CLI"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The Azure shared image gallery is a service in Azure that helps you manage and store images in a central location. It&#8217;s then easy to share those images with other subscriptions. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is however not that easy to share those images with other subscriptions if those subscriptions are tied to a different AAD tenant. <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/virtual-machines\/linux\/share-images-across-tenants\">There&#8217;s a doc in the Microsoft documentation<\/a> describing how to do this using the portal, but not how to do this using the AZ CLI.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The end-to-end script I&#8217;ll be describing here is hosted on <a href=\"https:\/\/github.com\/NillsF\/blog\/blob\/master\/image-gallery-cross-tenant-az-cli\/script.sh\">my GitHu<\/a>b. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to share an image with another tenant using the Azure CLI<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In order to share an image with another tenant, you&#8217;ll need the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>A shared image gallery in a subscription linked to the primary AAD tenant<\/li><li>An image in that shared image gallery<\/li><li>A cross-tenant Azure AD application<\/li><li>A service principal derived from that AAD application in both tenants.<\/li><li>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.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">To learn more about Azure AD applications and the link to service principals <a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/0\">c<\/a><a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/09\/29\/how-to-allow-users-to-create-service-principals-and-the-impact-on-managed-identity\/\">heck out this earlier blog post<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s start at the beginning:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Setting up some variables<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Throughout the script, I&#8217;ll be using a set of variables in bash. If you&#8217;re going to be running this yourself, make sure to change them to your values:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>RGNAME=\"sig-cross-tenant\"\nLOC=\"WESTUS2\"\nGALLERY=\"nfGallery\"\nVMNAME=\"sigsource\" #used to create an image from\nIMAGENAME=\"sigtestimage\"\nTENANT1=\"72f988bf-86f1-41af-91ab-2d7cd011db47\" #MSFT\nTENANT2=\"42949aa4-09db-4624-9a32-83e2e02758c5\" #MSDN\nTARGETSUBID=\"ea122fb3-39a8-411c-995c-3724e344095f\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Creating a VM and prepping it for image creation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;ll create an image later. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>##### \n# All of this executed in primary tenant\n#####\necho \"Creating RG and VM\"\n# Create RG\naz group create -n $RGNAME -l $LOC\n\n# Create a VM to create an image from\nvmip=$(az vm create -n $VMNAME -g $RGNAME --admin-username nilfranadmin \\\n    --ssh-key-values ~\/.ssh\/id_rsa.pub --image ubuntults --query publicIpAddress -o tsv)\n\n# Sleep 30 to make sure VM is available to run command\necho \"Sleeping 30 seconds to wait for VM to be fully ready\"\nsleep 30\n\n# Generalize VM\necho \"Generalizing VM\"\nssh -o StrictHostKeyChecking=no nilfranadmin@$vmip 'sudo waagent -deprovision+user -force'\n# If you're copy pasting, the SSH might kill the stream of commands. Make sure to also execute the commands below.\n\necho \"sleeping to ensure deprovision succeeded\"\nsleep 10\n\naz vm deallocate -g $RGNAME -n $VMNAME\naz vm generalize -g $RGNAME -n $VMNAME\n\nvmid=$(az vm show -g $RGNAME -n $VMNAME -o tsv --query id)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now that you have a generalized VM, you can create the SIG and the image version:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating the shared image gallery and image <\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;ll be creating the image based on the generalized VM:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Create image gallery \necho \"Creating sig\"\naz sig create --resource-group $RGNAME --gallery-name $GALLERY\nsigid=$(az sig show \\\n   --resource-group $RGNAME \\\n   --gallery-name $GALLERY \\\n   --query id -o tsv)\n\necho \"Creating image in sig\"\naz sig image-definition create \\\n   --resource-group $RGNAME \\\n   --gallery-name $GALLERY \\\n   --gallery-image-definition $GALLERY \\\n   --publisher $GALLERY \\\n   --offer $GALLERY \\\n   --sku $GALLERY \\\n   --os-type Linux \\\n   --os-state generalized\n\necho \"This next step will take a few minutes.\"\naz sig image-version create \\\n   --resource-group $RGNAME \\\n   --gallery-name $GALLERY \\\n   --gallery-image-definition $GALLERY \\\n   --gallery-image-version 1.0.0 \\\n   --target-regions $LOC \\\n   --replica-count 1 \\\n   --managed-image $vmid\n\n# Get image ID from sig\nsigimageid=$(az sig image-version show --gallery-image-definition $GALLERY \\\n                          --gallery-image-version 1.0.1 \\\n                          --gallery-name $GALLERY \\\n                          --resource-group $RGNAME \\\n                          --query id -o tsv)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Setting up applications and service principal in primary tenant<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;ll create the app in AD allowing cross-tenant registrations and then create a service principal.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, you&#8217;ll give that SP permissions to read from the SIG.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Get image ID from sig\nsigimageid=$(az sig image-version show --gallery-image-definition $GALLERY \\\n                          --gallery-image-version 1.0.1 \\\n                          --gallery-name $GALLERY \\\n                          --resource-group $RGNAME \\\n                          --query id -o tsv)\n\n# Create app\nappid=$(az ad app create --display-name $GALLERY \\\n                 --available-to-other-tenants true \\\n                 --reply-urls  \"https:\/\/www.microsoft.com\" \\\n                 --query appId -o tsv)\n\n# Create SP\naz ad sp create --id $appid\npw=$(az ad sp credential reset \\\n    --name $appid \\\n    --credential-description \"gallery-PW\" \\\n    --query password -o tsv)\necho \"Password below:\"\necho pw\n\n# Create role assignment\naz role assignment create --assignee $appid --role \"Reader\" --scope $sigid<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now that this has been setup, you&#8217;re ready to move to the secondary tenant to give the service principal permissions to create the VM. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating role assignment in secondary tenant<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>az ad sp create --id $appid\n\n# Give this SP permissions to create VMs\naz role assignment create --assignee $appid --role \"Contributor\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And now, you are ready to create a VM using an image in the other tenant&#8217;s SIG:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating the VM based on an image in another tenant&#8217;s SIG<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To create the VM based on the image, you&#8217;ll need to login using the service principal in both tenants, and then create the VM in the target subscription. To ensure you&#8217;re creating it in the target subscription, you can add the &#8211;subscription option in the az cli, as shown below:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>az account clear\naz login --service-principal -u $appid -p $pw --tenant $TENANT1\naz login --service-principal -u $appid -p $pw --tenant $TENANT2\n\n# Create RG in target\naz group create -n $RGNAME -l $LOC --subscription $TARGETSUBID\n# Create VM in target tenant\naz vm create \\\n  -g $RGNAME \\\n  -n $VMNAME \\\n  --image $sigimageid \\\n  --admin-username nilfranadmin \\\n  --ssh-key-values ~\/.ssh\/id_rsa.pub \\\n  --subscription $TARGETSUBID<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And this will create the VM, based on an image in another AAD tenant.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Azure shared image gallery is a service in Azure that helps you manage and store images in a central location. It&#8217;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&#8217;s [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1581,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[2,4],"tags":[8,29,165,174,173],"class_list":["post-1576","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure","category-management","tag-azure","tag-azure-ad","tag-identity","tag-service-principal","tag-shared-image-gallery"],"jetpack_featured_media_url":"https:\/\/nillsfblog.blob.core.windows.net\/media\/2021\/04\/2021-04-02-14_09_42-PowerPoint-Slide-Show-Customize-core-dumps-in-Azure-Kubernetes.pptx.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/1576","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/comments?post=1576"}],"version-history":[{"count":5,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/1576\/revisions"}],"predecessor-version":[{"id":1582,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/1576\/revisions\/1582"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media\/1581"}],"wp:attachment":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media?parent=1576"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/categories?post=1576"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/tags?post=1576"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}