diff --git a/modules/aws-terraform-backend/.terraform-docs.yml b/modules/aws-terraform-backend/.terraform-docs.yml new file mode 100644 index 000000000..49be8b854 --- /dev/null +++ b/modules/aws-terraform-backend/.terraform-docs.yml @@ -0,0 +1,48 @@ +formatter: "markdown" # this is required + +version: "" + +header-from: docs/header.md +footer-from: docs/footer.md + +recursive: + enabled: false + path: modules + include-main: true + +sections: + hide: [] + show: [] + +content: "" + +output: + file: "README.md" + mode: inject + template: |- + + {{ .Content }} + + +output-values: + enabled: false + from: "" + +sort: + enabled: true + by: name + +settings: + anchor: true + color: true + default: true + description: false + escape: true + hide-empty: false + html: true + indent: 2 + lockfile: true + read-comments: true + required: true + sensitive: true + type: true diff --git a/modules/aws-terraform-backend/README.md b/modules/aws-terraform-backend/README.md index 87d074f0b..8de094109 100644 --- a/modules/aws-terraform-backend/README.md +++ b/modules/aws-terraform-backend/README.md @@ -1,65 +1,136 @@ +# **AWS Terraform Backend Module** + +## Overview + +This module provisions the AWS infrastructure typically required by a Terraform backend: an S3 bucket for state files, optional DynamoDB locking, and IAM policies/roles to access backend resources. + +It also supports rendering an optional CloudFormation template and uploading it to S3 so another account can assume an administrative role for backend operations when needed. + +The module is intended for shared platform/backend setups where consistency, least-privilege access, and safe state handling are important. + +## Key Features + +- **S3 state bucket**: Creates an S3 bucket with versioning, encryption, and public access blocking. +- **Optional DynamoDB locking**: Creates a lock table when `locks_table_name` is provided. +- **IAM backend access role and policies**: Manages backend IAM role plus attachable policy for extra roles. +- **Optional CloudFormation role template upload**: Generates and uploads role template to S3 when enabled. + +## Basic Usage + +### Backend with DynamoDB locking + +```hcl +module "terraform_backend" { + source = "git::https://github.com/prefapp/tfm.git//modules/aws-terraform-backend" + + tfstate_bucket_name = "my-terraform-state-bucket" + tfstate_object_prefix = "envs/prod/terraform.tfstate" + locks_table_name = "my-terraform-locks" + + aws_account_id = "123456789012" + cloudformation_admin_role_for_client_account = "tf-backend-admin" +} +``` + +### Backend without DynamoDB locking (Terraform >= 1.11) + +```hcl +module "terraform_backend" { + source = "git::https://github.com/prefapp/tfm.git//modules/aws-terraform-backend" + + tfstate_bucket_name = "my-terraform-state-bucket" + tfstate_object_prefix = "envs/dev/terraform.tfstate" + locks_table_name = null + + aws_account_id = "123456789012" + cloudformation_admin_role_for_client_account = "tf-backend-admin" +} +``` + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5 | -| [aws](#requirement\_aws) | ~> 5.0 | +| [aws](#requirement\_aws) | ~> 6.40 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | ~> 5.0 | +| [aws](#provider\_aws) | 5.97.0 | + +## Modules + +No modules. ## Resources | Name | Type | |------|------| -| [aws_s3_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | -| [aws_s3_bucket_versioning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | -| [aws_s3_bucket_server_side_encryption_configuration](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | -| [aws_s3_bucket_public_access_block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | -| [aws_s3_object](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | -| [aws_dynamodb_table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/dynamodb_table) | resource | -| [aws_iam_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role)| resource | -| [aws_iam_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_role_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource | - +| [aws_dynamodb_table.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table) | resource | +| [aws_iam_policy.full](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.limited](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.client](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.extra_roles](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_s3_bucket.tfstate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket_public_access_block.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | +| [aws_s3_bucket_server_side_encryption_configuration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | +| [aws_s3_bucket_versioning.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [aws_s3_object.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| tfstate_bucket_name | Name of the S3 Bucket used for storing the Terraform state for the workspaces | string | -- | Y | -| tfstate_object_prefix | Prefix to the S3 bucket objects | string | -- | Y | -| locks_table_name | "Name of the locks DynamoDB table. Not needed if Terraform version is 1.11 or newer | string | null | N | -| aws_region | AWS Region | string | Default provider region (i.e. "us-east-1") | N | -| tags | Common tags for all resources | map(string) | {} | N | -| tfstate_force_destroy | Allow destroying the Terraform state bucket even if it contains objects | boolean | false | N | -| tfstate_enable_versioning | Enable versioning on the bucket | boolean | true | N | -| tfbackend_access_role_name | Terraform backend access role: role name for Terraform Backend | string | - | Y | -| backend_extra_roles | Additional roles to add to the Terraform backend access role | list(string) | [] | N | -| aws_account_id | AWS Account ID that will assume the role to access the S3 bucket and the dynamodb table | string | - | Y | -| cloudformation_admin_role_for_client_account | Role name that will assume the role to access the S3 bucket and the dynamodb table | string | - | Y | -| generate_cloudformation_role_for_client_account | Decide whether to generate a cloudformation stack with a iam role to access the S3 bucket and the dynamodb table | boolean | true | N | -| upload_cloudformation_role | Decide whether to upload to S3 the cloudformation stack | boolean | true | N | -| s3_bucket_cloudformation_role | Name of the S3 bucket where the cloudformation template will be uploaded | string | "" | Only if "upload_cloudformation_role" is true | -| s3_bucket_cloudformation_role_key | Key to use when uploading the template to S3 | string | cloudformation/rendered-template.yaml | N | -| create_github_iam | Controls if we want to assume the backend role from Github Actions | bool | false | N | -| github_repository | Name of the GitHub repository that will have access to the backend | string | "" | N | - +| [aws\_account\_id](#input\_aws\_account\_id) | AWS Account ID that will assume the role to access the S3 bucket and the dynamodb table | `string` | n/a | yes | +| [aws\_region](#input\_aws\_region) | AWS Region | `string` | `null` | no | +| [backend\_extra\_roles](#input\_backend\_extra\_roles) | Additional roles to add to the Terraform backend access role | `list(string)` | `[]` | no | +| [cloudformation\_admin\_role\_for\_client\_account](#input\_cloudformation\_admin\_role\_for\_client\_account) | Role name that will assume the role to access the S3 bucket and the dynamodb table | `string` | n/a | yes | +| [create\_github\_iam](#input\_create\_github\_iam) | Create IAM resources for GitHub | `bool` | `false` | no | +| [generate\_cloudformation\_role\_for\_client\_account](#input\_generate\_cloudformation\_role\_for\_client\_account) | Decide whether to generate a cloudformation stack with a iam role to access the account with administrative privileges | `bool` | `true` | no | +| [github\_repository](#input\_github\_repository) | Name of the GitHub repository to access the backend | `string` | `""` | no | +| [locks\_table\_name](#input\_locks\_table\_name) | Name of the locks DynamoDB table | `string` | `null` | no | +| [s3\_bucket\_cloudformation\_role](#input\_s3\_bucket\_cloudformation\_role) | Name of the S3 bucket where the cloudformation template will be uploaded | `string` | `""` | no | +| [s3\_bucket\_cloudformation\_role\_key](#input\_s3\_bucket\_cloudformation\_role\_key) | Key to use when uploading the template to S3 | `string` | `"cloudformation/rendered-template.yaml"` | no | +| [tags](#input\_tags) | Common tags for all resources | `map(string)` | `{}` | no | +| [tfbackend\_access\_role\_name](#input\_tfbackend\_access\_role\_name) | Terraform backend access role | `string` | `"terraform-backend-access-role"` | no | +| [tfstate\_bucket\_name](#input\_tfstate\_bucket\_name) | Name of the S3 Bucket used for storing the Terraform state for the workspaces | `string` | n/a | yes | +| [tfstate\_enable\_versioning](#input\_tfstate\_enable\_versioning) | Enable versioning on the bucket | `bool` | `true` | no | +| [tfstate\_force\_destroy](#input\_tfstate\_force\_destroy) | Allow destroying the Terraform state bucket even if it contains objects | `bool` | `false` | no | +| [tfstate\_object\_prefix](#input\_tfstate\_object\_prefix) | Prefix to the S3 bucket objects | `string` | n/a | yes | +| [upload\_cloudformation\_role](#input\_upload\_cloudformation\_role) | Decide whether to upload to S3 the cloudformation stack | `bool` | `true` | no | ## Outputs | Name | Description | |------|-------------| -| tfstate_bucket_arn | ARN of the Terraform State S3 bucket | -| tfstate_bucket_name | Name of the Terraform State S3 bucket | -| dynamodb_locks_table_arn | ARN of the DynamoDB table (empty if not created) | -| dynamodb_locks_table_name | Name of the DynamoDB table (empty if not created) | -| rendered_template_content | Cloudformation stack with a iam role to access the S3 bucket and the dynamodb table, in YAML format | -| s3_template_url | S3 URL of the uploaded template (only if `upload_cloudformation_role` is true) | +| [dynamodb\_locks\_table\_arn](#output\_dynamodb\_locks\_table\_arn) | ARN of the DynamoDB table (empty if not created) | +| [dynamodb\_locks\_table\_name](#output\_dynamodb\_locks\_table\_name) | Name of the DynamoDB table (empty if not created) | +| [rendered\_template\_content](#output\_rendered\_template\_content) | Cloudformation stack with a iam role to access the S3 bucket and the dynamodb table | +| [s3\_template\_url](#output\_s3\_template\_url) | S3 URL of the uploaded template (only if 'upload\_cloudformation\_role' is true) | +| [tfstate\_bucket\_arn](#output\_tfstate\_bucket\_arn) | ARN of the Terraform State S3 bucket | +| [tfstate\_bucket\_name](#output\_tfstate\_bucket\_name) | Name of the Terraform State S3 bucket | + +## Examples + +For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/aws-terraform-backend/_examples): + +- [Basic](https://github.com/prefapp/tfm/tree/main/modules/aws-terraform-backend/_examples/basic) - Terraform backend with S3 bucket and DynamoDB lock table. +- [Without Lock Table](https://github.com/prefapp/tfm/tree/main/modules/aws-terraform-backend/_examples/without-lock-table) - Terraform backend with S3 bucket only. + +## Resources + +- [Terraform S3 Backend](https://developer.hashicorp.com/terraform/language/backend/s3) +- [Amazon S3](https://aws.amazon.com/s3/) +- [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) +- [Terraform AWS Provider](https://registry.terraform.io/providers/hashicorp/aws/latest) + +## Support +For issues, questions, or contributions related to this module, please visit the repository's issue tracker: [https://github.com/prefapp/tfm/issues](https://github.com/prefapp/tfm/issues) diff --git a/modules/aws-terraform-backend/_examples/basic/main.tf b/modules/aws-terraform-backend/_examples/basic/main.tf new file mode 100644 index 000000000..6bb33985a --- /dev/null +++ b/modules/aws-terraform-backend/_examples/basic/main.tf @@ -0,0 +1,14 @@ +provider "aws" { + region = "eu-west-1" +} + +module "terraform_backend" { + source = "../../" + + tfstate_bucket_name = "example-terraform-state-bucket" + tfstate_object_prefix = "envs/prod/terraform.tfstate" + locks_table_name = "example-terraform-locks" + + aws_account_id = "123456789012" + cloudformation_admin_role_for_client_account = "tf-backend-admin" +} diff --git a/modules/aws-terraform-backend/_examples/without-lock-table/main.tf b/modules/aws-terraform-backend/_examples/without-lock-table/main.tf new file mode 100644 index 000000000..feb929d4a --- /dev/null +++ b/modules/aws-terraform-backend/_examples/without-lock-table/main.tf @@ -0,0 +1,14 @@ +provider "aws" { + region = "eu-west-1" +} + +module "terraform_backend" { + source = "../../" + + tfstate_bucket_name = "example-terraform-state-bucket" + tfstate_object_prefix = "envs/dev/terraform.tfstate" + locks_table_name = null + + aws_account_id = "123456789012" + cloudformation_admin_role_for_client_account = "tf-backend-admin" +} diff --git a/modules/aws-terraform-backend/docs/footer.md b/modules/aws-terraform-backend/docs/footer.md new file mode 100644 index 000000000..78a5ff0ad --- /dev/null +++ b/modules/aws-terraform-backend/docs/footer.md @@ -0,0 +1,17 @@ +## Examples + +For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/aws-terraform-backend/_examples): + +- [Basic](https://github.com/prefapp/tfm/tree/main/modules/aws-terraform-backend/_examples/basic) - Terraform backend with S3 bucket and DynamoDB lock table. +- [Without Lock Table](https://github.com/prefapp/tfm/tree/main/modules/aws-terraform-backend/_examples/without-lock-table) - Terraform backend with S3 bucket only. + +## Resources + +- [Terraform S3 Backend](https://developer.hashicorp.com/terraform/language/backend/s3) +- [Amazon S3](https://aws.amazon.com/s3/) +- [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) +- [Terraform AWS Provider](https://registry.terraform.io/providers/hashicorp/aws/latest) + +## Support + +For issues, questions, or contributions related to this module, please visit the repository's issue tracker: [https://github.com/prefapp/tfm/issues](https://github.com/prefapp/tfm/issues) diff --git a/modules/aws-terraform-backend/docs/header.md b/modules/aws-terraform-backend/docs/header.md new file mode 100644 index 000000000..faaf9faf8 --- /dev/null +++ b/modules/aws-terraform-backend/docs/header.md @@ -0,0 +1,48 @@ +# **AWS Terraform Backend Module** + +## Overview + +This module provisions the AWS infrastructure typically required by a Terraform backend: an S3 bucket for state files, optional DynamoDB locking, and IAM policies/roles to access backend resources. + +It also supports rendering an optional CloudFormation template and uploading it to S3 so another account can assume an administrative role for backend operations when needed. + +The module is intended for shared platform/backend setups where consistency, least-privilege access, and safe state handling are important. + +## Key Features + +- **S3 state bucket**: Creates an S3 bucket with versioning, encryption, and public access blocking. +- **Optional DynamoDB locking**: Creates a lock table when `locks_table_name` is provided. +- **IAM backend access role and policies**: Manages backend IAM role plus attachable policy for extra roles. +- **Optional CloudFormation role template upload**: Generates and uploads role template to S3 when enabled. + +## Basic Usage + +### Backend with DynamoDB locking + +```hcl +module "terraform_backend" { + source = "git::https://github.com/prefapp/tfm.git//modules/aws-terraform-backend" + + tfstate_bucket_name = "my-terraform-state-bucket" + tfstate_object_prefix = "envs/prod/terraform.tfstate" + locks_table_name = "my-terraform-locks" + + aws_account_id = "123456789012" + cloudformation_admin_role_for_client_account = "tf-backend-admin" +} +``` + +### Backend without DynamoDB locking (Terraform >= 1.11) + +```hcl +module "terraform_backend" { + source = "git::https://github.com/prefapp/tfm.git//modules/aws-terraform-backend" + + tfstate_bucket_name = "my-terraform-state-bucket" + tfstate_object_prefix = "envs/dev/terraform.tfstate" + locks_table_name = null + + aws_account_id = "123456789012" + cloudformation_admin_role_for_client_account = "tf-backend-admin" +} +``` diff --git a/modules/aws-terraform-backend/variables.tf b/modules/aws-terraform-backend/variables.tf index c6c86e7f5..3897a10af 100644 --- a/modules/aws-terraform-backend/variables.tf +++ b/modules/aws-terraform-backend/variables.tf @@ -14,12 +14,10 @@ variable "locks_table_name" { default = null } -data "aws_region" "current" {} - variable "aws_region" { description = "AWS Region" type = string - default = data.aws_region.current.name + default = null } variable "tags" { diff --git a/modules/aws-terraform-backend/versions.tf b/modules/aws-terraform-backend/versions.tf index 1a6c80da5..0c54474f4 100644 --- a/modules/aws-terraform-backend/versions.tf +++ b/modules/aws-terraform-backend/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "~> 5.97.0" + version = "~> 6.40" } } } diff --git a/specs/aws-terraform-backend/001-update-aws-provider-v6/plan.md b/specs/aws-terraform-backend/001-update-aws-provider-v6/plan.md new file mode 100644 index 000000000..fa4232899 --- /dev/null +++ b/specs/aws-terraform-backend/001-update-aws-provider-v6/plan.md @@ -0,0 +1,13 @@ +# Plan: Update AWS provider constraint to v6 for aws-terraform-backend + +**Spec:** `spec.md` +**Module:** `modules/aws-terraform-backend` + +## Steps + +1. Update `modules/aws-terraform-backend/versions.tf` to set AWS provider constraint to `~> 6.40`. +2. Fix `modules/aws-terraform-backend/variables.tf` by replacing the invalid variable default for `aws_region` with a Terraform-valid default. +3. Add `.terraform-docs.yml` and `docs/header.md` + `docs/footer.md` to the module, following existing AWS module conventions. +4. Regenerate module `README.md` with `terraform-docs .` from module directory. +5. Run module checks (`terraform fmt -check` and `terraform init -backend=false`) to verify formatting and provider resolution. +6. Update `tasks.md` with final completion status. diff --git a/specs/aws-terraform-backend/001-update-aws-provider-v6/spec.md b/specs/aws-terraform-backend/001-update-aws-provider-v6/spec.md new file mode 100644 index 000000000..a45b12d82 --- /dev/null +++ b/specs/aws-terraform-backend/001-update-aws-provider-v6/spec.md @@ -0,0 +1,37 @@ +# Specification: Update AWS provider constraint to v6 for aws-terraform-backend + +**Date:** 2026-06-08 +**Module:** `modules/aws-terraform-backend` +**Issue:** `#1290` (child of `#927`) + +## Problem + +The module currently pins the AWS provider to `~> 5.97.0` in `versions.tf`. Per the parent migration issue to AWS provider v6, this module must be updated to allow AWS provider major version 6. + +Additionally, validation is blocked because `variables.tf` sets `variable "aws_region"` default using a data source reference (`data.aws_region.current.name`), which is invalid in Terraform variable defaults. The module also lacks the standard `.terraform-docs.yml` + `docs/{header,footer}.md` structure used by AWS modules in this repository. + +## Goal + +Update the module provider constraint from `~> 5.97.0` to `~> 6.40`, fix `variables.tf` so Terraform initialization is valid, and standardize module docs generation through `terraform-docs` following the AWS module pattern in this repository. + +## Scope + +- Update `modules/aws-terraform-backend/versions.tf` required provider version for `hashicorp/aws`. +- Fix invalid default in `modules/aws-terraform-backend/variables.tf` for `variable "aws_region"`. +- Add `.terraform-docs.yml` and `docs/header.md` + `docs/footer.md` to align with AWS module documentation structure. +- Regenerate `modules/aws-terraform-backend/README.md` via `terraform-docs .` (generated, not hand-edited) so requirements/providers sections reflect v6. +- Run formatting/validation checks relevant to the touched module. + +## Out of Scope + +- Functional refactors unrelated to AWS provider major version migration. +- Cross-module updates. +- Any `CHANGELOG.md` modification (handled by Release Please automation). + +## Acceptance Criteria + +- `modules/aws-terraform-backend/versions.tf` sets AWS provider constraint to `~> 6.40`. +- `modules/aws-terraform-backend/variables.tf` no longer uses a data source reference in variable defaults. +- Module contains `.terraform-docs.yml` and `docs/header.md` + `docs/footer.md`. +- `modules/aws-terraform-backend/README.md` reflects AWS provider v6 requirement. +- Formatting and validation checks for the module complete without errors. diff --git a/specs/aws-terraform-backend/001-update-aws-provider-v6/tasks.md b/specs/aws-terraform-backend/001-update-aws-provider-v6/tasks.md new file mode 100644 index 000000000..a6c37c233 --- /dev/null +++ b/specs/aws-terraform-backend/001-update-aws-provider-v6/tasks.md @@ -0,0 +1,17 @@ +# Tasks: Update AWS provider constraint to v6 for aws-terraform-backend + +**Module:** `aws-terraform-backend` +**Spec:** `spec.md` +**Plan:** `plan.md` + +## Tasks + +- [x] 1. Create `spec.md`, `plan.md`, and `tasks.md` for this change. +- [x] 2. Update `modules/aws-terraform-backend/versions.tf` to require AWS provider `~> 6.40`. +- [x] 3. Fix `modules/aws-terraform-backend/variables.tf` to remove invalid variable default reference (`data.aws_region.current.name`). +- [x] 4. Add `.terraform-docs.yml` and `docs/header.md` + `docs/footer.md` to align with AWS module docs conventions. +- [x] 5. Regenerate `modules/aws-terraform-backend/README.md` using `terraform-docs` (without manual README edits). +- [x] 6. Run formatting/validation checks for the touched module (`terraform fmt -check`, `terraform init -backend=false -upgrade`, `terraform validate`). +- [x] 7. Final review and mark all tasks complete. + +**Status:** Complete