infrastructure-as-code-guardian

>

jpeetz/agent-skills1 installsMITSynced Aug 22

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: infrastructure-as-code-guardian
description: >
license: MIT
---

# Infrastructure as Code Guardian

Universal IaC agent skill—author, secure, manage, and migrate infrastructure across Terraform, Pulumi, CloudFormation, Ansible, and Bicep.

---

## Activation Triggers

### Positive Triggers (activate on these)

1. "Write a Terraform module for…"
2. "Secure this IaC codebase" / "Harden my infra config"
3. "Check for drift in staging" / "Run a drift detection workflow"
4. "Migrate from CloudFormation to Terraform" (or any cross-tool migration)
5. "Add cost optimization tags to my IaC" / "Reduce cloud costs via IaC"
6. "Set up remote state for this project" / "Fix state locking"
7. "Generate a least-privilege IAM policy from this Terraform"
8. "Review this Pulumi stack for security issues" / "IaC security audit"
9. "Create an Ansible playbook from this existing infra"
10. "Write a Bicep module and convert to ARM" / "Author Bicep for Azure"
11. "Set up GitOps pipeline for infrastructure" / "CI/CD for IaC"
12. "This Terraform plan shows drift—help me reconcile" / "Infra state is out of sync"
13. "What IaC tool should I use for…" / "Pulumi vs Terraform for this project"
14. "Convert Kubernetes YAML manifests to Crossplane compositions"
15. "Generate compliance report for my infrastructure" / "CIS benchmark for IaC"

### Near-Miss Negatives (do NOT activate)

1. "Deploy this Docker container to production" — operational deploy, not IaC authoring
2. "Write a Python script to list S3 buckets" — script authoring, not infrastructure definition
3. "How do I SSH into this EC2 instance?" — operational troubleshooting, not IaC

### Ambiguous Cases (ask clarifying question)

- "Fix my cloud" → ask: "Are you looking to fix IaC definitions, or troubleshoot a running deployment?"
- "Setup AWS" → ask: "Do you want to author infrastructure code, or configure an AWS account manually?"

---

## Tool Selection Decision Tree

When choosing an IaC tool, evaluate the following in order. Present your reasoning explicitly.

```
START
 │
 ├─ Is managed-by-CSP-only acceptable? (no external state)
 │   ├─ YES, AWS-only          → CloudFormation / AWS CDK
 │   ├─ YES, Azure-only         → Bicep / ARM
 │   └─ NO (multi-cloud needed) → Continue ▼
 │
 ├─ Is the team primarily developers comfortable with
 │  general-purpose languages?
 │   ├─ YES → Pulumi (TypeScript, Python, Go, C#)
 │   └─ NO  → Continue ▼
 │
 ├─ Is this a greenfield project with strong community
 │  / module ecosystem needs?
 │   ├─ YES → Terraform (HCL) or OpenTofu
 │   └─ NO  → Continue ▼
 │
 ├─ Is configuration management the primary goal
 │  (installing packages, managing config files on VMs)?
 │   ├─ YES                       → Ansible
 │   └─ NO (declarative infra)    → Continue ▼
 │
 ├─ Are you operating inside a Kubernetes-native
 │  platform team?
 │   ├─ YES → Crossplane
 │   └─ NO  → Terraform (default fallback with largest ecosystem)
 │
 └─ OUTCOME: Recommend tool with reasoning.
    Always note: Terraform (76% market share) is the safest default.
    Pulumi is the fastest-growing (45% YoY) and best for dev-centric teams.
```

### Quick Comparison Matrix

| Criterion              | Terraform   | Pulumi       | CFn         | Bicep       | Ansible     |
|------------------------|-------------|--------------|-------------|-------------|-------------|
| **Language**           | HCL         | TS/Py/Go/C#  | YAML/JSON   | Bicep DSL   | YAML        |
| **State Management**   | Explicit    | Explicit     | Implicit    | Implicit    | N/A         |
| **Multi-Cloud**        | Yes         | Yes          | AWS only    | Azure only  | Yes         |
| **Ecosystem Size**     | Largest     | Growing fast | Large (AWS) | Medium      | Large       |
| **Learning Curve**     | Moderate    | Low (for devs)| Steep       | Low         | Low         |
| **Drift Detection**    | plan        | refresh      | Drift Detect| what-if     | check mode  |
| **Secret Handling**    | Vault/sops  | ESC/Vault    | KMS/Secrets | Key Vault   | Vault/sops  |
| **CI/CD Integration**  | Excellent   | Excellent    | Good        | Good        | Good        |

