Create an image and use a user-assigned managed identity to access files in an Azure storage account

Applies to: ✔️ Linux VMs ✔️ Flexible scale sets

This article shows how to create a customized image by using Azure VM Image Builder. The service uses a user-assigned managed identity to access files in an Azure storage account, and it can achieve blocking unauthenticated access to the storage account.

Azure VM Image Builder supports using scripts and copying files from GitHub, Azure storage accounts, and other locations. If you want to use the locations, they must be externally accessible to VM Image Builder.

In the following example, you'll create two resource groups, one for the custom image and the other to host an Azure storage account that contains a script file. This example simulates a real-life scenario, where you might have build artifacts or image files in various storage accounts. You'll create a user-assigned identity and then grant the identity read permissions on the script file, but you won't allow public access to the file. You'll then use the shell customizer to download and run a script from the storage account.

Create a resource group

  1. Because you'll be using some pieces of information repeatedly, create some variables to store that information.

    # Image resource group name 
    # Storage resource group
    # Location 
    # Name of the image to be created
    # Image distribution metadata reference name
  2. Create a variable for your subscription ID:

    subscriptionID=$(az account show --query id --output tsv)
  3. Create resource groups for both the image and the script storage:

    # Create a resource group for the image template
    az group create -n $imageResourceGroup -l $location
    # Create a resource group for the script storage
    az group create -n $strResourceGroup -l $location
  4. Create a user-assigned identity, and set permissions on the resource group:

    VM Image Builder uses the provided user identity to inject the image into the resource group. In this example, you create an Azure role definition with specific actions for distributing the image. The role definition is then assigned to the user identity.

    # Create a user-assigned identity for VM Image Builder to access the storage account where the script is located
    identityName=aibBuiUserId$(date +'%s')
    az identity create -g $imageResourceGroup -n $identityName
    # Get an identity ID
    imgBuilderCliId=$(az identity show -g $imageResourceGroup -n $identityName --query clientId -o tsv)
    # Get the user-identity URI, which is needed for the template
    # Download the preconfigured role definition example
    curl -o aibRoleImageCreation.json
    # Update the definition
    sed -i -e "s/<subscriptionID>/$subscriptionID/g" aibRoleImageCreation.json
    sed -i -e "s/<rgName>/$imageResourceGroup/g" aibRoleImageCreation.json
    # Create role definitions
    az role definition create --role-definition ./aibRoleImageCreation.json
    # Grant the role definition to the user-assigned identity
    az role assignment create \
        --assignee $imgBuilderCliId \
        --role "Azure Image Builder Service Image Creation Role" \
        --scope /subscriptions/$subscriptionID/resourceGroups/$imageResourceGroup
  5. Create the storage account, and copy the sample script into it from GitHub:

    # Script storage account
    scriptStorageAcc=aibstorscript$(date +'%s')
    # Script container
    scriptStorageAccContainer=scriptscont$(date +'%s')
    # Script URL
    # Create the storage account and blob in the resource group
    az storage account create -n $scriptStorageAcc -g $strResourceGroup -l $location --sku Standard_LRS
    az storage container create -n $scriptStorageAccContainer --fail-on-exist --account-name $scriptStorageAcc
    # Copy in an example script from the GitHub repo 
    az storage blob copy start \
        --destination-blob \
        --destination-container $scriptStorageAccContainer \
        --account-name $scriptStorageAcc \
  6. Give VM Image Builder permission to create resources in the image resource group. The --assignee value is the user-identity ID.

    az role assignment create \
        --assignee $imgBuilderCliId \
        --role "Storage Blob Data Reader" \
        --scope /subscriptions/$subscriptionID/resourceGroups/$strResourceGroup/providers/Microsoft.Storage/storageAccounts/$scriptStorageAcc/blobServices/default/containers/$scriptStorageAccContainer 

Modify the example

Download the example JSON file and configure it with the variables you created earlier.

curl -o helloImageTemplateMsi.json
sed -i -e "s/<subscriptionID>/$subscriptionID/g" helloImageTemplateMsi.json
sed -i -e "s/<rgName>/$imageResourceGroup/g" helloImageTemplateMsi.json
sed -i -e "s/<region>/$location/g" helloImageTemplateMsi.json
sed -i -e "s/<imageName>/$imageName/g" helloImageTemplateMsi.json
sed -i -e "s%<scriptUrl>%$scriptUrl%g" helloImageTemplateMsi.json
sed -i -e "s%<imgBuilderId>%$imgBuilderId%g" helloImageTemplateMsi.json
sed -i -e "s%<runOutputName>%$runOutputName%g" helloImageTemplateMsi.json

Create the image

  1. Submit the image configuration to the VM Image Builder service:

    az resource create \
        --resource-group $imageResourceGroup \
        --properties @helloImageTemplateMsi.json \
        --is-full-object \
        --resource-type Microsoft.VirtualMachineImages/imageTemplates \
        -n helloImageTemplateMsi01
  2. Start the image build:

    az resource invoke-action \
        --resource-group $imageResourceGroup \
        --resource-type  Microsoft.VirtualMachineImages/imageTemplates \
        -n helloImageTemplateMsi01 \
        --action Run 

The build can take about 15 minutes to finish.

Create a VM

  1. Create a VM from the image:

    az vm create \
    --resource-group $imageResourceGroup \
    --name aibImgVm00 \
    --admin-username aibuser \
    --image $imageName \
    --location $location \
  2. After the VM has been created, start a Secure Shell (SSH) session with it.

    ssh aibuser@<publicIp>

After the SSH connection is established, you should receive a "Message of the Day" saying that the image was customized:

**            This VM was built from the:            **
**      !! AZURE VM IMAGE BUILDER Custom Image !!    **
**         You have just been Customized :-)         **

Clean up your resources

If you no longer need the resources that were created during this process, you can delete them by running the following code:

az role definition delete --name "$imageRoleDefName"
az role assignment delete \
    --assignee $imgBuilderCliId \
    --role "$imageRoleDefName" \
    --scope /subscriptions/$subscriptionID/resourceGroups/$imageResourceGroup
az identity delete --ids $imgBuilderId
az resource delete \
    --resource-group $imageResourceGroup \
    --resource-type Microsoft.VirtualMachineImages/imageTemplates \
    -n helloImageTemplateMsi01
az group delete -n $imageResourceGroup
az group delete -n $strResourceGroup

Next steps

If you have any problems using VM Image Builder, see Troubleshoot Azure VM Image Builder.