terraform-module-writer

Terraform Module Writer

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "terraform-module-writer" with this command: npx skills add dengineproblem/agents-monorepo/dengineproblem-agents-monorepo-terraform-module-writer

Terraform Module Writer

Эксперт по созданию высококачественных, переиспользуемых Terraform модулей с соблюдением industry best practices.

Module Structure

Standard Directory Layout

modules/ └── my-module/ ├── main.tf # Primary resource definitions ├── variables.tf # Input variable declarations ├── outputs.tf # Output value definitions ├── versions.tf # Provider version constraints ├── locals.tf # Local values and computations ├── data.tf # Data source definitions ├── README.md # Module documentation ├── CHANGELOG.md # Version history ├── examples/ │ ├── basic/ │ │ ├── main.tf │ │ └── outputs.tf │ └── advanced/ │ ├── main.tf │ └── outputs.tf └── tests/ └── module_test.go

versions.tf Template

terraform { required_version = ">= 1.5.0"

required_providers { aws = { source = "hashicorp/aws" version = ">= 5.0.0, < 6.0.0" } random = { source = "hashicorp/random" version = ">= 3.5.0" } } }

Input Variable Design

Variable Declaration Best Practices

variables.tf

Required variables (no default)

variable "name" { description = "Name prefix for all resources created by this module" type = string

validation { condition = can(regex("^[a-z][a-z0-9-]*$", var.name)) error_message = "Name must start with a letter and contain only lowercase letters, numbers, and hyphens." } }

variable "environment" { description = "Environment name (e.g., dev, staging, prod)" type = string

validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Environment must be one of: dev, staging, prod." } }

Optional variables with sensible defaults

variable "instance_type" { description = "EC2 instance type for the application servers" type = string default = "t3.medium"

validation { condition = can(regex("^[a-z][0-9][.][a-z]+$", var.instance_type)) error_message = "Instance type must be a valid AWS instance type format." } }

variable "enable_monitoring" { description = "Enable detailed monitoring for resources" type = bool default = true }

variable "min_instances" { description = "Minimum number of instances in the Auto Scaling Group" type = number default = 2

validation { condition = var.min_instances >= 1 && var.min_instances <= 100 error_message = "Minimum instances must be between 1 and 100." } }

variable "max_instances" { description = "Maximum number of instances in the Auto Scaling Group" type = number default = 10

validation { condition = var.max_instances >= 1 && var.max_instances <= 100 error_message = "Maximum instances must be between 1 and 100." } }

Complex Variable Types

Object type with optional attributes

variable "vpc_config" { description = "VPC configuration for the module" type = object({ vpc_id = string subnet_ids = list(string) security_group_ids = optional(list(string), []) assign_public_ip = optional(bool, false) })

validation { condition = length(var.vpc_config.subnet_ids) >= 2 error_message = "At least 2 subnet IDs required for high availability." } }

Map with specific value types

variable "tags" { description = "Additional tags to apply to all resources" type = map(string) default = {}

validation { condition = alltrue([for k, v in var.tags : can(regex("^[a-zA-Z][a-zA-Z0-9:/_-]*$", k))]) error_message = "Tag keys must start with a letter and contain only valid characters." } }

List of objects

variable "ingress_rules" { description = "List of ingress rules for the security group" type = list(object({ description = string from_port = number to_port = number protocol = string cidr_blocks = optional(list(string), []) security_groups = optional(list(string), []) })) default = []

validation { condition = alltrue([ for rule in var.ingress_rules : rule.from_port >= 0 && rule.from_port <= 65535 && rule.to_port >= 0 && rule.to_port <= 65535 ]) error_message = "Port numbers must be between 0 and 65535." } }

Sensitive variables

variable "database_password" { description = "Master password for the RDS instance" type = string sensitive = true

validation { condition = length(var.database_password) >= 16 error_message = "Database password must be at least 16 characters." } }

Local Values

locals.tf Template

locals {

Naming convention

name_prefix = "${var.name}-${var.environment}"

Common tags applied to all resources

common_tags = merge( var.tags, { Name = local.name_prefix Environment = var.environment ManagedBy = "terraform" Module = "my-module" Project = var.name } )

Computed values

is_production = var.environment == "prod" enable_encryption = local.is_production backup_retention = local.is_production ? 30 : 7 instance_count = local.is_production ? var.max_instances : var.min_instances

Derived configurations

monitoring_config = { enabled = var.enable_monitoring || local.is_production detailed = local.is_production retention_days = local.is_production ? 90 : 30 alarm_threshold = local.is_production ? 80 : 90 }

Dynamic block helpers

ingress_rules_map = { for idx, rule in var.ingress_rules : "${rule.protocol}-${rule.from_port}-${rule.to_port}" => rule }

Conditional resource naming

bucket_name = var.bucket_name != null ? var.bucket_name : "${local.name_prefix}-storage-${random_id.bucket.hex}" }

Resource Definitions

main.tf Template

main.tf

#------------------------------------------------------------------------------

Security Group

#------------------------------------------------------------------------------ resource "aws_security_group" "this" { name_prefix = "${local.name_prefix}-sg-" description = "Security group for ${var.name} ${var.environment}" vpc_id = var.vpc_config.vpc_id

tags = merge(local.common_tags, { Name = "${local.name_prefix}-sg" })

lifecycle { create_before_destroy = true } }

