Deploy Azure Resource Manager Templates

The azure-native:resources:Deployment resource, part of the Pulumi Azure Native provider, executes ARM template deployments within a resource group, managing the lifecycle of Azure resources defined in the template. This guide focuses on three capabilities: template deployment from external URIs and Template Specs, automatic rollback on deployment failure, and deployment modes and error handling.

Deployments require an existing resource group and reference ARM templates stored as URLs, Template Specs, or inline JSON. The examples are intentionally small. Combine them with your own templates, parameter files, and resource groups.

Deploy a template from a URI with SAS token

Most Azure deployments reference ARM templates stored in external locations like Azure Storage or GitHub. When templates live in private storage accounts, you need a SAS token to authenticate access.

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

const deployment = new azure_native.resources.Deployment("deployment", {
    deploymentName: "my-deployment",
    properties: {
        mode: azure_native.resources.DeploymentMode.Incremental,
        parameters: {},
        templateLink: {
            queryString: "sv=2019-02-02&st=2019-04-29T22%3A18%3A26Z&se=2019-04-30T02%3A23%3A26Z&sr=b&sp=rw&sip=168.1.5.60-168.1.5.70&spr=https&sig=xxxxxxxx0xxxxxxxxxxxxx%2bxxxxxxxxxxxxxxxxxxxx%3d",
            uri: "https://example.com/exampleTemplate.json",
        },
    },
    resourceGroupName: "my-resource-group",
});
import pulumi
import pulumi_azure_native as azure_native

deployment = azure_native.resources.Deployment("deployment",
    deployment_name="my-deployment",
    properties={
        "mode": azure_native.resources.DeploymentMode.INCREMENTAL,
        "parameters": {},
        "template_link": {
            "query_string": "sv=2019-02-02&st=2019-04-29T22%3A18%3A26Z&se=2019-04-30T02%3A23%3A26Z&sr=b&sp=rw&sip=168.1.5.60-168.1.5.70&spr=https&sig=xxxxxxxx0xxxxxxxxxxxxx%2bxxxxxxxxxxxxxxxxxxxx%3d",
            "uri": "https://example.com/exampleTemplate.json",
        },
    },
    resource_group_name="my-resource-group")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := resources.NewDeployment(ctx, "deployment", &resources.DeploymentArgs{
			DeploymentName: pulumi.String("my-deployment"),
			Properties: &resources.DeploymentPropertiesArgs{
				Mode:       resources.DeploymentModeIncremental,
				Parameters: resources.DeploymentParameterMap{},
				TemplateLink: &resources.TemplateLinkArgs{
					QueryString: pulumi.String("sv=2019-02-02&st=2019-04-29T22%3A18%3A26Z&se=2019-04-30T02%3A23%3A26Z&sr=b&sp=rw&sip=168.1.5.60-168.1.5.70&spr=https&sig=xxxxxxxx0xxxxxxxxxxxxx%2bxxxxxxxxxxxxxxxxxxxx%3d"),
					Uri:         pulumi.String("https://example.com/exampleTemplate.json"),
				},
			},
			ResourceGroupName: pulumi.String("my-resource-group"),
		})
		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 deployment = new AzureNative.Resources.Deployment("deployment", new()
    {
        DeploymentName = "my-deployment",
        Properties = new AzureNative.Resources.Inputs.DeploymentPropertiesArgs
        {
            Mode = AzureNative.Resources.DeploymentMode.Incremental,
            Parameters = null,
            TemplateLink = new AzureNative.Resources.Inputs.TemplateLinkArgs
            {
                QueryString = "sv=2019-02-02&st=2019-04-29T22%3A18%3A26Z&se=2019-04-30T02%3A23%3A26Z&sr=b&sp=rw&sip=168.1.5.60-168.1.5.70&spr=https&sig=xxxxxxxx0xxxxxxxxxxxxx%2bxxxxxxxxxxxxxxxxxxxx%3d",
                Uri = "https://example.com/exampleTemplate.json",
            },
        },
        ResourceGroupName = "my-resource-group",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.resources.Deployment;
import com.pulumi.azurenative.resources.DeploymentArgs;
import com.pulumi.azurenative.resources.inputs.DeploymentPropertiesArgs;
import com.pulumi.azurenative.resources.inputs.TemplateLinkArgs;
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 deployment = new Deployment("deployment", DeploymentArgs.builder()
            .deploymentName("my-deployment")
            .properties(DeploymentPropertiesArgs.builder()
                .mode("Incremental")
                .parameters(Map.ofEntries(
                ))
                .templateLink(TemplateLinkArgs.builder()
                    .queryString("sv=2019-02-02&st=2019-04-29T22%3A18%3A26Z&se=2019-04-30T02%3A23%3A26Z&sr=b&sp=rw&sip=168.1.5.60-168.1.5.70&spr=https&sig=xxxxxxxx0xxxxxxxxxxxxx%2bxxxxxxxxxxxxxxxxxxxx%3d")
                    .uri("https://example.com/exampleTemplate.json")
                    .build())
                .build())
            .resourceGroupName("my-resource-group")
            .build());

    }
}
resources:
  deployment:
    type: azure-native:resources:Deployment
    properties:
      deploymentName: my-deployment
      properties:
        mode: Incremental
        parameters: {}
        templateLink:
          queryString: sv=2019-02-02&st=2019-04-29T22%3A18%3A26Z&se=2019-04-30T02%3A23%3A26Z&sr=b&sp=rw&sip=168.1.5.60-168.1.5.70&spr=https&sig=xxxxxxxx0xxxxxxxxxxxxx%2bxxxxxxxxxxxxxxxxxxxx%3d
          uri: https://example.com/exampleTemplate.json
      resourceGroupName: my-resource-group

The templateLink property points to your template’s location. The uri specifies the template URL, while queryString provides the SAS token for authentication. The mode property controls how Azure applies the template: Incremental adds or updates resources without touching existing ones, while Complete removes resources not defined in the template.

Deploy a versioned template spec by resource ID

Template Specs store and version ARM templates as Azure resources, making them easier to share across teams and enforce governance.

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

const deployment = new azure_native.resources.Deployment("deployment", {
    deploymentName: "my-deployment",
    properties: {
        mode: azure_native.resources.DeploymentMode.Incremental,
        parameters: {},
        templateLink: {
            id: "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/my-resource-group/providers/Microsoft.Resources/TemplateSpecs/TemplateSpec-Name/versions/v1",
        },
    },
    resourceGroupName: "my-resource-group",
});
import pulumi
import pulumi_azure_native as azure_native

deployment = azure_native.resources.Deployment("deployment",
    deployment_name="my-deployment",
    properties={
        "mode": azure_native.resources.DeploymentMode.INCREMENTAL,
        "parameters": {},
        "template_link": {
            "id": "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/my-resource-group/providers/Microsoft.Resources/TemplateSpecs/TemplateSpec-Name/versions/v1",
        },
    },
    resource_group_name="my-resource-group")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := resources.NewDeployment(ctx, "deployment", &resources.DeploymentArgs{
			DeploymentName: pulumi.String("my-deployment"),
			Properties: &resources.DeploymentPropertiesArgs{
				Mode:       resources.DeploymentModeIncremental,
				Parameters: resources.DeploymentParameterMap{},
				TemplateLink: &resources.TemplateLinkArgs{
					Id: pulumi.String("/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/my-resource-group/providers/Microsoft.Resources/TemplateSpecs/TemplateSpec-Name/versions/v1"),
				},
			},
			ResourceGroupName: pulumi.String("my-resource-group"),
		})
		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 deployment = new AzureNative.Resources.Deployment("deployment", new()
    {
        DeploymentName = "my-deployment",
        Properties = new AzureNative.Resources.Inputs.DeploymentPropertiesArgs
        {
            Mode = AzureNative.Resources.DeploymentMode.Incremental,
            Parameters = null,
            TemplateLink = new AzureNative.Resources.Inputs.TemplateLinkArgs
            {
                Id = "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/my-resource-group/providers/Microsoft.Resources/TemplateSpecs/TemplateSpec-Name/versions/v1",
            },
        },
        ResourceGroupName = "my-resource-group",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.resources.Deployment;
import com.pulumi.azurenative.resources.DeploymentArgs;
import com.pulumi.azurenative.resources.inputs.DeploymentPropertiesArgs;
import com.pulumi.azurenative.resources.inputs.TemplateLinkArgs;
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 deployment = new Deployment("deployment", DeploymentArgs.builder()
            .deploymentName("my-deployment")
            .properties(DeploymentPropertiesArgs.builder()
                .mode("Incremental")
                .parameters(Map.ofEntries(
                ))
                .templateLink(TemplateLinkArgs.builder()
                    .id("/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/my-resource-group/providers/Microsoft.Resources/TemplateSpecs/TemplateSpec-Name/versions/v1")
                    .build())
                .build())
            .resourceGroupName("my-resource-group")
            .build());

    }
}
resources:
  deployment:
    type: azure-native:resources:Deployment
    properties:
      deploymentName: my-deployment
      properties:
        mode: Incremental
        parameters: {}
        templateLink:
          id: /subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/my-resource-group/providers/Microsoft.Resources/TemplateSpecs/TemplateSpec-Name/versions/v1
      resourceGroupName: my-resource-group

Instead of a URI, the templateLink.id property references a Template Spec by its full resource ID, including the version path. This approach centralizes template management and ensures teams deploy approved, versioned infrastructure definitions.

Roll back to a specific deployment on failure

Production deployments often need automatic rollback strategies to minimize downtime when new changes fail validation or cause runtime errors.

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

const deployment = new azure_native.resources.Deployment("deployment", {
    deploymentName: "my-deployment",
    properties: {
        mode: azure_native.resources.DeploymentMode.Complete,
        onErrorDeployment: {
            deploymentName: "name-of-deployment-to-use",
            type: azure_native.resources.OnErrorDeploymentType.SpecificDeployment,
        },
        parameters: {},
        templateLink: {
            uri: "https://example.com/exampleTemplate.json",
        },
    },
    resourceGroupName: "my-resource-group",
});
import pulumi
import pulumi_azure_native as azure_native

deployment = azure_native.resources.Deployment("deployment",
    deployment_name="my-deployment",
    properties={
        "mode": azure_native.resources.DeploymentMode.COMPLETE,
        "on_error_deployment": {
            "deployment_name": "name-of-deployment-to-use",
            "type": azure_native.resources.OnErrorDeploymentType.SPECIFIC_DEPLOYMENT,
        },
        "parameters": {},
        "template_link": {
            "uri": "https://example.com/exampleTemplate.json",
        },
    },
    resource_group_name="my-resource-group")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := resources.NewDeployment(ctx, "deployment", &resources.DeploymentArgs{
			DeploymentName: pulumi.String("my-deployment"),
			Properties: &resources.DeploymentPropertiesArgs{
				Mode: resources.DeploymentModeComplete,
				OnErrorDeployment: &resources.OnErrorDeploymentArgs{
					DeploymentName: pulumi.String("name-of-deployment-to-use"),
					Type:           resources.OnErrorDeploymentTypeSpecificDeployment,
				},
				Parameters: resources.DeploymentParameterMap{},
				TemplateLink: &resources.TemplateLinkArgs{
					Uri: pulumi.String("https://example.com/exampleTemplate.json"),
				},
			},
			ResourceGroupName: pulumi.String("my-resource-group"),
		})
		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 deployment = new AzureNative.Resources.Deployment("deployment", new()
    {
        DeploymentName = "my-deployment",
        Properties = new AzureNative.Resources.Inputs.DeploymentPropertiesArgs
        {
            Mode = AzureNative.Resources.DeploymentMode.Complete,
            OnErrorDeployment = new AzureNative.Resources.Inputs.OnErrorDeploymentArgs
            {
                DeploymentName = "name-of-deployment-to-use",
                Type = AzureNative.Resources.OnErrorDeploymentType.SpecificDeployment,
            },
            Parameters = null,
            TemplateLink = new AzureNative.Resources.Inputs.TemplateLinkArgs
            {
                Uri = "https://example.com/exampleTemplate.json",
            },
        },
        ResourceGroupName = "my-resource-group",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.resources.Deployment;
import com.pulumi.azurenative.resources.DeploymentArgs;
import com.pulumi.azurenative.resources.inputs.DeploymentPropertiesArgs;
import com.pulumi.azurenative.resources.inputs.OnErrorDeploymentArgs;
import com.pulumi.azurenative.resources.inputs.TemplateLinkArgs;
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 deployment = new Deployment("deployment", DeploymentArgs.builder()
            .deploymentName("my-deployment")
            .properties(DeploymentPropertiesArgs.builder()
                .mode("Complete")
                .onErrorDeployment(OnErrorDeploymentArgs.builder()
                    .deploymentName("name-of-deployment-to-use")
                    .type("SpecificDeployment")
                    .build())
                .parameters(Map.ofEntries(
                ))
                .templateLink(TemplateLinkArgs.builder()
                    .uri("https://example.com/exampleTemplate.json")
                    .build())
                .build())
            .resourceGroupName("my-resource-group")
            .build());

    }
}
resources:
  deployment:
    type: azure-native:resources:Deployment
    properties:
      deploymentName: my-deployment
      properties:
        mode: Complete
        onErrorDeployment:
          deploymentName: name-of-deployment-to-use
          type: SpecificDeployment
        parameters: {}
        templateLink:
          uri: https://example.com/exampleTemplate.json
      resourceGroupName: my-resource-group

