Markdown Editor

IaC Module Specification Template (OpenTofu, Terraform)

One reusable IaC module: inputs and outputs, guardrails, security baseline, tagging contract, SemVer rules, tests and policy as code, deprecation

Vận hànhĐầy đủSemVer 2.0.0; OpenTofu 1.13 and Terraform 1.17 module and test conventionsiacmodulespecification
Mở trong editorTải .md

Cách dùng: Use it when you build or own a reusable OpenTofu or Terraform module that other teams call. Fill the scope, interface principles and inputs (sections 1 to 3) first and review them with consumer teams before coding. The main pitfall is refactoring resource addresses or changing defaults in a minor release, which replaces or alters infrastructure for every caller.

Xem trước

IaC Module Specification: [Module name]

Specification for one reusable infrastructure-as-code module: purpose and scope, interface design, inputs and outputs, defaults and guardrails, security baseline, tagging contract, provider and tool compatibility, state and lifecycle rules, versioning with Semantic Versioning 2.0.0, tests including policy as code, usage examples, release, support and deprecation. Written for HCL modules used with OpenTofu or Terraform, with module structure aligned with the OpenTofu standard module structure and the native test command. Replace every [bracketed] value and every row marked Example. The worked example is a private object storage bucket module at Corvid Commerce, a fictional SaaS company.

Document control

FieldValue
Document ID[IAC-SPEC-014]
Module name[object-bucket]
Registry address[registry.example.com/namespace/name/provider]
Source repository[git URL]
Module version described[2.3.0]
Module status[Experimental / Supported / Deprecated / Retired]
Owning team[@platform-infrastructure]
Version[1.0]
Status[Draft / In review / Approved]
Owner[@module-maintainer]
Approvers/reviewers[@platform-lead], [@cloud-security-architect], [@finops-lead], [@consumer-team-rep]
Last updatedYYYY-MM-DD
Classification[Internal]

Approvals

RoleNameDecisionDate
Platform lead[@name][Approve / Approve with changes / Reject]YYYY-MM-DD
Cloud security architect[@name][Decision]YYYY-MM-DD
FinOps lead (tagging contract)[@name][Decision]YYYY-MM-DD
Consumer team representative[@name][Decision]YYYY-MM-DD

Revision history

VersionDateAuthorChange summary
0.1YYYY-MM-DD[@name]First draft for module 1.0.0
1.0YYYY-MM-DD[@name]Example: Approved; module 2.0.0 enforces encryption with customer-managed keys
1.1YYYY-MM-DD[@name]Example: Module 2.3.0 adds lifecycle_rules input

Contents

1. Purpose and scope

1.1 Purpose

[One paragraph: the capability this module provides and why a shared module is better than each team writing its own resources.]

Example: The object-bucket module creates a private object storage bucket that meets Corvid Commerce's security and cost allocation baseline by default: encrypted with a customer-managed key, public access blocked, versioning on, access logged, and tagged per the tagging standard. Product teams use it for application assets, exports and backups. Without it, teams copied resource blocks between repositories and drifted from the baseline (12 of 41 buckets failed the posture review in 2026-Q1).

1.2 Scope

In scopeOut of scope
Example: One bucket with its encryption, versioning, public access block, ownership controls, lifecycle rules and access logging targetThe KMS key itself (created by the kms-key module and passed in)
Example: A bucket policy that enforces TLS and denies uploads that request a different keyCross-account access grants (use the bucket-access-grant module)
Example: Optional replication configuration to a second regionStatic website hosting and public buckets (not supported by design)
[Item][Item]

1.3 Consumers and placement

ConsumerUseCalled from
Example: Product teams on the golden path "Java HTTP service"Application asset storageService infrastructure repository
Example: Data platform teamExport landing bucketsData platform stack
Example: Backup serviceBackup vault buckets with object lockBackup stack, through the backup-vault module
[Consumer][Use][Repository or stack]

2. Interface design principles

The module interface follows these rules:

  1. IF-01 Expose only inputs that callers legitimately need to vary. Security-critical settings are not inputs (section 5).
  2. IF-02 Use typed inputs with object and optional() attributes for grouped settings instead of many loose variables.
  3. IF-03 Every input and output has a one- or two-sentence description; the README input and output tables are generated from them.
  4. IF-04 Validate inputs in the module with validation blocks and clear error messages, so mistakes fail at plan time.
  5. IF-05 Do not configure providers inside the module. Callers pass providers, including aliased providers for a second region.
  6. IF-06 Outputs expose identifiers and attributes that other modules need; never output secrets, and mark sensitive outputs.
  7. IF-07 Prefer composition: small modules that callers combine over deep chains of modules calling modules.
  8. IF-08 Naming: inputs and outputs use snake_case; booleans are phrased positively (enable_replication, not disable_replication).

2.1 Repository layout

The layout follows the standard module structure: README, main, variables and outputs files at the root, with examples and tests in their own folders.

object-bucket/
  README.md            # usage, generated input and output tables
  SPEC.md              # this specification
  CHANGELOG.md         # Keep a Changelog format
  LICENSE
  main.tf              # resources
  variables.tf         # inputs (section 3)
  outputs.tf           # outputs (section 4)
  versions.tf          # required_version and required_providers (section 8)
  policy.tf            # bucket policy document
  examples/minimal/    # smallest valid call (section 12)
  examples/replicated/ # cross-region replication
  tests/               # *.tftest.hcl files (section 11)
  policy/tags.rego     # policy as code (section 11.3)

3. Inputs

IDInputTypeRequiredDefaultValidationDescription
IN-01 (Example)namestringYesNone3 to 50 characters, lower case letters, digits and hyphensLogical name; the module adds environment and a suffix to form the bucket name
IN-02 (Example)environmentstringYesNoneOne of dev, staging, prodDeployment environment; also used as a tag value
IN-03 (Example)kms_key_arnstringYesNoneMust be a key ARN, not an aliasCustomer-managed key for server-side encryption
IN-04 (Example)tagsmap(string)YesNoneMust contain the mandatory keys in section 7Caller tags, merged with module tags
IN-05 (Example)log_bucket_idstringYesNoneNon-emptyTarget bucket for server access logs
IN-06 (Example)lifecycle_ruleslist(object({...}))No[]Expiration days at least 1; transition days lower than expirationObject expiry and storage class transitions
IN-07 (Example)enable_replicationboolNofalseRequires IN-08 when trueReplicate objects to a second region
IN-08 (Example)replicationobject({ destination_bucket_arn = string, kms_key_arn = string })NonullBoth attributes set when IN-07 is trueReplication target
IN-09 (Example)force_destroyboolNofalseMust be false when environment = "prod"Allow deletion of a non-empty bucket (test environments only)
[IN-NN][input][type][Yes / No][default][rule][description]

Example of an input with validation, valid for both tools:

variable "environment" {
  description = "Deployment environment. Used in the bucket name and as the environment tag."
  type        = string

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "environment must be one of: dev, staging, prod."
  }
}

Cross-variable rules (IN-09 depends on IN-02) are enforced with a precondition on the bucket resource, which keeps the rule next to the resource it protects and works on every supported tool version.

4. Outputs

IDOutputTypeSensitiveDescriptionTypical consumer
OUT-01 (Example)bucket_idstringNoBucket nameApplication configuration
OUT-02 (Example)bucket_arnstringNoBucket ARNIAM policies, bucket-access-grant module
OUT-03 (Example)bucket_regional_domain_namestringNoRegional endpoint nameContent delivery configuration
OUT-04 (Example)replication_role_arnstringNoRole used for replication, null when disabledAudit and access reviews
[OUT-NN][output][type][Yes / No][description][consumer]

Removing an output or changing its type is a breaking change (section 10).

5. Defaults and guardrails

IDSettingBehaviourCan the caller change it?Enforced by
GR-01 (Example)Public access blockAll four public access settings onNoHard-coded in the module; organisation policy as second layer
GR-02 (Example)Encryption at restCustomer-managed key from IN-03; bucket key enabledKey: yes; turning encryption off: noHard-coded default SSE-KMS with the IN-03 key; bucket policy denies uploads that request a different key or SSE-S3
GR-03 (Example)TLS in transitBucket policy denies requests without secure transportNoBucket policy
GR-04 (Example)VersioningEnabledNoHard-coded
GR-05 (Example)Object ownershipBucket owner enforced (access control lists disabled)NoHard-coded
GR-06 (Example)Access loggingServer access logs to IN-05Target: yes; disabling: noRequired input
GR-07 (Example)Deletion protectionforce_destroy = false enforced in prod; deletion of prod buckets also denied by an organisation policy (deny DeleteBucket outside the break-glass role)force_destroy only outside prodPrecondition (IN-09); organisation policy; POL-05 on the plan
[GR-NN][Setting][Behaviour][Yes / No][Mechanism]

Defence in depth: module guardrails make the right thing easy; landing zone controls (service control policies, organisation policies and detective rules in the Landing Zone Design) catch resources created outside the module. Do not rely on either layer alone.

Exceptions: a team that needs a setting the module forbids raises an exception request with the reason, the compensating control, an expiry date and approval from the cloud security architect. Approved exceptions are listed here:

Exception IDTeamGuardrailReasonCompensating controlExpiryApproved by
EXC-01 (Example)Data platformGR-05A legacy partner integration still writes objects with access control listsPartner limited to one prefix; access reviewed monthly; migration to a bucket policy grant planned2027-03-31[@cloud-security-architect]
[EXC-NN][Team][GR ID][Reason][Control]YYYY-MM-DD[@approver]

6. Security baseline

IDRequirementThreat addressedControl framework referenceImplemented byVerified by
SEC-01 (Example)No public read or writeData exposure[Internal control ID]GR-01TST-03, POL-02
SEC-02 (Example)Encryption at rest with customer-managed keyDisclosure of stored data; key misuse[Control ID]GR-02TST-02, POL-03
SEC-03 (Example)Encryption in transit enforcedInterception[Control ID]GR-03TST-04
SEC-04 (Example)Recoverability of overwritten or deleted objectsRansomware, operator error[Control ID]GR-04, GR-07TST-02, POL-05
SEC-05 (Example)Access is loggedUndetected misuse[Control ID]GR-06TST-05
SEC-06 (Example)Least privilege replication rolePrivilege escalation[Control ID]Role scoped to source and destination buckets and keysTST-06
[SEC-NN][Requirement][Threat][Reference][GR ID][TST or POL ID]

Caller responsibilities that the module cannot enforce: who receives read and write access (use least privilege grants), the classification of data stored, key rotation and key policy (owned by the key module), and secrets handling in applications (see Secrets Management Standard, security/secrets-management-standard). For the threat analysis behind this table, see the Threat Model (security/threat-model).

7. Tagging contract

The Cost Allocation and Tagging Standard (ops/cost-allocation-tagging-standard) defines the mandatory keys and allowed values. This module implements that standard; if they disagree, the standard wins. The module is fixed in the next release, which is MAJOR when it rejects tag sets that were valid before (section 10).

