Create Azure Shared Image Galleries

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

Galleries belong to a resource group and require a subscription. Image definitions and versions are created as separate child resources. The examples are intentionally small. Combine them with your own image definitions, RBAC policies, and tagging strategy.

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. By default, galleries are private to the subscription unless you configure a sharingProfile.

Teams managing multiple subscriptions often need to share images across organizational boundaries without duplicating storage.

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 scope. Setting permissions to “Groups” enables sharing with specific Azure AD groups or subscriptions within your organization. This allows centralized image management while maintaining access control.

Publish images to the Azure community

Organizations that want to share images publicly with the broader Azure community can configure community gallery settings with licensing and contact information.

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 additional metadata in the communityGalleryInfo block. The eula property specifies licensing terms, publicNamePrefix creates a discoverable name, and publisherContact provides support information. Setting permissions to “Community” makes the gallery publicly accessible.

Enable soft deletion for accidental removal protection

Production galleries benefit from soft deletion to prevent permanent data loss from accidental deletions or automation errors.

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 gallery resources enter a soft-deleted state rather than being permanently removed, allowing recovery within a retention period.

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. They focus on configuring the gallery rather than provisioning everything around it.

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

  • Gallery identity configuration (managed identity)
  • 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

Sharing & Permissions
What sharing options are available for a gallery?
You can share galleries using Community or Groups permissions via the sharingProfile property.
How do I create a community gallery?
Set sharingProfile.permissions to Community and provide communityGalleryInfo with eula, publicNamePrefix, publisherContact, and publisherUri fields.
What's required in the communityGalleryInfo configuration?
When creating a community gallery, you must provide four fields: eula (end-user license agreement), publicNamePrefix (public name for the gallery), publisherContact (contact email), and publisherUri (publisher website).
Configuration & Properties
What properties can't I change after creating a gallery?
The location, galleryName, and resourceGroupName properties are immutable and cannot be changed after creation.
Can I update the gallery description after creation?
Yes, the description property is updatable and can be changed at any time.
How do I enable soft deletion for a gallery?
Configure softDeletePolicy with isSoftDeleteEnabled set to true to enable soft deletion protection.
How do I use a different Azure API version for this resource?
The default API version is 2024-03-03. To use other available versions (2022-03-03, 2022-08-03, 2023-07-03, 2025-03-03), generate a local SDK package using pulumi package add azure-native compute [ApiVersion].

Using a different cloud?

Explore compute guides for other cloud providers: