Create Azure Shared Image Galleries

The azure-native:compute:Gallery resource, part of the Pulumi Azure Native provider, defines a Shared Image Gallery that serves as a container for versioned VM images and application templates. This guide focuses on four capabilities: private gallery creation, cross-subscription sharing, community publishing, and soft deletion protection.

Galleries belong to resource groups and require appropriate Azure subscription permissions. The examples are intentionally small. Combine them with your own image definitions, versions, and RBAC policies.

Most organizations start by creating a private gallery to store and version custom VM images within their subscription.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const gallery = new azure_native.compute.Gallery("gallery", {
    description: "This is the gallery description.",
    galleryName: "myGalleryName",
    location: "West US",
    resourceGroupName: "myResourceGroup",
});
import pulumi
import pulumi_azure_native as azure_native

gallery = azure_native.compute.Gallery("gallery",
    description="This is the gallery description.",
    gallery_name="myGalleryName",
    location="West US",
    resource_group_name="myResourceGroup")
package main

import (
	compute "github.com/pulumi/pulumi-azure-native-sdk/compute/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewGallery(ctx, "gallery", &compute.GalleryArgs{
			Description:       pulumi.String("This is the gallery description."),
			GalleryName:       pulumi.String("myGalleryName"),
			Location:          pulumi.String("West US"),
			ResourceGroupName: pulumi.String("myResourceGroup"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var gallery = new AzureNative.Compute.Gallery("gallery", new()
    {
        Description = "This is the gallery description.",
        GalleryName = "myGalleryName",
        Location = "West US",
        ResourceGroupName = "myResourceGroup",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.compute.Gallery;
import com.pulumi.azurenative.compute.GalleryArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var gallery = new Gallery("gallery", GalleryArgs.builder()
            .description("This is the gallery description.")
            .galleryName("myGalleryName")
            .location("West US")
            .resourceGroupName("myResourceGroup")
            .build());

    }
}
resources:
  gallery:
    type: azure-native:compute:Gallery
    properties:
      description: This is the gallery description.
      galleryName: myGalleryName
      location: West US
      resourceGroupName: myResourceGroup

The galleryName provides a unique identifier within the resource group. The location determines where gallery metadata is stored. Without a sharingProfile, the gallery remains private to your subscription, accessible only through RBAC permissions.

Share images across subscriptions or tenants

Teams managing multiple subscriptions configure sharing profiles to control distribution scope.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const gallery = new azure_native.compute.Gallery("gallery", {
    description: "This is the gallery description.",
    galleryName: "myGalleryName",
    location: "West US",
    resourceGroupName: "myResourceGroup",
    sharingProfile: {
        permissions: azure_native.compute.GallerySharingPermissionTypes.Groups,
    },
});
import pulumi
import pulumi_azure_native as azure_native

gallery = azure_native.compute.Gallery("gallery",
    description="This is the gallery description.",
    gallery_name="myGalleryName",
    location="West US",
    resource_group_name="myResourceGroup",
    sharing_profile={
        "permissions": azure_native.compute.GallerySharingPermissionTypes.GROUPS,
    })
package main

import (
	compute "github.com/pulumi/pulumi-azure-native-sdk/compute/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewGallery(ctx, "gallery", &compute.GalleryArgs{
			Description:       pulumi.String("This is the gallery description."),
			GalleryName:       pulumi.String("myGalleryName"),
			Location:          pulumi.String("West US"),
			ResourceGroupName: pulumi.String("myResourceGroup"),
			SharingProfile: &compute.SharingProfileArgs{
				Permissions: pulumi.String(compute.GallerySharingPermissionTypesGroups),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var gallery = new AzureNative.Compute.Gallery("gallery", new()
    {
        Description = "This is the gallery description.",
        GalleryName = "myGalleryName",
        Location = "West US",
        ResourceGroupName = "myResourceGroup",
        SharingProfile = new AzureNative.Compute.Inputs.SharingProfileArgs
        {
            Permissions = AzureNative.Compute.GallerySharingPermissionTypes.Groups,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.compute.Gallery;
import com.pulumi.azurenative.compute.GalleryArgs;
import com.pulumi.azurenative.compute.inputs.SharingProfileArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var gallery = new Gallery("gallery", GalleryArgs.builder()
            .description("This is the gallery description.")
            .galleryName("myGalleryName")
            .location("West US")
            .resourceGroupName("myResourceGroup")
            .sharingProfile(SharingProfileArgs.builder()
                .permissions("Groups")
                .build())
            .build());

    }
}
resources:
  gallery:
    type: azure-native:compute:Gallery
    properties:
      description: This is the gallery description.
      galleryName: myGalleryName
      location: West US
      resourceGroupName: myResourceGroup
      sharingProfile:
        permissions: Groups

The sharingProfile property controls access boundaries. Setting permissions to “Groups” enables sharing with specific Azure AD groups or subscriptions. This allows centralized image management while distributing access across organizational boundaries.

Publish images to the Azure community

Organizations that want to share VM images publicly configure community gallery settings with publisher metadata.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const gallery = new azure_native.compute.Gallery("gallery", {
    description: "This is the gallery description.",
    galleryName: "myGalleryName",
    location: "West US",
    resourceGroupName: "myResourceGroup",
    sharingProfile: {
        communityGalleryInfo: {
            eula: "eula",
            publicNamePrefix: "PirPublic",
            publisherContact: "pir@microsoft.com",
            publisherUri: "uri",
        },
        permissions: azure_native.compute.GallerySharingPermissionTypes.Community,
    },
});
import pulumi
import pulumi_azure_native as azure_native

gallery = azure_native.compute.Gallery("gallery",
    description="This is the gallery description.",
    gallery_name="myGalleryName",
    location="West US",
    resource_group_name="myResourceGroup",
    sharing_profile={
        "community_gallery_info": {
            "eula": "eula",
            "public_name_prefix": "PirPublic",
            "publisher_contact": "pir@microsoft.com",
            "publisher_uri": "uri",
        },
        "permissions": azure_native.compute.GallerySharingPermissionTypes.COMMUNITY,
    })
package main

import (
	compute "github.com/pulumi/pulumi-azure-native-sdk/compute/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewGallery(ctx, "gallery", &compute.GalleryArgs{
			Description:       pulumi.String("This is the gallery description."),
			GalleryName:       pulumi.String("myGalleryName"),
			Location:          pulumi.String("West US"),
			ResourceGroupName: pulumi.String("myResourceGroup"),
			SharingProfile: &compute.SharingProfileArgs{
				CommunityGalleryInfo: &compute.CommunityGalleryInfoArgs{
					Eula:             pulumi.String("eula"),
					PublicNamePrefix: pulumi.String("PirPublic"),
					PublisherContact: pulumi.String("pir@microsoft.com"),
					PublisherUri:     pulumi.String("uri"),
				},
				Permissions: pulumi.String(compute.GallerySharingPermissionTypesCommunity),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var gallery = new AzureNative.Compute.Gallery("gallery", new()
    {
        Description = "This is the gallery description.",
        GalleryName = "myGalleryName",
        Location = "West US",
        ResourceGroupName = "myResourceGroup",
        SharingProfile = new AzureNative.Compute.Inputs.SharingProfileArgs
        {
            CommunityGalleryInfo = new AzureNative.Compute.Inputs.CommunityGalleryInfoArgs
            {
                Eula = "eula",
                PublicNamePrefix = "PirPublic",
                PublisherContact = "pir@microsoft.com",
                PublisherUri = "uri",
            },
            Permissions = AzureNative.Compute.GallerySharingPermissionTypes.Community,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.compute.Gallery;
import com.pulumi.azurenative.compute.GalleryArgs;
import com.pulumi.azurenative.compute.inputs.SharingProfileArgs;
import com.pulumi.azurenative.compute.inputs.CommunityGalleryInfoArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var gallery = new Gallery("gallery", GalleryArgs.builder()
            .description("This is the gallery description.")
            .galleryName("myGalleryName")
            .location("West US")
            .resourceGroupName("myResourceGroup")
            .sharingProfile(SharingProfileArgs.builder()
                .communityGalleryInfo(CommunityGalleryInfoArgs.builder()
                    .eula("eula")
                    .publicNamePrefix("PirPublic")
                    .publisherContact("pir@microsoft.com")
                    .publisherUri("uri")
                    .build())
                .permissions("Community")
                .build())
            .build());

    }
}
resources:
  gallery:
    type: azure-native:compute:Gallery
    properties:
      description: This is the gallery description.
      galleryName: myGalleryName
      location: West US
      resourceGroupName: myResourceGroup
      sharingProfile:
        communityGalleryInfo:
          eula: eula
          publicNamePrefix: PirPublic
          publisherContact: pir@microsoft.com
          publisherUri: uri
        permissions: Community

Community galleries require communityGalleryInfo with publisher contact details, a public name prefix for discoverability, and licensing terms (eula). Setting permissions to “Community” makes images available to all Azure users. The publicNamePrefix appears in the Azure Marketplace catalog.

Enable soft deletion for accidental removal protection

Production galleries often enable soft deletion to prevent permanent data loss from accidental deletions.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const gallery = new azure_native.compute.Gallery("gallery", {
    description: "This is the gallery description.",
    galleryName: "myGalleryName",
    location: "West US",
    resourceGroupName: "myResourceGroup",
    softDeletePolicy: {
        isSoftDeleteEnabled: true,
    },
});
import pulumi
import pulumi_azure_native as azure_native

gallery = azure_native.compute.Gallery("gallery",
    description="This is the gallery description.",
    gallery_name="myGalleryName",
    location="West US",
    resource_group_name="myResourceGroup",
    soft_delete_policy={
        "is_soft_delete_enabled": True,
    })
package main

import (
	compute "github.com/pulumi/pulumi-azure-native-sdk/compute/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewGallery(ctx, "gallery", &compute.GalleryArgs{
			Description:       pulumi.String("This is the gallery description."),
			GalleryName:       pulumi.String("myGalleryName"),
			Location:          pulumi.String("West US"),
			ResourceGroupName: pulumi.String("myResourceGroup"),
			SoftDeletePolicy: &compute.SoftDeletePolicyArgs{
				IsSoftDeleteEnabled: pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var gallery = new AzureNative.Compute.Gallery("gallery", new()
    {
        Description = "This is the gallery description.",
        GalleryName = "myGalleryName",
        Location = "West US",
        ResourceGroupName = "myResourceGroup",
        SoftDeletePolicy = new AzureNative.Compute.Inputs.SoftDeletePolicyArgs
        {
            IsSoftDeleteEnabled = true,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.compute.Gallery;
import com.pulumi.azurenative.compute.GalleryArgs;
import com.pulumi.azurenative.compute.inputs.SoftDeletePolicyArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var gallery = new Gallery("gallery", GalleryArgs.builder()
            .description("This is the gallery description.")
            .galleryName("myGalleryName")
            .location("West US")
            .resourceGroupName("myResourceGroup")
            .softDeletePolicy(SoftDeletePolicyArgs.builder()
                .isSoftDeleteEnabled(true)
                .build())
            .build());

    }
}
resources:
  gallery:
    type: azure-native:compute:Gallery
    properties:
      description: This is the gallery description.
      galleryName: myGalleryName
      location: West US
      resourceGroupName: myResourceGroup
      softDeletePolicy:
        isSoftDeleteEnabled: true

The softDeletePolicy property controls deletion behavior. When isSoftDeleteEnabled is true, deleted galleries enter a soft-deleted state rather than being permanently removed, allowing recovery within Azure’s retention window.

Beyond these examples

These snippets focus on specific gallery-level features: private and shared gallery creation, community publishing with metadata, and soft deletion protection. They’re intentionally minimal rather than full image management solutions.

The examples may reference pre-existing infrastructure such as Azure resource groups and Azure subscriptions with appropriate permissions. They focus on configuring the gallery rather than provisioning everything around it.

To keep things focused, common gallery patterns are omitted, including:

  • Identity configuration for managed identities
  • Resource tagging for organization and cost tracking
  • Gallery image definitions and versions (separate resources)
  • RBAC permissions for gallery access control

These omissions are intentional: the goal is to illustrate how each gallery feature is wired, not provide drop-in image management modules. See the Gallery resource reference for all available configuration options.

Let's create Azure Shared Image Galleries

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Gallery Configuration & Immutability
What properties can't I change after creating a gallery?
The location, galleryName, and resourceGroupName properties are immutable and cannot be modified after creation. Changes to these properties require recreating the gallery.
What's the minimal configuration needed to create a gallery?
You need four properties: description, galleryName, location, and resourceGroupName.
Can I update my gallery's description after creation?
Yes, the description property is updatable and can be modified after the gallery is created.
Sharing & Permissions
What are the sharing permission options for my gallery?
You can set sharingProfile.permissions to either Community (public sharing) or Groups (sharing with specific groups).
How do I create a community gallery?

Set sharingProfile.permissions to Community and provide communityGalleryInfo with these required fields:

  1. eula - End-user license agreement
  2. publicNamePrefix - Public name prefix for the gallery
  3. publisherContact - Publisher contact email
  4. publisherUri - Publisher URI
Soft Deletion & API Versioning
Can I enable soft deletion on my gallery?
Yes, set softDeletePolicy.isSoftDeleteEnabled to true to enable soft deletion for the gallery.
How do I use a different API version for this resource?
Generate a local SDK package using the CLI command pulumi package add azure-native compute [ApiVersion]. Available versions include 2022-03-03, 2022-08-03, and 2023-07-03.

Using a different cloud?

Explore compute guides for other cloud providers: