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.
git clone https://github.com/majiayu000/claude-skill-registry
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"
skills/data/arm-template-functions/SKILL.mdARM 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
for outputsreference(deploymentName).outputs.propertyName.value - Requires
in nested deploymentexpressionEvaluationOptions.scope: inner - Cannot use
insidereference()
orresourceId()
in outputslistKeys()
Conditional Deployment Warning:
evaluates even if resource is conditionally not deployedreference()- 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 →
orsubscriptionResourceId()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:
- Pass resource names as parameters
- Store complex expressions in variables (where allowed)
- Use nested deployment outputs only for simple string/number values
- 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