module-conventions
Mandatory structural guidelines for consistent infrastructure-as-code module organization.
Install
mkdir -p .claude/skills/module-conventions && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/13886" && unzip -o skill.zip -d .claude/skills/module-conventions && rm skill.zipInstalls to .claude/skills/module-conventions
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Binding rules for every module in rad-modules — TF file layout, variables.tf structure with UIMeta, provider-auth impersonation, and common deployment-ID / project / trusted-users patterns.Key capabilities
- →Define a standard directory layout for OpenTofu modules
- →Annotate variables with UIMeta tags for UI rendering
- →Standardize provider authentication configurations
- →Enforce common variable patterns like deployment_id and trusted_users
- →Specify content for README.md and module-specific documentation
How it works
This skill establishes binding rules for OpenTofu module structure, including file layout, variable definitions with UIMeta tags, and provider authentication patterns. It ensures consistency across modules for `rad-launcher` variable validation and RAD UI rendering.
Inputs & outputs
When to use module-conventions
- →Organize new infrastructure module files
- →Apply mandatory UIMeta tags to variables
- →Standardize module directory layout
About this skill
Module Conventions
Every module under modules/ is an independent OpenTofu root module and shares the same structural conventions. Deviating from them breaks either rad-launcher variable validation or the RAD UI rendering. Treat these rules as load-bearing.
Directory Layout
A module directory looks like this (Bank_GKE shown as the canonical multi-file example; AKS_GKE and EKS_GKE are simpler):
modules/<Module_Name>/
├── README.md # Short summary + Usage + Requirements/Providers/Resources/Inputs/Outputs tables
├── tests/ # Module test fixtures (the long-form deep dive lives at docs/modules/<Module_Name>.md, NOT inside the module)
├── main.tf # Locals, random_id, data.google_project, google_project_service.enabled_services
├── variables.tf # All inputs, annotated with UIMeta tags (see below)
├── versions.tf # OR provider.tf — required_providers + required_version
├── provider-auth.tf # OR provider.tf — google / azurerm / aws provider config
├── network.tf # VPC / subnet / firewall / NAT
├── <feature>.tf # e.g. gke.tf, asm.tf, hub.tf, deploy.tf, glb.tf, mcs.tf, istiosidecar.tf
├── outputs.tf # deployment_id + project_id at minimum
├── manifests/ # or templates/ — static or templated Kubernetes YAML
└── modules/ # optional, nested module-local helpers (not cross-module)
└── <helper>/
├── main.tf
├── variables.tf
└── ...
Rules:
- No symlinks. Modules do not share TF files. If
Bank_GKEandMC_Bank_GKEneed similarasm.tf, each has its own copy. - Nested modules (e.g.
modules/AKS_GKE/modules/attached-install-manifest/) are scoped to one parent module only; they must not be referenced from other modules in the repo. - Kubernetes templates live under
manifests/(raw YAML) ortemplates/(Go-template.yaml.tplrendered bytemplatefile(...)). Pick one per module based on whether any values are substituted. - License header: every
.tffile should begin with the Apache 2.0 block-comment header. Copy it from a neighbouring file when creating a new one. Three existingversions.tffiles (Bank_GKE,Migration_Center,VMware_Engine) currently lack it, which is whyscripts/check_conventions.pyreports a missing header as WARN rather than FAIL. - Naming: files are lowercase with hyphens (
provider-auth.tf), module directory names arePascalCase_WithUnderscores, HCL resource names aresnake_case.
variables.tf Structure
Variables are organized into numbered sections using // SECTION N: or # SECTION N: comments. The ordering below is the established convention:
# SECTION 1: Deployment → module_description, module_dependency, module_services,
# credit_cost, require_credit_purchases, enable_purge,
# public_access, deployment_id, resource_creator_identity,
# trusted_users, enable_services
# SECTION 2: Project → project_id
# SECTION 3: Network → create_network, network_name, subnet_name, ip_cidr_ranges, ...
# SECTION 4: Cluster → create_cluster, cluster_name_prefix, k8s_version, release_channel, ...
# SECTION 5: IAM / Creds → client_id/tenant_id/subscription_id/client_secret (Azure),
# aws_access_key/aws_secret_key (AWS)
# SECTION 6+: Feature-specific (e.g. service mesh, config management, application)
enable_servicesbelongs in group 0 (SECTION 1: Deployment). Place it at the end of the Deployment section (order=109) so the API-enabling toggle is grouped with other platform-level deployment controls rather than with project-specific inputs. Use{{UIMeta group=0 order=109 }}.
Not every module needs every section — AKS_GKE has no dedicated network section because AKS manages its own VNet, and Istio_GKE merges IAM into cluster setup. The numbering should still follow this order wherever the section is present.
Every Module Ships These Ten Standard Variables
The variables below exist in nearly every module and must keep their exact names, types, and defaults. rad-launcher looks for them; the RAD UI renders them in a standard panel. Two carry documented exceptions: trusted_users is Kubernetes-specific and is deliberately omitted by Container_Migration, Migration_Center and VMware_Engine, and enable_services is omitted by AKS_GKE, EKS_GKE and Migration_Center. Two further variables — module_documentation (docs URL) and shared_users (platform-only visibility list) — are declared by all eight modules and belong in the same group-0 panel. scripts/check_conventions.py enforces this list at WARN level; run it before opening a PR.
| Variable | Type | Default | Notes |
|---|---|---|---|
module_description | string | module-specific text | Shown in catalog |
module_dependency | list(string) | e.g. ["GCP Project"] | Deploy order |
module_services | list(string) | e.g. ["GCP","GKE",...] | UI tags |
credit_cost | number | 0 | Platform credits; every module in this repo currently ships 0 |
require_credit_purchases | bool | false | |
enable_purge | bool | true | |
public_access | bool | true | Catalog visibility |
deployment_id | string | null | 4-char suffix; null ⇒ auto |
resource_creator_identity | string | "[email protected]" | Impersonated SA |
trusted_users | list(string) | [] | Cluster-admin emails |
trusted_users should carry the duplicate-and-whitespace validations from AKS_GKE/variables.tf; copy them when adding to a new module.
UIMeta Tags
Every variable description ends with a {{UIMeta ...}} tag (inside the description string, not a comment) that drives UI rendering:
variable "gcp_region" {
description = "GCP region where the GKE cluster ... Defaults to 'us-central1'. {{UIMeta group=2 order=302 updatesafe }}"
type = string
default = "us-central1"
}
Parameters:
group=N— UI panel grouping, corresponding loosely to SECTION (0=Deployment, 1=Project, 2=Network, etc.).order=NNN— sort order within the group. Gaps are fine; leave room to insert new variables.updatesafe— presence flag, not a key=value. Include it for variables that can change in place without recreating the module (e.g.trusted_users,resource_creator_identity, region-pinned lookups). Omit it for variables that force replacement (e.g. cluster names, network CIDRs).
Sensitive credentials (client_secret, aws_secret_key, etc.) must also set sensitive = true on the variable itself — the UIMeta tag alone does not mark them secret.
Description Copy Style
Variable descriptions are one flowing paragraph and follow this shape:
[What it is / effect] [Format or example] [Default] [Consequences of change]. {{UIMeta ... }}
Example: "Kubernetes version to deploy on the AKS cluster, specified as major.minor (e.g. '1.34'). Must be a version currently supported by AKS in the selected azure_region. The patch version is managed automatically by AKS. Defaults to '1.34'. {{UIMeta group=4 order=403 updatesafe }}"
Keep this style when editing — the RAD UI shows the description verbatim in tooltips.
Provider Authentication
Two patterns exist; pick based on whether the module touches Google APIs that must run as the impersonated service account.
Pattern A — Direct provider (used by AKS_GKE, EKS_GKE)
Single provider.tf with all required providers and a direct provider "google" block. No impersonation — authentication comes from the caller's Application Default Credentials / Cloud Build service account.
# provider.tf
terraform {
required_providers {
google = { source = "hashicorp/google", version = ">=5.0.0" }
azurerm = { source = "hashicorp/azurerm", version = "~> 4.0" }
helm = { source = "hashicorp/helm", version = "~> 2.0" }
random = { source = "hashicorp/random", version = "3.6.2" }
}
required_version = ">= 0.13"
}
provider "google" { project = var.project_id }
provider "azurerm" {
features {}
tenant_id = var.tenant_id
client_id = var.client_id
client_secret = var.client_secret
subscription_id = var.subscription_id
}
Pattern B — Impersonated provider (used by Bank_GKE, MC_Bank_GKE, Istio_GKE, Container_Migration, Migration_Center, VMware_Engine)
Split versions.tf (provider requirements only) + provider-auth.tf (runtime auth via service-account impersonation). This is required when the module provisions GCP resources that require a specific owner.
# provider-auth.tf — impersonation pattern, copy verbatim
provider "google" {
impersonate_service_account = length(var.resource_creator_identity) != 0 ? var.resource_creator_identity : null
}
provider "google-beta" {
impersonate_service_account = length(var.resource_creator_identity) != 0 ? var.resource_creator_identity : null
}
If a new module needs google-beta, it must use Pattern B so the beta provider also gets the impersonated token.
main.tf Boilerplate
Every module's main.tf starts with this scaffold. The exact shape varies (AKS_GKE uses an unconditional random_id, Bank_GKE makes it conditional), but the ingredients are identical:
locals {
random_id = var.deployment_id != null ? var.deployment_id : random_id.default[0].hex
project = try(data.google_project.existing_project, null)
project_number = try(local.project.number, null)
default_apis = [ /* module-specific list */ ]
project_services = var.enable_services ? local.default_apis : []
}
resource "random_id" "default" {
count = var.deployment_id == null ? 1 : 0
byte_length = 2
}
data "google_project" "existing_project" {
project_id = trimspace(var.project_id)
}
resource "google_project_service" "enabled_se
---
*Content truncated.*
Limitations
- →Deviating from these rules breaks `rad-launcher` variable validation or RAD UI rendering
- →Modules do not share TF files; no symlinks are allowed
- →Nested modules are scoped to one parent module only
How it compares
This skill provides a prescriptive set of conventions for OpenTofu modules, ensuring uniformity and compatibility with specific tooling, unlike ad-hoc module development.
Compared to similar skills
module-conventions side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| module-conventions (this skill) | 0 | 2mo | No flags | Intermediate |
| aws-solution-architect | 20 | 3mo | Review | Advanced |
| terraform-module-library | 7 | 5mo | No flags | Advanced |
| azure-deployment-preflight | 7 | 6mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
aws-solution-architect
alirezarezvani
Design AWS architectures for startups using serverless patterns and IaC templates. Use when asked to design serverless architecture, create CloudFormation templates, optimize AWS costs, set up CI/CD pipelines, or migrate to AWS. Covers Lambda, API Gateway, DynamoDB, ECS, Aurora, and cost optimization.
terraform-module-library
wshobson
Build reusable Terraform modules for AWS, Azure, and GCP infrastructure following infrastructure-as-code best practices. Use when creating infrastructure modules, standardizing cloud provisioning, or implementing reusable IaC components.
azure-deployment-preflight
github
Performs comprehensive preflight validation of Bicep deployments to Azure, including template syntax validation, what-if analysis, and permission checks. Use this skill before any deployment to Azure to preview changes, identify potential issues, and ensure the deployment will succeed. Activate when users mention deploying to Azure, validating Bicep files, checking deployment permissions, previewing infrastructure changes, running what-if, or preparing for azd provision.
terraform-azurerm-set-diff-analyzer
github
Analyze Terraform plan JSON output for AzureRM Provider to distinguish between false-positive diffs (order-only changes in Set-type attributes) and actual resource changes. Use when reviewing terraform plan output for Azure resources like Application Gateway, Load Balancer, Firewall, Front Door, NSG, and other resources with Set-type attributes that cause spurious diffs due to internal ordering changes.
terraform-skill
sickn33
Terraform infrastructure as code best practices
aws-advisor
tech-leads-club
Expert AWS Cloud Advisor for architecture design, security review, and implementation guidance. Leverages AWS MCP tools for accurate, documentation-backed answers. Use when user asks about AWS architecture, security, service selection, migrations, troubleshooting, or learning AWS. Triggers on AWS, Lambda, S3, EC2, ECS, EKS, DynamoDB, RDS, CloudFormation, CDK, Terraform, Serverless, SAM, IAM, VPC, API Gateway, or any AWS service.