---

## Authoring Guidelines per Tool

### Terraform (HCL)

**Structure:**
```hcl
# Standard module layout
modules/
  networking/
    main.tf        # Primary resources
    variables.tf   # Input variables with types + descriptions
    outputs.tf     # Exported values
    versions.tf    # Provider + Terraform version constraints
    README.md      # Usage, examples, API surface
```

**HCL Patterns to Enforce:**
- Always pin provider versions in `versions.tf` with `>=` lower bound and `~>` pessimistic constraint
- Use `for_each` over `count` for resource iteration (deterministic keys prevent destroy/recreate cascades)
- Prefix all outputs with the module name to avoid collisions: `output "vpc_id"` → `output "networking_vpc_id"`
- Use `dynamic` blocks sparingly—prefer explicit blocks for readability
- Every variable must have `type` and `description`; sensitive vars must have `sensitive = true`
- Use `terraform-docs` to generate README from variable/output metadata
- Apply `lifecycle { prevent_destroy = true }` on stateful resources (databases, storage buckets)
- Use `lifecycle { ignore_changes = [tags["LastModified"]] }` for externally-managed tag mutations

**State Management:**
```hcl
# Remote state with locking (S3 + DynamoDB example)
terraform {
  backend "s3" {
    bucket         = "myorg-terraform-state-${var.environment}"
    key            = "networking/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-state-locks"
    encrypt        = true
  }
}
```

**Anti-Patterns to Flag:**
- `terraform plan | sh` (blind apply) → recommend `terraform apply` with review
- Hardcoded ARNs → use `data` sources or `var.*` references
- Missing `required_providers` block → enforce in `versions.tf`
- Embedding secrets in `.tf` files → recommend Vault/sops/ESC

---

### Pulumi (TypeScript / Python)

**TypeScript Patterns:**
```typescript
// Component Resource pattern (Pulumi best practice)
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

interface VpcComponentArgs {
  cidrBlock: string;
  enableDnsHostnames?: boolean;
  tags?: pulumi.Input<{ [key: string]: pulumi.Input<string> }>;
}

export class VpcComponent extends pulumi.ComponentResource {
  public readonly vpc: aws.ec2.Vpc;
  public readonly publicSubnets: aws.ec2.Subnet[];
  public readonly privateSubnets: aws.ec2.Subnet[];

  constructor(name: string, args: VpcComponentArgs, opts?: pulumi.ComponentResourceOptions) {
    super("myorg:networking:Vpc", name, {}, opts);

    this.vpc = new aws.ec2.Vpc(`${name}-vpc`, {
      cidrBlock: args.cidrBlock,
      enableDnsHostnames: args.enableDnsHostnames ?? true,
      tags: { ...args.tags, Name: `${name}-vpc` },
    }, { parent: this });

    // Register outputs
    this.registerOutputs({
      vpc: this.vpc,
      publicSubnets: this.publicSubnets,
    });
  }
}
```

**Python Patterns:**
```python
from pulumi import ComponentResource, ResourceOptions, Output
from pulumi_aws import ec2

class VpcComponent(ComponentResource):
    def __init__(self, name: str, args: VpcComponentArgs, opts: ResourceOptions = None):
        super().__init__("myorg:networking:Vpc", name, {}, opts)

        self.vpc = ec2.Vpc(
            f"{name}-vpc",
            cidr_block=args.cidr_block,
            enable_dns_hostnames=True,
            tags={"Name": f"{name}-vpc"},
            opts=ResourceOptions(parent=self),
        )

        self.register_outputs({"vpc": self.vpc})
```

**Pulumi-Specific Guardrails:**
- Use `ComponentResource` for all reusable abstractions (never raw `new Resource()` in stacks)
- Stack references for cross-stack data: `new pulumi.StackReference("org/project/stack")`
- Use `pulumi.output` and `apply()` for computed values; avoid `Promise.all` anti-patterns
- Enable Pulumi ESC for secret management; never hardcode secrets in stack configs
- Use `pulumi policy pack` for organization-wide compliance (e.g., `--policy-pack awsguard`)
- Handle `ResourceOptions` propagation: `protect`, `deleteBeforeReplace`, `ignoreChanges`

