# 07t-Terraform Deploy

> Executes Azure deployments using generated Terraform configurations. Runs bootstrap and deploy scripts, performs terraform plan preview, manages phase-aware deployment lifecycle. Step 6 of the 7-step agentic workflow.

- Skill: `tools-only/07t-terraform-deploy` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds add tools-only/07t-terraform-deploy`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/07t-terraform-deploy/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/tools-only/07t-terraform-deploy

---


# Terraform Deploy Agent

**Step 6** of the 7-step workflow: `requirements → architect → design → terraform-plan → terraform-code → [deploy] → as-built`

## MANDATORY: Read Skills First

**Before doing ANY work**, read these skills:

1. **Read** `.github/skills/azure-defaults/SKILL.md` — regions, tags, security baseline,
   and the **Terraform Conventions** section
2. **Read** `.github/skills/azure-artifacts/SKILL.md` — H2 template for
   `06-deployment-summary.md`
3. **Read** `.github/skills/azure-artifacts/templates/06-deployment-summary.template.md`
   — use as structural skeleton (replicate badges, TOC, navigation, attribution)

## DO / DON'T

### DO

- ✅ Validate Azure CLI token FIRST (`az account get-access-token`) — NOT just `az account show`
- ✅ Verify the state backend storage account exists BEFORE running `terraform init`
- ✅ Offer to run `bootstrap-backend.sh`/`bootstrap-backend.ps1` if backend resources are missing
- ✅ Run `terraform validate` and `terraform fmt -check` before planning
- ✅ Check `04-implementation-plan.md` for deployment strategy (phased/single)
- ✅ If phased: deploy one phase at a time with `var.deployment_phase` and approval gates
- ✅ Present `terraform plan` output summary and wait for user approval before applying
- ✅ Require explicit approval for ANY resource destruction (`- destroy`) operations
- ✅ Generate `06-deployment-summary.md` after deployment
- ✅ Run `terraform output` and query Azure Resource Graph post-deployment
- ✅ Update `agent-output/{project}/README.md` — mark Step 6 complete, add your artifacts

### DON'T

- ❌ Deploy without running `terraform plan` first
- ❌ Skip phase gates when plan specifies phased deployment
- ❌ Use `terraform -target` — the code is already phase-gated via `var.deployment_phase`
- ❌ Auto-approve production deployments (require explicit user confirmation)
- ❌ Proceed if `terraform plan` shows resource destruction without user approval
- ❌ Proceed if `terraform validate` fails
- ❌ Create or modify Terraform configurations — hand back to Terraform Code agent
- ❌ Run `terraform init` without verifying the backend storage account exists first

## Prerequisites Check

Before starting, validate:

1. `infra/terraform/{project}/main.tf` exists
2. `05-implementation-reference.md` exists in `agent-output/{project}/`
3. If either missing, STOP and request handoff to Terraform Code agent

## Deployment Workflow

### Step 1: Azure CLI Authentication Validation

> **CRITICAL**: `az account show` can succeed with stale cached metadata even when
> no valid ARM token exists. Always validate with a real token acquisition.

```bash
# Informational check only — NOT sufficient for auth validation
az account show --output table

# MANDATORY: Verify real ARM token acquisition
az account get-access-token --resource https://management.azure.com/ --output none
```

**If token acquisition fails** ("User does not exist in MSAL token cache"):

1. Run `az login --use-device-code` — works reliably in devcontainers/Codespaces
2. Run `az account set --subscription {subscription-id}`
3. Re-run `az account get-access-token` to confirm
4. Only then proceed with planning/deployment

### Step 2: State Backend Verification

Verify the Azure Storage Account backend exists before initializing:

```bash
# Check if the backend resource group and storage account exist
az storage account show \
  --name {storage_account_name} \
  --resource-group {resource_group_name} \
  --output none 2>/dev/null && echo "Backend exists" || echo "Backend missing"
```

**If backend is missing:**

Present the user with the option to run the bootstrap script:

```text
⚠️ State backend not found.
  Storage Account: {name}
  Resource Group: {rg}

Would you like to run bootstrap-backend.sh to create it?
Reply "bootstrap" to proceed, or create manually first.
```

On approval, run:

```bash
cd infra/terraform/{project}
chmod +x bootstrap-backend.sh
./bootstrap-backend.sh
```

Or on Windows: `pwsh -File bootstrap-backend.ps1`

### Step 3: Validate Configuration

```bash
cd infra/terraform/{project}

# Initialize with backend configuration
terraform init

# Validate syntax and configuration
terraform validate

# Check formatting
terraform fmt -check -recursive
```

If `terraform validate` fails → STOP, report errors, hand off to Terraform Code agent.
If `terraform fmt -check` fails → report formatting issues (safe-to-fix, not a hard stop).

### Step 4: Plan Preview

Run `terraform plan` and classify all changes:

```bash
terraform plan \
  -out=tfplan \
  -var="environment={env}" \
  [-var="deployment_phase={phase}"]
```

**Change Classification:**

| Symbol      | Change Type | Action                                     |
| ----------- | ----------- | ------------------------------------------ |
| `+`         | Create      | Review new resources                       |
| `-`         | Destroy     | **STOP — Requires explicit user approval** |
| `~`         | Update      | Review in-place property changes           |
| `-/+`       | Replace     | **STOP — Resource recreation, data risk**  |
| `<=>`       | Move        | Review — usually safe                      |
| (no symbol) | Read        | Safe — data source refresh                 |

**Deprecation scan**: Check plan output for:
`deprecated|sunset|end.of.life|no.longer.supported|retiring`
If detected, STOP and report.

Present the plan summary table. **Do NOT apply without explicit user approval.**

### Step 4.5: Pre-Deploy Adversarial Review (1 pass)

After terraform plan completes and before apply, invoke `challenger-review-subagent` via `#runSubagent`:

- `artifact_path` = `agent-output/{project}/06-deployment-summary.md` (or the terraform plan output captured above)
- `project_name` = `{project}`
- `artifact_type` = `deployment-preview`
- `review_focus` = `comprehensive`
- `pass_number` = `1`
- `prior_findings` = `null`

Write result to `agent-output/{project}/challenge-findings-deployment.json`.

Include findings in the deployment approval gate.
If `must_fix` count > 0, flag prominently and require explicit user acknowledgement before proceeding.

### Step 5: Phase-Aware Deployment

Read `04-implementation-plan.md` and check the `## Deployment Phases` section:

**If phased deployment:**

Deploy each phase sequentially:

1. Run plan for the current phase:
   ```bash
   terraform plan -out=tfplan -var="deployment_phase={phase_name}" [-var-file=...]
   ```
2. Present plan summary and wait for user approval
3. Execute: `terraform apply tfplan`
4. Run `terraform output` for the completed phase
5. Verify phase resources via Azure Resource Graph (Step 6 below)
6. Present phase completion summary with approval gate to continue
7. Repeat for next phase

Or use the deploy script:

```bash
# Linux/macOS
bash deploy.sh --phase foundation

# Windows
pwsh -File deploy.ps1 -Phase foundation
```

**If single deployment:**

```bash
terraform plan -out=tfplan
# Present plan, get approval
terraform apply tfplan
```

### Step 6: Post-Deployment Verification

After successful `terraform apply`, verify the deployed resources:

```bash
# Get Terraform outputs
terraform output

# Query deployed resources via Azure Resource Graph
az graph query -q \
  "Resources | where resourceGroup =~ '{rg-name}' | project name, type, location, provisioningState"

# Check resource health
az graph query -q \
  "HealthResources | where resourceGroup =~ '{rg-name}' | project name, properties.availabilityState"
```

Report:

- Total resources deployed by phase
- Any resources not in `Succeeded` provisioning state
- Resource health availability status
- Key `terraform output` values (endpoints, IDs — redact any secrets)

## Stopping Rules

**STOP IMMEDIATELY if:**

- `az account get-access-token` fails (auth not valid)
- State backend storage account does not exist AND user hasn't approved bootstrap
- `terraform validate` returns errors
- `terraform plan` shows Destroy (`-`) or Replace (`-/+`) operations without explicit approval
- `terraform plan` shows >10 resource changes — summarize and confirm
- User has not approved deployment
- Deprecation signals detected in plan output

**PLAN-ONLY MODE:**
If user selects "Run Plan Only" handoff, execute plan and present summary but
DO NOT run `terraform apply`. Generate `06-deployment-summary.md` with plan results
and mark status as "Plan Only — Not Applied".

## Known Issues

| Issue                                      | Workaround                                                                   |
| ------------------------------------------ | ---------------------------------------------------------------------------- |
| `terraform init` fails — backend missing   | Run `bootstrap-backend.sh` first                                             |
| Backend state lock held                    | `terraform force-unlock {lease-id}` (requires explicit approval)             |
| MSAL token stale (devcontainer/Codespaces) | `az login --use-device-code` in the same terminal                            |
| `azurerm` provider init slow               | Provider cache: `TF_PLUGIN_CACHE_DIR=/home/vscode/.terraform.d/plugin-cache` |
| Azure extension auth ≠ CLI auth            | VS Code extension and `az` CLI use separate token stores                     |
| `terraform fmt -check` fails               | Run `terraform fmt -recursive` to auto-fix, then re-check                    |

## Output Files

| File               | Location                                          |
| ------------------ | ------------------------------------------------- |
| Deployment Summary | `agent-output/{project}/06-deployment-summary.md` |

Include attribution header from the template file (do not hardcode).
After saving, run `npm run lint:artifact-templates` and fix any errors for your artifact.

## Validation Checklist

- [ ] Azure CLI authenticated (`az account get-access-token` succeeds)
- [ ] State backend storage account verified (or bootstrapped)
- [ ] `terraform init` completed successfully
- [ ] `terraform validate` passes with no errors
- [ ] `terraform plan` completed and reviewed
- [ ] No unapproved Destroy or Replace operations
- [ ] No deprecation signals in plan output
- [ ] User approval obtained before `terraform apply`
- [ ] Deployment completed successfully (all resources `Succeeded`)
- [ ] Post-deployment ARG verification passed
- [ ] `terraform output` values captured
- [ ] `06-deployment-summary.md` saved with correct H2 headings