The onErrorDeployment property defines rollback behavior. When type is SpecificDeployment, Azure redeploys the deployment named in deploymentName if the current deployment fails. This provides a safety net for risky changes.

Roll back to the last successful deployment on failure

When you don’t know which specific deployment to roll back to, Azure can automatically identify and redeploy the most recent successful deployment.

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

const deployment = new azure_native.resources.Deployment("deployment", {
    deploymentName: "my-deployment",
    properties: {
        mode: azure_native.resources.DeploymentMode.Complete,
        onErrorDeployment: {
            type: azure_native.resources.OnErrorDeploymentType.LastSuccessful,
        },
        parameters: {},
        templateLink: {
            uri: "https://example.com/exampleTemplate.json",
        },
    },
    resourceGroupName: "my-resource-group",
});
import pulumi
import pulumi_azure_native as azure_native

deployment = azure_native.resources.Deployment("deployment",
    deployment_name="my-deployment",
    properties={
        "mode": azure_native.resources.DeploymentMode.COMPLETE,
        "on_error_deployment": {
            "type": azure_native.resources.OnErrorDeploymentType.LAST_SUCCESSFUL,
        },
        "parameters": {},
        "template_link": {
            "uri": "https://example.com/exampleTemplate.json",
        },
    },
    resource_group_name="my-resource-group")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := resources.NewDeployment(ctx, "deployment", &resources.DeploymentArgs{
			DeploymentName: pulumi.String("my-deployment"),
			Properties: &resources.DeploymentPropertiesArgs{
				Mode: resources.DeploymentModeComplete,
				OnErrorDeployment: &resources.OnErrorDeploymentArgs{
					Type: resources.OnErrorDeploymentTypeLastSuccessful,
				},
				Parameters: resources.DeploymentParameterMap{},
				TemplateLink: &resources.TemplateLinkArgs{
					Uri: pulumi.String("https://example.com/exampleTemplate.json"),
				},
			},
			ResourceGroupName: pulumi.String("my-resource-group"),
		})
		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 deployment = new AzureNative.Resources.Deployment("deployment", new()
    {
        DeploymentName = "my-deployment",
        Properties = new AzureNative.Resources.Inputs.DeploymentPropertiesArgs
        {
            Mode = AzureNative.Resources.DeploymentMode.Complete,
            OnErrorDeployment = new AzureNative.Resources.Inputs.OnErrorDeploymentArgs
            {
                Type = AzureNative.Resources.OnErrorDeploymentType.LastSuccessful,
            },
            Parameters = null,
            TemplateLink = new AzureNative.Resources.Inputs.TemplateLinkArgs
            {
                Uri = "https://example.com/exampleTemplate.json",
            },
        },
        ResourceGroupName = "my-resource-group",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.resources.Deployment;
import com.pulumi.azurenative.resources.DeploymentArgs;
import com.pulumi.azurenative.resources.inputs.DeploymentPropertiesArgs;
import com.pulumi.azurenative.resources.inputs.OnErrorDeploymentArgs;
import com.pulumi.azurenative.resources.inputs.TemplateLinkArgs;
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 deployment = new Deployment("deployment", DeploymentArgs.builder()
            .deploymentName("my-deployment")
            .properties(DeploymentPropertiesArgs.builder()
                .mode("Complete")
                .onErrorDeployment(OnErrorDeploymentArgs.builder()
                    .type("LastSuccessful")
                    .build())
                .parameters(Map.ofEntries(
                ))
                .templateLink(TemplateLinkArgs.builder()
                    .uri("https://example.com/exampleTemplate.json")
                    .build())
                .build())
            .resourceGroupName("my-resource-group")
            .build());

    }
}
resources:
  deployment:
    type: azure-native:resources:Deployment
    properties:
      deploymentName: my-deployment
      properties:
        mode: Complete
        onErrorDeployment:
          type: LastSuccessful
        parameters: {}
        templateLink:
          uri: https://example.com/exampleTemplate.json
      resourceGroupName: my-resource-group

