> ## Documentation Index
> Fetch the complete documentation index at: https://docs.amnify.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Variable Types & Secrets

This page covers every variable type Amnify renders, with paired `.tf` ↔ `template.json` examples, and the encryption flow for sensitive variables.

For the field-by-field manifest reference, see [template.json Reference](/cspm-deploy/create-custom-templates/template-json-reference). This page is the practical companion: how each `type` actually behaves.

***

## `string`

The default. Renders a single-line text input. With `options`, renders a dropdown. With `sensitive: true`, renders a masked password field.

### Plain string

```json theme={null}
{
  "name": "resource_group_name",
  "type": "string",
  "description": "Name of the resource group",
  "required": true,
  "sensitive": false
}
```

```hcl theme={null}
variable "resource_group_name" {
  description = "Name of the resource group"
  type        = string
}
```

### String with dropdown (`options`)

```json theme={null}
{
  "name": "location",
  "type": "string",
  "description": "Azure region",
  "default": "West Europe",
  "required": true,
  "sensitive": false,
  "options": ["West Europe", "North Europe", "East US"]
}
```

```hcl theme={null}
variable "location" {
  description = "Azure region"
  type        = string
  default     = "West Europe"
}
```

The user's choice is passed to Terraform verbatim — no transformation. Choose `options` values that are also valid Terraform inputs (e.g. AWS region codes, Azure region names).

***

## `number`

Numeric input, parsed and serialized as a JSON number.

```json theme={null}
{
  "name": "backup_retention_days",
  "type": "number",
  "description": "Backup retention (7-35 days)",
  "default": 7,
  "required": false,
  "sensitive": false
}
```

```hcl theme={null}
variable "backup_retention_days" {
  description = "Backup retention (7-35 days)"
  type        = number
  default     = 7
}
```

For range checks, use a `validation` block in your `.tf` file:

```hcl theme={null}
variable "backup_retention_days" {
  type    = number
  default = 7
  validation {
    condition     = var.backup_retention_days >= 7 && var.backup_retention_days <= 35
    error_message = "Backup retention must be between 7 and 35 days."
  }
}
```

***

## `bool`

Renders as a toggle / checkbox.

```json theme={null}
{
  "name": "enable_versioning",
  "type": "bool",
  "description": "Enable bucket versioning",
  "default": false,
  "required": true,
  "sensitive": false
}
```

```hcl theme={null}
variable "enable_versioning" {
  description = "Enable bucket versioning"
  type        = bool
  default     = false
}
```

