Example — Azure Resource Group
This is a complete walkthrough of authoring the simplest possible useful template: a single Azure Resource Group. The files match the shipped tpl-azure-resource-group verbatim, so you can copy them, change the id, and ship.
By the end of this page you will have a working template that:
- Provisions one
azurerm_resource_group. - Lets the user name it.
- Lets the user pick from a curated list of Azure regions.
- Surfaces the resource group ID, name, and location as outputs.
1. Directory Layout
In your template repository:
templates/
└── azure/
└── my-resource-group/
├── main.tf
└── template.json
The directory name (my-resource-group) is what users will see in the URL slug. Keep it short and snake-case.
2. main.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {}
subscription_id = var.subscription_id
}
variable "subscription_id" {
description = "Azure Subscription ID"
type = string
}
variable "resource_group_name" {
description = "Name of the resource group"
type = string
}
variable "location" {
description = "Azure region for the resource group"
type = string
default = "West Europe"
}
variable "tags" {
description = "Tags to apply to the resource group"
type = map(string)
default = {}
}
resource "azurerm_resource_group" "main" {
name = var.resource_group_name
location = var.location
tags = var.tags
}
output "resource_group_id" {
value = azurerm_resource_group.main.id
}
output "resource_group_name" {
value = azurerm_resource_group.main.name
}
output "location" {
value = azurerm_resource_group.main.location
}
What is happening, block by block
- The
terraform { required_providers }block pins the Azure provider to~> 4.0. This locks the major version while allowing minor and patch updates. - The
provider "azurerm"block configures Azure. Thefeatures {}block is required by the provider (even when empty).subscription_idcomes from a variable so each deployment can target a different subscription. variable "subscription_id"is auto-injected by Amnify from the project's environment config. It must be declared but does not appear intemplate.json.variable "tags"is also auto-injected (project + deployment metadata). Declare it; do not duplicate it intemplate.json.variable "resource_group_name"is the only field the user must fill in.variable "location"is also user-facing, but has a sensible default ("West Europe").- The
resourceblock creates the resource group, applying the user's chosen name, location, and Amnify-injected tags. - The
outputblocks expose three values to Amnify after apply. Users see them on the deployment run page.
3. template.json
{
"id": "tpl-azure-my-resource-group",
"name": "Azure Resource Group",
"description": "Create an Azure Resource Group to organize and manage related cloud resources.",
"category": "infrastructure",
"cloud_provider": "azure",
"iac_type": "terraform",
"icon": "FolderOpen",
"variables": [
{
"name": "subscription_id",
"type": "string",
"description": "Azure Subscription ID",
"required": true,
"sensitive": false
},
{
"name": "resource_group_name",
"type": "string",
"description": "Name of the resource group",
"required": true,
"sensitive": false
},
{
"name": "location",
"type": "string",
"description": "Azure region for the resource group",
"default": "West Europe",
"required": true,
"sensitive": false,
"options": [
"West Europe",
"North Europe",
"East US",
"East US 2",
"West US 2",
"Southeast Asia",
"UK South",
"Central US",
"Germany West Central"
]
}
]
}
What is happening, field by field
id— change this fromtpl-azure-my-resource-groupto whatever stable identifier you want. It must be unique within your organization.name— display name on the template card.description— shown beneath the name in the library and on the deployment wizard.category: "infrastructure"— categorizes the template under "Infrastructure" in the library filter.cloud_provider: "azure"— must match the parent directory.iac_type: "terraform"— only supported value today.icon: "FolderOpen"— any Lucide icon name.FolderOpenreads well for a "container" resource.variables— three entries:subscription_idis exposed as a required field so the user picks the subscription. (Some templates omit this and rely entirely on the project's auto-injection — both patterns work.)resource_group_nameis a free-text required field.locationis a required field with adefaultand anoptionsarray, so it renders as a dropdown of Azure regions.
Notice that tags is in main.tf but not in template.json. That is intentional — Amnify auto-injects tags. Adding it to template.json would force users to fill it in by hand for every deployment.
4. Validate Locally
cd templates/azure/my-resource-group
terraform init -backend=false
terraform validate
Expected output:
Success! The configuration is valid.
If validate fails, the most common causes are:
- A variable name in
template.jsonthat is missing inmain.tf(or vice versa). - A typo in the provider version constraint or resource attribute.
- Missing
features {}block in theazurermprovider.
5. Publish
git add templates/azure/my-resource-group
git commit -m "Add custom Azure Resource Group template"
git push origin main
Then in Amnify:
- Navigate to Deploy → Templates.
- Click Sync Templates.
- Your new template appears in the library within a few seconds, ready for users to deploy.
What This Template Does Not Cover
This is an intentionally minimal example. To explore the full set of features Amnify supports, work through:
- Example — AWS VPC —
list(object), nestedobject,min_items, deeply nestedlist(object), and option-driven enums. - template.json Reference —
condition,resource_type,parent_field. - Variable Types & Secrets —
sensitivevariables and the encryption flow.