With the Build Azure Virtual Machine Image action, you can now create custom virtual machine images that contain artifacts produced in your CI/CD workflows and have pre-installed software. This action not only lets you build customized images but also distribute them using image managing Azure services like Shared Image Gallery. These images can then be used for creating Virtual Machines or Virtual Machine Scale Sets
The definition of this Github Action is in action.yml.
Note that this action uses the Azure Image Builder service in the background for creating and publishing images.
- User Assigned Managed Identity: A managed identity is required for Azure Image Builder(AIB) to distribute images(Shared Image Gallery or Managed Image). You must create an Azure user-assigned managed identity that will be used during the image build to read and write images. You then need to grant it permission to do specific actions using a custom role with the following json(replace your subscription id and resource group name) and assign it to the managed identity.
{
"Name": "Image Creation Role",
"IsCustom": true,
"Description": "Azure Image Builder access to create resources for the image build",
"Actions": [
"Microsoft.Compute/galleries/read",
"Microsoft.Compute/galleries/images/read",
"Microsoft.Compute/galleries/images/versions/read",
"Microsoft.Compute/galleries/images/versions/write",
"Microsoft.Compute/images/write",
"Microsoft.Compute/images/read",
"Microsoft.Compute/images/delete"
],
"NotActions": [
],
"AssignableScopes": [
"/subscriptions/<subscriptionID>/resourceGroups/<rgName>"
]
}
Learn more about configuring permissions for Azure Image builder Service using Powershell or Azure CLI.
-
resource-group-name
: Required. This is the resource group where the action creates a storage for saving artifacts needed for customized image. Azure image builder also uses the same resource group for Image Template creation. -
image-builder-template-name
: The name of the image builder template resource to be used for creating and running the Image builder service. If you already have an AIB Template file downloaded in the runner, then you can give the full filepath to that as well. E.g. ${{ GITHUB.WORKSPACE }}/vmImageTemplate/ubuntuCustomVM.json. Note that incase a filepath is provided in this action input, then parameters in the file will take precedence over action inputs. Irrespective, customizer section of action is always executed. -
location
: This is the location where the Azure Image Builder(AIB) will run. Eg, 'eastus2'. AIB supports only a specific set of locations. The source images must be present in this location, so for example, if you are using Shared Image Gallery, a replica must exist in that region. This is optional if AIB template filepath is provided inimage-builder-template
input. -
build-timeout-in-minutes
: Optional. Time after which the build is cancelled. Defaults to 240. -
vm-size
: Optional. By default AIB uses a "Standard_D1_v2" build VM, however, you can override this. Check out different VM sizes offered in azure here. -
managed-identity
: As mentioned in pre-requisites, AIB will use the user assigned managed identity to add the image into the resource group. It takes the full identifier for managed identity or if you have the managed identity in the same resource group then just the name of managed identity suffices. Refer the sample input value below. You can find more details about identity creation here. This is input is optional if AIB template filepath is provided inimage-builder-template
input./subscriptions/xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/my-dev-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/my-imagebuild-identity
-
source-os-type
: Required. The OS type of the base image(i.e. source image). It can be set to [ Linux | Windows ]. -
source-image-type
: The base image type that will be used for creating the custom image. This should be set to one of three types: [ PlatformImage | SharedImageGallery | ManagedImage ]. This is input is optional if AIB template filepath is provided inimage-builder-template
input -
source-image
: This is the resource identifier for base image. A source image should be present in the same Azure region set in the input value oflocation
. This is input is optional if AIB template filepath is provided inimage-builder-template
input-
If the
source-image-type
is PlatformImage, then the value ofsource-image
will be the urn of image. Format 'publisher:offer:sku:version' E.g. Ubuntu:Canonical:18.04-LTS:latest. You can run the following AZ CLI command to list images availableaz vm image list or az vm image show
-
if the
source-image-type
is ManagedImage - the value of source-image is the resourceId of the source image, for example:/subscriptions/<subscriptionID>/resourceGroups/<rgName>/providers/Microsoft.Compute/images/<imageName>
-
If the
source-image-type
is SharedImageGallery - You need to pass in the resourceId of the image version for example:/subscriptions/<subscriptionID>/resourceGroups/<sigResourceGroup>/providers/Microsoft.Compute/galleries/<sigName>/images/<imageDefName>/versions/<versionNumber>
-
-
customizer-source
: Optional. This takes the path to a directory in the runner. This is the directory where you can keep all the artifacts that need to be added to the base image for customization. By default, the value is _${{ GITHUB.WORKSPACE }}/workflow-artifacts. -
customizer-script
: Optional. This takes multi inline powershell or shell commands and use variables to point to directories inside the downloaded location. -
customizer-destination
: Optional. This is the directory in the customized image where artifacts are copied to. The default path of customizer-destination would depend on the OS defined in 'source-os-type' field. For windows it is C:\ and for linux it is /tmp/. Note that for many Linux OS's, on a reboot, the /tmp directory contents are deleted. So if you need these artifacts to persist you need to write customizer script to copy them to a persistent location. Here is a sample input for customizer-script:customizer-script: | sudo mkdir /buildArtifacts sudo cp -r /tmp/ /buildArtifacts/
-
customizer-windows-update
: Optional. Applicable for only windows images. The value is a boolean. If set to true, the image builder will run Windows update at the end of the customizations and also handle the reboots if required. By default the value is set to 'false' -
dist-type
: Optional. This takes your choice for distributing the built image. It can be set to [ ManagedImage | SharedImageGallery | VHD ]. By default its ManagedImage. -
dist-resource-id
: Optional. Takes the full resource identifier.- If the dist-type is SharedImageGallery the value can be:
/subscriptions/<subscriptionID>/resourceGroups/<rgName>/providers/Microsoft.Compute/galleries/<galleryName>/images/<imageDefName>
- If the dist-type is ManagedImage, the value should be of format:
/subscriptions/<subscriptionID>/resourceGroups/<rgName>/providers/Microsoft.Compute/images/<imageName>
- If the image-type is VHD, You do not need to pass this parameter
- If the dist-type is SharedImageGallery the value can be:
-
dist-location
: Optional. This is required only when SharedImageGallery is thedist-type
-
dist-image-tags
: Optional. These are user defined tags that are added to the custom image created. They take key value pairs as input. E.g. 'version:beta'
name: create_custom_windows_image
on: push
jobs:
BUILD-CUSTOM-IMAGE:
runs-on: ubuntu-latest
steps:
- name: CHECKOUT
uses: actions/checkout@v2
- name: AZURE LOGIN
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: BUILD WEBAPP
run: sudo ${{ GITHUB.WORKSPACE }}/webApp/buildscript.sh # Runs necessary build scripts and copies built artifacts to ${{ GITHUB.WORKSPACE }}/workflow-artifacts
- name: BUILD-CUSTOM-VM-IMAGE
uses: azure/build-vm-image@v0
with:
resource-group-name: 'myResourceGroup'
managed-identity: 'myImageBuilderIdentity'
location: 'eastus2'
source-os-type: 'windows'
source-image: MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest
customizer-script: |
& 'c:\workflow-artifacts\webApp\webconfig.ps1'
The above workflow will use a Microsoft Windows Server platform image as base image, inject files present in directory ${{ GITHUB.WORKSPACE }}/worflow-artifacts
of GitHub runner into the base image at default customizer-destination
directory and run image customizations(E.g. Set up IIS web server, configure bindings etc) using script webconfig.ps1, finally it will distribute the baked custom image as a Managed Image(default distribution)
on: push
jobs:
job1:
runs-on: ubuntu-latest
name: Create Custom Linux Image
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Create Workflow Artifacts
run: |
cd "$GITHUB_WORKFLOW"
mkdir worflow-artifacts/
echo "echo Installing World... " > $GITHUB_WORKSPACE/workflow-artifacts/install-world.sh # You can have your own installation script here
- name: Login via Az module
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: Build and Distribute Custom VM Image
uses: azure/build-vm-image@v0
with:
resource-group-name: 'myResourceGroup'
location: 'eastus2'
managed-identity: 'myImageBuilderIdentity'
source-os-type: 'linux'
source-image-type: 'PlatformImage'
source-image: Canonical:UbuntuServer:18.04-LTS:latest
customizer-source: ${{ GITHUB.WORKSPACE }}/workflow-artifacts
customizer-script: |
sudo mkdir /buildArtifacts
sudo cp -r /tmp/ /buildArtifacts/
sh /buildArtifacts/workflow-artifacts/install-world.sh
The above workflow will use a linux platform image as base image, inject files present in directory ${{ GITHUB.WORKSPACE }}/worflow-artifacts
of GitHub runner into the base image at default customizer-destination
directory and run install-world.sh script. Finally it will distribute the baked custom image as a Managed Image(default distribution)
on: push
jobs:
BUILD-CUSTOM-UBUNTU-IMAGE:
runs-on: ubuntu-latest
steps:
- name: CHECKOUT
uses: actions/checkout@v2
- name: AZURE LOGIN
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: BUILD WEBAPP
run: sudo ${{ GITHUB.WORKSPACE }}/webApp/buildscript.sh # Run necessary build scripts and copies built artifacts to ${{ GITHUB.WORKSPACE }}/workflow-artifacts
- name: BUILD-CUSTOM-VM-IMAGE
uses: azure/build-vm-image@v0
with:
resource-group-name: 'myResourceGroup'
managed-identity: 'myImageBuilderIdentity'
location: 'eastus2'
source-os-type: 'linux'
source-image: Canonical:UbuntuServer:18.04-LTS:latest
customizer-script: |
sh /tmp/workflow-artifacts/install.sh
dist-type: 'SharedImageGallery'
dist-resource-id: '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Compute/galleries/AppTeam/images/ImagesWithApp'
dist-location: 'eastus2'
The above workflow will use a linux platform image as base image, inject files present in directory ${{ GITHUB.WORKSPACE }}/worflow-artifacts
of GitHub runner into the base image at default customizer-destination
directory and run install.sh script. Finally it will distribute the baked custom image through Shared Image Gallery
You can easily create a Virtual Machine using AZ CLI commands. Here is a simple example to do it using GitHub workflows.
- name: CREATE VM
uses: azure/CLI@v1
with:
azcliversion: 2.0.72
inlineScript: |
az vm create --resource-group myResourceGroup --name "app-vm-${{ GITHUB.RUN_NUMBER }}" --admin-username vmUserName --admin-password "${{ secrets.VM_PWD }}" --location eastus2 \
--image "${{ steps.<workflow_step_id>.outputs.custom-image-uri }}"
You can also take a look at the end to end tutorial that describes how to use this action and also create a Virtual machine from customized image.
With the Azure login Action, you can perform an Azure login using Azure service principal. The credentials of Azure Service Principal can be added as secrets in the GitHub repository and then used in the workflow. Follow the below steps to generate credentials and store in github.
-
Prerequisite: You should have installed Azure cli on your local machine to run the command or use the cloudshell in the Azure portal. To install Azure cli, follow Install Azure Cli. To use cloudshell, follow CloudShell Quickstart. After you have one of the above ready, follow these steps:
-
Run the below Azure cli command and copy the output JSON object to your clipboard.
az ad sp create-for-rbac --name "myApp" --role contributor \
--scopes /subscriptions/{subscription-id} \
--sdk-auth
# Replace {subscription-id} with the subscription identifiers
# The command should output a JSON object similar to this:
{
"clientId": "<GUID>",
"clientSecret": "<GUID>",
"subscriptionId": "<GUID>",
"tenantId": "<GUID>",
(...)
}
- Define a 'New secret' under your GitHub repository settings -> 'Secrets' menu. Lets name it 'AZURE_CREDENTIALS'.
- Paste the contents of the clipboard as the value of the above secret variable.
- Use the secret variable in the Azure Login Action(Refer to the examples above)
If needed, you can modify the Azure CLI command to further reduce the scope for which permissions are provided. Here is the command that gives contributor access to only a resource group.
az ad sp create-for-rbac --name "myApp" --role contributor \
--scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group} \
--sdk-auth
# Replace {subscription-id}, {resource-group} with the subscription and resource group identifiers.
You can also provide permissions to multiple scopes using the Azure CLI command:
az ad sp create-for-rbac --name "myApp" --role contributor \
--scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group1} \
/subscriptions/{subscription-id}/resourceGroups/{resource-group2} \
--sdk-auth
# Replace {subscription-id}, {resource-group1}, {resource-group2} with the subscription and resource group identifiers.
If you have any changes you’d like to see or suggestions for this action, we’d love your feedback ❤️ . Please feel free to raise a GitHub issue in this repository describing your suggestion. This would enable us to label and track it properly. You can do the same if you encounter a problem with the feature as well.
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.