Why Azure Teams Are Moving from ARM Templates to .NET

Posted on

Azure Resource Manager (ARM) templates are powerful, but painful. If you’ve ever tried to provision cloud infrastructure using ARM, you know the challenges:

  • Templates that started simple… and now span thousands of lines
  • Manual configuration stitched together with bespoke deployment logic
  • Lack of support for key services like Databricks
  • Slow, error-prone deployments that require multiple manual steps
  • No reuse, no testing, and no relief

ARM wasn’t built for the complexity of modern Azure workloads. If you’re already familiar with general-purpose languages, there’s a better path: Pulumi.

Top Pain Points of ARM Templates

Pain PointARM TemplatesPulumi (C#)
LanguageJSONC#, Python, TypeScript, or Go
Code ReuseNone—copy/paste onlyClasses, functions, modules
LogicComplex condition syntaxif / switch / for loops
IDE SupportVery limitedFull IntelliSense and refactoring
TestingNoneUnit tests with xUnit/NUnit
DebuggingPost-deploy troubleshootingStep-through debugging in IDE
RefactoringManual editsStandard IDE workflows

Pulumi: The Obvious Upgrade for .NET and Azure

Pulumi solves these problems at their root. It lets you define your Azure infrastructure using C#, the same language you’re already using to build your applications. With Pulumi, you get:

✅ Familiar programming languages
✅ Type safety and compile-time validation
✅ Full IDE support (IntelliSense, refactoring, debugging)
✅ Built-in support for Databricks, Kubernetes, and more
✅ Automated config and secrets management
✅ Easy testing, reuse, and CI/CD integration

From Static Templates to Software Engineering

Here’s an example of how different it is to build a storage account:

⛔ ARM Template (JSON)

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "type": "string",
      "metadata": { "description": "Globally unique, 3–24 lowercase letters and numbers." }
    }
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2021-09-01",
      "name": "[parameters('storageAccountName')]",
      "location": "[resourceGroup().location]",
      "sku": { "name": "Standard_LRS" },
      "kind": "StorageV2",
      "properties": {}
    }
  ],
  "outputs": {
    "storageAccountId": {
      "type": "string",
      "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
    }
  }
}

✅ Azure with Pulumi in C#

using Pulumi;
using Pulumi.AzureNative.Storage;

class MyStack : Stack
{
    public MyStack()
    {
        var storageAccount = new StorageAccount("mystorage", new StorageAccountArgs
        {
            ResourceGroupName = "my-rg",
            Sku = new SkuArgs { Name = SkuName.Standard_LRS },
            Kind = Kind.StorageV2
        });
    }
}

It’s clean. It’s familiar. And it works seamlessly with your .NET tooling. Pulumi is open source, and your Pulumi Cloud account is free for individuals and small teams, with advanced editions for large enterprises. Try Pulumi Cloud for FREE->

Why It Just Works for .NET Teams

If your organization is built on Azure and .NET:

  • Use C# across both infrastructure and application layers
  • Share libraries and logic between services
  • Test infrastructure the same way you test code
  • Integrate with Azure DevOps and GitHub Actions
  • Manage secrets, environments, and policies as code

Pulumi supports all the Azure services ARM does (and more), while giving you flexibility and control ARM never could.

Summary

ARM templates weren’t designed to scale with the complexity of today’s cloud environments. They’re static, verbose, hard to test, and increasingly brittle.

Pulumi gives you the tools to manage Azure the way you manage software: modular, testable, scalable, and secure.

If you’re already building with C# and .NET, you’re 90% of the way there. Why suffer through another slow, error-prone ARM deployment?