| name | iac |
| description | Infrastructure as Code best practices with Terraform, Ansible, Pulumi, and CloudFormation. Use when bootstrapping cloud environments, managing remote state backends, recovering from state issues, automating CI/CD pipelines for infrastructure, modularizing infrastructure for reuse, enforcing drift detection, or performing risk assessments before applying changes. |
| metadata | {"authors":"pluginagentmarketplace, omer-metin, williamzujkowski","sources":["https://playbooks.com/skills/pluginagentmarketplace/custom-plugin-devops/iac","https://playbooks.com/skills/omer-metin/skills-for-antigravity/infrastructure-as-code","https://playbooks.com/skills/williamzujkowski/standards/infrastructure-as-code"]} |
Infrastructure as Code (IaC) Best Practices
Overview
Pragmatic guidance and battle-tested patterns for provisioning and managing cloud infrastructure with Terraform, Pulumi, Ansible, and CloudFormation. Focuses on preserving state, preventing drift, reducing blast radius, and protecting production environments. Grounded in real operational failures and prescriptive patterns.
When to Use
- Bootstrapping cloud environments and standardized resources
- Configuring servers, roles, and deployments using Ansible playbooks
- Managing and recovering Terraform state in remote backends
- Automating CI/CD pipelines for infrastructure changes
- Modularizing infrastructure for reuse across teams
- Performing risk assessments before terraform apply/stack updates
- Investigating production outages tied to state drift or misapplied changes
- Enforcing drift detection and remediation
Core Principles
1. Treat State as Sacred
- Enforce remote backends with backups and locking
- Never store state locally in team environments
- Enable versioning on state storage (S3 versioning, etc.)
- Restrict access to state files via IAM policies
2. Always Plan Before Apply
- Run and review an explicit plan before applying changes
- Use
--check/diff modes to preview changes and detect drift
- Require human review of plans for production workspaces
3. Limit Blast Radius
- Split infrastructure into small, purpose-driven modules
- Use separate workspaces/projects for prod vs non-prod
- Enable resource targeting sparingly
- Implement policy checks that block destructive changes
4. Secure Secrets
- Never store plaintext secrets in state
- Integrate secret managers (AWS Secrets Manager, HashiCorp Vault)
- Use encrypted variables and ansible-vault for Ansible
- Rotate exposed secrets immediately
5. Validate Early and Often
- Run
terraform fmt and validate in CI
- Implement drift detection on a schedule
- Use linters (TFLint, Checkov) for best practice enforcement
Terraform Workflow
Init / Plan / Apply / Destroy Cycle
terraform init
terraform plan -out=tfplan
terraform apply tfplan
terraform destroy
Remote State Setup
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
State Recovery
terraform force-unlock <lock-id>
terraform import aws_vpc.main vpc-12345678
terraform state rm aws_instance.old
aws s3api list-object-versions --bucket my-terraform-state --prefix prod/terraform.tfstate
aws s3api get-object --bucket my-terraform-state --key prod/terraform.tfstate --version-id <version> restored.tfstate
Ansible Patterns
Playbook Best Practices
- hosts: webservers
roles:
- common
- nginx
- app-deploy
ansible-playbook -i inventory/production site.yml
ansible-playbook site.yml --check --diff
Secrets with Ansible Vault
ansible-vault encrypt secrets.yml
ansible-vault edit secrets.yml
ansible-playbook site.yml --ask-vault-pass
Modularization Strategy
When to Build a Module vs Copy Code
| Scenario | Approach |
|---|
| Used across 2+ projects/environments | Create a module |
| One-off configuration | Inline resources |
| Complex logic with many variables | Module with clear interface |
| Simple 1-2 resources | Inline unless shared |
Module Boundaries
modules/
├── networking/ # VPC, subnets, route tables
├── compute/ # EC2, ASG, ALB
├── data/ # RDS, ElastiCache, DynamoDB
├── security/ # IAM, security groups, KMS
└── monitoring/ # CloudWatch, SNS, alarms
CI/CD Integration
Pipeline Stages
- Lint -
terraform fmt -check, tflint
- Validate -
terraform validate
- Security Scan -
checkov, trivy
- Plan -
terraform plan -out=tfplan
- Review - Human approval for production
- Apply -
terraform apply tfplan
- Drift Check - Scheduled comparison of live vs state
Drift Detection
terraform plan -detailed-exitcode
Anti-Patterns to Avoid
| Anti-Pattern | Risk | Fix |
|---|
| Local state files | Data loss, no collaboration | Use remote backend with locking |
| Monolithic modules | Large blast radius, slow plans | Split into focused modules |
| Secrets in state | Security breach | Use secret managers + write-only args |
| No plan review | Accidental destruction | Require human approval for prod |
| Manual console changes | State drift | Enforce IaC-only changes policy |
| No resource tagging | Cost tracking impossible | Enforce tags via policy |
FAQ
How do I handle a state lock caused by a failed operation?
Wait for the operation to complete or use terraform force-unlock with the lock ID after confirming no active processes are modifying the state.
When should I import resources instead of recreating them?
Import when resources already exist in the cloud and you want to adopt them into IaC without downtime or when deletion would be disruptive.
How do I protect production from accidental deletes?
Use separate workspaces for prod, require mandatory plan reviews and approvals, enable resource targeting sparingly, and implement policy checks that block destructive changes to critical resources.
How do I avoid state conflicts with multiple people?
Use a remote backend that supports locking (S3 + DynamoDB or Terraform Cloud) and require plan review before apply.