Setting onErrorDeployment.type to LastSuccessful tells Azure to find and redeploy the most recent successful deployment automatically. This is simpler than tracking deployment names manually but requires at least one successful deployment in the resource group’s history.

Beyond these examples

These snippets focus on specific deployment-level features: template deployment from URIs and Template Specs, error handling and automatic rollback, and deployment modes. They’re intentionally minimal rather than full infrastructure deployments.

The examples may reference pre-existing infrastructure such as resource groups, ARM templates or Template Specs, and previous successful deployments for rollback scenarios. They focus on configuring the deployment rather than provisioning the templates or resource groups themselves.

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

  • Template parameters and parameter files
  • Inline template definitions (template property)
  • Deployment location and tagging
  • What-if validation and deployment scopes

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

Let's deploy Azure Resource Manager Templates

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Deployment Configuration
What happens if my resource group doesn't exist when I create a deployment?
The deployment will fail. The resource group must already exist before creating the deployment.
What properties can't I change after creating a deployment?
Both deploymentName and resourceGroupName are immutable. You’ll need to delete and recreate the deployment to change either property.
What's the difference between Incremental and Complete deployment modes?
Incremental mode adds or updates resources without affecting existing resources not in the template. Complete mode deletes any resources in the resource group that aren’t defined in the template.
Template Sources
How do I deploy a template from a URL with a SAS token?
Use templateLink with both uri (the template URL) and queryString (containing the SAS token parameters). The queryString keeps the token separate from the URI.
How do I deploy a TemplateSpec instead of a template URL?
Set templateLink.id to the TemplateSpec resource ID (e.g., /subscriptions/.../providers/Microsoft.Resources/TemplateSpecs/TemplateSpec-Name/versions/v1).
What's the queryString parameter used for in templateLink?
The queryString parameter holds SAS token parameters when deploying templates from Azure Storage, keeping authentication separate from the template URI.
Error Handling & Rollback
How do I configure automatic rollback if my deployment fails?

Use onErrorDeployment with type set to either:

  • LastSuccessful to rollback to the last successful deployment
  • SpecificDeployment (with deploymentName) to rollback to a specific deployment
API Versions
How do I use a different Azure API version for deployments?
Generate a local SDK package using the Pulumi CLI: pulumi package add azure-native resources [ApiVersion]. Available versions include 2020-10-01, 2021-01-01, 2021-04-01, 2022-09-01, 2023-07-01, 2024-07-01, 2024-11-01, 2025-03-01, and 2025-04-01.

Using a different cloud?

Explore compute guides for other cloud providers: