Skip to main content

Writing the Terraform Files

Your .tf files describe the infrastructure to be provisioned. Amnify treats them as standard Terraform — there is no DSL, no preprocessor, and no Amnify-specific syntax. There are, however, a small number of conventions and constraints you must follow so that Amnify can run them inside its execution environment.


Required File Structure

A template directory must contain at least one .tf file (conventionally main.tf). All .tf files in the directory are downloaded together at deploy time and treated as a single Terraform module.

A complete main.tf typically contains five sections:

  1. The terraform { required_providers { ... } } block declaring providers and versions.
  2. The provider "..." { ... } block configuring the provider.
  3. variable blocks for every input.
  4. resource blocks for the infrastructure being provisioned.
  5. output blocks for values to surface back to users.

You can split these across multiple files (e.g. providers.tf, variables.tf, outputs.tf). The shipped Amnify templates use a single main.tf for simplicity.


Provider Setup

Azure

terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}

provider "azurerm" {
features {}
subscription_id = var.subscription_id
}

The subscription_id is auto-injected by Amnify from the project's environment configuration. You only need to declare a variable "subscription_id" of type string — you do not need to expose it in template.json.

AWS

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = var.region
}

The region is auto-injected from the environment configuration (you can still declare it in template.json if you want users to override it per deployment — see how aws/vpc/template.json does this with an options array).


Variables Auto-Injected by Amnify

Amnify merges these into terraform.tfvars.json at deploy time. Declare a matching variable block in your .tf file so Terraform accepts the value, but you do not need to (and should not) duplicate them in template.json:

VariableProviderSourceType
subscription_idAzureProject's Azure environment configstring
regionAWSProject's AWS environment configstring
locationAzureProject default; overridable per deploymentstring
tagsAllProject tag policy + deployment metadatamap(string)

If you want users to override one of these per deployment (e.g. a custom AWS region), add it to the variables array in template.json. The user-supplied value takes precedence over the auto-injected one in the merge.


Rules Amnify Imposes

These rules exist because Amnify executes terraform init and terraform apply for you. Violating them will cause init to fail or the wrong behaviour at runtime.

Do not declare a backend block

Amnify injects its own remote backend (Azure Storage) during terraform init. If you add your own backend block, init will fail or your state will go to the wrong place.

# ❌ DO NOT DO THIS
terraform {
backend "s3" { ... }
}

# ❌ DO NOT DO THIS
terraform {
backend "azurerm" { ... }
}

Variable names must match template.json

Every entry in the variables array of template.json must correspond to a variable block in your .tf files with the same name. Amnify writes the user-supplied values to terraform.tfvars.json keyed by name, and Terraform will fail if the variable is not declared.

Use snake_case for variable names

All shipped templates use snake_case (resource_group_name, vpc_id, admin_password). The UI does not care, but consistency makes deployments easier to read in logs and the state file.

Do not rely on .tf sensitive = true for secrets

Setting sensitive = true on a Terraform variable block only affects Terraform's own log output. It does not mark the variable as a secret in Amnify. To get UI masking and at-rest encryption, set "sensitive": true in template.json.

# This alone is NOT enough for Amnify to treat the value as a secret:
variable "admin_password" {
type = string
sensitive = true # only affects Terraform CLI output
}

validation blocks are honoured but not the primary check

Terraform's variable block can include validation { condition = ... } rules. These run at terraform plan time and will fail the deployment if violated. They are useful as a last-resort safety net, but the primary validation should live in template.json (via required, options, min_items, etc.) so that errors surface in the UI before plan is even attempted.


Outputs

Anything you declare in an output block is captured by Amnify after terraform apply and stored on the deployment run. Users can see the values in the run detail view, and downstream pipelines can consume them.

output "resource_group_id" {
value = azurerm_resource_group.main.id
}

output "resource_group_name" {
value = azurerm_resource_group.main.name
}

Mark sensitive outputs with sensitive = true if you do not want them displayed in plaintext in the UI:

output "connection_string" {
value = azurerm_postgresql_flexible_server.main.fqdn
sensitive = true
}

Validating Locally

Before pushing, validate your template offline:

cd templates/<provider>/<my-template>
terraform init -backend=false
terraform validate

-backend=false skips the backend init (since you do not declare one and Amnify injects it at deploy time). terraform validate will catch syntax errors and undeclared variable references, which are the two most common authoring mistakes.


Next Steps