DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Power of Azure B Series Virtual Machines
  • Azure Virtual Machines
  • Keep Your Application Secrets Secret
  • What Is Azure Site Recovery Service?

Trending

  • It’s Not About Control — It’s About Collaboration Between Architecture and Security
  • Mastering Fluent Bit: Installing and Configuring Fluent Bit on Kubernetes (Part 3)
  • Unlocking the Benefits of a Private API in AWS API Gateway
  • AI Meets Vector Databases: Redefining Data Retrieval in the Age of Intelligence
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. 6 Easy Ways to Manage and Harden VM Images in Azure

6 Easy Ways to Manage and Harden VM Images in Azure

In this article, take a look at six ways to manage and harden VM images in Azure.

By 
Boris Zaikin user avatar
Boris Zaikin
DZone Core CORE ·
Oct. 02, 20 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
5.5K Views

Join the DZone community and get the full member experience.

Join For Free

Managing VM Images may be a nightmare. Here are 6 simple approaches on how to seamlessly build, share, test, and copy images in Azure. And as a bonus, you also get how to build image notifications based on Event-Driven-Architecture.

Problem

Whether you are managing 100 Virtual Machines or 1000+ that build and harden VM images, do you manually manage your images? If you do, you know that it is quite expensive and leads to hard-to-detect errors and potential security vulnerabilities.

Here is how I approach this issue for my customers. My procedure of creating image management for Azure.

1. Building an Image Using the Azure Image Builder

The Azure Image Builder is a service that allows you to create custom images with Azure CLI. The image creation based on the JSON template, example below.

JSON
 




x
48


 
1
{
2
    "type": "Microsoft.VirtualMachineImages/imageTemplates",
3
    "apiVersion": "2019-05-01-preview",
4
    "location": "<region>",
5
    "dependsOn": [],
6
    "tags": {
7
        "imagebuilderTemplate": "ubuntu1804",
8
        "userIdentity": "enabled"
9
            },
10
        "identity": {
11
            "type": "UserAssigned",
12
                    "userAssignedIdentities": {
13
                    "<imgBuilderId>": {}
14
                        
15
                }
16
                },
17
    "properties": {
18

          
19
        "buildTimeoutInMinutes" : 80,
20
        
21
        "vmProfile": 
22
            {
23
            "vmSize": "Standard_D1_v2",
24
            "osDiskSizeGB": 30
25
            },
26

          
27
        "source": {
28
            "type": "PlatformImage",
29
                "publisher": "Canonical",
30
                "offer": "UbuntuServer",
31
                "sku": "18.04-LTS",
32
                "version": "latest"
33
            
34
        },
35
        "customize": [
36
            {
37
                "type": "Shell",
38
                "name": "RunScriptFromSource",
39
                "scriptUri": "https://raw.githubusercontent.com/danielsollondon/azvmimagebuilder/master/quickquickstarts/customizeScript.sh"
40
            }          
41

          
42
        ],
43
        "distribute": 
44
            [
45

          
46
            ]
47
        }
48
    }


I have to cut the original template as it is quite long. Here you can find full example.

The ARM template is quite simple it contains the following properties:

  • Identity section is required, you have to create Managed Identity for image builder to have an access to create and edit images
  • VmProfile is to set up VM configuration plan
  • Source allows you to specify base image parameters. I use the latest Ubuntu Server 18.04 LTS from Canonical
    The customization section allows you to specify VM hardening scripts.
  • The customization section allows you to specify VM hardening scripts.

Here you can see the full list of the Image Builder options.

Below you can find CLI commands that build the image based on the mentioned JSON template.

JSON
 




xxxxxxxxxx
1
17


 
1
....
2
# submit the image configuration to the VM Image Builder Service
3

          
4
az resource create \
5
    --resource-group $imageResourceGroup \
6
    --properties @ubuntuImageTemplate.json \
7
    --is-full-object \
8
    --resource-type Microsoft.VirtualMachineImages/imageTemplates \
9
    -n helloImageTemplateLinux01