Booleans pair well with `condition` to reveal advanced fields only when the toggle is on. See the [Conditional Visibility](/cspm-deploy/create-custom-templates/template-json-reference#condition--conditional-visibility) section.

***

## `object`

A group of nested fields. Use it when you have a logical sub-structure (e.g. a "backup config" with retention + window + geo flags) that is always present as a single unit.

```json theme={null}
{
  "name": "nacl",
  "type": "object",
  "description": "Network ACL config (omit to use VPC default)",
  "required": false,
  "sensitive": false,
  "schema": [
    {
      "name": "ingress_default_action",
      "type": "string",
      "required": true,
      "sensitive": false,
      "options": ["allow", "deny"]
    },
    {
      "name": "egress_default_action",
      "type": "string",
      "required": true,
      "sensitive": false,
      "options": ["allow", "deny"]
    }
  ]
}
```

```hcl theme={null}
variable "nacl" {
  type = object({
    ingress_default_action = string
    egress_default_action  = string
  })
  default = null
}
```

If the object is optional in `template.json` (`"required": false`), use `default = null` (or `optional(object({...}))`) in Terraform.

***

## `list(object)`

A repeating group of nested fields. Renders with **add** and **remove** controls in the UI. Use it for things like "list of subnets", "list of firewall rules", "list of IAM bindings".

```json theme={null}
{
  "name": "subnets",
  "type": "list(object)",
  "description": "List of subnets to create",
  "required": true,
  "sensitive": false,
  "min_items": 1,
  "schema": [
    {
      "name": "name",
      "type": "string",
      "required": true,
      "sensitive": false
    },
    {
      "name": "cidr",
      "type": "string",
      "required": true,
      "sensitive": false
    },
    {
      "name": "availability_zone",
      "type": "string",
      "required": true,
      "sensitive": false
    }
  ]
}
```

```hcl theme={null}
variable "subnets" {
  description = "List of subnets to create"
  type = list(object({
    name              = string
    cidr              = string
    availability_zone = string
  }))
}
```

`min_items` enforces "at least N entries" in the UI. There is no `max_items` field today — use Terraform `validation` if you need an upper bound.

### Nesting `list(object)` inside `list(object)`

You can nest as deeply as you need. The shipped AWS VPC template does this for NACL ingress/egress rules: each subnet has an optional NACL object, which contains a list of ingress rules and a list of egress rules.

```json theme={null}
{
  "name": "subnets",
  "type": "list(object)",
  "schema": [
    { "name": "name", "type": "string", "required": true, "sensitive": false },
    {
      "name": "nacl",
      "type": "object",
      "required": false,
      "sensitive": false,
      "schema": [
        {
          "name": "ingress_rules",
          "type": "list(object)",
          "required": true,
          "sensitive": false,
          "schema": [
            { "name": "rule_number", "type": "number", "required": true, "sensitive": false },
            { "name": "protocol", "type": "string", "required": true, "sensitive": false }
          ]
        }
      ]
    }
  ]
}
```

The matching Terraform uses `optional(...)` for non-required nested objects:

```hcl theme={null}
variable "subnets" {
  type = list(object({
    name = string
    nacl = optional(object({
      ingress_rules = list(object({
        rule_number = number
        protocol    = string
      }))
    }))
  }))
}
```

***

## Sensitive Variables

Setting `"sensitive": true` in `template.json` triggers Amnify's secret-handling pipeline. Here is exactly what happens:

```mermaid theme={null}
flowchart LR
    U["User types value<br/>in masked input"]
    E["Amnify API encrypts<br/>(AES-256-GCM, org-scoped)"]
    DB[("DB stores ciphertext<br/>iv:authTag:cipher")]
    W["Worker reads ciphertext<br/>at deploy time"]
    D["Worker decrypts<br/>in memory"]
    T["Written to<br/>terraform.tfvars.json"]

    U --> E --> DB --> W --> D --> T
```

Key properties:

* **Plaintext is never persisted.** Only the encrypted `iv:authTag:ciphertext` triple lives in the database, in the `sensitive_variables` JSON column on the deployment row.
* **Encryption key is org-scoped.** Each organization has its own salt; values cannot be decrypted across orgs.
* **Decryption happens once, in memory, just before `terraform.tfvars.json` is written.** The plaintext exists only inside the deploy worker process, for the duration of one deployment run.
* **The UI never re-displays the plaintext.** Once submitted, users see a masked indicator. To change the value, they enter a new one — they cannot retrieve the old one.

### Declaring a sensitive variable

```json theme={null}
{
  "name": "admin_password",
  "type": "string",
  "description": "Admin password",
  "required": true,
  "sensitive": true
}
```

```hcl theme={null}
variable "admin_password" {
  description = "Admin password"
  type        = string
  sensitive   = true   # optional; only affects Terraform CLI log output
}
```

The `sensitive = true` on the Terraform side is independent of Amnify's encryption — it just prevents Terraform from printing the value in plan/apply logs. Set both: `sensitive` in JSON for Amnify storage, and `sensitive = true` in `.tf` for Terraform log hygiene.

### Sensitive outputs

If a deployment produces a secret value (e.g. a generated connection string), mark the output as sensitive:

```hcl theme={null}
output "connection_string" {
  value     = "postgres://..."
  sensitive = true
}
```

The Amnify UI will mask sensitive outputs by default; users can reveal them with an explicit click.

***

## Auto-Injected Variables

Amnify merges several values into `terraform.tfvars.json` automatically. You declare a `variable` block for them in your `.tf` file, but you do **not** add them to `template.json`:

| Variable          | Provider | Source                                                             |
| ----------------- | -------- | ------------------------------------------------------------------ |
| `subscription_id` | Azure    | Project's Azure environment config                                 |
| `region`          | AWS      | Project's AWS environment config                                   |
| `location`        | Azure    | Project default; can be overridden by including in `template.json` |
| `tags`            | All      | Project tag policy + deployment metadata, merged                   |

If you want users to override one of these per deployment, add a matching entry to `template.json` — the user-supplied value takes precedence in the merge.

***

## Next Steps

* [Example — Azure Resource Group](/cspm-deploy/create-custom-templates/example-azure-resource-group) — apply this to a simple template.
* [Example — AWS VPC](/cspm-deploy/create-custom-templates/example-aws-vpc) — apply this to a template that exercises `list(object)` deeply.
