Infrastructure-as-Code Security
SkillCloud & infraTerraform, CloudFormation, and Pulumi hardening: state as a secret store, pinned providers and modules, encryption and network defaults, drift, and the privilege of the pipeline that applies the plan. Use when generating infrastructure code, reviewing IaC changes in a pull request, configuring a state backend, or setting up a new cloud account or workspace.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Infrastructure-as-Code Security skill
What this skill tells your AI
The instructions your AI receives, as published by shieldnet-360/secure-vibe in skills/iac-security/SKILL.md and read by ahel’s review.
Rules (for AI agents)
ALWAYS
- Treat the state file as a secret store. Every value that passes through
Terraform — a generated password, an RDS master credential, a private key, the
contents of a data source — is written to state in plaintext.
sensitive = trueonly hides a value from CLI output; it changes nothing about what is stored. So read access to the backend is read access to those secrets, and the backend's access policy has to be written on that basis. - Configure a remote backend with encryption at rest, state locking, and
versioning. Prefer the backend's own locking where it exists — recent Terraform
supports S3-native locking, so a separate DynamoDB lock table is no longer required
for new stacks.
references/backends-and-state.mdhas the per-backend form. - Pin providers and modules to an exact version or a pessimistic constraint
(
~> 5.42), and pin a third-party module sourced from a git URL to a commit SHA, not a branch or tag. A module is code that runs with your apply credentials, and a registry or repository you do not control is a supply-chain input like any other. - Give the apply identity the least privilege that the stack actually needs, and
keep it separate from the plan identity. A pipeline role with account-wide admin
turns any CI compromise into a full account compromise —
cicd-securityowns hardening the pipeline itself,iam-best-practicesowns the role's shape. - Encrypt persistent resources by default with a customer-managed key: object storage, block storage, managed databases, queues, log groups. Prefer a module or default that applies it, so a new resource is encrypted because of how the code is organised rather than because someone remembered.
- Deny inbound by default and open ports deliberately. Administrative and database
ports reachable from
0.0.0.0/0are the finding — SSH, RDP, and the managed database ports especially — and "it is only dev" does not change the exposure.references/backends-and-state.mdlists the ports worth failing a build over. - Run
plan(orpulumi preview, a CloudFormation change set) in CI, require human approval before applying to production, and run scheduled drift detection that raises an issue when the cloud diverges from the code. Drift is where a break-glass change made at 3am quietly becomes the permanent configuration. - Scope roles and resource policies with the provider's own condition mechanism —
source account, source ARN, organization ID, TLS-only access on storage.
iam-best-practicesowns policy design; what belongs here is that the condition is expressed in the code rather than clicked into the console.
NEVER
- Hardcode provider credentials in code or
.tfvars(access_key,secret_key,client_secret,service_account_key). Use OIDC federation from CI, the platform's workload identity, or a secret manager. - Commit
terraform.tfstate,terraform.tfstate.backup,.pulumi/, or a.tfvarsholding real values — see the state rule above for why the file is a credential dump even when the code only references variables. - Use
local-exec/null_resourceto fetch a secret at apply time. It lands in state, and the provisioner runs on whatever machine ranapply, with that machine's identity. - Disable provider TLS verification (
skip_tls_verify,insecure = true).
KNOWN FALSE POSITIVES
- A bastion deliberately reachable on SSH is not the same finding as a database open to the world — but "bastion" is not the exemption, its configuration is: key-only authentication, MFA, and a narrow source range. A session-manager or identity-aware-proxy path that removes the public port entirely is better than hardening one.
- Resources that are meant to be internet-facing: CDN distributions, load-balancer listeners on 80/443, API gateways, function URLs.
- Backend bootstrap resources — the bucket and lock mechanism the backend itself uses — cannot live in the state they provide. A one-time local backend that is then migrated is the normal resolution, not a finding.
- An unencrypted resource that holds nothing but public data, where the tag or classification says so explicitly.
Context (for humans)
IaC mistakes scale by construction: one bad module is applied into every account that
consumes it, and the blast radius is whatever the apply credential can reach. That
credential is the part most reviews skip. A pipeline that can create IAM roles can
create itself a better one, so the privilege of the thing running apply bounds
everything else in this skill.
The other recurring surprise is state. Practitioners reach for sensitive = true and
believe the value is protected; it is a display flag. Anything Terraform manages, it
has seen, and anything it has seen is in the state file — which is why "who can read
the backend" is a more consequential question than most of the resource-level settings
a scanner will flag.
References
references/verifying-findings.md— confirm or refute a finding, then lock itreferences/backends-and-state.md— backend and locking options per tool, what is stored in state and how to reduce it, and the port list worth failing a build overchecklists/terraform_hardening.yamlchecklists/cloudformation_hardening.yaml- CIS AWS Foundations Benchmark.
- Terraform recommended practices.
- NIST SP 800-53 Rev. 5.
Signals
- GitHub stars
- 22
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
iac-security- Source
- github.com/shieldnet-360/secure-vibe