---
name: opentofu-ansible-scaffold
description: Scaffold a new AWS infrastructure project using the OpenTofu + Ansible two-phase provisioning pattern. Use when deploying a new application to AWS, creating VPC/EC2/RDS/S3 infrastructure, setting up Docker Compose with Caddy reverse proxy, or scaffolding a complete infrastructure-as-code project with security best practices.
disable-model-invocation: true
argument-hint: "[app-name]"
---

# OpenTofu + Ansible Infrastructure Scaffold

You are an infrastructure engineer scaffolding a new AWS deployment using the two-phase provisioning pattern: OpenTofu for AWS resources, Ansible for application configuration via Docker Compose.

## Required reading

Before generating any code, read these reference files thoroughly:

- **Best practices**: `${CLAUDE_SKILL_DIR}/references/best-practices.md` — all OpenTofu, Ansible, AWS, Docker, and secrets patterns
- **Pre-commit checklist**: `${CLAUDE_SKILL_DIR}/references/checklist.md` — review every generated file against this
- **Templates directory**: `${CLAUDE_SKILL_DIR}/assets/opentofu-ansible/templates/` — starter templates with placeholders

## Procedure

### Step 1: Gather requirements

Ask the user the following questions (skip any already answered via arguments or context):

1. **App name** — used for naming (e.g., `lightdash`, `metabase`, `n8n`). Default from `$ARGUMENTS` if provided.
2. **Description** — one-liner for what this deploys.
3. **AWS components needed** — which of these? (default: all checked)
   - [x] VPC + subnets (public + private)
   - [x] EC2 + Elastic IP
   - [x] RDS PostgreSQL
   - [x] S3 bucket
   - [ ] SES (email)
   - [ ] ElastiCache Redis
   - [ ] CloudFront
4. **Access pattern** — VPN-only (default), public via ALB, or public direct?
5. **Authentication** — password, Google SSO (OIDC), SAML, or none?
6. **Docker image** — the main application image (e.g., `lightdash/lightdash:0.1467.3`)
7. **App port** — the port the app listens on inside the container (e.g., `8080`)
8. **Health endpoint** — the path for health checks (e.g., `api/v1/health`, `status`). Default: `api/v1/health`
9. **Domain** — FQDN or IP-based access?
10. **Environment** — start with sandbox (default)?

### Step 2: Generate the scaffold

Generate the full project structure using the templates as a starting point. The target directory structure is:

```
<app-name>/
  opentofu/
    main.tf              # All infra resources
    variables.tf         # Typed variables with descriptions
    outputs.tf           # Outputs for Ansible bridge
    backends/
      sandbox.hcl        # S3 backend config
    environments/
      sandbox.tfvars     # Environment-specific values
    .gitignore           # Ignore .terraform/, *.tfstate
  ansible/
    ansible.cfg          # Standard config with dynamic inventory
    inventory.py         # Dynamic inventory bridge (reads tofu output)
    deploy.yaml          # Main playbook (secrets fetched via AWS CLI)
    roles/<app-name>/
      defaults/main.yml  # Role defaults
      tasks/main.yml     # Secret fetching + deploy tasks
      handlers/main.yml  # Container restart handlers
      templates/
        docker-compose.yml.j2  # Docker Compose with Caddy
        Caddyfile.j2           # Reverse proxy config
        .env.j2                # App environment variables
```

**Customization rules:**
- Replace ALL `{{ placeholder }}` markers in templates with actual values based on user answers
- If RDS is not needed, remove RDS resources, secrets, and related Ansible tasks
- If S3 is not needed, remove S3 resources and IAM policies
- If SES is needed, add SES identity verification, SMTP IAM user, and SSM parameters
- Adjust security group rules based on access pattern (VPN CIDRs vs. 0.0.0.0/0)
- Add OIDC/SSO environment variables to .env.j2 if authentication requires it

### Step 3: Validate

Run these commands and fix any issues:

```bash
cd <app-name>/opentofu && tofu fmt -recursive && tofu validate
chmod +x <app-name>/ansible/inventory.py
cd <app-name>/ansible && ansible-playbook deploy.yaml --syntax-check
```

### Step 4: Provide next steps

After generating, tell the user:

1. **First-time setup:**
   ```bash
   cd <app-name>/opentofu
   tofu init -backend-config=backends/sandbox.hcl
   tofu plan -var-file=environments/sandbox.tfvars
   tofu apply -var-file=environments/sandbox.tfvars
   ```

2. **Deploy the app:**
   ```bash
   cd <app-name>/ansible
   ansible-playbook deploy.yaml
   ```

3. **What to fill in manually** — list any placeholder values in sandbox.tfvars that need real values (AMI ID, VPN CIDRs, SSH key name, etc.)

4. **Secret provisioning** — explain how to provide initial secrets via `-var` flags on first apply

## Key principles (from best-practices.md)

- Never use placeholder defaults for secrets — use `sensitive = true` with no default
- Always use `name_prefix` (not `name`) for security groups — enables `create_before_destroy`
- Always set IMDSv2 with `http_put_response_hop_limit = 2` for Docker hosts
- Always encrypt EBS volumes and S3 buckets (SSE-KMS)
- Always use explicit private route tables (never rely on the VPC default)
- Always use `no_log: true` on any Ansible task touching secrets
- Always use `delegate_to: localhost` for AWS API calls in Ansible
- Pin Docker image versions — never use `latest`
- Set memory limits on all Docker services

## Validation

- [ ] `tofu fmt -recursive` passes with no changes
- [ ] `tofu validate` succeeds
- [ ] `ansible-playbook deploy.yaml --syntax-check` passes
- [ ] No placeholder markers (`{{ }}`) remain in generated files
- [ ] All secrets use `sensitive = true` with no default values
- [ ] Security groups use `name_prefix`, not `name`
- [ ] Docker images are pinned to specific versions
