autojanet/skills/opentofu-module/SKILL.md
Zoë cc74ad0bd0
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
fix: use library/ Harbor project, add skills, fix pipeline secrets
- .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/
2026-05-30 15:43:14 -07:00

166 lines
4.6 KiB
Markdown

---
name: opentofu-module
description: 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
1. Define variables + outputs **before** writing resources — prevents refactoring
2. Write `versions.tf` first — sets provider constraints
3. Write resources in `main.tf`
4. `tofu fmt -recursive` and `tflint` before plan
5. `checkov -d .` — fix HIGH/CRITICAL before applying
6. `tofu plan -out=plan.out` → review → `tofu apply plan.out`
7. **Never `tofu apply` without 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
```hcl
terraform {
required_version = ">= 1.6"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
```
### variables.tf
```hcl
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
```hcl
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:
```hcl
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)
```hcl
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:PrincipalOrgID` condition instead
## State Operations
```bash
# 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)