---

### CloudFormation (YAML)

**Best-Practice Structure:**
```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: >
  Production VPC stack with public/private subnets, NAT gateways,
  and VPC endpoints for S3 and DynamoDB. (SOC 2 compliant)

Parameters:
  Environment:
    Type: String
    AllowedValues: [dev, staging, prod]
    Description: Deployment environment name

  VpcCidr:
    Type: String
    Default: "10.0.0.0/16"
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/([0-9]|[1-2][0-9]|3[0-2]))$"

Mappings:
  SubnetConfig:
    us-east-1:
      PublicA: "10.0.1.0/24"
      PrivateA: "10.0.10.0/24"

Conditions:
  IsProduction: !Equals [!Ref Environment, "prod"]

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCidr
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Environment
          Value: !Ref Environment
        - Key: ManagedBy
          Value: CloudFormation

Outputs:
  VpcId:
    Value: !Ref VPC
    Export:
      Name: !Sub "${AWS::StackName}-VpcId"
```

**CloudFormation Rules:**
- Always use `!Sub` over `Fn::Join` for readability
- Export stack outputs with unique names: `${AWS::StackName}-ResourceId`
- Use `Mappings` for region-specific values; `Conditions` for environment branching
- Enable `TerminationProtection` on production stacks
- Use `AWS::CloudFormation::StackSet` for multi-region/multi-account deployments
- Run `cfn-lint` and `cfn-nag` in pre-commit; integrate into CI pipeline
- Avoid embedding secrets in Parameters—use `AWS::SecretsManager::Secret` or `resolve:ssm-secure:/path`

---

### Ansible

**Playbook Structure:**
```yaml
---
# site.yml — Entry-point playbook
- name: Configure production web servers
  hosts: webservers
  become: true
  vars_files:
    - vars/production.yml
  roles:
    - common
    - nginx
    - node_exporter
  pre_tasks:
    - name: Verify minimum disk space
      ansible.builtin.assert:
        that: ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_available') | first > 1073741824
        fail_msg: "Insufficient disk space on / "
  post_tasks:
    - name: Send deployment notification
      ansible.builtin.uri:
        url: "{{ slack_webhook_url }}"
        method: POST
        body: '{"text": "Deployment complete on {{ inventory_hostname }}"}'
```

**Ansible IaC Patterns:**
- Use `ansible.builtin.*` FQCNs for all modules (future-proof against collections changes)
- Separate inventory by environment: `inventories/production/hosts.yml`
- Encrypt secrets with `ansible-vault`; never store plaintext in vars files
- Use `check_mode: true` for dry-run validation before production applies
- `--diff` flag for configuration change previews (acts as drift detection)
- Idempotency is mandatory—every task must produce the same result on repeat runs
- Use `ansible-lint` with production ruleset; integrate into pre-commit hooks

---

### Bicep

**Module Patterns:**
```bicep
// modules/network.bicep
param location string = resourceGroup().location
param vnetName string
param addressPrefix string = '10.0.0.0/16'
param subnetPrefixes array = ['10.0.1.0/24', '10.0.2.0/24']
param tags object = {}

@description('Tags to apply to all resources, merged with env tags')
param environmentTags object

var effectiveTags = union(tags, environmentTags)

resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' = {
  name: vnetName
  location: location
  tags: effectiveTags
  properties: {
    addressSpace: {
      addressPrefixes: [addressPrefix]
    }
    subnets: [for (prefix, i) in subnetPrefixes: {
      name: 'subnet-${i}'
      properties: {
        addressPrefix: prefix
      }
    }]
  }
}

output vnetId string = vnet.id
output vnetNameOut string = vnet.name
```

