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.
Create a private gallery for custom images
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 FREEFrequently Asked Questions
Gallery Configuration & Immutability
location, galleryName, and resourceGroupName properties are immutable and cannot be modified after creation. Changes to these properties require recreating the gallery.description, galleryName, location, and resourceGroupName.description property is updatable and can be modified after the gallery is created.Sharing & Permissions
sharingProfile.permissions to either Community (public sharing) or Groups (sharing with specific groups).Set sharingProfile.permissions to Community and provide communityGalleryInfo with these required fields:
eula- End-user license agreementpublicNamePrefix- Public name prefix for the gallerypublisherContact- Publisher contact emailpublisherUri- Publisher URI
Soft Deletion & API Versioning
softDeletePolicy.isSoftDeleteEnabled to true to enable soft deletion for the gallery.pulumi package add azure-native compute [ApiVersion]. Available versions include 2022-03-03, 2022-08-03, and 2023-07-03.