This skill guides writing Infrastructure as Code using OpenTofu (open-source Terraform fork). Use when creating .tf files, managing cloud infrastructure, configuring providers, or designing reusable modules.
Writes OpenTofu infrastructure code for cloud resources and providers. Triggers when you create .tf files, configure providers, or design infrastructure modules.
/plugin marketplace add majesticlabs-dev/majestic-marketplace/plugin install majestic-devops@majestic-marketplaceThis skill is limited to using the following tools:
resources/hcl-patterns.mdresources/makefile-automation.mdresources/post-provisioning.mdresources/project-scaffolding.mdresources/provider-examples.mdresources/state-management.mdALWAYS start with the simplest approach. Only add complexity when explicitly requested.
| Aspect | ✅ Simple (Default) | ❌ Overengineered |
|---|---|---|
| Structure | Flat .tf files in one directory | Nested modules/ + environments/ directories |
| Modules | None or only remote registry modules | Custom local modules for simple resources |
| Environments | Workspaces OR single tfvars | Duplicate directory per environment |
| Variables | Inline defaults, minimal tfvars | Complex variable hierarchies |
| File count | 3-5 .tf files total | 15+ files across nested directories |
Rule: If you can define everything in 5 flat .tf files, DO IT.
infra/
├── main.tf # All resources
├── variables.tf # Input variables
├── outputs.tf # Outputs
├── versions.tf # Provider versions
└── terraform.tfvars # Variable values (gitignored)
OpenTofu is a community-driven, open-source fork of Terraform under MPL-2.0 license, maintained by the Linux Foundation. It uses HashiCorp Configuration Language (HCL) for declarative infrastructure management across cloud providers.
Prioritize:
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
tags = {
Name = "${var.project}-web"
Environment = var.environment
}
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
variable "environment" {
description = "Deployment environment (dev, staging, prod)"
type = string
default = "dev"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "instance_types" {
description = "Map of environment to instance type"
type = map(string)
default = {
dev = "t3.micro"
staging = "t3.small"
prod = "t3.medium"
}
}
output "instance_ip" {
description = "Public IP of the web instance"
value = aws_instance.web.public_ip
sensitive = false
}
output "database_password" {
description = "Generated database password"
value = random_password.db.result
sensitive = true
}
locals {
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "OpenTofu"
}
name_prefix = "${var.project}-${var.environment}"
}
resource "aws_instance" "server" {
count = var.server_count
ami = var.ami_id
instance_type = var.instance_type
tags = {
Name = "${local.name_prefix}-server-${count.index}"
}
}
resource "aws_iam_user" "users" {
for_each = toset(var.user_names)
name = each.value
path = "/users/"
}
resource "aws_security_group_rule" "ingress" {
for_each = var.ingress_rules
type = "ingress"
from_port = each.value.port
to_port = each.value.port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_blocks
security_group_id = aws_security_group.main.id
}
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = var.instance_type
depends_on = [
aws_db_instance.database,
aws_elasticache_cluster.cache
]
}
resource "aws_instance" "critical" {
ami = var.ami_id
instance_type = var.instance_type
lifecycle {
prevent_destroy = true
create_before_destroy = true
ignore_changes = [
tags["LastUpdated"],
user_data
]
}
}
# Replace when AMI changes
resource "aws_instance" "immutable" {
ami = var.ami_id
instance_type = var.instance_type
lifecycle {
replace_triggered_by = [
null_resource.ami_trigger
]
}
}
modules/
└── vpc/
├── main.tf # Primary resources
├── variables.tf # Input variables
├── outputs.tf # Output values
├── versions.tf # Required providers
└── README.md # Documentation
module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
environment = var.environment
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"]
}
# Remote module with version
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = local.cluster_name
cluster_version = "1.29"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
}
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/network/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
terraform {
encryption {
key_provider "pbkdf2" "main" {
passphrase = var.state_encryption_passphrase
}
method "aes_gcm" "encrypt" {
keys = key_provider.pbkdf2.main
}
state {
method = method.aes_gcm.encrypt
enforced = true
}
plan {
method = method.aes_gcm.encrypt
enforced = true
}
}
}
# List resources in state
tofu state list
# Show specific resource
tofu state show aws_instance.web
# Move resource (refactoring)
tofu state mv aws_instance.old aws_instance.new
# Remove from state (without destroying)
tofu state rm aws_instance.imported
# Import existing resource
tofu import aws_instance.web i-1234567890abcdef0
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = local.common_tags
}
}
# Multiple provider configurations
provider "aws" {
alias = "us_west"
region = "us-west-2"
}
resource "aws_instance" "west" {
provider = aws.us_west
# ...
}
# Environment variables (preferred)
# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
# AWS_PROFILE for named profiles
# Named profile from ~/.aws/credentials (recommended)
provider "aws" {
region = var.aws_region
profile = "suppli" # Uses [suppli] section from ~/.aws/credentials
}
# Or explicit (NOT recommended for secrets)
provider "aws" {
region = var.aws_region
access_key = var.aws_access_key # Use env vars instead
secret_key = var.aws_secret_key
}
# Assume role
provider "aws" {
region = var.aws_region
assume_role {
role_arn = "arn:aws:iam::123456789012:role/DeployRole"
session_name = "TofuDeployment"
}
}
# Create and switch workspaces
tofu workspace new dev
tofu workspace new staging
tofu workspace new prod
# Switch workspace
tofu workspace select prod
# List workspaces
tofu workspace list
# Use workspace in configuration
locals {
environment = terraform.workspace
instance_type = {
dev = "t3.micro"
staging = "t3.small"
prod = "t3.medium"
}[terraform.workspace]
}
infrastructure/
├── modules/ # Shared modules
│ ├── vpc/
│ └── eks/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ └── terraform.tfvars
│ ├── staging/
│ │ ├── main.tf
│ │ └── terraform.tfvars
│ └── prod/
│ ├── main.tf
│ └── terraform.tfvars
# Initialize working directory
tofu init
# Validate configuration
tofu validate
# Format code
tofu fmt -recursive
# Preview changes
tofu plan -out=plan.tfplan
# Apply changes
tofu apply plan.tfplan
# Destroy infrastructure
tofu destroy
# Show current state
tofu show
# Refresh state from actual infrastructure
tofu refresh
When writing OpenTofu/Terraform code:
.tfstate or .tfvars with secrets to VCStofu plan before every applylifecycle.prevent_destroy for critical resourceslocals for computed values and tagsfor_each over count for named resourcesresource "aws_eip" "static" {
count = var.create_elastic_ip ? 1 : 0
instance = aws_instance.web.id
}
resource "aws_security_group" "main" {
name = "${local.name_prefix}-sg"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
For detailed patterns and examples:
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.