Install
$ agentstack add skill-bitwise-media-group-skills-terraform-style ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Terraform style conventions
Apply these conventions to every module and root configuration; match them when editing existing code. They are load-bearing for readability and call-site ergonomics, not cosmetic preferences. For the rationale behind each rule and extended examples, see [reference.md](reference.md).
1. Collections: set(string) for unordered, unique items
Use set(string) (not list(string)) whenever elements must be unique and order is irrelevant — subnet IDs, instance types, CIDRs, ARNs, repository names, regions, namespaces. Uniqueness becomes intrinsic, for_each gets stable keys without toset(), and the type documents intent.
variable "private_subnet_ids" {
description = "Private subnet IDs the cluster spans."
type = set(string)
}
Reserve list(...) for genuinely ordered data or where duplicates are meaningful. Terraform auto-converts a set to a list when an argument requires one.
2. Never name a resource this
- A module's single primary resource →
main(aws_eks_cluster.main). - A
for_each/countresource → a singular noun for one element (aws_ecr_repository.repo). - A secondary singleton → a purpose word (
aws_iam_openid_connect_provider.irsa,
aws_ecr_lifecycle_policy.retention).
The name should read well at the reference site, not echo the resource type (aws_eks_node_group.node_group is redundant; .main is not).
3. Prefer name_prefix over name
For resources whose name is unique within an account/region (IAM roles and policies, instance profiles, security groups, launch templates, target groups), use name_prefix with a trailing -. The provider appends a random suffix, so two stacks — or a create-before-destroy replacement — never collide on a hard-coded name.
resource "aws_iam_role" "node" {
name_prefix = "${var.cluster_name}-node-" # good: stands up twice without a clash
}
# avoid: name = "${var.cluster_name}-node"
Keep the prefix ≤ 38 chars: the AWS random suffix is 26 and IAM role names cap at 64.
4. Toggle resources with for_each, not count
Gate a resource on a boolean with for_each over a one-or-zero-element set, keeping a stable address instead of a positional index:
resource "aws_iam_role" "node_windows" {
for_each = toset(var.enable_windows_nodes ? ["true"] : [])
# referenced as aws_iam_role.node_windows["true"].arn — not [0]
}
Reserve count for genuine cardinality (N identical resources).
5. Don't prefix outputs with the module name
Outputs read as module.., so a concept prefix duplicates the module name at every call site. Name the output for the attribute it exposes:
output "endpoint" { value = aws_eks_cluster.main.endpoint } # module.cluster.endpoint
# avoid: output "cluster_endpoint" → module.cluster.cluster_endpoint
6. Group cohesive variables into one object
Prefer one object variable over a cluster of flat scalars that are always configured together. Give every attribute optional(type, default) and the variable default = {} so module "x" {} still works and callers set only what they need:
variable "endpoint_access" {
description = "Cluster API endpoint exposure."
type = object({
private = optional(bool, true)
public = optional(bool, false)
public_cidrs = optional(set(string), [])
})
default = {}
}
Group only what is genuinely cohesive — unrelated knobs stay flat.
7. Group cohesive outputs into one object
When several outputs are facets of one concept and would share a prefix (oidc_arn, oidc_url), fold them into a single object output named for the concept:
output "node_role" {
description = "IAM role shared by worker nodes."
value = {
arn = aws_iam_role.node.arn
name = aws_iam_role.node.name
}
}
A primary resource's own top-level attributes (name, arn, endpoint) stay flat.
8. null over empty-string defaults
Never default an optional string to "". Use default = null for a flat variable, or optional(string) with no second argument for an object attribute — null is the unambiguous "unset" signal. Consumers absorb the null with coalesce(var.proxy.http, "") or compact([...]).
9. Canonical file layout
Provider and required_version constraints live in terraform.tf — never versions.tf. A module consists of terraform.tf, main.tf, variables.tf, outputs.tf, README.md, plus optional domain-specific files (iam-*.tf). Every variable and output carries a description.
To scaffold a new module with this layout, use the terraform-module skill; before committing, run the terraform-validate workflow.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: bitwise-media-group
- Source: bitwise-media-group/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.