Generated banner for Tf Best Practices
Generated logo for Tf Best Practices

Tf Best Practices

AWSOfficialPublished by AWS

  • Skill
  • Tool integration
  • Cloud and DevOps
  • Apache-2.0
  • Low risk
  • Has scripts

From the author

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.
— the description by AWS

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

tf-best-practices provides a structured approach to authoring and validating AWS Terraform code. It functions as a two-part system: an authoring guide that defines security posture rules before code generation, and a read-only policy gate that evaluates generated Terraform against those rules after it is written. The skill is designed to be integrated into migration workflows, providing deterministic verdicts and actionable fix hints without modifying the underlying files.

What it does

  • Provides security posture rules for authoring AWS Terraform.
  • Executes a read-only policy check on generated Terraform directories.
  • Generates machine-readable JSON reports detailing policy violations.
  • Offers specific fix hints for identified infrastructure issues.
  • Validates migration artifacts including database and deployment configurations.
  • Supports a multi-stage validation protocol including fmt, init, and validate.

When to use it

  • Before writing Terraform code to ensure adherence to security posture rules.
  • After generating Terraform to verify compliance with defined policy gates.
  • When integrating automated validation into a migration workflow.
  • To identify and remediate infrastructure configuration issues during development.

Advantages

  • Enforces consistent security standards across generated infrastructure.
  • Provides actionable feedback with file and line-specific remediation hints.
  • Operates as a read-only tool that does not modify user files.
  • Functions without requiring provider downloads or active registry connections.
  • Integrates into existing migration pipelines as a deterministic gate.

Usage examples

“Check the generated terraform/ directory for policy violations.”
The skill runs the policy checker against the specified directory and returns a JSON report containing any identified violations and corresponding fix hints.
“What are the security requirements for an internet-facing ALB?”
The skill provides the posture rules requiring an HTTPS listener on port 443 and an HTTP redirect to HTTPS on port 80.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/aws/tf-best-practices/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip tf-best-practices.zip -d .agents/skills

Documentation by AWS

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:

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.

