Claude-skill-registry ARM Template Functions

Expert knowledge for using Azure Resource Manager (ARM) template functions, especially reference(), listKeys(), and resourceId() in subscription-level and nested deployments. Use when working with ARM templates, encountering template validation errors, or implementing cross-scope resource references.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/arm-template-functions" ~/.claude/skills/majiayu000-claude-skill-registry-arm-template-functions && rm -rf "$T"
manifest: skills/data/arm-template-functions/SKILL.md
source content

ARM Template Functions Skill

When to Use This Skill

  • Working with Azure ARM templates (azuredeploy.json files)
  • Encountering template validation errors related to functions
  • Implementing subscription-level deployments
  • Using nested deployments with cross-scope references
  • Accessing resource properties in outputs sections

Critical Rules for ARM Template Functions

reference() Function

Valid Usage Locations:

  • ✅ Outputs section
  • ✅ Properties object of resource definitions
  • ❌ Top-level resource properties (type, name, location)
  • ❌ Count property in copy loops

Scope Considerations:

// ✅ CORRECT: Simple reference in same scope
"outputs": {
  "myOutput": {
    "value": "[reference('myResourceName').someProperty]"
  }
}

// ❌ INCORRECT: Cannot nest reference() inside other functions in outputs
"outputs": {
  "myOutput": {
    "value": "[listKeys(resourceId('rg', 'type', reference('dep').outputs.name.value))]"
  }
}

Nested Deployment References:

  • Use
    reference(deploymentName).outputs.propertyName.value
    for outputs
  • Requires
    expressionEvaluationOptions.scope: inner
    in nested deployment
  • Cannot use
    reference()
    inside
    resourceId()
    or
    listKeys()
    in outputs

Conditional Deployment Warning:

  • reference()
    evaluates even if resource is conditionally not deployed
  • This can cause deployment failures
  • Always ensure referenced resources exist

listKeys() Function

Requirements:

  • Can only be used in outputs section or resource properties
  • Requires fully qualified resource ID
  • Resource must exist before evaluation
  • Requires correct API version

Valid Pattern:

"outputs": {
  "storageKey": {
    "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-01-01').keys[0].value]"
  }
}

Security Warning:

  • Never expose sensitive list functions (listKeys, listSecrets) in outputs
  • Output values are stored in deployment history
  • Anyone with read access to deployment can see outputs

resourceId() Function Scope Variations

Resource Group Scope (default):

"[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]"
// Returns: /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{name}

Cross-Resource Group:

"[resourceId(parameters('resourceGroupName'), 'Microsoft.Storage/storageAccounts', parameters('name'))]"

Subscription Scope:

"[subscriptionResourceId('Microsoft.EventHub/namespaces', parameters('name'))]"
// Returns: /subscriptions/{sub}/providers/Microsoft.EventHub/namespaces/{name}

⚠️ CRITICAL: Cross-Scope from Subscription Template to Resource Group Resources

When in a subscription-level template referencing resources IN a resource group:

// ❌ WRONG - Will fail with "not valid subscription identifier"
"[resourceId(parameters('resourceGroupName'), 'Microsoft.Storage/storageAccounts', parameters('name'))]"

// ✅ CORRECT - Must include subscription ID
"[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Storage/storageAccounts', parameters('name'))]"

This applies to:

  • listKeys() calls
  • Any resourceId() in outputs or properties
  • Diagnostic settings eventHubAuthorizationRuleId

Management Group/Tenant:

"[tenantResourceId('Microsoft.Authorization/policyDefinitions', parameters('name'))]"

⚠️ Critical: In subscription-level templates, use

subscriptionResourceId()
NOT
resourceId()
for subscription-scoped resources.

Common Errors and Solutions

Error: "The template function 'reference' is not expected at this location"

Cause: Using

reference()
inside another function in outputs section

Solution: Store the referenced value in variables, or restructure to avoid nested reference calls:

// ❌ INCORRECT
"outputs": {
  "connectionString": {
    "value": "[listKeys(resourceId('rg', 'type', reference('deployment').outputs.name.value))]"
  }
}

// ✅ CORRECT: Use parameters or variables for the name
"outputs": {
  "connectionString": {
    "value": "[listKeys(resourceId(parameters('resourceGroupName'), 'Microsoft.EventHub/namespaces/eventhubs/authorizationRules', parameters('namespace'), parameters('hub'), parameters('ruleName')), '2022-10-01-preview').primaryConnectionString]"
  }
}

// ✅ ALTERNATIVE: Use nested deployment outputs for simple values only
"outputs": {
  "simpleName": {
    "value": "[reference('deploymentName').outputs.nameOutput.value]"
  }
}

Error: "The content for this response was already consumed"

Cause: Azure CLI bug in versions 2.74.0 and earlier with subscription-level template validation

Solution:

# Update Azure CLI
brew upgrade azure-cli  # macOS
# or
apt-get update && apt-get upgrade azure-cli  # Linux

Error: Resource not found in outputs

Cause: Incorrect scope function or missing resource group parameter

Solution: Match the scope function to deployment level:

  • Resource group deployment →
    resourceId()
  • Subscription deployment →
    subscriptionResourceId()
    or
    resourceId(resourceGroupName, ...)
  • Management group deployment →
    managementGroupResourceId()

Best Practices for Nested Deployments

Subscription-Level Template with Resource Group Nested Deployment

{
  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
  "resources": [
    {
      "type": "Microsoft.Resources/deployments",
      "name": "nestedDeployment",
      "resourceGroup": "[parameters('resourceGroupName')]",
      "properties": {
        "mode": "Incremental",
        "expressionEvaluationOptions": {
          "scope": "inner"  // Critical for reference() in nested outputs
        },
        "template": {
          // Inner template deploys to resource group
          "outputs": {
            "resourceName": {
              "value": "[parameters('name')]"  // Simple values work
            }
          }
        }
      }
    }
  ],
  "outputs": {
    // Access nested outputs
    "name": {
      "value": "[reference('nestedDeployment').outputs.resourceName.value]"
    },

    // Use outputs in other functions - pass as parameters, not nested reference
    "connectionString": {
      "value": "[listKeys(resourceId(parameters('resourceGroupName'), 'Microsoft.Storage/storageAccounts', parameters('storageName')), '2023-01-01').keys[0].value]"
    }
  }
}

Key Pattern: Avoid Nested Function Calls

The Golden Rule: In outputs, DO NOT nest

reference()
inside
listKeys()
or
resourceId()
.

Instead:

  1. Pass resource names as parameters
  2. Store complex expressions in variables (where allowed)
  3. Use nested deployment outputs only for simple string/number values
  4. Build resource IDs with parameters, not with reference() results

Validation Commands

# Validate subscription-level template
az deployment sub validate \
  --location eastus \
  --template-file azuredeploy.json \
  --parameters @parameters.json

# Validate resource group template
az deployment group validate \
  --resource-group myResourceGroup \
  --template-file azuredeploy.json \
  --parameters @parameters.json

# What-if preview (subscription level)
az deployment sub what-if \
  --location eastus \
  --template-file azuredeploy.json \
  --parameters @parameters.json

Additional Resources