10

          
11
# Run the image build
12

          
13
az resource invoke-action \
14
     --resource-group $imageResourceGroup \
15
     --resource-type  Microsoft.VirtualMachineImages/imageTemplates \
16
     -n helloImageTemplateLinux01 \
17
     --action Run


Before you run this template you should enable the Azure Image Builder, set permissions, create resource groups, and create the managed identity. Here you can find a step-by-step process. 

Advantages

Templates and the process itself is easy to understand and it can be easily integrated with Azure DevOps.

Disadvantages

The image builder is still in the review therefore it is not recommended using it in the production. Set up can be a bit difficult in comparison to Hashicorp Packer. (explain how to work with the Packer in the next section).

2. Building an Image Using the Hashicorp Packer

Hashicorp Packer is a multi-platform solution that allows building custom images based on JSON templates. The JSON templates are well-structured and based on an easy-to-understand object model. The JSON templates have just three root objects: Communicators, Builders, Provisioners, and Post-Processors. Below you can find the JSON template.

JSON
 




xxxxxxxxxx
1
36


 
1
{
2
"builders": [{
3
"type": "azure-arm",
4
"client_id": "client_id",
5
"client_secret": "client_secret",
6
"tenant_id": "tenant_id",
7
"subscription_id": "subscription_id",
8
"managed_image_resource_group_name": "HBI",
9
"managed_image_name": "test-image-sig",
10
"os_type": "Linux",
11
"image_publisher": "Canonical",
12
"image_offer": "UbuntuServer",
13
"image_sku": "16.04-LTS",
14
"azure_tags": {
15
"dept": "Test",
16
"task": "Image test"
17
},
18
"storage_account": "packerimgestest",
19
"capture_container_name": "image-vhd",
20
"capture_name_prefix": "image-pref",
21
"resource_group_name": "imageholder-sig-rg",
22
"location": "West Europe",
23
"vm_size": "Standard_DS2_v2"
24
}],
25
"provisioners": [{
26
"execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'",
27
"inline": [
28
"apt-get update",
29
"apt-get upgrade -y",
30
"apt-get -y install nginx",
31
"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
32
],
33
"inline_shebang": "/bin/sh -x",
34
"type": "shell"
35
}]
36
}


JSON template creates the VHD image of Ubuntu with a preinstalled NGINX web server and other updates. Here you can find a lot of other templates.

To set up packer you can use Chocolatie package manager:

JSON
 




xxxxxxxxxx
1


 
1
choco install packer -y


You can run the JSON template using the following command: 

JSON
 




xxxxxxxxxx
1


 
1
packer build <path/your/template.json>


Before your run the template you need to create Management Identity or Service Principal with proper permissions, For example: 

JSON
 




xxxxxxxxxx
1


 
1
az ad sp create-for-rbac -n "ImageContributor" 
2
 --role contributor `    
3
 --scopes /subscriptions/<subscription_id> `     
4
 --sdk-auth > az-principal.auth


This command automatically creates JSON file with clientId, clientSecret, tenantId, subscriptionId and other fields.

Here you can find other details of setting up and run the Packer templates.

Advantages

The Packer and JSON templates simple to understand. They allow you to quickly set up the environment and start building images. There is also a strong community. Plus, the Packer supports multiple cloud providers.

Disadvantages

I have not found any serious disadvantages. However, while building an image, the packer always removes the disk that is required in some operations with images. For example, for copying an image.

3. Sharing Images

To share images in Azure, you may use the Shared Image Gallery. It can:

  • Create image definition
  • Keep versions of an image
  • Share an image

For example, you can share an image across your Azure subscriptions, resource groups, and tenants.

In the current scenario, you can create a user group or a single user/service principal, assign contributor rights only for this shared image gallery. 

JSON
 




xxxxxxxxxx
1


 
1
az ad sp create-for-rbac -n "ImageContributor" 
2
 --role contributor `    
3
 --scopes /subscriptions/<subscr-id>/resourceGroups/sig-we-rg/providers/Microsoft.Compute/galleries/testsig


As a result, users from the group can create the Virtual Machine based on the Ubuntu image from this Shared Image Gallery in the different subscriptions. 

JSON
 




xxxxxxxxxx
1


 
1
az vm create `
2
  --resource-group myResourceGroup `
3
  --name UbuntuVM`
