Tf Best Practices logo

Tf Best Practices

OrganizationPopular
aws
tf-best-practices

Best-practice authoring guidance AND a read-only policy gate for AWS Terraform generated by a migration skill. Load during any phase that writes a terraform/ directory — first as the "what to emit" posture rules + security-baseline spec, then after writing as the deterministic policy verdict. Read-only: it reports whether the generated Terraform passes; it never edits .tf files, never touches .phase-status.json, and never decides phase completion. Complements (does not replace) terraform fmt/init/validate.

Overview

Publisheraws
Repositoryagent-toolkit-for-aws
Skill nametf-best-practices
Stars
2.8K
Forks
337
Bundled files
4
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 4 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by aws on GitHub. Read the source before you install it.

Installation

Install the Tf Best Practices AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/aws/agent-toolkit-for-aws/tree/main/plugins/aws-startup-advisor/skills/tf-best-practices
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/aws/agent-toolkit-for-aws.git /tmp/agent-toolkit-for-aws
mkdir -p .claude/skills
cp -r /tmp/agent-toolkit-for-aws/plugins/aws-startup-advisor/skills/tf-best-practices .claude/skills/tf-best-practices
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Tf Best Practices in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Tf Best Practices on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Tf Best Practices is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

tf-best-practices — Generated-IaC posture rules + read-only policy gate

A shared authoring guide and verdict producer, not a workflow. It answers two questions for a phase that generates AWS Terraform:

  1. Before writing — "what security posture must the generated terraform/ follow?" (the posture rules + the baseline.tf account-hardening spec)
  2. After writing — "does the generated terraform/ pass policy?" (a deterministic, read-only verdict + a machine-readable report)

Routing — load the part that matches your context

This skill is entered at two touchpoints in the caller's Generate flow, with the caller's own terraform-authoring work in between. The caller states which touchpoint it is at when it loads this skill, and reads the corresponding part:

Caller contextLoadWhy
About to author terraform/ (before writing)Part 1 → references/security-posture-rules.mdThe "what to emit" AWS authoring rules (gate-enforced + authoring-only + compliance-conditional).
terraform/ written, ready to validate (after writing)Part 2 → references/terraform-validation.md + run the gate scriptThe fmt → init → validate → policy protocol and the read-only verdict.

Everything this skill states is source-cloud-agnostic (pure AWS Terraform). Any GCP/Heroku detection or artifact reading is the caller's job; where a rule needs a caller-known fact (e.g. declared compliance frameworks), the caller passes it as a caller-context signal — see references/security-posture-rules.md § Caller-context signals.

Boundary (read this first)

This unit is a verdict producer, never a mutator. Its entire write surface is the JSON verdict it is asked to emit. Specifically it MUST NOT:

  • edit, format, or rewrite any .tf file (the caller owns remediation),
  • read or write .phase-status.json or any run-state file (interpreter-owned),
  • decide whether a phase may complete, or prompt the user (caller policy).

