Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- .woodpecker.yaml: image paths -> library/autojanet-{agent,dispatcher}
- .woodpecker.yaml: secret names RS_HARBOR_USER / RS_HARBOR_PASS (global)
- container/Dockerfile: restore COPY skills/, skills/ populated from opencode config
- skills/: 84 opencode skills bundled into image
- k8s/manifests: update image refs to library/
4.6 KiB
4.6 KiB
| name | description |
|---|---|
| opentofu-module | Use when writing, editing, or reviewing OpenTofu/Terraform modules or configurations across AWS, Azure, OCI, or homelab environments. Triggers on tasks involving HCL, tofu commands, state management, IAM, EKS, IRSA, backends, or AFT. |
OpenTofu Module Skill
Overview
Write and maintain OpenTofu modules for Zoe's infrastructure. Use tofu commands (not terraform). Providers span AWS (primary), Azure, and OCI free tier.
Workflow
- Define variables + outputs before writing resources — prevents refactoring
- Write
versions.tffirst — sets provider constraints - Write resources in
main.tf tofu fmt -recursiveandtflintbefore plancheckov -d .— fix HIGH/CRITICAL before applyingtofu plan -out=plan.out→ review →tofu apply plan.out- Never
tofu applywithout a plan file in production
Standard Module Structure
modules/<name>/
main.tf # resources
variables.tf # input variables with descriptions and types
outputs.tf # exported values
versions.tf # required_providers with version constraints
README.md # auto-generated by terraform-docs — do NOT write manually
Key Patterns
versions.tf
terraform {
required_version = ">= 1.6"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
variables.tf
variable "cluster_name" {
description = "Name of the EKS cluster"
type = string
}
variable "tags" {
description = "Tags to apply to all resources"
type = map(string)
default = {}
}
S3 Backend
terraform {
backend "s3" {
bucket = "company-tofu-state"
key = "env/production/cluster/terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
}
IRSA (IAM Roles for Service Accounts) — EKS
Comes up constantly. Full pattern:
data "aws_eks_cluster" "cluster" {
name = var.cluster_name
}
data "aws_iam_openid_connect_provider" "cluster" {
url = data.aws_eks_cluster.cluster.identity[0].oidc[0].issuer
}
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [data.aws_iam_openid_connect_provider.cluster.arn]
}
condition {
test = "StringEquals"
variable = "${replace(data.aws_iam_openid_connect_provider.cluster.url, "https://", "")}:sub"
values = ["system:serviceaccount:${var.namespace}:${var.service_account_name}"]
}
}
}
resource "aws_iam_role" "irsa" {
name = "${var.cluster_name}-${var.name}-irsa"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
tags = var.tags
}
Cross-Account AssumeRole (OrganizationAccountAccessRole)
data "aws_iam_policy_document" "trust" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
# Use the role ARN pattern — NOT AWSReservedSSO_* (causes MalformedPolicyDocument)
identifiers = ["arn:aws:iam::${var.management_account_id}:role/${var.deployer_role_name}"]
}
}
}
Critical Gotcha: AWSReservedSSO_* Roles
NEVER use AWSReservedSSO_* ARNs as IAM principals in trust policies.
- Error:
MalformedPolicyDocument: Invalid principal in policy - Fix: Use the underlying permission set role name pattern, or use
aws:PrincipalOrgIDcondition instead
State Operations
# Push local state to remote after manual work
tofu state push terraform.tfstate --force
# Import existing resource
tofu import aws_s3_bucket.example my-bucket-name
# Move resource between state paths (refactoring)
tofu state mv module.old.aws_instance.web module.new.aws_instance.web
Toolchain Quick Reference
| Tool | Command | Purpose |
|---|---|---|
| Format | tofu fmt -recursive |
Before every commit |
| Lint | tflint |
Catch provider-specific issues |
| Security | checkov -d . |
Fix HIGH/CRITICAL before apply |
| Docs | terraform-docs markdown . > README.md |
README generation only |
| Plan | tofu plan -out=plan.out |
Always use plan files in prod |
Environment Notes
- AWS: EKS, EBS, EFS, IAM, S3, Lambda, CodePipeline, GuardDuty, RDS Aurora, Organizations/AFT
- Azure: AKS, Azure DevOps — state backend uses Azure Blob Storage
- OCI: Free tier, budgets, some compute
- AFT: Used for AWS org account provisioning via Terraform
- State backends: S3+DynamoDB (AWS), Azure Blob (Azure)