4
  --image "/subscriptions/<subscr-id>/resourceGroups/sig-we-rg/providers/Microsoft.Compute/galleries/testsig/images/ubuntu-server-image-def/versions/1.0.0" \
5
  --admin-username azureuser \
6
  --generate-ssh-keys


Advantages

The Azure Shared Image gallery easily allows you to build, share, manage, and customize images within your organization. SIG has Azure CLI, so you can easily automate image distribution.

Disadvantages

The image remains in a shared access gallery. Thus, it physically stays there. So, when you share it across subscriptions, you cannot change or remove it independently for each subscription.

4. Copying Images

By copying images, you can deliver images from one subscription to another or from one resource group to another. All copied images are independent of each other. You can copy images using Azure Image Copy extension, or implement manual copying using Go. Let us have a look at the examples below.

Copying Image With Az Copy Extension

To copy images between subscriptions, I use Az Image Copy extension. It creates a new image (from the source image) in Resource Group A.
 Install copy extension:

Java
 




xxxxxxxxxx
1


 
1
az extension add --name image-copy-extension


Copy command: 

Java
 




xxxxxxxxxx
1


 
1
az image copy --source-resource-group Azure-Resource-Group-B `
2
              --source-object-name image-version-1.0 `
3
              --target-resource-group Azure-Resource-Group-A `
4
              --target-location westeurope `