resource "aws_security_group_rule" "ingress" { for_each = local.ingress_rules_map

type = "ingress" security_group_id = aws_security_group.this.id description = each.value.description from_port = each.value.from_port to_port = each.value.to_port protocol = each.value.protocol cidr_blocks = length(each.value.cidr_blocks) > 0 ? each.value.cidr_blocks : null source_security_group_id = length(each.value.security_groups) > 0 ? each.value.security_groups[0] : null }

resource "aws_security_group_rule" "egress" { type = "egress" security_group_id = aws_security_group.this.id description = "Allow all outbound traffic" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] }

#------------------------------------------------------------------------------

Launch Template

#------------------------------------------------------------------------------ resource "aws_launch_template" "this" { name_prefix = "${local.name_prefix}-lt-" description = "Launch template for ${var.name} ${var.environment}" image_id = data.aws_ami.amazon_linux.id instance_type = var.instance_type

network_interfaces { associate_public_ip_address = var.vpc_config.assign_public_ip security_groups = concat([aws_security_group.this.id], var.vpc_config.security_group_ids) delete_on_termination = true }

iam_instance_profile { arn = aws_iam_instance_profile.this.arn }

monitoring { enabled = local.monitoring_config.detailed }

metadata_options { http_endpoint = "enabled" http_tokens = "required" # IMDSv2 http_put_response_hop_limit = 1 }

block_device_mappings { device_name = "/dev/xvda" ebs { volume_size = var.root_volume_size volume_type = "gp3" encrypted = local.enable_encryption kms_key_id = local.enable_encryption ? var.kms_key_id : null delete_on_termination = true } }

tag_specifications { resource_type = "instance" tags = local.common_tags }

tag_specifications { resource_type = "volume" tags = local.common_tags }

tags = local.common_tags

lifecycle { create_before_destroy = true } }

#------------------------------------------------------------------------------

Auto Scaling Group

#------------------------------------------------------------------------------ resource "aws_autoscaling_group" "this" { name_prefix = "${local.name_prefix}-asg-" desired_capacity = var.desired_instances min_size = var.min_instances max_size = var.max_instances vpc_zone_identifier = var.vpc_config.subnet_ids health_check_type = "ELB" health_check_grace_period = 300

launch_template { id = aws_launch_template.this.id version = "$Latest" }

dynamic "tag" { for_each = local.common_tags content { key = tag.key value = tag.value propagate_at_launch = true } }

lifecycle { create_before_destroy = true ignore_changes = [desired_capacity] } }

Data Sources

data.tf Template

data.tf

Get current AWS account info

data "aws_caller_identity" "current" {} data "aws_region" "current" {} data "aws_partition" "current" {}

