> Terraform, CloudFormation, and Pulumi analysis. Module structure, state management, drift prevention, and security posture for infrastructure definitions.
Installs into .claude/skills of the current project.
Are you the author of Harness Infrastructure As Code?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/intense-visions-harness-infrastructure-as-code)
# Harness Infrastructure as Code
> Terraform, CloudFormation, and Pulumi analysis. Module structure, state management, drift prevention, and security posture for infrastructure definitions.
## When to Use
- When reviewing or designing Terraform, CloudFormation, or Pulumi configurations
- When auditing IaC module structure, naming conventions, and state management
- On PRs that modify infrastructure definitions or add new cloud resources
- NOT for CI/CD pipeline configuration (use harness-deployment)
- NOT for container orchestration (use harness-containerization)
- NOT for application-level security (use harness-security-review)
## Process
### Phase 1: DETECT -- Identify IaC Tool and Structure
1. **Detect IaC tooling.** Scan the project for infrastructure definitions:
- `*.tf` files -- Terraform (HCL)
- `terraform/` directory with `.terraform.lock.hcl`
- `cloudformation/`, `*.template.yaml`, `*.template.json` -- CloudFormation
- `Pulumi.yaml`, `Pulumi.*.yaml` -- Pulumi
- `cdk.json`, `cdk.out/` -- AWS CDK
- `infrastructure/`, `infra/` -- common IaC directories
2. **Identify provider and backend.** Parse configuration for:
- Cloud providers (AWS, GCP, Azure) and their versions
- State backend (S3, GCS, Azure Blob, Terraform Cloud, local)
- Provider authentication method (environment variables, profiles, OIDC)
- Lock file presence and provider version constraints
3. **Map module structure.** Build a dependency tree of modules:
- Root modules and their child module references
- Module source types (local path, registry, git)
- Module versioning (pinned vs. unpinned)
- Input variables and output values per module
- Shared modules used across multiple root configurations
4. **Detect environment separation.** Identify how environments are managed:
- Workspaces (Terraform workspaces)
- Directory-per-environment (`environments/dev/`, `environments/prod/`)
- Variable files per environment (`terraform.tfvars`, `prod.tfvars`)
- Backend configuration per environment
5. **Present detection summary:**
```
IaC Detection:
Tool: Terraform v1.7
Provider: AWS (us-east-1, us-west-2)
Backend: S3 with DynamoDB locking
Modules: 8 local, 3 registry
Environments: dev, staging, prod (directory-per-env)
State files: 3 (one per environment)
```
---
### Phase 2: ANALYZE -- Evaluate Patterns and Anti-Patterns
1. **Check state management.** Verify state is properly configured:
- Remote backend with locking (not local state for shared infrastructure)
- State encryption at rest enabled
- State file does not contain secrets in plain text
- State is segmented per environment (no single state file for all environments)
- Backend configuration uses variables, not hardcoded values
2. **Evaluate module design.** Check modules for:
- Single responsibility (one module does one thing)
- Input validation (variable validation blocks or type constraints)
- Output completeness (downstream consumers can get what they need)
- No hardcoded values that should be variables
- README or documentation for each module
- Consistent naming conventions across modules
3. **Check resource naming and tagging.** Verify:
- Resources follow a consistent naming convention (e.g., `{project}-{env}-{resource}`)
- Required tags are present on all taggable resources (environment, team, cost-center)
- Tag values are consistent (no mix of "prod" and "production")
- Names do not contain environment-specific values in shared modules
4. **Analyze dependency management.** Check for:
- Provider version constraints (required_providers block)
- Module version pinning (exact version or range)
- Lock file committed to version control
- No circular dependencies between modules
- Implicit dependencies are made explicit with depends_on where needed
5. **Check for common anti-patterns:**
- Monolithic root modules (everything in one configuration)
- Hardcoded AMI IDs, account numbers, or region values
- Resources created outside of IaC (drift risk)
- Overly permissive IAM policies (wildcards on actions or resources)
- Missing lifecycle rules (prevent_destroy on critical resources)
---
### Phase 3: DESIGN -- Recommend Structure and Patterns
1. **Recommend module decomposition.** If monolithic configurations are detected:
- Propose a module hierarchy based on service boundaries
- Separate networking, compute, storage, and security into distinct modules
- Design shared modules for common patterns (e.g., tagged S3 bucket, VPC)
- Provide module interface design (inputs, outputs)
2. **Design state management strategy.** Recommend:
- One state file per environment per service
- Remote backend with encryption and locking
- State import plan for any resources created outside IaC
- Cross-state data sharing via `terraform_remote_state` or SSM parameters
3. **Recommend drift detection workflow.** Design a process to catch manual changes:
- Scheduled `terraform plan` in CI to detect drift
- Alert on any planned changes that were not initiated by a PR
- Runbook for reconciling detected drift (import vs. revert)
- Tag resources as IaC-managed for auditability
4. **Design environment promotion.** Recommend a workflow for infrastructure changes:
- Changes applied to dev first, then promoted to staging, then production
- Variable files per environment with appropriate overrides
- Approval gates before production applies
- Plan output reviewed as part of PR process
5. **Recommend security hardening.** For each provider:
- Least-privilege IAM roles for IaC execution
- No inline policies (use managed policies or policy documents)
- Encryption enabled by default on all storage resources
- Network security groups with explicit deny rules
- Sensitive variables marked with `sensitive = true`
---
### Phase 4: VALIDATE -- Verify Configuration Correctness
1. **Run static analysis.** Execute available validation tools:
- Terraform: `terraform validate`, `terraform fmt -check`
- CloudFormation: `cfn-lint` or `aws cloudformation validate-template`
- Pulumi: type checking via the chosen language runtime
- CDK: `cdk synth` to verify template generation
- General: `tflint`, `checkov`, or `tfsec` for security checks
2. **Verify variable completeness.** For each root module:
- All declared variables have descriptions
- Required variables have no default values
- Optional variables have sensible defaults
- Variable types are specific (not `any`)
- Validation blocks exist for constrained values (e.g., allowed regions)
3. **Check plan safety.** If a plan output is available:
- No unexpected resource deletions
- No changes to resources marked with `prevent_destroy`
- Replacements are expected (not caused by force-new arguments)
- Plan matches the intended change described in the PR
4. **Verify security posture.** Run security-focused checks:
- No public S3 buckets or storage accounts
- No security groups allowing 0.0.0.0/0 on sensitive ports
- Encryption enabled on RDS, EBS, S3, and other storage
- IAM policies follow least privilege
- No credentials or secrets in variable defaults or outputs
5. **Generate validation report:**
```
IaC Validation: [PASS/WARN/FAIL]
Format check: PASS (all files formatted)
Validate: PASS (no syntax errors)
Security scan: WARN (2 findings)
- modules/storage/main.tf: S3 bucket missing server-side encryption
- modules/network/main.tf: security group allows 0.0.0.0/0 on port 22
Module design: WARN (3 modules missing input validation)
State management: PASS (remote backend with locking)
Recommendations:
1. Add aws_s3_bucket_server_side_encryption_configuration resource
2. Restrict SSH access to VPN CIDR range
3. Add variable validation blocks to network, compute, and storage modules
```
---
## Harness Integration
- **`harness skill run harness-infrastructure-as-code`** -- Primary invocation for IaC analysis.
- **`harness validate`** -- Run after configuration changes to verify project health.
- **`harness check-deps`** -- Verify IaC tool dependencies are installed.
- **`emit_interaction`** -- Present design recommendations and gather decisions on module structure.
## Success Criteria
- IaC tooling, provider, and backend are correctly identified
- Module structure is mapped with dependency relationships
- State management is verified as remote, encrypted, and locked
- Resource naming and tagging follow consistent conventions
- Security posture is evaluated with no critical findings unaddressed
- Static analysis tools pass without errors
## Examples
### Example: Terraform AWS Multi-Environment Setup
```
Phase 1: DETECT
Tool: Terraform v1.6.4
Provider: AWS (hashicorp/aws ~> 5.0)
Backend: S3 (us-east-1) with DynamoDB locking
Modules: 5 local (vpc, ecs, rds, s3, iam), 2 registry (datadog, cloudwatch)
Environments: dev, staging, prod (directory-per-env with shared modules)
Phase 2: ANALYZE
State management: PASS (remote, encrypted, locked, per-env)
Module design: WARN
- modules/ecs has 450 lines -- recommend splitting into ecs-cluster
and ecs-service modules
- modules/rds missing variable validation for instance_class
Naming: PASS (consistent {project}-{env}-{resource} pattern)
Tags: WARN (cost-center tag missing on 3 resources)
Anti-patterns: 1 hardcoded AMI in modules/ecs/main.tf
Phase 3: DESIGN
1. Split modules/ecs into ecs-cluster and ecs-service
2. Add data source for AMI lookup instead of hardcoded value
3. Add variable validation: instance_class must be db.t3.* or db.r6g.*
4. Add cost-center tag to default_tags in provider configuration
5. Add scheduled terraform plan for drift detection in CI
Phase 4: VALIDATE
terraform fmt: PASS
terraform validate: PASS
tfsec: WARN (2 findings -- see above)
checkov: PASS
Result: WARN -- 5 improvements recommended, no blocking issues
```
### Example: CloudFormation with CDK
```
Phase 1: DETECT
Tool: AWS CDK v2.120 (TypeScript)
Provider: AWS (us-west-2)
Backend: CloudFormation (managed by CDK)
Stacks: 3 (NetworkStack, ComputeStack, StorageStack)
Environments: dev and prod via CDK context
Phase 2: ANALYZE
Stack design: PASS (clean separation by concern)
Cross-stack references: PASS (using CfnOutput and Fn::ImportValue)
Security: WARN
- ComputeStack: EC2 instance has public IP and open SSH
- StorageStack: DynamoDB table missing point-in-time recovery
CDK constructs: Using L2 constructs (good -- higher abstraction)
Phase 3: DESIGN
1. Add bastion host pattern instead of direct SSH to EC2
2. Enable point-in-time recovery on DynamoDB table
3. Add cdk-nag for automated security checks in synthesis
4. Add stack-level tags via Tags.of(stack).add()
Phase 4: VALIDATE
cdk synth: PASS (3 templates generated)
cfn-lint: PASS (all templates valid)
Security: WARN (2 findings)
Result: WARN -- 2 security improvements needed
```
## Rationalizations to Reject
| Rationalization | Reality |
| ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| "We store state locally because it's just a dev environment" | Local state is not shared between team members. Two developers running `terraform apply` against the same environment with diverged local state will produce conflicting resource definitions, duplicate resources, or state corruption that requires manual recovery. |
| "We haven't pinned the provider version because we want to automatically get security patches" | Unpinned providers can silently change resource behavior on `terraform init`. A `~> 5.0` constraint without an upper bound can pull a provider with breaking changes. Pin the minor version and upgrade explicitly via reviewed PRs so changes are intentional. |
| "That S3 bucket has public access because it hosts our static site" | Static site hosting does not require a public bucket ACL. CloudFront with an Origin Access Control (OAC) policy serves files from a private bucket. Public bucket ACLs are a common misconfiguration vector because they apply to all objects, including accidentally uploaded sensitive files. |
| "We'll tag resources properly before we go to production" | Untagged resources accumulate. Cost allocation reports become impossible, security audits cannot identify owners, and decommissioning requires manual investigation of every resource. Tagging must be enforced at resource creation — retroactive tagging at scale is a weeks-long engineering project. |
| "Manual changes are fine for urgent hotfixes — we'll import them to Terraform afterward" | Manual changes without immediate import create drift that may be overwritten by the next `terraform apply`. The "import it later" step is almost never done. Every manual change that goes unimported erodes the reliability guarantee that IaC provides. |
## Gates
- **No local state for shared infrastructure.** Terraform configurations managing shared resources must use a remote backend with locking. Local state is blocking for any non-experimental configuration.
- **No unpinned provider versions.** Provider version constraints must be explicit. Using `>=` without an upper bound or omitting version constraints entirely is a blocking finding.
- **No public access to sensitive resources.** S3 buckets, databases, or storage accounts with public access enabled are blocking security findings.
- **No credentials in IaC files.** Hardcoded access keys, passwords, or tokens in Terraform variables, CloudFormation parameters, or Pulumi configuration are blocking findings.
## Escalation
- **When state is corrupted or diverged:** Do not attempt automatic recovery. Report the state of divergence, recommend `terraform state pull` for backup, and advise manual reconciliation with a plan review before any apply.
- **When resources exist outside IaC management:** Recommend a phased import strategy. Provide `terraform import` commands for each resource and note that import does not generate configuration -- the HCL must be written manually.
- **When module versions are significantly outdated:** Present the version gap and changelog summary. If breaking changes exist, recommend a separate PR for the upgrade with a plan review before applying.
- **When IaC tool version conflicts exist between team members:** Recommend pinning the tool version in `.terraform-version` (tfenv) or `Pulumi.yaml` and adding version checks to CI.