Tag key (from the standard's tag dictionary)SourceRule in this module
cost-centerCaller (IN-04)Required; validated against the pattern in the standard (Example: cc- and 4 digits)
owner-teamCaller (IN-04)Required; team slug from the team directory, not a person
serviceCaller (IN-04)Required; service ID from the service catalogue
environmentModule, from IN-02Set by the module; a caller value is overwritten so the tag cannot disagree with the input. The module accepts dev, staging and prod, a subset of the standard's values
data-classificationCaller (IN-04)Required, because a bucket is a data store
managed-byModuleFixed value iac

Keys and values follow the standard's format rules (lower case letters, digits and hyphens), so the module does not add its version as a tag; the version is pinned in the caller's module block and found through usage tracking (13.2).

Contract rules:

  1. TAG-01 Every taggable resource the module creates MUST carry the merged tag set, including roles and replication resources.
  2. TAG-02 Merge order: provider default tags, then caller tags, then module-owned tags. Module-owned keys always win.
  3. TAG-03 A missing mandatory key MUST fail at plan time (input validation) and again in the policy check (POL-01).
  4. TAG-04 Resources that cannot be tagged are listed in the README so the FinOps team can allocate them by rule.

The periodic Cloud Cost Review (ops/finops-cost-review) reports tag compliance; resources created by this module SHOULD show 100%.

8. Compatibility and dependencies

DependencyConstraintTested versions (Example, checked 2026-10-24)Notes
OpenTofu>= 1.8.01.13.2 and the oldest supported minor.tofu files are not used, so the module stays readable by both tools
Terraform>= 1.8.01.17.0 and the oldest supported minorNative tests need 1.6 or later; provider mocking needs 1.7 or later
Cloud provider plugin>= 6.0, < 7.06.70.0A new provider major version is adopted in a module major release
Other modulesNone calledComposition happens in the caller
terraform {
  required_version = ">= 1.8.0"

  required_providers {
    aws = {
      source                = "hashicorp/aws"
      version               = ">= 6.0, < 7.0"
      configuration_aliases = [aws.replica]
    }
  }
}

configuration_aliases declares that the module expects an aliased provider aws.replica for the replica region (IF-05). Every caller must then pass it, even with replication off (pass the default provider twice). If that burden is too high for most callers, split replication into its own module instead.

Support rule (Example): test against the latest release and the oldest supported minor of each tool; raise the minimum only in a minor release with a changelog note, unless it forces callers to upgrade a tool major version, which is a major release. Features that exist in only one tool (for example OpenTofu state encryption, OpenTofu 1.12 prevent_destroy expressions, or features added to Terraform after the fork) are not used inside the module; callers may use them in their root configuration.

9. State, lifecycle and drift

  1. LC-01 Resource addresses inside the module are part of the contract with every caller's state. Renaming or moving a resource MUST include a moved block, kept for at least one major version.
  2. LC-02 Changes that force replacement of a stateful resource are breaking changes and need a migration guide.
  3. LC-03 The module does not set prevent_destroy. In Terraform every lifecycle argument must be a literal inside the resource block, so a caller cannot set it on a resource in a called module, and a literal true would also block deletion in test environments. OpenTofu 1.12.0 and later accept a reference to an input variable, but the module stays tool-neutral (section 8). Production protection comes from GR-07 (no force_destroy in prod, an organisation policy that denies bucket deletion, POL-05 on every plan) and versioning (GR-04). A team that needs prevent_destroy uses a separate object-bucket-protected variant that sets it to true, published from the same repository and test suite.
  4. LC-04 Adopting existing resources uses import blocks in the caller's root module, documented in the README migration section.
  5. LC-05 Drift detection runs a scheduled plan per consuming stack (Example: daily); any drift on a guardrail attribute raises a security finding, other drift a ticket to the owning team.

Example: when 2.0.0 removed count from the bucket, a moved block from aws_s3_bucket.this[0] to aws_s3_bucket.this kept every caller's bucket in place.

10. Versioning and changelog

The module follows Semantic Versioning 2.0.0: MAJOR for incompatible changes, MINOR for backward-compatible additions, PATCH for backward-compatible fixes. Versions 0.y.z are for initial development and are not used in production stacks. The organisation's wider rules are in the Versioning and Release Policy (dev/versioning-release-policy).

ChangeRelease typeReason
Remove or rename an input or outputMAJORCallers must change code
Make an optional input requiredMAJORExisting calls fail
Change a default that alters created resourcesMAJORInfrastructure changes without caller action
Tighten a validation so that previously valid values failMAJORExisting calls may fail
Change that forces replacement of a stateful resourceMAJORData loss risk; needs migration guide
Raise the provider constraint to a new major versionMAJORCallers must upgrade the provider
Add an optional input with a default that changes nothingMINORBackward compatible
Add an outputMINORBackward compatible
Add a new guardrail that only affects new resources and is required by the security baselineMINOR with notice, or MAJOR if existing resources changeDecide per case; record the decision
Fix a bug, or change only documentation, without changing the interface or intended behaviourPATCHBackward compatible

Changelog: CHANGELOG.md in Keep a Changelog format, one section per release with Added, Changed, Deprecated, Removed, Fixed and Security headings. Commit messages MAY follow Conventional Commits to automate release notes.

VersionDateTypeSummary
2.0.0 (Example)2026-03-02MAJORkms_key_arn and data-classification tag required; default provider-managed encryption removed
2.1.0 (Example)2026-05-11MINORAdded optional replication (IN-07, IN-08)
2.2.0 (Example)2026-07-20MINORAdded module-owned managed-by tag (data-classification has been required since 2.0.0)
2.3.0 (Example)2026-09-14MINORAdded lifecycle_rules input (IN-06)
[x.y.z]YYYY-MM-DD[Type][Summary]

Callers pin with a pessimistic constraint on the minor version, for example version = "~> 2.3", which allows 2.3 and later 2.x releases but not 3.0.

11. Testing

11.1 Test layers

LayerToolWhat it provesRunsGate
Format and validatetofu fmt -check, tofu validate (or the Terraform equivalents)Syntax and internal consistencyEvery commitBlocking
Lint and static security scan[Linter], [IaC security scanner]Provider-specific mistakes, known misconfigurationsEvery commitBlocking on high findings
Unit tests (plan)Native test command, command = plan, mock providers where neededInputs validate; guardrails present; tags mergedEvery pull requestBlocking
Policy as codeOpen Policy Agent with Conftest on the plan JSONOrganisation rules hold for the planned resourcesEvery pull request, and in every consuming pipelineBlocking
Integration tests (apply)Native test command, command = apply, in a sandbox accountResources are created as specified and are destroyed afterwardsBefore each release, nightlyBlocking for release
Example testsPlan of each folder in examples/Examples stay validEvery pull requestBlocking

11.2 Test cases

IDTestTypeExpected result
TST-01 (Example)Invalid environment valueUnit, expect_failures on the variablePlan fails with the validation message
TST-02 (Example)Default callUnitEncryption uses IN-03 key; versioning enabled
TST-03 (Example)Default callUnitAll four public access block settings true
TST-04 (Example)Bucket policyUnitPolicy contains a deny for non-TLS requests
TST-05 (Example)LoggingUnitLogging target equals IN-05
TST-06 (Example)Replication enabledIntegrationObject written to source appears in destination, encrypted with destination key
TST-07 (Example)force_destroy = true with environment = "prod"Unit, expect_failures on the bucket resourcePlan fails with the precondition message
TST-08 (Example)TagsUnitEvery taggable resource has the mandatory keys; environment equals IN-02
[TST-NN][Test][Type][Expected]

Example unit test file (tests/defaults.tftest.hcl), runnable with tofu test or terraform test:

mock_provider "aws" {}

mock_provider "aws" {
  alias = "replica"
}

variables {
  name          = "assets"
  environment   = "dev"
  kms_key_arn   = "arn:aws:kms:eu-west-1:111122223333:key/00000000-0000-0000-0000-000000000000"
  log_bucket_id = "corvid-dev-access-logs"
  tags          = { cost-center = "cc-1042", owner-team = "kestrel", service = "checkout-api", data-classification = "internal" }
}

run "public_access_is_blocked" {
  command = plan

  assert {
    condition = alltrue([
      aws_s3_bucket_public_access_block.this.block_public_acls,
      aws_s3_bucket_public_access_block.this.block_public_policy,
      aws_s3_bucket_public_access_block.this.ignore_public_acls,
      aws_s3_bucket_public_access_block.this.restrict_public_buckets,
    ])
    error_message = "Public access block must be fully enabled."
  }
}

run "prod_rejects_force_destroy" {
  command = plan

  variables {
    environment   = "prod"
    force_destroy = true
  }

  expect_failures = [aws_s3_bucket.this]
}

11.3 Policy as code

Policies run against the JSON form of a saved plan (tofu show -json plan.out or terraform show -json plan.out) in this module's pipeline and in every consuming pipeline, so they also catch resources created outside the module.

IDPolicySeveritySource rule
POL-01 (Example)Mandatory tag keys present on every taggable resourceDenyTagging standard
POL-02 (Example)No bucket without a full public access blockDenySEC-01
POL-03 (Example)No bucket encrypted only with a provider-managed key in prodDenySEC-02
POL-04 (Example)Module source pinned to a version, not a branchWarnSection 10
POL-05 (Example)No delete action on a bucket in a prod plan without an approved change recordDenyGR-07
[POL-NN][Policy][Deny / Warn][Reference]

Example policy POL-01 in Rego (OPA 1.x syntax). It reads tags_all, which includes provider default tags, covers only resource types listed as taggable, and also requires data-classification on data stores (section 7). If a tag value is unknown until apply, the rule reports it as missing; resolve such cases with known values in the plan:

package terraform.tags

universal := {"cost-center", "owner-team", "service", "environment"}

taggable := {"aws_s3_bucket", "aws_iam_role"}

data_store := {"aws_s3_bucket"}

required_for(t) := universal | {"data-classification"} if {
	t in data_store
} else := universal

deny contains msg if {
	rc := input.resource_changes[_]
	rc.mode == "managed"
	rc.type in taggable
	some action in rc.change.actions
	action in {"create", "update"}
	tags := object.get(rc.change.after, "tags_all", {})
	missing := required_for(rc.type) - {k | tags[k]}
	count(missing) > 0
	msg := sprintf("%s is missing required tags: %v", [rc.address, missing])
}

11.4 Acceptance criteria for a release

  1. All blocking layers in 11.1 pass on the release commit.
  2. Every guardrail in section 5 and every requirement in section 6 has at least one passing test or policy.
  3. Integration tests leave no resources behind (sandbox inventory checked after the run).
  4. The changelog entry and the release type match the table in section 10, confirmed by a second maintainer.
  5. For a major release, a migration guide exists and was tested on one real consuming stack.

12. Usage examples

Minimal call (examples/minimal):

module "assets_bucket" {
  source    = "registry.corvid.example/platform/object-bucket/aws"
  version   = "~> 2.3"
  providers = { aws = aws, aws.replica = aws } # no replication: pass the default twice (section 8)

  name          = "assets"
  environment   = "prod"
  kms_key_arn   = module.assets_key.key_arn
  log_bucket_id = data.terraform_remote_state.logging.outputs.access_log_bucket_id
  tags          = local.service_tags # mandatory keys from section 7, except environment
}

Replicated call (examples/replicated):

module "exports_bucket" {
  source    = "registry.corvid.example/platform/object-bucket/aws"
  version   = "~> 2.3"
  providers = { aws = aws, aws.replica = aws.eu_central_1 }

  name               = "exports"
  environment        = "prod"
  kms_key_arn        = module.exports_key.key_arn
  log_bucket_id      = data.terraform_remote_state.logging.outputs.access_log_bucket_id
  tags               = local.service_tags
  enable_replication = true
  replication = {
    destination_bucket_arn = module.exports_replica_bucket.bucket_arn
    kms_key_arn            = module.exports_replica_key.key_arn
  }
}

Both examples are planned in CI on every pull request, and TST-06 applies the replicated one in the sandbox.

13. Release, support and deprecation

13.1 Release process

  1. A maintainer opens a release pull request that updates CHANGELOG.md and this specification.
  2. CI runs every layer in 11.1; a second maintainer approves.
  3. The merge creates a signed tag vX.Y.Z, and the registry publishes the version.
  4. The release note is posted in [channel] and, for a major release, consuming teams named in 1.3 are notified directly.

13.2 Support policy (Example values)

ItemCommitment
Supported major versionsCurrent major and the previous major
Security fixesBack-ported to the previous major for 6 months after a new major
Response to issuesTriage within 2 working days
Usage trackingRegistry download data and a scheduled search for module sources in all infrastructure repositories

13.3 Deprecation

  1. Announce deprecation of a version line or of the whole module with a reason, a replacement and a removal date at least [90] days ahead.
  2. Mark inputs as deprecated in their description and emit a warning through a check block where useful, before removing them in the next major release.
  3. Track remaining consumers weekly from usage tracking; contact owners directly in the last 30 days.
  4. After the removal date, the version stays in the registry for reproducibility but receives no fixes; the module status becomes Retired.

14. Glossary and references

14.1 Glossary

TermMeaning in this specification
ModuleA set of configuration files in one directory, called by other configurations with inputs and outputs
Root moduleThe configuration where commands are run; it calls child modules
GuardrailA setting the module enforces and callers cannot override
Policy as codeRules written in a policy language and evaluated automatically against planned changes
Plan JSONMachine-readable form of a saved plan, used by policy tools
moved blockDeclaration that a resource address changed, so state is updated instead of replaced
Pessimistic constraintVersion constraint ~> that allows only the rightmost component to increase
DriftDifference between the declared configuration and the real infrastructure

14.2 References

ReferenceVersion or date checkedUse
Semantic Versioning 2.0.0, https://semver.org/2.0.0, checked 2026-10-24Release type rules (section 10)
OpenTofu documentation, standard module structure and tofu test, https://opentofu.org/docs/OpenTofu 1.13.2 (released 2026-10-21)Repository layout, test files and blocks
Terraform documentation, tests, https://developer.hashicorp.com/terraform/language/testsTerraform 1.17.0 (released 2026-10-21)Test command versions (tests 1.6, mocks 1.7)
Open Policy Agent, https://www.openpolicyagent.org/OPA 1.21.4; Conftest 0.71.1Policy as code on plan JSON
Keep a Changelog, https://keepachangelog.com/1.1.0Changelog format
Conventional Commits, https://www.conventionalcommits.org/1.0.0Optional commit convention
Cost Allocation and Tagging Standard (ops/cost-allocation-tagging-standard)[Version]Tag keys and values

Standard and licence note

Based on Semantic Versioning 2.0.0 (semver.org, CC BY 3.0) for release rules, and structure aligned with the OpenTofu standard module structure and the native test command documented for OpenTofu and Terraform; the policy example uses Open Policy Agent (Apache-2.0). No vendor documentation text is reproduced; code samples are original examples. OpenTofu is a trademark of LF Projects, LLC; Terraform and HashiCorp, AWS and Amazon S3, and Open Policy Agent are trademarks of their owners; this template is not affiliated with or endorsed by them. This template is original text written for this collection. OpenTofu is licensed under MPL-2.0; Terraform 1.6 and later is under the Business Source License 1.1. Check the current official documentation and tool versions before use. Template licence: MIT.

Template liên quan