seb1n/infrastructure-as-code
Define, deploy, and manage cloud infrastructure as code using tools like Terraform, Pulumi, CloudFormation, and CDK, ensuring consistency, repeatability, and version control.
npx skills add https://github.com/seb1n/awesome-ai-agent-skills --skill infrastructure-as-code
This skill enables the agent to design, generate, and manage infrastructure as code (IaC) for cloud environments. The agent can produce configurations for Terraform, Pulumi, AWS CloudFormation, and AWS CDK, implementing the full plan/apply workflow with proper state management, modular design, and drift detection. IaC ensures that infrastructure is versioned alongside application code, enabling reproducible deployments, peer review of infrastructure changes, and automated provisioning across environments.
terraform plan, pulumi preview) to generate a detailed diff of proposed changes, then presents the plan for user review before applying. The agent verifies that no unexpected resources are being destroyed or recreated. Only after explicit approval does the agent execute the apply step to provision infrastructure.terraform plan, pulumi refresh) to compare actual infrastructure state against the declared configuration. Any out-of-band changes made via the console or CLI are flagged and either reconciled back to the IaC definition or explicitly imported into state. This ensures the IaC code remains the single source of truth.Provide the agent with your cloud provider, the resources to provision, sizing requirements, and any constraints such as compliance standards or cost budgets.
Example prompt:
Create Terraform configuration for an AWS environment with:
- VPC with public and private subnets across 2 AZs
- An EC2 bastion host in the public subnet
- An RDS PostgreSQL instance in the private subnet
- Security groups allowing SSH to bastion and app-to-database traffic only
main.tf:
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
provider "aws" {
region = var.aws_region
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0"
name = "${var.project}-vpc"
cidr = "10.0.0.0/16"
azs = ["${var.aws_region}a", "${var.aws_region}b"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
private_subnets = ["10.0.10.0/24", "10.0.20.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
tags = var.common_tags
}
resource "aws_security_group" "bastion" {
name_prefix = "${var.project}-bastion-"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.allowed_ssh_cidr]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(var.common_tags, { Name = "${var.project}-bastion-sg" })
}
resource "aws_instance" "bastion" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
subnet_id = module.vpc.public_subnets[0]
vpc_security_group_ids = [aws_security_group.bastion.id]
key_name = var.key_pair_name
associate_public_ip_address = true
tags = merge(var.common_tags, { Name = "${var.project}-bastion" })
}
resource "aws_security_group" "rds" {
name_prefix = "${var.project}-rds-"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.bastion.id]
}
tags = merge(var.common_tags, { Name = "${var.project}-rds-sg" })
}
resource "aws_db_instance" "postgres" {
identifier = "${var.project}-db"
engine = "postgres"
engine_version = "16.1"
instance_class = var.db_instance_class
allocated_storage = 20
max_allocated_storage = 100
storage_encrypted = true
db_name = var.db_name
username = var.db_username
password = var.db_password
db_subnet_group_name = module.vpc.database_subnet_group_name
vpc_security_group_ids = [aws_security_group.rds.id]
skip_final_snapshot = false
final_snapshot_identifier = "${var.project}-db-final"
backup_retention_period = 7
multi_az = var.environment == "production"
tags = var.common_tags
}
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
output "bastion_public_ip" {
value = aws_instance.bastion.public_ip
}
output "rds_endpoint" {
value = aws_db_instance.postgres.endpoint
}
variables.tf:
variable "aws_region" { default = "us-east-1" }
variable "project" { default = "myproject" }
variable "environment" { default = "production" }
variable "allowed_ssh_cidr" { description = "CIDR block allowed to SSH to bastion" }
variable "key_pair_name" { description = "EC2 key pair name" }
variable "db_instance_class" { default = "db.t3.medium" }
variable "db_name" { default = "appdb" }
variable "db_username" { default = "appuser" }
variable "db_password" { sensitive = true }
variable "common_tags" {
type = map(string)
default = { ManagedBy = "terraform", Project = "myproject" }
}
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as apigateway from "@pulumi/aws-apigateway";
const config = new pulumi.Config();
const stage = pulumi.getStack();
// DynamoDB table for the API
const table = new aws.dynamodb.Table("items-table", {
attributes: [{ name: "id", type: "S" }],
hashKey: "id",
billingMode: "PAY_PER_REQUEST",
tags: { Environment: stage, ManagedBy: "pulumi" },
});
// Lambda function for API handlers
const lambdaRole = new aws.iam.Role("api-lambda-role", {
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: { Service: "lambda.amazonaws.com" },
}],
}),
});
new aws.iam.RolePolicyAttachment("lambda-basic", {
role: lambdaRole.name,
policyArn: aws.iam.ManagedPolicies.AWSLambdaBasicExecutionRole,
});
new aws.iam.RolePolicyAttachment("lambda-dynamodb", {
role: lambdaRole.name,
policyArn: aws.iam.ManagedPolicies.AmazonDynamoDBFullAccess,
});
const handler = new aws.lambda.Function("api-handler", {
runtime: aws.lambda.Runtime.NodeJS20dX,
handler: "index.handler",
code: new pulumi.asset.AssetArchive({
".": new pulumi.asset.FileArchive("./lambda"),
}),
role: lambdaRole.arn,
environment: {
variables: {
TABLE_NAME: table.name,
STAGE: stage,
},
},
memorySize: 256,
timeout: 30,
tags: { Environment: stage, ManagedBy: "pulumi" },
});
// API Gateway REST API
const api = new apigateway.RestAPI("items-api", {
routes: [
{ path: "/items", method: "GET", eventHandler: handler },
{ path: "/items", method: "POST", eventHandler: handler },
{ path: "/items/{id}", method: "GET", eventHandler: handler },
{ path: "/items/{id}", method: "DELETE", eventHandler: handler },
],
stageName: stage,
});
export const apiUrl = api.url;
export const tableName = table.name;
sensitive in Terraform or use Pulumi's secret encryption. Never hardcode secrets in IaC files. Integrate with AWS Secrets Manager or HashiCorp Vault for runtime secret injection.terraform force-unlock (with the lock ID) only after confirming no other apply is running. Pulumi provides pulumi cancel for the same scenario.terraform plan regularly to detect drift and either revert the manual change or import it with terraform import. Avoid manual changes to IaC-managed resources.aws_security_group_rule resources.required_providers and upgrade deliberately with a tested plan/apply cycle.terraform_remote_state data sources to share outputs.Take seb1n/infrastructure-as-code from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.