Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- [Terraform] S3 state bucket versioning and documented state rollback procedure in `infra-terraform/README.md`

### Changed

- Updated architecture diagram with latest logos (`docs/architecture-diagram/FAST-architecture-20260403.png`)
- [Terraform] Switched state backend from DynamoDB-based locking to S3 native locking (`use_lockfile`); Terraform >= 1.11 now required. Migration guidance for existing deployments in `infra-terraform/README.md`

### Security

- [Terraform] Public-access-block recommended on state bucket prerequisites

## [0.4.1] - 2026-03-25

Expand Down
2 changes: 1 addition & 1 deletion docs/TERRAFORM_DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This guide walks you through deploying the Fullstack AgentCore Solution Template

Before deploying, ensure you have:

- **Terraform** >= 1.5.0 (see [Install Terraform](https://developer.hashicorp.com/terraform/install))
- **Terraform** >= 1.11.0 (see [Install Terraform](https://developer.hashicorp.com/terraform/install)). Required for S3 native state locking -- see [State Management](../infra-terraform/README.md#state-management) for remote backend setup.
- **AWS CLI** configured with credentials (`aws configure`) - see [AWS CLI Configuration guide](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html)
- **Python 3.11+** (for the frontend deployment script)
- **Docker** (only required for `backend_deployment_type = "docker"`) - see [Install Docker Engine](https://docs.docker.com/engine/install/). Verify with `docker ps`. Alternatively, [Finch](https://github.com/runfinch/finch) can be used on Mac. See [below](#docker-cross-platform-build-setup-required-for-non-arm-machines) if you have a non-ARM machine.
Expand Down
76 changes: 68 additions & 8 deletions infra-terraform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,26 +143,86 @@ The modules are deployed in this order:

## State Management

By default, Terraform uses **local state** (`terraform.tfstate`). For team collaboration, use the S3 backend:
By default, Terraform uses **local state** (`terraform.tfstate`). For team collaboration, use the S3 backend with native locking (`use_lockfile`, requires Terraform >= 1.11) and bucket versioning:

```bash
# 1. Create S3 bucket & DynamoDB table (one-time)
# 1. Create the state bucket (one-time)
aws s3 mb s3://YOUR-BUCKET-NAME --region us-east-1
aws dynamodb create-table --table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST --region us-east-1

# 2. Copy and edit the backend config
# 2. Enable versioning (keeps prior state objects for rollback)
aws s3api put-bucket-versioning \
--bucket YOUR-BUCKET-NAME \
--versioning-configuration Status=Enabled

# 3. Block public access on the state bucket
aws s3api put-public-access-block \
--bucket YOUR-BUCKET-NAME \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

# 4. Copy and edit the backend config
cp backend.tf.example backend.tf
# Edit backend.tf with your bucket name

# 3. Migrate state
# 5. Migrate state
terraform init -migrate-state
```

See `backend.tf.example` for the full configuration.

### Rolling back state

With bucket versioning enabled, a prior `terraform.tfstate` object can be restored without `terraform state` surgery. **Only do this when no operator is running Terraform against the stack** -- coordinate before restoring.

```bash
# 1. List historical versions of the state object
aws s3api list-object-versions \
--bucket YOUR-BUCKET-NAME \
--prefix fast/terraform.tfstate

# 2. Restore a prior version by copying it over the current object
aws s3api copy-object \
--bucket YOUR-BUCKET-NAME \
--key fast/terraform.tfstate \
--copy-source "YOUR-BUCKET-NAME/fast/terraform.tfstate?versionId=<PRIOR_VERSION_ID>"

# 3. Run plan first to verify the drift matches expectations before any apply
terraform plan
```

Versioning is **not retroactive**: only state objects written after versioning was enabled can be recovered. Enable versioning on day one, not after an incident.

### Migrating from DynamoDB-only locking

Earlier versions of this template used a DynamoDB `terraform-locks` table. To migrate, use a two-phase rollout so teams running both old and new configs remain safe. Every operator must be on Terraform >= 1.11 before starting.

**Phase 1 -- dual-lock transition.** Temporarily keep both locks active so any `apply` from either config is protected:

```hcl
terraform {
backend "s3" {
bucket = "YOUR-BUCKET-NAME"
key = "fast/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks" # keep during transition
use_lockfile = true # new
encrypt = true
}
}
```

Run `terraform init -reconfigure` and have every operator pull the dual-lock config before phase 2.

If versioning was not enabled on the existing bucket, enable it now with the `put-bucket-versioning` command above. Prior state objects are not recoverable retroactively.

**Phase 2 -- cut over to S3-only.** Once everyone is on the dual-lock config, remove `dynamodb_table` (matching the shipped `backend.tf.example`), run `terraform init -reconfigure`, then delete the table:

```bash
aws dynamodb delete-table --table-name terraform-locks --region us-east-1
```

The `dynamodb_table` argument is deprecated in Terraform 1.11 and may be removed in a future major release, so do not stay on the dual-lock config indefinitely.

## Resource Reference

| Resource Type | Terraform Resource |
Expand Down
37 changes: 23 additions & 14 deletions infra-terraform/backend.tf.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,37 @@
# S3 Remote Backend Configuration
# =============================================================================
#
# Uses S3 native locking (use_lockfile), which requires Terraform >= 1.11.
# DynamoDB is no longer needed -- locking and versioning are handled by S3.
#
# To enable remote state storage:
# 1. Create S3 bucket and DynamoDB table (see commands below)
# 1. Create the S3 bucket with versioning and public-access-block (see below)
# 2. Copy this file: cp backend.tf.example backend.tf
# 3. Update bucket/table names below
# 3. Update the bucket name below
# 4. Run: terraform init -migrate-state
#
# Prerequisites (run once):
# aws s3 mb s3://YOUR-BUCKET-NAME --region us-east-1
# aws dynamodb create-table \
# --table-name terraform-locks \
# --attribute-definitions AttributeName=LockID,AttributeType=S \
# --key-schema AttributeName=LockID,KeyType=HASH \
# --billing-mode PAY_PER_REQUEST \
# --region us-east-1
# aws s3 mb s3://YOUR-TERRAFORM-STATE-BUCKET --region us-east-1
#
# aws s3api put-bucket-versioning \
# --bucket YOUR-TERRAFORM-STATE-BUCKET \
# --versioning-configuration Status=Enabled
#
# aws s3api put-public-access-block \
# --bucket YOUR-TERRAFORM-STATE-BUCKET \
# --public-access-block-configuration \
# BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
#
# Already using DynamoDB-based locking? See the "Migrating from DynamoDB-only
# locking" section in infra-terraform/README.md for the two-phase procedure.
# =============================================================================

terraform {
backend "s3" {
bucket = "YOUR-TERRAFORM-STATE-BUCKET" # Change this
key = "fast/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
bucket = "YOUR-TERRAFORM-STATE-BUCKET" # Change this
key = "fast/terraform.tfstate"
region = "us-east-1"
use_lockfile = true
encrypt = true
}
}
2 changes: 1 addition & 1 deletion infra-terraform/modules/amplify-hosting/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

terraform {
required_version = ">= 1.5.0"
required_version = ">= 1.11.0"

required_providers {
aws = {
Expand Down
2 changes: 1 addition & 1 deletion infra-terraform/modules/backend/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

terraform {
required_version = ">= 1.5.0"
required_version = ">= 1.11.0"

required_providers {
aws = {
Expand Down
2 changes: 1 addition & 1 deletion infra-terraform/modules/cognito/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

terraform {
required_version = ">= 1.5.0"
required_version = ">= 1.11.0"

required_providers {
aws = {
Expand Down
2 changes: 1 addition & 1 deletion infra-terraform/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

terraform {
required_version = ">= 1.5.0"
required_version = ">= 1.11.0"

required_providers {
aws = {
Expand Down
Loading