Before infrastructure completion, the caller must run scripts/validate-terraform-report.py on the saved validation-report.json and require exit 0. The checker enforces the shared v2 completion contract. Its RUNTIME_REPORT_OK result does not establish that Terraform ran or passed.

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)
{
  "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

Companion checks for migration artifacts

Migration callers can also run the shared read-only scripts/validate-migration-artifacts.py against their complete migration directory:

python3 "$PLUGIN_ROOT/skills/tf-best-practices/scripts/validate-migration-artifacts.py" \
  "$MIGRATION_DIR" --json "$MIGRATION_DIR/artifact-validation-status.json"

This produces ARTIFACTS_OK, ARTIFACTS_FAIL, or ARTIFACTS_ERROR, separately from the Terraform policy verdict. The caller must inspect the real exit code and report; it must not synthesize success. Supported checks cover PostgreSQL verification control flow, EB/database security-group wiring, GitHub Actions step references and deployment operations, and literal RDS time windows. Unsupported structures require review. This is not a general Bash safety proof, provider validation, or proof that a deployment succeeds.

Heroku callers add --require-postgres-script when PostgreSQL is in the design, and --require-eb-workflow when GitHub Actions is the selected EB deployment method. Other callers must not impose those Heroku-specific requirements. The checker never executes a generated migration script or changes input files or phase state; only the explicit JSON report path may be written.

For RDS/Aurora generation, scripts/rds_windows.py resolves independent daily backup and weekly maintenance windows from caller preferences. It rejects invalid or explicitly conflicting choices rather than silently changing them. Callers use its actual JSON values when authoring Terraform.

Terraform policy rules

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

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

Files

scripts/test_validate_terraform_policy.py

"""Tests for validate-terraform-policy.py (tf-best-practices, read-only verdict)."""

from __future__ import annotations

import importlib.util
import json
import subprocess  # nosec B404 — test-only, inputs are hardcoded literals
import sys
import tempfile
from pathlib import Path

PLUGIN_SKILL_ROOT = Path(__file__).resolve().parents[1]
SCRIPT = PLUGIN_SKILL_ROOT / "scripts" / "validate-terraform-policy.py"
FIXTURES = PLUGIN_SKILL_ROOT / "fixtures" / "terraform-policy"
GOOD_FIXTURE = FIXTURES / "good-https-redirect"
BAD_HTTP_FORWARD = FIXTURES / "bad-http-forward"
INTERNAL_ALB = FIXTURES / "internal-alb-only"
BAD_RDS_PUBLIC_UNENCRYPTED = FIXTURES / "bad-rds-public-unencrypted"
GOOD_RDS_PRIVATE_ENCRYPTED = FIXTURES / "good-rds-private-encrypted"
BAD_DB_SG_PUBLIC = FIXTURES / "bad-db-sg-public"
GOOD_DB_SG_SCOPED = FIXTURES / "good-db-sg-scoped"
BAD_WILDCARD_IAM = FIXTURES / "bad-wildcard-iam"
GOOD_SCOPED_IAM = FIXTURES / "good-scoped-iam"
BAD_WILDCARD_IAM_LISTFORM = FIXTURES / "bad-wildcard-iam-listform"
GOOD_IAM_SCOPED_LIST = FIXTURES / "good-iam-scoped-list"
GOOD_INTERNET_NLB = FIXTURES / "good-internet-nlb"
BAD_SG_PUBLIC_SSH = FIXTURES / "bad-sg-public-ssh"
GOOD_SG_PUBLIC_WEBAPP = FIXTURES / "good-sg-public-webapp"
BAD_SG_PUBLIC_IPV6 = FIXTURES / "bad-sg-public-ipv6"
GOOD_SG_IPV6_SCOPED = FIXTURES / "good-sg-ipv6-scoped"
BAD_ELASTICACHE_UNENCRYPTED = FIXTURES / "bad-elasticache-unencrypted"
GOOD_ELASTICACHE_ENCRYPTED = FIXTURES / "good-elasticache-encrypted"
BAD_ELASTICACHE_CLUSTER_REDIS_UNENCRYPTED = (
    FIXTURES / "bad-elasticache-cluster-redis-unencrypted"
)
GOOD_ELASTICACHE_CLUSTER_REDIS_ENCRYPTED = (
    FIXTURES / "good-elasticache-cluster-redis-encrypted"
)
GOOD_ELASTICACHE_CLUSTER_MEMCACHED = FIXTURES / "good-elasticache-cluster-memcached"
GOOD_QUOTED_PORT_HTTPS = FIXTURES / "good-quoted-port-https"
BAD_DB_SG_PUBLIC_QUOTED_PORT = FIXTURES / "bad-db-sg-public-quoted-port"
GOOD_HEROKU_EB_LB = FIXTURES / "good-heroku-eb-loadbalanced"
GOOD_HEROKU_EB_ONLY = FIXTURES / "good-heroku-eb-only"
GOOD_HEROKU_EB_SINGLEINSTANCE = FIXTURES / "good-heroku-eb-singleinstance"
GOOD_HEROKU_EB_CODEPIPELINE = FIXTURES / "good-heroku-eb-codepipeline"
BAD_HEROKU_EB_ELASTICACHE = FIXTURES / "bad-heroku-eb-elasticache"


def _load_validator_module():
    """Import the validator for unit-level tests (its filename is hyphenated, so
    it is not importable as a normal module name)."""
    spec = importlib.util.spec_from_file_location("validate_terraform_policy", SCRIPT)
    assert spec is not None and spec.loader is not None
    module = importlib.util.module_from_spec(spec)
    # Register before exec: @dataclass resolves annotations via sys.modules.
    sys.modules[spec.name] = module
    spec.loader.exec_module(module)
    return module


VALIDATOR = _load_validator_module()


def run_policy_validator(terraform_dir: Path, json_out: Path | None = None) -> tuple[int, str]:
    cmd = [sys.executable, str(SCRIPT), str(terraform_dir)]
    if json_out is not None:
        cmd += ["--json", str(json_out)]
    result = subprocess.run(cmd, capture_output=True, text=True)  # nosec B603
    return result.returncode, result.stdout + result.stderr


def test_good_https_redirect_passes() -> None:
    code, out = run_policy_validator(GOOD_FIXTURE)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_bad_http_forward_fails() -> None:
    code, out = run_policy_validator(BAD_HTTP_FORWARD)
    assert code == 1, out
    assert "POLICY_FAIL" in out
    assert "redirect" in out.lower()


def test_heroku_eb_loadbalanced_passes() -> None:
    # EB compute fronted by a STANDALONE ALB (aws_lb_listener present) + RDS. Web
    # 80/443 open to 0.0.0.0/0 on the ALB is a legitimate web pattern and must NOT
    # be flagged; only admin/datastore ports are.
    code, out = run_policy_validator(GOOD_HEROKU_EB_LB)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_heroku_eb_only_passes_vacuously() -> None:
    # Pure EB LoadBalanced env, no standalone aws_lb — EB provisions the ALB from
    # setting blocks the static checker cannot read, so the ALB rules pass with no
    # listener to inspect. Documents that EB-managed ALB posture is authoring-only.
    code, out = run_policy_validator(GOOD_HEROKU_EB_ONLY)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_heroku_eb_singleinstance_passes() -> None:
    # EB SingleInstance: public-subnet instance, 80/443 open to the world, no ALB.
    # Web ports are not flagged (only admin/datastore ports are) → POLICY_OK.
    code, out = run_policy_validator(GOOD_HEROKU_EB_SINGLEINSTANCE)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_heroku_eb_codepipeline_required_wildcard_passes() -> None:
    # AWS does not support resource-level permissions for CreateStorageLocation;
    # its isolated Resource "*" statement is required, not an over-broad grant.
    code, out = run_policy_validator(GOOD_HEROKU_EB_CODEPIPELINE)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_required_wildcard_action_cannot_hide_other_actions() -> None:
    with tempfile.TemporaryDirectory() as tmp:
        terraform_dir = Path(tmp)
        (terraform_dir / "policy.tf").write_text(
            '''
resource "aws_iam_role_policy" "mixed" {
  role = "example-role"
  policy = jsonencode({
    Statement = [{
      Effect   = "Allow"
      Action   = ["elasticbeanstalk:CreateStorageLocation", "s3:*"]
      Resource = "*"
    }]
  })
}
'''
        )
        code, out = run_policy_validator(terraform_dir)
    assert code == 1, out
    assert "no_wildcard_iam" in out


def test_heroku_policy_check_runs_after_eks_fragment() -> None:
    phase_dir = (
        PLUGIN_SKILL_ROOT.parent
        / "heroku-to-aws"
        / "references"
        / "phases"
        / "generate"
    )
    phase = (phase_dir / "generate.md").read_text()
    terraform_fragment = (phase_dir / "generate-terraform.md").read_text()
    assembler = (phase_dir / "generate-assemble.md").read_text()

    assert phase.index("_id: eks-generate") < phase.index("_assemble:")
    assert "Defer the authoritative Terraform policy check to the assembler" in terraform_fragment
    assert "Authoritative Terraform policy check (after all Terraform producers)" in assembler
    assert "after `eks-generate`" in assembler


def test_heroku_eb_bad_elasticache_and_http_forward_fails() -> None:
    # BOTH the unencrypted-ElastiCache and the HTTP-forward violations must fire —
    # assert both rule IDs from --json so an alb_http_redirect regression can't hide
    # behind the elasticache failure.
    with tempfile.TemporaryDirectory() as tmp:
        out_path = Path(tmp) / "verdict.json"
        code, _ = run_policy_validator(BAD_HEROKU_EB_ELASTICACHE, json_out=out_path)
        report = json.loads(out_path.read_text())
    assert code == 1
    assert report["policy_status"] == "POLICY_FAIL"
    rules = {v["rule"] for v in report["violations"]}
    assert "elasticache_encryption_at_rest" in rules, rules
    assert "alb_http_redirect" in rules, rules


def test_internal_alb_skips_https_requirement() -> None:
    code, out = run_policy_validator(INTERNAL_ALB)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_json_report_written_on_pass() -> None:
    with tempfile.TemporaryDirectory() as tmp:
        out_path = Path(tmp) / "verdict.json"
        code, _ = run_policy_validator(GOOD_FIXTURE, json_out=out_path)
        assert code == 0
        report = json.loads(out_path.read_text())
        assert report["policy_status"] == "POLICY_OK"
        assert report["violations"] == []


def test_json_report_written_on_fail() -> None:
    with tempfile.TemporaryDirectory() as tmp:
        out_path = Path(tmp) / "verdict.json"
        code, _ = run_policy_validator(BAD_HTTP_FORWARD, json_out=out_path)
        assert code == 1
        report = json.loads(out_path.read_text())
        assert report["policy_status"] == "POLICY_FAIL"
        rules = {v["rule"] for v in report["violations"]}
        assert "alb_http_redirect" in rules


def test_missing_directory_is_usage_error() -> None:
    code, out = run_policy_validator(Path("/nonexistent/terraform/dir"))
    assert code == 2, out
    assert "not_a_directory" in out


def test_no_tf_files_flagged() -> None:
    with tempfile.TemporaryDirectory() as tmp:
        code, out = run_policy_validator(Path(tmp))
        assert code == 1, out
        assert "No .tf files" in out


def test_rds_public_and_unencrypted_fails() -> None:
    code, out = run_policy_validator(BAD_RDS_PUBLIC_UNENCRYPTED)
    assert code == 1, out
    assert "POLICY_FAIL" in out


def test_rds_public_unencrypted_reports_both_rules() -> None:
    with tempfile.TemporaryDirectory() as tmp:
        out_path = Path(tmp) / "verdict.json"
        run_policy_validator(BAD_RDS_PUBLIC_UNENCRYPTED, json_out=out_path)
        rules = {v["rule"] for v in json.loads(out_path.read_text())["violations"]}
        assert "rds_not_public" in rules
        assert "rds_encryption_at_rest" in rules


def test_rds_private_encrypted_passes() -> None:
    # Also proves variable-driven storage_encrypted fails open (not flagged).
    code, out = run_policy_validator(GOOD_RDS_PRIVATE_ENCRYPTED)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_db_sg_public_ingress_fails() -> None:
    code, out = run_policy_validator(BAD_DB_SG_PUBLIC)
    assert code == 1, out
    assert "db_sg_no_public_ingress" in out


def test_db_sg_scoped_passes() -> None:
    # Proves: app-SG-scoped DB ingress OK; public 443 not flagged as a DB port;
    # separate aws_vpc_security_group_ingress_rule fails open (not correlated).
    code, out = run_policy_validator(GOOD_DB_SG_SCOPED)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_wildcard_iam_fails() -> None:
    code, out = run_policy_validator(BAD_WILDCARD_IAM)
    assert code == 1, out
    assert "no_wildcard_iam" in out


def test_scoped_iam_passes() -> None:
    # Proves scoped policy OK and assume-role trust policy is not a wildcard hit.
    code, out = run_policy_validator(GOOD_SCOPED_IAM)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_wildcard_iam_listform_fails() -> None:
    # Regression: Resource = ["*"] (single-element list) must fail, like "*".
    code, out = run_policy_validator(BAD_WILDCARD_IAM_LISTFORM)
    assert code == 1, out
    assert "no_wildcard_iam" in out


def test_iam_scoped_list_passes() -> None:
    # A list of scoped actions / resource ARNs is not a wildcard — must pass.
    code, out = run_policy_validator(GOOD_IAM_SCOPED_LIST)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_internet_facing_nlb_not_flagged() -> None:
    # Regression: an internet-facing Network LB (L4) has no HTTPS:443 listener
    # by design and must NOT trip the ALB HTTPS rule.
    code, out = run_policy_validator(GOOD_INTERNET_NLB)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_sg_public_admin_ingress_fails() -> None:
    code, out = run_policy_validator(BAD_SG_PUBLIC_SSH)
    assert code == 1, out
    assert "sg_no_public_admin_ingress" in out


def test_sg_public_admin_ingress_reports_ssh_and_redis() -> None:
    with tempfile.TemporaryDirectory() as tmp:
        out_path = Path(tmp) / "verdict.json"
        run_policy_validator(BAD_SG_PUBLIC_SSH, json_out=out_path)
        summaries = " ".join(
            v["summary"] for v in json.loads(out_path.read_text())["violations"]
        )
        assert "22 (SSH)" in summaries
        assert "6379 (Redis)" in summaries


def test_sg_public_webapp_passes() -> None:
    # Public web ports (80/443), a high game-port range, and a privately-scoped
    # SSH rule must all pass — no false positive from the sensitive-port rule.
    code, out = run_policy_validator(GOOD_SG_PUBLIC_WEBAPP)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_sg_public_ipv6_ingress_fails() -> None:
    """Regression: ipv6_cidr_blocks = ["::/0"] is public exposure and must fail.

    The old checker searched r'cidr_blocks\\s*=\\s*\\[' which also matches the tail
    of `ipv6_cidr_blocks`, and never looked for ::/0 at all — so an SG opening
    SSH/Postgres to the entire IPv6 internet was reported POLICY_OK.
    """
    code, out = run_policy_validator(BAD_SG_PUBLIC_IPV6)
    assert code == 1, out
    assert "sg_no_public_admin_ingress" in out
    assert "db_sg_no_public_ingress" in out


def test_sg_public_ipv6_ingress_names_the_ipv6_cidr() -> None:
    # The report must say ::/0, not 0.0.0.0/0 — the IPv4 list here is benign.
    with tempfile.TemporaryDirectory() as tmp:
        out_path = Path(tmp) / "verdict.json"
        run_policy_validator(BAD_SG_PUBLIC_IPV6, json_out=out_path)
        summaries = " ".join(
            v["summary"] for v in json.loads(out_path.read_text())["violations"]
        )
        assert "::/0" in summaries
        assert "22 (SSH)" in summaries
        assert "0.0.0.0/0" not in summaries


def test_ipv4_public_ingress_caught_when_ipv6_list_declared_first() -> None:
    """Regression: the pre-fix regex made the IPv4 check ORDER-DEPENDENT.

    r'cidr_blocks\\s*=\\s*\\[' matched the tail of `ipv6_cidr_blocks`, so when the
    IPv6 attribute came first the checker read the IPv6 list and never looked at
    cidr_blocks at all. A plain `0.0.0.0/0` on port 22 — the exact case this rule
    exists to catch, with no IPv6 exposure involved — silently returned
    POLICY_OK. Flipping the two lines was enough to make it fail correctly.
    """
    sg = """
resource "aws_security_group" "ipv4_ssh_open" {
  name   = "test-sg"
  vpc_id = "vpc-123"

  ingress {
    from_port        = 22
    to_port          = 22
    protocol         = "tcp"
    %s
  }
}
"""
    ipv6_first = 'ipv6_cidr_blocks = ["2001:db8:1234::/48"]\n    cidr_blocks      = ["0.0.0.0/0"]'
    ipv4_first = 'cidr_blocks      = ["0.0.0.0/0"]\n    ipv6_cidr_blocks = ["2001:db8:1234::/48"]'
    for label, attrs in (("ipv6-first", ipv6_first), ("ipv4-first", ipv4_first)):
        with tempfile.TemporaryDirectory() as tmp:
            (Path(tmp) / "vpc.tf").write_text(sg % attrs, encoding="utf-8")
            code, out = run_policy_validator(Path(tmp))
            assert code == 1, f"{label}: 0.0.0.0/0 on SSH must fail, got: {out}"
            assert "22 (SSH)" in out, f"{label}: {out}"
            assert "0.0.0.0/0" in out, f"{label}: must name the IPv4 range, got: {out}"


def test_sg_ipv6_scoped_passes() -> None:
    # Public web over ::/0 is fine; SSH/Postgres over a scoped IPv6 prefix must
    # not fire. Guards against over-matching any ipv6_cidr_blocks value.
    code, out = run_policy_validator(GOOD_SG_IPV6_SCOPED)
    assert code == 0, out
    assert "POLICY_OK" in out


def _sg_fixture(attrs: str, port: int = 22) -> str:
    return f"""
resource "aws_security_group" "under_test" {{
  name   = "sg"
  vpc_id = "vpc-123"

  ingress {{
    from_port = {port}
    to_port   = {port}
    protocol  = "tcp"
    {attrs}
  }}
}}
"""


def _verdict(tf_body: str) -> tuple[int, str]:
    with tempfile.TemporaryDirectory() as tmp:
        (Path(tmp) / "vpc.tf").write_text(tf_body, encoding="utf-8")
        return run_policy_validator(Path(tmp))


def test_cidr_attribute_sharing_line_with_opening_brace_is_read() -> None:
    """Regression: a line-anchored pattern alone hides an attribute that shares
    its block's opening line, which is valid HCL:

        ingress { cidr_blocks = ["0.0.0.0/0"]

    An earlier revision of this fix used a bare `^\\s*` anchor and traded the
    ipv6 tail-match bug for this false negative (main: POLICY_FAIL -> POLICY_OK).
    The `(?:^|\\{)` alternation keeps both properties.
    """
    body = """
resource "aws_security_group" "brace_same_line" {
  name   = "sg"
  vpc_id = "vpc-123"

  ingress { cidr_blocks = ["0.0.0.0/0"]
    from_port = 22
    to_port   = 22
    protocol  = "tcp"
  }
}
"""
    code, out = _verdict(body)
    assert code == 1, f"0.0.0.0/0 on the brace line must fail, got: {out}"
    assert "22 (SSH)" in out, out


def test_noncanonical_ipv6_zero_prefix_spellings_fire() -> None:
    """`::/0` has many legal spellings and Terraform normalises none of them, so
    the check canonicalises via ipaddress rather than substring-matching text."""
    for spelling in ("::/0", "::0/0", "0:0:0:0:0:0:0:0/0", "0000:0000:0000:0000:0000:0000:0000:0000/0"):
        code, out = _verdict(_sg_fixture(f'ipv6_cidr_blocks = ["{spelling}"]'))
        assert code == 1, f"{spelling} is the whole IPv6 internet, must fail: {out}"
        assert "sg_no_public_admin_ingress" in out, f"{spelling}: {out}"


def test_scoped_and_nonliteral_cidrs_still_fail_open() -> None:
    """The canonicalising check must not widen what counts as public: scoped
    prefixes and non-literal values stay unflagged (documented fail-open)."""
    for spelling in ("10.0.0.0/8", "2001:db8:1234::/48", "0.0.0.0/1", "128.0.0.0/1"):
        attr = "ipv6_cidr_blocks" if ":" in spelling else "cidr_blocks"
        code, out = _verdict(_sg_fixture(f'{attr} = ["{spelling}"]'))
        assert code == 0, f"{spelling} is not the whole internet, must pass: {out}"
    for nonliteral in ("var.admin_cidrs", "[var.cidr]", "local.allowed"):
        inner = nonliteral if nonliteral.startswith("[") else f"[{nonliteral}]"
        code, out = _verdict(_sg_fixture(f"cidr_blocks = {inner}"))
        assert code == 0, f"non-literal {nonliteral} must fail open: {out}"


def test_cidr_named_only_in_a_comment_is_not_an_allowed_range() -> None:
    """False positive: a correctly-scoped SG that merely MENTIONS 0.0.0.0/0 in a
    comment inside the list was reported POLICY_FAIL, blocking a valid migration.

    Also pins the opposite direction — stripping comments must not swallow a real
    entry that follows one, which a naive split-on-comma would have done.
    """
    scoped_with_comment = """
    cidr_blocks = [
      # TODO: was 0.0.0.0/0
      "10.0.0.0/8",
    ]"""
    code, out = _verdict(_sg_fixture(scoped_with_comment, port=3389))
    assert code == 0, f"a commented CIDR is not an allowed range: {out}"

    comment_then_real = """
    cidr_blocks = [
      # temporary, remove before prod
      "0.0.0.0/0",
    ]"""
    code, out = _verdict(_sg_fixture(comment_then_real, port=3389))
    assert code == 1, f"a real 0.0.0.0/0 after a comment must still fire: {out}"
    assert "3389 (RDP)" in out, out


def test_commented_out_cidr_attribute_is_not_read() -> None:
    # A commented-out attribute must not be read as set; the live value here is
    # an SG reference, so this SG is correctly scoped and must pass.
    body = _sg_fixture('# cidr_blocks = ["0.0.0.0/0"]\n    security_groups = ["sg-abc123"]')
    code, out = _verdict(body)
    assert code == 0, f"commented-out cidr_blocks must not fire: {out}"


def test_comment_marker_inside_string_does_not_swallow_real_cidr() -> None:
    """False negative: comment stripping must share the lexer, not re-scan with a
    regex. A regex sees the `/*` inside the description STRING as a comment
    opener and deletes everything up to the `*/` in the later line comment —
    including the live `cidr_blocks = ["0.0.0.0/0"]` between them, reporting a
    public SSH ingress as POLICY_OK. The lexer knows string interiors are not
    comment openers, so the public range survives stripping and fires.
    """
    body = _sg_fixture(
        'description = "temp /* migration window"\n'
        '    cidr_blocks = ["0.0.0.0/0"]\n'
        "    # closes */ in runbook"
    )
    code, out = _verdict(body)
    assert code == 1, f"0.0.0.0/0 after a string containing /* must still fire: {out}"
    assert "22 (SSH)" in out, out


def test_elasticache_unencrypted_fails() -> None:
    code, out = run_policy_validator(BAD_ELASTICACHE_UNENCRYPTED)
    assert code == 1, out
    assert "elasticache_encryption_at_rest" in out


def test_elasticache_encrypted_passes() -> None:
    # Encrypted RG passes; variable-driven RG fails open (not flagged).
    code, out = run_policy_validator(GOOD_ELASTICACHE_ENCRYPTED)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_elasticache_cluster_redis_unencrypted_fails() -> None:
    # Single-node Redis aws_elasticache_cluster with no encryption must fail.
    code, out = run_policy_validator(BAD_ELASTICACHE_CLUSTER_REDIS_UNENCRYPTED)
    assert code == 1, out
    assert "elasticache_encryption_at_rest" in out


def test_elasticache_cluster_redis_encrypted_passes() -> None:
    # Redis aws_elasticache_cluster with transit_encryption_enabled on must pass
    # (at_rest_encryption_enabled is not valid on this resource).
    code, out = run_policy_validator(GOOD_ELASTICACHE_CLUSTER_REDIS_ENCRYPTED)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_elasticache_cluster_memcached_exempt_passes() -> None:
    # A Memcached cluster stays exempt (fail open) and must not be flagged.
    code, out = run_policy_validator(GOOD_ELASTICACHE_CLUSTER_MEMCACHED)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_block_form_forward_https_listener_does_not_false_fail() -> None:
    """Regression: a valid HTTPS listener whose default_action uses a NESTED
    forward { ... } block before `type` must NOT be misparsed as missing/wrong.

    The old r'default_action{[^}]*?type' regex stopped at the first '}' (the end
    of the nested block) and failed to read `type`, producing a false POLICY_FAIL.
    """
    with tempfile.TemporaryDirectory() as tmp:
        (Path(tmp) / "compute.tf").write_text(
            """
resource "aws_lb" "app" {
  name     = "app-alb"
  internal = false
}

resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.app.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS13-1-2-2021-06"
  # Placeholder — the checker only tests for the presence of `certificate_arn`,
  # not its value (see has_certificate_arn). A literal ACM ARN here trips secret
  # scanners for no benefit, so use a variable reference.
  certificate_arn   = var.acm_certificate_arn

  default_action {
    forward {
      target_group {
        arn = aws_lb_target_group.app.arn
      }
    }
    type = "forward"
  }
}

resource "aws_lb_listener" "http_redirect" {
  load_balancer_arn = aws_lb.app.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    type = "redirect"
    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}
""",
            encoding="utf-8",
        )
        code, out = run_policy_validator(Path(tmp))
        assert code == 0, f"block-form forward should pass, got: {out}"
        assert "POLICY_OK" in out


def test_quoted_port_https_listener_passes() -> None:
    """Regression: Terraform accepts a quoted integer for number arguments, so a
    correct HTTPS listener written as port = "443" must NOT be falsely flagged.

    Reading only bare integers left port=None, so https_ok was empty and a valid
    stack failed with rule=alb_https_listener.
    """
    code, out = run_policy_validator(GOOD_QUOTED_PORT_HTTPS)
    assert code == 0, out
    assert "POLICY_OK" in out


def test_quoted_port_db_sg_public_ingress_fails() -> None:
    """Regression: from_port / to_port = "5432" with cidr_blocks = ["0.0.0.0/0"]
    is a world-open database and must fire, not silently pass."""
    code, out = run_policy_validator(BAD_DB_SG_PUBLIC_QUOTED_PORT)
    assert code == 1, out
    assert "db_sg_no_public_ingress" in out


def test_quoted_port_sg_public_admin_ingress_fails() -> None:
    """Same quoted-port regression for the sensitive-port rule (SSH on 22)."""
    with tempfile.TemporaryDirectory() as tmp:
        (Path(tmp) / "vpc.tf").write_text(
            """
resource "aws_security_group" "bad" {
  name = "bad-ssh-sg"

  ingress {
    from_port   = "22"
    to_port     = "22"
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
""",
            encoding="utf-8",
        )
        code, out = run_policy_validator(Path(tmp))
        assert code == 1, out
        assert "sg_no_public_admin_ingress" in out
        assert "22 (SSH)" in out


def test_attr_int_reads_bare_and_quoted_integers() -> None:
    assert VALIDATOR._attr_int("  port = 443\n", "port") == 443
    assert VALIDATOR._attr_int('  port = "443"\n', "port") == 443
    assert VALIDATOR._attr_int('  from_port = "0"\n', "from_port") == 0


def test_attr_int_rejects_non_literal_quoted_values() -> None:
    """Precision guard: a quoted value must be ENTIRELY digits. Anything else is
    non-literal and must return None so the rules keep failing open."""
    for body in (
        '  port = "443x"\n',
        '  port = "x443"\n',
        '  port = "443-444"\n',
        '  port = "${var.port}"\n',
        '  port = " 443"\n',
        '  port = ""\n',
        "  port = var.port\n",
        "  port = local.ports[0]\n",
    ):
        assert VALIDATOR._attr_int(body, "port") is None, body


def test_attr_int_absent_attribute_is_none() -> None:
    assert VALIDATOR._attr_int('  protocol = "HTTPS"\n', "port") is None


# A listener whose own `port`/`protocol` are written AFTER its default_action.
# Valid HCL — `terraform fmt` does not reorder attributes relative to blocks — and
# the nested redirect carries both a `port` and a `protocol`, so a whole-body read
# returns the redirect's 443/HTTPS instead of the listener's own 80/HTTP.
_LISTENER_ATTRS_AFTER_NESTED_BLOCK = """{
  load_balancer_arn = aws_lb.app.arn

  default_action {
    type = "redirect"
    redirect {
      port     = "443"
      protocol = "HTTPS"
    }
  }

  port     = 80
  protocol = "HTTP"
}"""


def test_attrs_read_from_own_block_not_nested_block() -> None:
    """Accepting quoted integers makes a nested `port = "443"` a match candidate
    where a bare-only pattern could never match one, so reads are scoped to the
    block's own attributes. Pinned for both readers: _attr_string had the same
    whole-body weakness before this change and shares the scoping now."""
    assert VALIDATOR._attr_int(_LISTENER_ATTRS_AFTER_NESTED_BLOCK, "port") == 80
    assert (
        VALIDATOR._attr_string(_LISTENER_ATTRS_AFTER_NESTED_BLOCK, "protocol") == "HTTP"
    )


def test_attr_nested_only_value_is_not_promoted() -> None:
    """An attribute that exists ONLY inside a nested block is absent from the
    enclosing block — returning the nested value would invent an attribute.

    Written as `terraform fmt` emits it (one attribute per line), which is the form
    that actually reaches the line-anchored pattern; a collapsed one-line
    `redirect { port = "443" }` never matches and so would not exercise scoping."""
    body = '{\n  default_action {\n    redirect {\n      port = "443"\n    }\n  }\n}'
    assert VALIDATOR._attr_int(body, "port") is None


def test_attr_interpolation_does_not_hide_later_attributes() -> None:
    """`${...}` braces are balanced, so an interpolated value earlier in the body
    must not make a following top-level attribute unreadable."""
    body = '{\n  identifier        = "${var.project}-db"\n  storage_encrypted = true\n}'
    assert VALIDATOR._attr_string(body, "storage_encrypted") == "true"


def test_quote_nested_in_interpolation_does_not_end_the_string() -> None:
    """A `"` inside `${...}` opens a NESTED string; it must not end the outer one.

    Regression guard: the first lexer set `state = "code"` on any `"` seen in a
    string, so an inner string's closing quote ended the outer string and the rest
    of the line was scanned as code — putting textual braces back into the depth
    count. That is the exact failure class the lexer was added to close, reached by
    a different route, and it is reachable from ordinary HCL (`replace(v, "{", "")`,
    `templatefile("t.tpl", {"a"="{"})`).

    Both directions matter: an opening brace skews depth (the attribute is judged
    nested and skipped), a closing brace truncates the body outright.
    """
    for label, body in (
        ("open brace in nested quote", '{\n  identifier = "${lookup(var.m, "a{b")}"\n  port = 80\n}'),
        ("close brace in nested quote", '{\n  n = "${replace(v, "}", "")}"\n  port = 80\n}'),
        ("both braces in nested quote", '{\n  n = "${replace(v, "{}", "")}"\n  port = 80\n}'),
        ("nested quote, no brace", '{\n  a = "${lookup(var.tags, "Name")}"\n  port = 80\n}'),
        ("two interpolations", '{\n  a = "${f("x{")}-${g("y}")}"\n  port = 80\n}'),
    ):
        assert VALIDATOR._attr_int(body, "port") == 80, label


def test_lexically_irrelevant_braces_do_not_hide_attributes() -> None:
    """A brace in a comment, string, or heredoc is not a block delimiter, so it must
    not shift a real top-level attribute out of scope.

    Regression guard: an earlier revision counted raw `{`/`}` and DID hide the
    attribute here. For these rules a hidden attribute means a MISSED violation
    (`publicly_accessible = true` read as absent, hence compliant), so "fail open"
    is the wrong default — the value is a plain top-level literal, not ambiguous."""
    for label, body in (
        ("hash comment", '{\n  # primary { instance\n  port = 80\n}'),
        ("slash comment", '{\n  // primary { instance\n  port = 80\n}'),
        ("block comment", '{\n  /* primary { instance */\n  port = 80\n}'),
        ("string", '{\n  identifier = "primary { db"\n  port = 80\n}'),
        ("unbalanced string", '{\n  name = "a { b"\n  port = 80\n}'),
        ("heredoc", '{\n  policy = <<EOT\n  { not code }\nEOT\n  port = 80\n}'),
    ):
        assert VALIDATOR._attr_int(body, "port") == 80, label


def test_extraction_not_truncated_by_lexically_irrelevant_closing_brace() -> None:
    """A `}` in a comment, string, or heredoc body is not a delimiter, so it must
    not end the resource body early.

    Regression guard: raw brace counting in `_extract_braced_block` truncated the
    body before `publicly_accessible = true`, so the resource reached the rules
    with the offending attribute already discarded — zero violations."""
    for label, filler in (
        ("hash comment", "  # capacity review } pending"),
        ("slash comment", "  // capacity review } pending"),
        ("block comment", "  /* capacity review } pending */"),
        ("string", '  identifier = "primary } db"'),
        ("heredoc", "  description = <<EOT\n  a closing } brace\nEOT"),
        ("closing brace in a quote nested in an interpolation", '  n = "${replace(v, "}", "")}"'),
    ):
        content = (
            'resource "aws_db_instance" "pg" {\n'
            f"{filler}\n"
            "  publicly_accessible = true\n"
            "}\n"
        )
        blocks = VALIDATOR._extract_blocks(content, "aws_db_instance")
        assert len(blocks) == 1, label
        assert (
            VALIDATOR._attr_string(blocks[0][1], "publicly_accessible") == "true"
        ), label


def test_regular_heredoc_terminator_must_be_unindented() -> None:
    """`<<TAG` requires the terminator at column 0; only `<<-TAG` tolerates
    indentation. Treating both as tolerant exits a regular heredoc early, so its
    body text is read as real code — here an in-heredoc `port = 443` was returned
    instead of the listener's own `port = 80`."""
    regular = '{\n  policy = <<EOT\n  EOT\n  port = 443\nEOT\n  port = 80\n}'
    assert VALIDATOR._attr_int(regular, "port") == 80
    indented = '{\n  policy = <<-EOT\n  { not code }\n  EOT\n  port = 80\n}'
    assert VALIDATOR._attr_int(indented, "port") == 80


def test_attribute_presence_probe_matches_value_scoping() -> None:
    """`_has_own_attr` must agree with `_attr_string` about what counts as set.

    The encryption rules read "attribute present but unreadable" as
    variable-driven and skip. If presence counted a commented-out assignment the
    reader ignores, a resource whose attribute is genuinely ABSENT — hence
    unencrypted by default — would be skipped instead of flagged."""
    only_commented = '{\n  identifier = "pg"\n  /*\n  storage_encrypted = false\n  */\n}'
    assert VALIDATOR._has_own_attr(only_commented, "storage_encrypted") is False
    assert VALIDATOR._attr_string(only_commented, "storage_encrypted") is None
    real = '{\n  storage_encrypted = var.encrypt\n}'
    assert VALIDATOR._has_own_attr(real, "storage_encrypted") is True
    assert VALIDATOR._attr_string(real, "storage_encrypted") is None


def test_commented_out_resource_is_not_extracted() -> None:
    """A commented-out resource declaration is not a resource."""
    content = (
        '# resource "aws_db_instance" "ghost" {\n'
        "#   publicly_accessible = true\n"
        "# }\n"
    )
    assert VALIDATOR._extract_blocks(content, "aws_db_instance") == []


def test_commented_out_attribute_is_not_read() -> None:
    """An attribute that only appears inside a comment is not set."""
    assert VALIDATOR._attr_int("{\n  # port = 443\n}", "port") is None
    assert VALIDATOR._attr_int("{\n  // port = 443\n}", "port") is None
    assert VALIDATOR._attr_int("{\n  /*\n  port = 443\n  */\n}", "port") is None
    assert (
        VALIDATOR._attr_string('{\n  /*\n  protocol = "HTTPS"\n  */\n}', "protocol")
        is None
    )


def test_every_fixture_matches_its_good_bad_prefix() -> None:
    """Enforce the naming invariant the fixture set relies on: `good-*` (and the
    internal-ALB case) must be POLICY_OK, `bad-*` must be POLICY_FAIL. Without
    this, each fixture is wired up by hand and a new one added without its own
    test would silently get no coverage."""
    fixture_dirs = sorted(d for d in FIXTURES.iterdir() if d.is_dir())
    # Exact, not `>=`: a floor cannot detect fixtures being deleted down to it,
    # which is the removal this assertion exists to catch. Update deliberately
    # when adding or removing a fixture.
    assert len(fixture_dirs) == 31, f"fixture count changed: {[d.name for d in fixture_dirs]}"
    for fixture in fixture_dirs:
        code, out = run_policy_validator(fixture)
        if fixture.name.startswith("bad-"):
            assert code == 1, f"{fixture.name} must FAIL: {out}"
            assert "POLICY_FAIL" in out, f"{fixture.name}: {out}"
        else:
            assert code == 0, f"{fixture.name} must pass: {out}"
            assert "POLICY_OK" in out, f"{fixture.name}: {out}"

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Python, Needs Node.js

Security

What our scanner noticed· Low risk

  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
AWS
Publisher
AWS (Official)
Source
aws/agent-toolkit-for-aws · plugins/aws-startup-advisor/skills/tf-best-practices · commit 0a218f8
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • NOTICE (added by AutoGineer from NOTICE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by AWS or AWS.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download tf-best-practices.zip