**Bicep Best Practices:**
- Use `@description()` decorator on all params and outputs
- Use Bicep parameter files (`.bicepparam`) for environment-specific values
- Run `bicep build` before deployment to catch compile-time errors
- Use `what-if` deployment for change preview (Azure's equivalent of `terraform plan`)
- Pin API versions explicitly; avoid `@latest`-style references in production
- Use modules with semantic version references via module registries (`br:*`)
- Use `existing` keyword for referencing externally-managed resources

---

## Security Hardening Checklist

Execute this checklist on every IaC codebase review. Flag violations with severity and fix suggestions.

### 1. Least Privilege IAM (CRITICAL)

- [ ] No `*` in Action arrays—use explicit action lists
- [ ] No `*` in Resource arrays (except where unavoidable like CloudWatch Logs)
- [ ] No `"Effect": "Allow"` combined with `"Resource": "*"` and `"Action": "*"` (triple-star anti-pattern)
- [ ] S3 bucket policies have explicit `Principal`; no `"Principal": {"AWS": "*"}` unless intentional public access
- [ ] IAM Roles use `AssumeRolePolicyDocument` with least-privilege trust relationships
- [ ] IAM Policies follow the "250 IAM best practices" checklist: condition keys, source IP restriction, MFA enforcement

**Terraform Fix Example:**
```hcl
# BAD — triple-star anti-pattern
data "aws_iam_policy_document" "bad" {
  statement {
    effect    = "Allow"
    actions   = ["*"]
    resources = ["*"]
  }
}

# GOOD — least privilege with conditions
data "aws_iam_policy_document" "good" {
  statement {
    effect = "Allow"
    actions = [
      "s3:GetObject",
      "s3:PutObject",
    ]
    resources = ["${aws_s3_bucket.app.arn}/uploads/*"]
    condition {
      test     = "StringEquals"
      variable = "s3:x-amz-acl"
      values   = ["bucket-owner-full-control"]
    }
  }
}
```

### 2. Secret Management (CRITICAL)

- [ ] No secrets in `.tf`, `.ts`, `.yaml`, `.bicep` files (check with `gitleaks` or `trufflehog`)
- [ ] Database passwords, API keys, private keys stored in: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, Pulumi ESC
- [ ] State files encrypted at rest (S3 SSE, Azure storage encryption, GCS CMEK)
- [ ] State files encrypted in transit (TLS 1.2+)
- [ ] `sensitive = true` on all Terraform output values containing secrets
- [ ] Ansible vault encrypts all `vars/secrets.yml` files
- [ ] Pulumi stack config secrets marked with `--secret` flag

**Detection Script:**
```bash
# Quick secret-in-code scan
gitleaks detect --source . --no-git --verbose 2>/dev/null || \
  trufflehog filesystem . --no-update --fail 2>/dev/null
```

### 3. Encryption (HIGH)

- [ ] S3 buckets: SSE-S3 minimum, SSE-KMS recommended; `block_public_access` enabled
- [ ] RDS/Database: storage_encrypted = true; encryption at rest enabled
- [ ] EBS volumes: `encrypted = true` by default (enforce via SCP or AWS Config rule)
- [ ] EKS/Container: secrets encryption with KMS; envelope encryption on etcd
- [ ] ELB/ALB: HTTPS listeners only; HTTP → HTTPS redirect; minimum TLS 1.2
- [ ] CloudFront: `viewer_protocol_policy = "redirect-http-to-https"`
- [ ] KMS key rotation enabled (automatic annual rotation or custom schedule)

### 4. Network Isolation (HIGH)

- [ ] Security groups: no `0.0.0.0/0` ingress unless intentional (e.g., public ALB)
- [ ] Security groups: restricted egress to necessary CIDRs/prefix-lists only
- [ ] NACLs: stateless rules as defense-in-depth layer
- [ ] Private subnets: no direct internet route; NAT Gateway for outbound-only
- [ ] VPC endpoints for AWS services (S3, DynamoDB) to avoid internet traversal
- [ ] WAF attached to all public-facing ALBs and CloudFront distributions
- [ ] Network firewall or equivalent for egress filtering in production

### 5. Logging & Monitoring (MEDIUM)

- [ ] CloudTrail enabled in all regions with log file validation
- [ ] VPC Flow Logs enabled on all VPCs
- [ ] S3 access logs enabled on sensitive buckets
- [ ] Load balancer access logs enabled
- [ ] GuardDuty enabled in all regions
- [ ] Security Hub enabled with CIS benchmark standard
- [ ] Config rules for continuous compliance monitoring

### 6. Cost Optimization Guardrails (MEDIUM)

- [ ] Resource tagging strategy enforced: `Environment`, `Owner`, `CostCenter`, `Project`
- [ ] Auto-scaling configured with min/max bounds (not static instance counts)
- [ ] Reserved Instances / Savings Plans for predictable workloads
- [ ] S3 lifecycle policies: transition to IA/Glacier, expire old versions
- [ ] NAT Gateway count minimized (use single-AZ for dev, multi-AZ for prod)
- [ ] RDS: stop-dev-instances automation for non-production after hours
- [ ] Lambda: memory and timeout tuned (not default 128MB/3s for everything)

---

## State Management Best Practices

### Terraform / OpenTofu

1. **Remote State is Mandatory.** No local state files in production repos. Use S3 + DynamoDB (AWS), Azure Storage (Azure), GCS (GCP).
2. **State File Isolation.** One state file per environment (dev/staging/prod), per region, and per logical boundary (networking, compute, data).
3. **State Locking.** Always enable DynamoDB locks (AWS) or equivalent. Prevents concurrent applies that corrupt state.
4. **State Encryption.** Enable server-side encryption on state storage. `encrypt = true` in backend config.
5. **Workspace Strategy.** Use separate backends or directory separation. Avoid `terraform workspace` for long-lived environments—they share the same backend and credentials.
6. **State Access Control.** Restrict state bucket access to CI/CD service roles and senior engineers. No developer IAM users with direct state read/write.
7. **State Versioning.** Enable bucket versioning on state storage for rollback capability.
8. **State Import Hygiene.** `terraform import` should be followed immediately by cleanup of manual resource configs. Document every import in a migration log.
9. **State Refresh.** Run `terraform apply -refresh-only` before plan to reconcile external changes.
10. **Never Edit State Files.** Use `terraform state mv`, `terraform state rm`, `terraform state pull`—never edit JSON state manually.

### Pulumi

1. **Pulumi Cloud or Self-Managed Backend.** Pulumi Cloud (SaaS) is the default and recommended. For self-managed, use S3/Azure/GCS with the Pulumi service managing locking.
2. **Stack Configuration.** Separate stacks per environment. Use `pulumi config set --secret` for sensitive values.
3. **State Export.** `pulumi stack export` for backup; `pulumi stack import` for recovery.
4. **Resource Protection.** `protect: true` on stateful resources (RDS, DynamoDB) to prevent accidental deletion.
5. **State History.** Pulumi Cloud retains deployment history; self-managed backends use object versioning.

### CloudFormation

1. **Change Sets.** Always create a Change Set and review before executing. Never direct-update production stacks.
2. **Stack Policies.** Define stack policies that prevent updates/replacements of stateful resources during routine changes.
3. **Nested Stacks.** Use nested stacks for large deployments to stay within resource limits (500 resources per stack). Export shared outputs via `Fn::ImportValue`.
4. **StackSets.** For multi-account/multi-region: use StackSets with service-managed permissions.

### Cross-Cutting Rules

- **Never share state between tools.** A resource created by Terraform should not be managed by Pulumi in the same state.
- **State backup is non-negotiable.** Automate daily state file backups to a separate, immutable bucket.
- **State access audit.** Log all state file reads/writes. Alert on unexpected access patterns.

---

## Drift Detection Workflow

Drift = configuration in code ≠ actual infrastructure state. Drift causes outages, security gaps, and compliance failures.

### Standard Workflow

```
1. IDENTIFY
   ├─ Terraform:  terraform plan -detailed-exitcode
   ├─ Pulumi:     pulumi refresh --diff
   ├─ CFn:        aws cloudformation detect-stack-drift
   ├─ Bicep:      az deployment group what-if
   └─ Ansible:    ansible-playbook --check --diff site.yml

2. CLASSIFY
   ├─ EXPECTED DRIFT   (auto-scaling events, tag auto-updates)
   │   └─ Update code to match reality (terraform import / pulumi import)
   └─ UNEXPECTED DRIFT (manual console changes, security group widens)
       └─ Investigate audit logs → Reconcile or revert

3. RECONCILE
   ├─ Prefer code-as-truth:  update infrastructure to match code
   ├─ Import manual changes: terraform import / pulumi import / CFn resource import
   └─ Document exceptions:   lifecycle { ignore_changes = [...] }

4. PREVENT
   ├─ Restrict console access (break-glass only, with auto-revert)
   ├─ Run drift detection on schedule (hourly for prod, daily for staging)
   ├─ Alert on drift via CloudWatch/PagerDuty/Slack
   └─ Auto-remediate with AWS Config rules or OPA policies
```

### CI/CD Integration

```yaml
# GitHub Actions — Drift Check Job
drift-check:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: hashicorp/setup-terraform@v3
    - name: Terraform Plan (Drift Check)
      id: plan
      run: |
        terraform init -backend-config="key=${{ env.TF_STATE_KEY }}"
        terraform plan -detailed-exitcode -out=tfplan
      continue-on-error: true
    - name: Alert on Drift
      if: steps.plan.outcome == 'failure'
      uses: slackapi/slack-github-action@v2
      with:
        payload: '{"text": "🚨 Drift detected in ${{ github.repository }} — ${{ env.ENVIRONMENT }}"}'
```

### Drift Remediation Patterns

| Drift Type                  | Action                                            |
|----------------------------|---------------------------------------------------|
| Manual SG rule addition    | Revert manually; audit CloudTrail; enforce SCP    |
| Auto-scaling group change  | Update `desired_count` in IaC to match            |
| Tag mutation by external tool | Add `lifecycle { ignore_changes = [tags] }`    |
| IAM policy widened         | Immediate revert; security incident response       |
| RDS instance type changed  | Update IaC to match; verify change window approval |
| S3 bucket policy changed   | Revert; enable `block_public_access`; alert        |

---

## Migration Patterns (Cross-Tool)

### CloudFormation → Terraform

1. Use `terraform import` with resource mapping:
   ```bash
   terraform import aws_vpc.main vpc-0a1b2c3d4e5f67890
   ```
2. Alternatively: `former2` (ex-cloudformer) → generates Terraform/AWS CDK from existing infra
3. Post-import: `terraform plan` to verify no diffs; then remove CloudFormation stack after confirming resources are imported (use `DeletionPolicy: Retain` during transition)

### Terraform → Pulumi

1. Use `pulumi import` with Terraform state:
   ```bash
   pulumi import --from terraform terraform.tfstate
   ```
2. Use `tf2pulumi` to convert HCL to TypeScript/Python: `tf2pulumi ./modules/*.tf`
3. Validate with `pulumi preview` before going live

### ARM → Bicep

1. `bicep decompile` for automated conversion: `az bicep decompile --file template.json`
2. Manual review required—Bicep decompilation is not lossless for complex templates

---

## Quick Reference

### Terraform One-Liners

```bash
terraform fmt -recursive -diff              # Format all .tf files, show diffs
terraform validate                           # Syntax check
terraform plan -out=tfplan                   # Generate plan file
terraform apply tfplan                       # Apply from plan (safe)
terraform plan -detailed-exitcode            # Returns 2 if drift exists
terraform state list                         # List all managed resources
terraform state rm resource.addr             # Stop managing (don't destroy)
terraform import resource.addr resource-id   # Adopt existing resource
terraform output -json                       # Machine-readable outputs
terraform console                            # Interactive REPL
terraform graph | dot -Tpng > graph.png      # Dependency visualization
```

### Pulumi One-Liners

```bash
pulumi preview --json                         # Machine-readable plan
pulumi up --yes --skip-preview                # Quick apply (with caution)
pulumi refresh --diff                         # Detect drift
pulumi stack export --file state.json         # Backup state
pulumi config set aws:region us-east-1        # Set config
pulumi config set --secret dbPassword         # Set secret
pulumi import --from terraform state.tfstate  # Migrate from Terraform
pulumi policy pack                            # Generate policy pack skeleton
```

### CloudFormation One-Liners

```bash
aws cloudformation validate-template --template-body file://template.yaml
aws cloudformation create-change-set --stack-name prod --change-set-name review-me
aws cloudformation detect-stack-drift --stack-name prod
aws cloudformation describe-stack-resource-drifts --stack-name prod
cfn-lint template.yaml                        # Third-party linter
cfn_nag_scan --input-path template.yaml       # Security scan
```

### Security Scanning One-Liners

```bash
# Terraform
tfsec .                                       # Terraform security scanner
checkov -d .                                  # Policy-as-code (any IaC tool)
trivy config .                                # Misconfiguration scanner

# CloudFormation
cfn_nag_scan --input-path template.yaml

# Ansible
ansible-lint site.yml

# Bicep
az bicep lint --file main.bicep

# Universal secret scanning
gitleaks detect --source . --no-git
trufflehog filesystem . --no-update
```

---

## Common Pitfalls

### 1. Hardcoded Secrets (CRITICAL — #1 cause of security incidents)

**Symptoms:** passwords, API keys, private keys in `.tf`, `.ts`, `.yaml` files.
**Fix:** Use Vault dynamic secrets, AWS Secrets Manager, Pulumi ESC, or Azure Key Vault with data-source lookups.
**Detection:** `gitleaks detect --source . --verbose`

### 2. Overly Permissive IAM (CRITICAL)

**Symptoms:** `"Action": "*"`, `"Resource": "*"` in IAM policy documents.
**Fix:** Explicit action lists; resource ARN constraints; IAM Access Analyzer validation.
**Tool:** `checkov -d . --check CKV_AWS_*` for Terraform; `cfn_nag_scan` for CloudFormation.

### 3. Missing Lifecycle Rules (HIGH)

**Symptoms:** Database destroyed during routine update because `prevent_destroy` was absent.
**Fix:** Every stateful resource needs:
```hcl
lifecycle {
  prevent_destroy = true
}
```

### 4. No Remote State (HIGH)

**Symptoms:** `terraform.tfstate` in git repository; state file conflicts between teammates.
**Fix:** Configure remote backend with locking before the first `terraform apply`.

### 5. State-Only Changes Without Infrastructure Updates (MEDIUM)

**Symptoms:** `terraform state rm` used to "fix" issues instead of updating code.
**Fix:** Always update `.tf` files first; only use state manipulation as an emergency bridge.

### 6. Unpinned Provider Versions (MEDIUM)

**Symptoms:** CI pipeline breaks because provider released a breaking change.
**Fix:** Pin both Terraform core and provider versions with `required_providers` blocks.

### 7. Monolithic State Files (MEDIUM)

**Symptoms:** Plan times >10 minutes; blast radius covers entire infrastructure.
**Fix:** Split state by environment, region, and logical domain. Use `terraform output` and data sources for cross-state references.

### 8. Ignoring Drift (MEDIUM)

**Symptoms:** `terraform plan` shows unexpected changes long after initial apply.
**Fix:** Schedule drift checks; restrict console access; use SCPs to prevent manual modifications.

### 9. Missing Resource Tagging (LOW — Cost Impact: HIGH)

**Symptoms:** Untaggable resources lead to untracked cloud spend.
**Fix:** Enforce tags with Terraform `default_tags` on provider block or Pulumi `transformations`. Cost allocation tags in AWS Billing.

### 10. Inappropriate `count` Usage (LOW)

**Symptoms:** Changing a list element reorders resources and triggers destroy/recreate.
**Fix:** Use `for_each` with a map or `toset()`—deterministic keys prevent cascading changes.

---

## SEO Metadata

### Primary Keywords
- infrastructure as code
- IaC security hardening
- Terraform best practices
- Pulumi patterns
- drift detection IaC
- IaC state management
- CloudFormation to Terraform migration
- IaC CI/CD pipeline

### Secondary Keywords
- Bicep module patterns
- Ansible playbook structure
- IaC cost optimization
- IaC compliance checklist
- CIS benchmark IaC
- least privilege IAM IaC
- OpenTofu migration
- Crossplane compositions

### Search Intent Alignment
- **Informational:** "What is the best IaC tool for multi-cloud?" → Decision Tree section
- **Tutorial:** "How to secure Terraform code" → Security Hardening Checklist
- **Comparative:** "Terraform vs Pulumi vs CloudFormation" → Comparison Matrix
- **Troubleshooting:** "How to fix Terraform drift" → Drift Detection Workflow
- **Migration:** "How to migrate CloudFormation to Terraform" → Migration Patterns

More DevOps & Infrastructure skills

← All DevOps & Infrastructure skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY