mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
---
|
||||
name: terraform-aws
|
||||
description: Provision AWS infrastructure with Terraform. Create modules, manage state, and implement IaC best practices. Use when deploying AWS resources declaratively.
|
||||
license: MIT
|
||||
metadata:
|
||||
author: devops-skills
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
# Terraform AWS
|
||||
|
||||
Provision and manage AWS infrastructure with Terraform.
|
||||
|
||||
## Provider Configuration
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
|
||||
backend "s3" {
|
||||
bucket = "terraform-state"
|
||||
key = "prod/terraform.tfstate"
|
||||
region = "us-east-1"
|
||||
}
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
region = var.region
|
||||
|
||||
default_tags {
|
||||
tags = {
|
||||
Environment = var.environment
|
||||
ManagedBy = "terraform"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Example Resources
|
||||
|
||||
```hcl
|
||||
resource "aws_vpc" "main" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
enable_dns_hostnames = true
|
||||
|
||||
tags = { Name = "main-vpc" }
|
||||
}
|
||||
|
||||
resource "aws_instance" "web" {
|
||||
ami = data.aws_ami.amazon_linux.id
|
||||
instance_type = "t3.micro"
|
||||
subnet_id = aws_subnet.public.id
|
||||
|
||||
tags = { Name = "web-server" }
|
||||
}
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
```hcl
|
||||
module "vpc" {
|
||||
source = "terraform-aws-modules/vpc/aws"
|
||||
|
||||
name = "my-vpc"
|
||||
cidr = "10.0.0.0/16"
|
||||
|
||||
azs = ["us-east-1a", "us-east-1b"]
|
||||
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
|
||||
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
|
||||
|
||||
enable_nat_gateway = true
|
||||
}
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
terraform init
|
||||
terraform plan -out=plan.tfplan
|
||||
terraform apply plan.tfplan
|
||||
terraform destroy
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Use remote state with locking
|
||||
- Implement module structure
|
||||
- Use workspaces or separate states per environment
|
||||
- Pin provider versions
|
||||
- Use data sources for AMIs
|
||||
|
||||
## Related Skills
|
||||
|
||||
- [aws-vpc](../aws-vpc/) - VPC networking
|
||||
- [aws-iam](../aws-iam/) - IAM policies
|
||||
@@ -0,0 +1,152 @@
|
||||
# AWS VPC Module Template
|
||||
# Production-ready VPC with public and private subnets
|
||||
|
||||
variable "vpc_cidr" {
|
||||
description = "CIDR block for VPC"
|
||||
type = string
|
||||
default = "10.0.0.0/16"
|
||||
}
|
||||
|
||||
variable "availability_zones" {
|
||||
description = "Availability zones"
|
||||
type = list(string)
|
||||
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
|
||||
}
|
||||
|
||||
variable "enable_nat_gateway" {
|
||||
description = "Enable NAT Gateway for private subnets"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
locals {
|
||||
public_subnets = [for i, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, i)]
|
||||
private_subnets = [for i, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, i + 10)]
|
||||
}
|
||||
|
||||
# VPC
|
||||
resource "aws_vpc" "main" {
|
||||
cidr_block = var.vpc_cidr
|
||||
enable_dns_hostnames = true
|
||||
enable_dns_support = true
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-vpc"
|
||||
}
|
||||
}
|
||||
|
||||
# Internet Gateway
|
||||
resource "aws_internet_gateway" "main" {
|
||||
vpc_id = aws_vpc.main.id
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-igw"
|
||||
}
|
||||
}
|
||||
|
||||
# Public Subnets
|
||||
resource "aws_subnet" "public" {
|
||||
count = length(var.availability_zones)
|
||||
vpc_id = aws_vpc.main.id
|
||||
cidr_block = local.public_subnets[count.index]
|
||||
availability_zone = var.availability_zones[count.index]
|
||||
map_public_ip_on_launch = true
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-public-${var.availability_zones[count.index]}"
|
||||
"kubernetes.io/role/elb" = "1"
|
||||
}
|
||||
}
|
||||
|
||||
# Private Subnets
|
||||
resource "aws_subnet" "private" {
|
||||
count = length(var.availability_zones)
|
||||
vpc_id = aws_vpc.main.id
|
||||
cidr_block = local.private_subnets[count.index]
|
||||
availability_zone = var.availability_zones[count.index]
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-private-${var.availability_zones[count.index]}"
|
||||
"kubernetes.io/role/internal-elb" = "1"
|
||||
}
|
||||
}
|
||||
|
||||
# Elastic IP for NAT Gateway
|
||||
resource "aws_eip" "nat" {
|
||||
count = var.enable_nat_gateway ? 1 : 0
|
||||
domain = "vpc"
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-nat-eip"
|
||||
}
|
||||
}
|
||||
|
||||
# NAT Gateway
|
||||
resource "aws_nat_gateway" "main" {
|
||||
count = var.enable_nat_gateway ? 1 : 0
|
||||
allocation_id = aws_eip.nat[0].id
|
||||
subnet_id = aws_subnet.public[0].id
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-nat"
|
||||
}
|
||||
|
||||
depends_on = [aws_internet_gateway.main]
|
||||
}
|
||||
|
||||
# Public Route Table
|
||||
resource "aws_route_table" "public" {
|
||||
vpc_id = aws_vpc.main.id
|
||||
|
||||
route {
|
||||
cidr_block = "0.0.0.0/0"
|
||||
gateway_id = aws_internet_gateway.main.id
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-public-rt"
|
||||
}
|
||||
}
|
||||
|
||||
# Private Route Table
|
||||
resource "aws_route_table" "private" {
|
||||
vpc_id = aws_vpc.main.id
|
||||
|
||||
dynamic "route" {
|
||||
for_each = var.enable_nat_gateway ? [1] : []
|
||||
content {
|
||||
cidr_block = "0.0.0.0/0"
|
||||
nat_gateway_id = aws_nat_gateway.main[0].id
|
||||
}
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-private-rt"
|
||||
}
|
||||
}
|
||||
|
||||
# Route Table Associations
|
||||
resource "aws_route_table_association" "public" {
|
||||
count = length(var.availability_zones)
|
||||
subnet_id = aws_subnet.public[count.index].id
|
||||
route_table_id = aws_route_table.public.id
|
||||
}
|
||||
|
||||
resource "aws_route_table_association" "private" {
|
||||
count = length(var.availability_zones)
|
||||
subnet_id = aws_subnet.private[count.index].id
|
||||
route_table_id = aws_route_table.private.id
|
||||
}
|
||||
|
||||
# Outputs
|
||||
output "vpc_id" {
|
||||
value = aws_vpc.main.id
|
||||
}
|
||||
|
||||
output "public_subnet_ids" {
|
||||
value = aws_subnet.public[*].id
|
||||
}
|
||||
|
||||
output "private_subnet_ids" {
|
||||
value = aws_subnet.private[*].id
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
# Terraform AWS Best Practices
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
project/
|
||||
├── main.tf # Main configuration
|
||||
├── variables.tf # Input variables
|
||||
├── outputs.tf # Output values
|
||||
├── locals.tf # Local values
|
||||
├── data.tf # Data sources
|
||||
├── versions.tf # Provider versions
|
||||
├── terraform.tfvars # Variable values (git-ignored)
|
||||
├── modules/ # Local modules
|
||||
│ └── vpc/
|
||||
│ ├── main.tf
|
||||
│ ├── variables.tf
|
||||
│ └── outputs.tf
|
||||
└── environments/ # Environment configs
|
||||
├── dev/
|
||||
├── staging/
|
||||
└── prod/
|
||||
```
|
||||
|
||||
## State Management
|
||||
|
||||
### Remote State with S3
|
||||
```hcl
|
||||
terraform {
|
||||
backend "s3" {
|
||||
bucket = "company-terraform-state"
|
||||
key = "project/env/terraform.tfstate"
|
||||
region = "us-east-1"
|
||||
encrypt = true
|
||||
dynamodb_table = "terraform-locks"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### State Locking
|
||||
```hcl
|
||||
resource "aws_dynamodb_table" "terraform_locks" {
|
||||
name = "terraform-locks"
|
||||
billing_mode = "PAY_PER_REQUEST"
|
||||
hash_key = "LockID"
|
||||
|
||||
attribute {
|
||||
name = "LockID"
|
||||
type = "S"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### Use IAM Roles, Not Credentials
|
||||
```hcl
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
# No access_key or secret_key - use IAM role or env vars
|
||||
}
|
||||
```
|
||||
|
||||
### Enable Encryption Everywhere
|
||||
```hcl
|
||||
resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
|
||||
bucket = aws_s3_bucket.example.id
|
||||
|
||||
rule {
|
||||
apply_server_side_encryption_by_default {
|
||||
sse_algorithm = "aws:kms"
|
||||
kms_master_key_id = aws_kms_key.example.arn
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use Sensitive Variables
|
||||
```hcl
|
||||
variable "database_password" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
```
|
||||
|
||||
## Tagging Strategy
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
common_tags = {
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
ManagedBy = "terraform"
|
||||
Owner = var.team
|
||||
CostCenter = var.cost_center
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_instance" "example" {
|
||||
# ... configuration ...
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Name = "example-instance"
|
||||
Role = "web"
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Module Best Practices
|
||||
|
||||
### Version Pinning
|
||||
```hcl
|
||||
module "vpc" {
|
||||
source = "terraform-aws-modules/vpc/aws"
|
||||
version = "5.0.0" # Pin specific version
|
||||
|
||||
# ... configuration ...
|
||||
}
|
||||
```
|
||||
|
||||
### Variable Validation
|
||||
```hcl
|
||||
variable "environment" {
|
||||
type = string
|
||||
description = "Environment name"
|
||||
|
||||
validation {
|
||||
condition = contains(["dev", "staging", "prod"], var.environment)
|
||||
error_message = "Environment must be dev, staging, or prod."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Plan Before Apply
|
||||
```bash
|
||||
terraform plan -out=tfplan
|
||||
terraform apply tfplan
|
||||
```
|
||||
|
||||
### Use Workspaces or Directories for Environments
|
||||
```bash
|
||||
# Workspaces
|
||||
terraform workspace new prod
|
||||
terraform workspace select prod
|
||||
|
||||
# Or separate directories (recommended)
|
||||
cd environments/prod
|
||||
terraform apply
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Data Sources for Existing Resources
|
||||
```hcl
|
||||
data "aws_vpc" "existing" {
|
||||
filter {
|
||||
name = "tag:Name"
|
||||
values = ["main-vpc"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "new" {
|
||||
vpc_id = data.aws_vpc.existing.id
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
### Dynamic Blocks
|
||||
```hcl
|
||||
resource "aws_security_group" "example" {
|
||||
# ...
|
||||
|
||||
dynamic "ingress" {
|
||||
for_each = var.ingress_rules
|
||||
content {
|
||||
from_port = ingress.value.from_port
|
||||
to_port = ingress.value.to_port
|
||||
protocol = ingress.value.protocol
|
||||
cidr_blocks = ingress.value.cidr_blocks
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/bin/bash
|
||||
# Terraform AWS Project Initialization Script
|
||||
# Usage: ./tf-init.sh <project-name> [region]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_NAME="${1:-}"
|
||||
REGION="${2:-us-east-1}"
|
||||
|
||||
if [ -z "$PROJECT_NAME" ]; then
|
||||
echo "Usage: $0 <project-name> [region]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "========================================="
|
||||
echo "Terraform AWS Project Setup"
|
||||
echo "Project: $PROJECT_NAME"
|
||||
echo "Region: $REGION"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
mkdir -p "$PROJECT_NAME"
|
||||
cd "$PROJECT_NAME"
|
||||
|
||||
# Create main.tf
|
||||
cat > main.tf << EOF
|
||||
terraform {
|
||||
required_version = ">= 1.0"
|
||||
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
|
||||
# Uncomment for remote state
|
||||
# backend "s3" {
|
||||
# bucket = "${PROJECT_NAME}-tfstate"
|
||||
# key = "terraform.tfstate"
|
||||
# region = "${REGION}"
|
||||
# encrypt = true
|
||||
# dynamodb_table = "${PROJECT_NAME}-tflock"
|
||||
# }
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
region = var.region
|
||||
|
||||
default_tags {
|
||||
tags = {
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
ManagedBy = "terraform"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create variables.tf
|
||||
cat > variables.tf << EOF
|
||||
variable "project_name" {
|
||||
description = "Project name for tagging"
|
||||
type = string
|
||||
default = "${PROJECT_NAME}"
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment (dev, staging, prod)"
|
||||
type = string
|
||||
default = "dev"
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
description = "AWS region"
|
||||
type = string
|
||||
default = "${REGION}"
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create outputs.tf
|
||||
cat > outputs.tf << EOF
|
||||
output "region" {
|
||||
description = "AWS region"
|
||||
value = var.region
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create terraform.tfvars
|
||||
cat > terraform.tfvars << EOF
|
||||
project_name = "${PROJECT_NAME}"
|
||||
environment = "dev"
|
||||
region = "${REGION}"
|
||||
EOF
|
||||
|
||||
# Create .gitignore
|
||||
cat > .gitignore << EOF
|
||||
# Terraform
|
||||
.terraform/
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
*.tfvars.json
|
||||
crash.log
|
||||
*.tfplan
|
||||
|
||||
# Keep tfvars template
|
||||
!terraform.tfvars.example
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
.vscode/
|
||||
EOF
|
||||
|
||||
# Initialize Terraform
|
||||
echo ""
|
||||
echo "Initializing Terraform..."
|
||||
terraform init
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "Project created successfully!"
|
||||
echo ""
|
||||
echo "Files created:"
|
||||
ls -la
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. cd $PROJECT_NAME"
|
||||
echo " 2. Edit terraform.tfvars"
|
||||
echo " 3. Add resources to main.tf"
|
||||
echo " 4. terraform plan"
|
||||
echo " 5. terraform apply"
|
||||
echo "========================================="
|
||||
Reference in New Issue
Block a user