Get latest Amazon Linux 2023 AMI

data "aws_ami" "amazon_linux" { most_recent = true owners = ["amazon"]

filter { name = "name" values = ["al2023-ami-*-x86_64"] }

filter { name = "virtualization-type" values = ["hvm"] }

filter { name = "architecture" values = ["x86_64"] } }

Get VPC info if not provided

data "aws_vpc" "selected" { id = var.vpc_config.vpc_id }

Get subnet info for AZ distribution

data "aws_subnet" "selected" { for_each = toset(var.vpc_config.subnet_ids) id = each.value }

Get available AZs

data "aws_availability_zones" "available" { state = "available" filter { name = "opt-in-status" values = ["opt-in-not-required"] } }

Output Definitions

outputs.tf Template

outputs.tf

#------------------------------------------------------------------------------

Primary Outputs

#------------------------------------------------------------------------------ output "id" { description = "The unique identifier for this module instance" value = aws_autoscaling_group.this.id }

output "name" { description = "The name prefix used for all resources" value = local.name_prefix }

output "arn" { description = "ARN of the Auto Scaling Group" value = aws_autoscaling_group.this.arn }

#------------------------------------------------------------------------------

Security Group Outputs

#------------------------------------------------------------------------------ output "security_group_id" { description = "ID of the security group created for this module" value = aws_security_group.this.id }

output "security_group_arn" { description = "ARN of the security group" value = aws_security_group.this.arn }

output "security_group_name" { description = "Name of the security group" value = aws_security_group.this.name }

#------------------------------------------------------------------------------

Launch Template Outputs

#------------------------------------------------------------------------------ output "launch_template_id" { description = "ID of the launch template" value = aws_launch_template.this.id }

output "launch_template_arn" { description = "ARN of the launch template" value = aws_launch_template.this.arn }

output "launch_template_latest_version" { description = "Latest version of the launch template" value = aws_launch_template.this.latest_version }

#------------------------------------------------------------------------------

Computed/Derived Outputs

#------------------------------------------------------------------------------ output "ami_id" { description = "ID of the AMI used for instances" value = data.aws_ami.amazon_linux.id }

output "availability_zones" { description = "List of availability zones where resources are deployed" value = distinct([for s in data.aws_subnet.selected : s.availability_zone]) }

output "configuration" { description = "Summary of the module configuration" value = { environment = var.environment instance_type = var.instance_type min_instances = var.min_instances max_instances = var.max_instances monitoring_enabled = local.monitoring_config.enabled encryption_enabled = local.enable_encryption } }

#------------------------------------------------------------------------------

Sensitive Outputs

#------------------------------------------------------------------------------ output "iam_role_arn" { description = "ARN of the IAM role attached to instances" value = aws_iam_role.this.arn sensitive = false }

Conditional Resource Creation

Using count vs for_each

Use count for simple on/off toggles

resource "aws_cloudwatch_metric_alarm" "high_cpu" { count = var.enable_monitoring ? 1 : 0

alarm_name = "${local.name_prefix}-high-cpu" comparison_operator = "GreaterThanThreshold" evaluation_periods = 2 metric_name = "CPUUtilization" namespace = "AWS/EC2" period = 300 statistic = "Average" threshold = local.monitoring_config.alarm_threshold alarm_description = "CPU utilization exceeded threshold" alarm_actions = var.alarm_actions

dimensions = { AutoScalingGroupName = aws_autoscaling_group.this.name }

tags = local.common_tags }

Use for_each for collections

resource "aws_s3_bucket" "logs" { for_each = var.enable_logging ? toset(["access", "audit", "error"]) : toset([])

bucket = "${local.name_prefix}-${each.key}-logs"

tags = merge(local.common_tags, { LogType = each.key }) }

for_each with complex objects

resource "aws_route53_record" "this" { for_each = { for record in var.dns_records : "${record.name}-${record.type}" => record }

zone_id = var.route53_zone_id name = each.value.name type = each.value.type ttl = each.value.ttl records = each.value.records }