5
              --target-subscription 111111-2222-2222-0000-0000000 ` # Subscription A
6
              --cleanup


Important. Packer removes the disk automatically after managed image is created.

Advantages

Az Image Copy extension is simple to use and automate.

Disadvantages

An image must contain a managed disc, otherwise the copy process fails with ‘Resource not found’ error message.

Copy Image Manually

If Az Copy Image Extension does not work for you, you can create a VHD image in the storage account and copy it to another destination storage account.

The process workflow:

  1. Create 2 Storage accounts, Source and Destination
  2. Create the VHD image in the Source storage account. You can use the Packer script from the first section.
  3. Generate Shared Access Signature for the VHD Source image. Here you can find an example of how to do it with Azure CLI.

4. And copy the image. To demonstrate I use the following Go script. You can also use the AzCopy tool, here the example of how to do it.

Go
 




xxxxxxxxxx
1
100


 
1

          
2
package main
3

          
4
import (
5
    "context"
6
    "encoding/json"
7
    "fmt"
8
    "io/ioutil"
9
    "log"
10
    "net/url"
11
    "os"
12
    "time"
13

          
14
    "github.com/Azure/azure-storage-blob-go/azblob"
15
    "github.com/Azure/go-autorest/autorest"
16
    "github.com/Azure/go-autorest/autorest/azure"
17
    "github.com/Azure/go-autorest/autorest/azure/auth"
18
)
19

          
20
var (
21
    ctx        = context.Background()
22
    clientData clientInfo
23
    authorizer autorest.Authorizer
24
)
25

          
26
type clientInfo struct {
27
    SubscriptionID string
28
}
29

          
30
const (
31
    accountKey        = "ACCOUNT_KEY"
32
    accountName       = "ACCOUNT_NAME"
33
    azureAuthLocation = "AZURE_AUTH_LOCATION"
34
)
35

          
36
func init() {
37
    var err error
38
    authorizer, err = auth.NewAuthorizerFromFile(azure.PublicCloud.ResourceManagerEndpoint)
39
    if err != nil {
40
        log.Fatalf("Failed to get config: %v", err)
41
    }
42

          
43
    authInfo, err := readJSON(os.Getenv(azureAuthLocation))
44
    if err != nil {
45
        log.Fatalf("JSON Reading issues: %+v", err)
46
    }
47

          
48
    clientData.SubscriptionID = (*authInfo)["subscriptionId"].(string)
49
}
50

          
51
func main() {
52
    accountName, accountKey := accountInfo()
53

          
54
    // Set up Destination image file
55
    targetBlobURL, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/destination-image.vhd", accountName))
56

          
57
    // Set up and get image source, using Shared Acess Signature
58
    src, _ := url.Parse("https://<SOURCE-STORAGE-ACCOUNT-NAME>.blob.core.windows.net/source-image.vhd?sp=r&st=2020-05-12T12:52:55Z&se=2021-05-12T20:52:55Z&spr=https&sv=2019-10-10&sr=b&sig=sdfsdfsdfs323423443df")
59

          
60
    // Create and get SP credentials
61
    credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
62
    if err != nil {
63
        log.Fatal(err)
64
    }
65

          
66
    blobURL := azblob.NewBlobURL(*targetBlobURL, azblob.NewPipeline(credential, azblob.PipelineOptions{}))
67

          
68
  // Start copy
69
    startCopy, err := blobURL.StartCopyFromURL(ctx, *src, nil, azblob.ModifiedAccessConditions{}, azblob.BlobAccessConditions{})
70
    if err != nil {
71
        log.Fatal(err)
72
    }
73

          
74
    copyID := startCopy.CopyID()
75
    copyStatus := startCopy.CopyStatus()
76
    for copyStatus == azblob.CopyStatusPending {
77
        time.Sleep(time.Second * 2)
78
        getMetadata, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
79
        if err != nil {
80
            log.Fatal(err)
81
        }
82
        copyStatus = getMetadata.CopyStatus()
83
    }
84

          
85
    fmt.Printf("Copy Status %s to %s: ID=%s, Status=%s\n", src.String(), blobURL, copyID, copyStatus)
86
}
87

          
88
func accountInfo() (string, string) {
89
    return return os.Getenv(accountName), os.Getenv(accountKey)
90
}
91

          
92
func readJSON(path string) (*map[string]interface{}, error) {
93
    data, err := ioutil.ReadFile(path)
94
    if err != nil {
95
        log.Fatalf("failed to read file: %v", err)
96
    }
97
    contents := make(map[string]interface{})
98
    _ = json.Unmarshal(data, &contents)
99
    return &contents, nil
100
}


Advantages

The current image coping workflow is fully custom. Therefore, it can be changed at any time. Also, it can be useful when the Az Image Copy extension does not work you. For example, when it removes the managed disk while creating an image.

Disadvantages

You have to implement all workflow steps including:

  • creating VHD
  • generating and managing SAS token
  • copying an image
  • cleaning up
  • converting an image

5. Image and Disk Converting

The operations to convert a VHD image to a Managed Disk, or a Managed Disk to a VHD image. This is useful when you need to distribute images across your organization with Azure Shared Image Gallery in the image copy process and when you need you to spin up a new VM.

Also, it is useful when you have some legacy VHD and you need to support it, install updates, set up automatic backups, use availability, and availability zones.

Here is a list of advantages of the Managed Image.

Converting VHD Image to the Managed Disk

PowerShell
 




xxxxxxxxxx
1
31


 
1
# Provide the subscription Id where Managed Disks will be created
2
$subscriptionId = '00000-00000-0000-0000-0000000'
3

          
4
# Provide the name of your resource group where Managed Disks will be created. 
5
$resourceGroupName ='HBI'
6

          
7
# Provide the name of the Managed Disk
8
$diskName = 'image-test-disk'
9

          
10
# The Disk It should be greater than the VHD file size.
11
$diskSize = '130'
12

          
13
# Storage LRS. Options: Premium_LRS, Standard_LRS, etc
14
$storageType = 'Premium_LRS'
15

          
16
# Set Azure region (e.g. westus, westeurope), Ensure that location of the VHD image (alongside with Storage Account), 
17
# and future Managed Disk is the same.
18
$location = 'westeurope'
19

          
20
# Set URI of the VHD file (page blob) in a storage account.
21
$sourceVHDURI = 'https://imagesstorage.blob.core.windows.net/test-image-disk.vhd'
22

          
23
# Set the Resource Id of the Source storage account where VHD file is stored. You can avoid it if VHD in the same subscription
24
$storageAccountId = '/subscriptions/subscription-id/resourceGroups/test-rg/providers/Microsoft.Storage/storageAccounts/imagesstorage'
25

          
26
#Set the context to the subscription Id where Managed Disk will be created
27
Select-AzSubscription -SubscriptionId $SubscriptionId
28

          
29
$diskConfig = New-AzDiskConfig -AccountType $storageType -Location $location -CreateOption Import -StorageAccountId $storageAccountId -SourceUri $sourceVHDURI
30

          
31
New-AzDisk -Disk $diskConfig -ResourceGroupName $resourceGroupName -DiskName $diskName


Convert Managed Disk to Managed Image

PowerShell
 




xxxxxxxxxx
1
16


 
1
$rgName = "HBI"
2
$location = "westeurope"
3
$imageName = "boriszn-test-imagefromdisk"
4

          
5

          
6
# Get disk 
7
$disk = Get-AzDisk -ResourceGroupName 'HBI' -DiskName 'image-test-disk' 
8
$diskId = $disk.Id
9

          
10
# Create Managed Image Config with Managed Disk Info 
11
# OS Types (Linux, Windows)
12
$imageConfig = New-AzImageConfig -Location $location
13
$imageConfig = Set-AzImageOsDisk -Image $imageConfig -OsState Generalized -OsType Linux -ManagedDiskId $diskId
14

          
15
# Create the image.
16
$image = New-AzImage -ImageName $imageName -ResourceGroupName $rgName -Image $imageConfig


Advantages

With both cmdlets, you can deliver an image to your Azure Shared Image across different subscriptions and resource groups.

Disadvantages

The conversion process can be complicated because some images can be outdated, and the conversion process may fail.


6. Testing Images

To test images, you can simply spin-up the new VM from the image gallery and use an image definition.

Java
 




xxxxxxxxxx
1


 
1
az vm create `
2
   --resource-group container-image-rg `
3
   --name vm-test `