The caller (a migration skill's Generate phase) owns: the fix-and-retry loop that edits the .tf it generated, terraform fmt auto-apply, the retry/skip/abort prompt, the Phase Completion gate, and every .phase-status.json write. See the consuming skill's generate phase for how the verdict feeds those decisions.

Consumers: gcp-to-aws (prose Generate) and heroku-to-aws (DSL Generate). The contract is source-agnostic; each caller wires the two touchpoints in its own Generate idiom — gcp-to-aws as prose steps, heroku-to-aws as a fragment step plus a fail-closed _postconditions assert enforced by the interpreter.

Part 1 — Authoring posture (load before writing terraform/)

Emit generated Terraform that satisfies the posture in references/security-posture-rules.md.

These are the "what good AWS Terraform looks like" rules. Following them makes the Part 2 gate pass by construction. This unit does not read the caller's artifacts — it consumes only caller-context signals the caller passes in.

Scope. security-posture-rules.md covers, in three tiers:

  • Gate-enforced (Part 2 verifies statically): ALB TLS, no-public-database, RDS + ElastiCache encryption-at-rest, no-public-DB-port ingress, no-public admin/datastore-port ingress, no-wildcard-IAM.
  • Authoring-only (not gate-checkable, still required): deletion_protection, master-password-via-Secrets-Manager, S3 hardening, Fargate/EKS/ECR settings, private-subnet placement, backups, baseline monitoring.
  • Compliance-conditional (emitted when the caller declares soc2/pci/hipaa/fedramp): VPC flow logs, S3 access logging, secret rotation, customer-managed KMS.

Still the caller's own generation concern (candidates to migrate here later): the account-hardening baseline.tf layer (CloudTrail, GuardDuty, Config, Security Hub).

Part 2 — Policy gate (run after writing terraform/)

Run the read-only checker against the generated directory. Resolve the script path relative to the plugin root ($PLUGIN_ROOT/skills/tf-best-practices/scripts/...), the same convention the plugin uses for its other scripts:

bash
python3 "$PLUGIN_ROOT/skills/tf-best-practices/scripts/validate-terraform-policy.py" "$TERRAFORM_DIR" --json "$VERDICT_PATH"
  • $TERRAFORM_DIR — required, caller-supplied: the generated terraform/ directory (e.g. $MIGRATION_DIR/terraform). This skill never defaults or discovers it — the caller always passes the path it wrote Terraform to.
  • --json $VERDICT_PATH — optional; writes a machine-readable verdict the caller can merge into its own validation-report.json.

The policy check is one stage of a larger validation flow (fmt → init → validate → policy). The full protocol — including offline-fallback behavior and how the policy verdict maps into a validation-report.json — is documented in references/terraform-validation.md. That protocol is descriptive: the caller owns the fmt/init/validate execution, the fix-and-retry loop, and the report write; this unit contributes only the read-only policy stage + verdict shape.

Exit codes → caller action

ExitstdoutMeaningCaller does
0POLICY_OKposture satisfiedproceed
1POLICY_FAILviolations presentread violations[], edit the named .tf sites, re-run (caller's retry budget)
2(usage error)bad path / IOsurface to user; do not treat as pass

Verdict shape (--json)

json
{
  "check": "policy",
  "policy_status": "POLICY_OK | POLICY_FAIL",
  "violations": [
    {
      "check": "policy",
      "rule": "alb_https_listener | alb_http_redirect | no_tf_files",
      "file": "compute.tf",
      "line": 7,
      "severity": "error",
      "summary": "human-readable violation",
      "fix_hint": "concrete remediation the caller can apply"
    }
  ]
}

Each violations[] entry is actionable evidence — file + line + fix_hint tell the caller exactly what to edit. The caller applies the edit; this unit only reports.

Policy rules enforced today

Every rule is fail-open on ambiguity — it fires only on unambiguous, in-block literal evidence, so a valid stack is never falsely blocked (a POLICY_FAIL is a hard completion gate for the caller, so a false positive would block a real migration).

Internet-facing ALB TLS posture (an ALB is internet-facing when internal is absent, false, or variable-driven — fail-safe):

  • alb_https_listener — must have an HTTPS listener on 443 with certificate_arn and a forward action.
  • alb_http_redirect — an HTTP :80 listener must redirect to HTTPS, never forward to targets. Internal ALBs (internal = true) are exempt.

Elastic Beanstalk ALBs are invisible to these rules. The ALB rules inspect standalone aws_lb_listener blocks. An EB LoadBalanced environment provisions its ALB from aws_elastic_beanstalk_environment setting blocks, which the static checker does not read — so a pure-EB design passes the ALB rules vacuously (no listener to inspect). EB listener/TLS posture is therefore authoring-only, not gate-enforced. (Fixtures good-heroku-eb-only and good-heroku-eb-singleinstance document this; good-heroku-eb-loadbalanced carries a standalone ALB so the listener rules are exercised on real blocks.)

Managed database exposure & encryption (aws_db_instance, aws_rds_cluster):

  • rds_not_public — must not set publicly_accessible = true (absent/variable → fail-open).
  • rds_encryption_at_rest — must set storage_encrypted = true; missing or literal false fires (RDS defaults to unencrypted), variable-driven fails open. S3 is not checked (default SSE-S3 since Jan 2023).

ElastiCache encryption (aws_elasticache_replication_group, Redis aws_elasticache_cluster):

  • elasticache_encryption_at_rest — a replication group must set at_rest_encryption_enabled = true; missing or literal false fires, variable-driven fails open.
  • elasticache_cluster_encryption — a Redis-engine aws_elasticache_cluster (single-node: engine = "redis", no replication_group_id) must set BOTH at_rest_encryption_enabled = true and transit_encryption_enabled = true; missing or literal false on either fires, variable-driven fails open. engine = "memcached" clusters (and variable-driven/absent engine) are exempt — Memcached does not support these attributes.

Security group ingress:

  • db_sg_no_public_ingress — an inline aws_security_group ingress covering 5432/3306 must not allow 0.0.0.0/0 or ::/0.
  • sg_no_public_admin_ingress — an inline ingress must not open a curated never-public admin/datastore port (22, 3389, 6379, 11211, 27017, 9200/9300, 5601) to 0.0.0.0/0 or ::/0. Web (80/443) and app/game ports are not flagged; DB ports are handled by the rule above. Both check cidr_blocks and ipv6_cidr_blocks independently, so a benign IPv4 list does not mask an open IPv6 one. Both: separate aws_security_group_rule / aws_vpc_security_group_ingress_rule resources fail open (not correlated).

IAM least-privilege (aws_iam_policy, aws_iam_role_policy, aws_iam_group_policy, aws_iam_user_policy):

  • no_wildcard_iam — an Allow statement must not use Action/Resource "*". The one narrow exception is an isolated elasticbeanstalk:CreateStorageLocation statement with Resource = "*" because AWS does not support resource-level permissions for that action. aws_iam_policy_document data sources and assume-role trust policies fail open.

The checker is a zero-dependency static HCL reader (no terraform init, no provider download) — it runs even when the registry is unreachable. It uses brace-depth matching for nested blocks, so a valid HTTPS listener written with a nested forward { ... } block is not a false failure.

Fixtures (also the checker's regression suite)

fixtures/terraform-policy/ holds intentionally-shaped Terraform used by scripts/test_validate_terraform_policy.py:

  • bad-http-forward/ — internet-facing ALB that forwards plaintext HTTP → MUST POLICY_FAIL.
  • internal-alb-only/ — internal ALB on HTTP → MUST POLICY_OK (HTTP allowed internally).
  • good-https-redirect/ — the correct pattern → POLICY_OK.

These are deliberately non-compliant test data (never deployed). They are excluded from the repo-wide checkov scan via .checkov.yaml skip-path; do not "harden" them — doing so breaks the tests that assert the failure paths.

Verification

bash
# from skills/tf-best-practices/
uv run --python 3.12 --with pytest python -m pytest scripts/test_validate_terraform_policy.py -q

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Tf Best Practices AI skill do?

Best-practice authoring guidance AND a read-only policy gate for AWS Terraform generated by a migration skill. Load during any phase that writes a terraform/ directory — first as the "what to emit" posture rules + security-baseline spec, then after writing as the deterministic policy verdict. Read-only: it reports whether the generated Terraform passes; it never edits .tf files, never touches .phase-status.json, and never decides phase completion. Complements (does not replace) terraform fmt/init/validate.

Why use Tf Best Practices on TypingMind?

Because you install it once and use it with any model. Tf Best Practices is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Tf Best Practices in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/aws/agent-toolkit-for-aws/tree/main/plugins/aws-startup-advisor/skills/tf-best-practices. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Tf Best Practices?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Tf Best Practices?

As many as you like. As long as a model supports skills, you can use Tf Best Practices with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Tf Best Practices AI skill free?

Yes. It is published on GitHub by aws under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