Module Composition

Root Module Example

examples/complete/main.tf

terraform { required_version = ">= 1.5.0" }

provider "aws" { region = var.region }

Network module

module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 5.0"

name = "${var.name}-vpc" cidr = "10.0.0.0/16"

azs = ["${var.region}a", "${var.region}b", "${var.region}c"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

enable_nat_gateway = true single_nat_gateway = var.environment != "prod"

tags = var.tags }

Application module

module "app" { source = "../../"

name = var.name environment = var.environment

vpc_config = { vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.private_subnets }

instance_type = var.instance_type min_instances = var.min_instances max_instances = var.max_instances enable_monitoring = true

ingress_rules = [ { description = "HTTP from ALB" from_port = 80 to_port = 80 protocol = "tcp" security_groups = [module.alb.security_group_id] } ]

tags = var.tags }

ALB module

module "alb" { source = "terraform-aws-modules/alb/aws" version = "~> 8.0"

name = "${var.name}-alb" load_balancer_type = "application" vpc_id = module.vpc.vpc_id subnets = module.vpc.public_subnets

target_groups = [ { name = "${var.name}-tg" backend_protocol = "HTTP" backend_port = 80 target_type = "instance" } ]

tags = var.tags }

Outputs

output "vpc_id" { value = module.vpc.vpc_id }

output "app_security_group_id" { value = module.app.security_group_id }

output "alb_dns_name" { value = module.alb.lb_dns_name }

Documentation

README.md Template

Module Name

Brief description of what this module creates.

Features

  • Feature 1
  • Feature 2
  • Feature 3

Usage

Basic

module "example" {
  source = "github.com/org/terraform-aws-module?ref=v1.0.0"

  name        = "my-app"
  environment = "prod"

  vpc_config = {
    vpc_id     = "vpc-12345678"
    subnet_ids = ["subnet-1", "subnet-2"]
  }
}

Advanced

module "example" {
  source = "github.com/org/terraform-aws-module?ref=v1.0.0"

  name        = "my-app"
  environment = "prod"

  vpc_config = {
    vpc_id             = "vpc-12345678"
    subnet_ids         = ["subnet-1", "subnet-2"]
    security_group_ids = ["sg-existing"]
    assign_public_ip   = false
  }

  instance_type     = "t3.large"
  min_instances     = 3
  max_instances     = 10
  enable_monitoring = true

  ingress_rules = [
    {
      description = "HTTPS"
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      cidr_blocks = ["10.0.0.0/8"]
    }
  ]

  tags = {
    Team    = "platform"
    CostCenter = "12345"
  }
}

Requirements

Name
Version

terraform
>= 1.5.0

aws
>= 5.0.0, &#x3C; 6.0.0

Providers

Name
Version

aws
>= 5.0.0

Inputs

Name
Description
Type
Default
Required

name
Name prefix for resources
string

n/a
yes

environment
Environment (dev/staging/prod)
string

n/a
yes

vpc_config
VPC configuration
object

n/a
yes

instance_type
EC2 instance type
string

"t3.medium"

no

min_instances
Minimum ASG size
number

2

no

max_instances
Maximum ASG size
number

10

no

Outputs

Name
Description

id
Auto Scaling Group ID

security_group_id
Security Group ID

launch_template_id
Launch Template ID

License

MIT

---

## Лучшие практики

1. **Используй версионирование** — семантическое версионирование для модулей
2. **Валидируй входные переменные** — используй validation blocks
3. **Документируй всё** — описания для variables и outputs
4. **Избегай hardcoded значений** — всё должно быть configurable
5. **Используй for_each вместо count** — лучше управление state
6. **Группируй связанные ресурсы** — логическая организация main.tf
7. **Тестируй модули** — используй terratest или terraform test
8. **Следуй naming conventions** — консистентное именование ресурсов

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

social-media-marketing

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

video-marketing

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

frontend-design

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

k6-load-test

No summary provided by upstream source.

Repository SourceNeeds Review