4
   --image "/subscriptions/<subscription-id>/resourceGroups/container-image-rg/providers/Microsoft.Compute/galleries/test-gallery/images/image-test-def" `
5
   --generate-ssh-keys 


Advantages

The command and process itself quite simple.

Disadvantages

It does not check whether specific software and services are installed correctly in the JSON configuration. So this logic has to be implemented separately.


The Images Pub/Sub Subsystem Concept (Bonus)

Managing images across several subscriptions or resource groups can be difficult, especially when you constantly producing new versions of images, or you have some automated process to spin-up new virtual machines.

In this case, you need to build a notification system for notifying different components when a new image was created, a new version or image definition appears in the Azure Shared Image Gallery. You can easily create it using Azure Event Grid with filters.

Below you can see how it can be done with the event grid. You can use Queue, Webhook, and even Azure Service Bus to deliver messages to the target component.

The following example demonstrates how you can create an event grid with image filters.

Java
 




xxxxxxxxxx
1


 
1
az eventgrid event-subscription create `
2
                --source-resource-id $topicid `
3
                --name $eventSubscriptionName `
4
                --endpoint-type "storagequeue" `
5
                --endpoint $queueid `
6
                --included-event-types Microsoft.Resources.ResourceWriteSuccess `
7
                --advanced-filter data.operationName StringContains "Microsoft.Compute/virtualMachines/write" `
8
                --advanced-filter data.operationName StringContains "Microsoft.Compute/galleries/images/write" `
9
                --advanced-filter data.operationName StringContains "Microsoft.Compute/galleries/images/versions/write"


Here you can find the complete Azure DevOps pipeline with the steps on how to deploy this Event Grid and link with other required resources.

Conclusion

That’s it. Based on these ways you can easily set up images Hardening and management pipeline in the Azure DevOps.

Virtual Machine azure

Opinions expressed by DZone contributors are their own.

Related

  • Power of Azure B Series Virtual Machines
  • Azure Virtual Machines
  • Keep Your Application Secrets Secret
  • What Is Azure Site Recovery Service?

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!