Document AWS CLI teardown and remove Terragrunt destroy option
This commit is contained in:
parent
bc54f640d8
commit
7aa12dca34
@ -17,22 +17,19 @@ on:
|
||||
inputs:
|
||||
deploy_action:
|
||||
type: choice
|
||||
options: [plan, apply, destroy]
|
||||
options: [plan, apply]
|
||||
default: plan
|
||||
|
||||
env:
|
||||
TF_WORKDIR: iac-template/terraform-hcl-standard/aws-cloud
|
||||
TG_ROOT: iac-template/terraform-hcl-standard/aws-cloud/bootstrap
|
||||
DEPLOY_ACTION: ${{ github.event.inputs.deploy_action || 'plan' }}
|
||||
TG_VERSION: 0.67.14
|
||||
|
||||
jobs:
|
||||
bootstrap:
|
||||
name: "Bootstrap Modules"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
target: [bootstrap/state/, bootstrap/lock, bootstrap/identity]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -41,22 +38,20 @@ jobs:
|
||||
cat <<'SUMMARY' >> "$GITHUB_STEP_SUMMARY"
|
||||
## Bootstrap scope
|
||||
- IAM: create Terraform deploy role and automation user for DevOps
|
||||
- S3: create remote state bucket (versioned + SSE)
|
||||
- DynamoDB: create state lock table for Terraform CRUD workflows
|
||||
- S3: create remote state bucket (versioned + SSE + public access block)
|
||||
- DynamoDB: create state lock table with encryption + PITR
|
||||
|
||||
Resource names and regions follow iac-template/terraform-hcl-standard/aws-cloud/config/accounts/bootstrap.yaml.
|
||||
Terragrunt orchestrates state → lock → identity. Resource names and regions follow iac-template/terraform-hcl-standard/aws-cloud/config/accounts/bootstrap.yaml.
|
||||
SUMMARY
|
||||
|
||||
- uses: hashicorp/setup-terraform@v3
|
||||
with:
|
||||
terraform_version: 1.9.5
|
||||
|
||||
- name: Restore Terraform state
|
||||
uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: tfstate-${{ matrix.target }}
|
||||
path: ${{ env.TF_WORKDIR }}/${{ matrix.target }}
|
||||
- name: Install Terragrunt
|
||||
run: |
|
||||
curl -L "https://github.com/gruntwork-io/terragrunt/releases/download/v${TG_VERSION}/terragrunt_linux_amd64" -o terragrunt
|
||||
sudo install terragrunt /usr/local/bin/terragrunt
|
||||
|
||||
- name: AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
@ -65,97 +60,28 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_BOOTSTRAP_SECRET_ACCESS_KEY }}
|
||||
aws-region: ap-northeast-1
|
||||
|
||||
- name: Init
|
||||
working-directory: ${{ env.TF_WORKDIR }}/${{ matrix.target }}
|
||||
run: make init
|
||||
|
||||
- name: Plan
|
||||
- name: Terragrunt Plan
|
||||
if: env.DEPLOY_ACTION == 'plan'
|
||||
working-directory: ${{ env.TF_WORKDIR }}/${{ matrix.target }}
|
||||
run: make plan
|
||||
working-directory: ${{ env.TG_ROOT }}
|
||||
run: terragrunt run-all plan --terragrunt-non-interactive
|
||||
|
||||
- name: Apply
|
||||
- name: Terragrunt Apply
|
||||
if: env.DEPLOY_ACTION == 'apply'
|
||||
working-directory: ${{ env.TF_WORKDIR }}/${{ matrix.target }}
|
||||
run: make apply
|
||||
|
||||
- name: Load bootstrap config for destroy
|
||||
if: env.DEPLOY_ACTION == 'destroy'
|
||||
run: |
|
||||
python -m pip install --quiet pyyaml
|
||||
python - <<'PY'
|
||||
import yaml
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
cfg_path = Path("iac-template/terraform-hcl-standard/aws-cloud/config/accounts/bootstrap.yaml")
|
||||
cfg = yaml.safe_load(cfg_path.read_text())
|
||||
|
||||
env_path = Path(os.environ["GITHUB_ENV"])
|
||||
current_env = env_path.read_text() if env_path.exists() else ""
|
||||
env_path.write_text(
|
||||
current_env
|
||||
+ f"BOOTSTRAP_BUCKET={cfg['state']['bucket_name']}\n"
|
||||
+ f"BOOTSTRAP_REGION={cfg['region']}\n"
|
||||
+ f"BOOTSTRAP_DYNAMODB_TABLE={cfg['state']['dynamodb_table_name']}\n"
|
||||
+ f"BOOTSTRAP_ROLE_NAME={cfg['iam']['role_name']}\n"
|
||||
+ f"BOOTSTRAP_TERRAFORM_USER={cfg['iam']['terraform_user_name']}\n"
|
||||
)
|
||||
PY
|
||||
|
||||
- name: Destroy
|
||||
if: env.DEPLOY_ACTION == 'destroy'
|
||||
working-directory: ${{ env.TF_WORKDIR }}/${{ matrix.target }}
|
||||
env:
|
||||
AWS_REGION: ${{ env.BOOTSTRAP_REGION }}
|
||||
run: |
|
||||
if [ "${{ matrix.target }}" = "bootstrap-s3" ]; then
|
||||
make destroy bucket_name=${BOOTSTRAP_BUCKET} region=${BOOTSTRAP_REGION}
|
||||
elif [ "${{ matrix.target }}" = "bootstrap-dynamodb" ]; then
|
||||
make destroy table_name=${BOOTSTRAP_DYNAMODB_TABLE} region=${BOOTSTRAP_REGION}
|
||||
else
|
||||
make destroy role_name=${BOOTSTRAP_ROLE_NAME} terraform_user_name=${BOOTSTRAP_TERRAFORM_USER}
|
||||
fi
|
||||
working-directory: ${{ env.TG_ROOT }}
|
||||
run: terragrunt run-all apply --terragrunt-non-interactive
|
||||
|
||||
- name: Save Outputs
|
||||
if: env.DEPLOY_ACTION == 'apply'
|
||||
working-directory: ${{ env.TF_WORKDIR }}/${{ matrix.target }}
|
||||
run: terraform output -json > ../../outputs_${{ matrix.target }}.json
|
||||
working-directory: ${{ env.TG_ROOT }}
|
||||
run: |
|
||||
mkdir -p outputs
|
||||
for dir in state lock identity; do
|
||||
terragrunt output -json --terragrunt-working-dir $dir > outputs/${dir}.json
|
||||
done
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: env.DEPLOY_ACTION == 'apply'
|
||||
with:
|
||||
name: outputs-${{ matrix.target }}
|
||||
path: iac-template/terraform-hcl-standard/aws-cloud/outputs_${{ matrix.target }}.json
|
||||
name: bootstrap-outputs
|
||||
path: ${{ env.TG_ROOT }}/outputs
|
||||
retention-days: 30
|
||||
|
||||
aggregate:
|
||||
name: "Aggregate Bootstrap Outputs"
|
||||
runs-on: ubuntu-latest
|
||||
needs: bootstrap
|
||||
|
||||
if: ${{ github.event.inputs.deploy_action == 'apply' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./outputs
|
||||
|
||||
- name: Merge Outputs
|
||||
run: |
|
||||
shopt -s globstar nullglob
|
||||
echo "{" > final_bootstrap_outputs.json
|
||||
f=true
|
||||
for x in outputs/**/outputs_*.json; do
|
||||
k=$(basename $x .json | sed 's/outputs_//')
|
||||
[ "$f" = true ] && f=false || echo "," >> final_bootstrap_outputs.json
|
||||
echo "\"$k\": $(cat $x)" >> final_bootstrap_outputs.json
|
||||
done
|
||||
echo "}" >> final_bootstrap_outputs.json
|
||||
|
||||
- run: cat final_bootstrap_outputs.json
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bootstrap-final-output
|
||||
path: final_bootstrap_outputs.json
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
# AWS Bootstrap (Terraform + Terragrunt)
|
||||
|
||||
This bootstrap stack provisions the shared primitives required for Terraform automation on AWS using the **terraform-hcl-standard** baseline. It delivers an auditable, deterministic foundation that can be reused across environments.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **state**: Versioned, SSE-encrypted S3 bucket with public access blocked for Terraform state storage.
|
||||
- **lock**: DynamoDB table with point-in-time recovery (PITR) and server-side encryption for state locking and auditability.
|
||||
- **identity**: Terraform deploy role plus automation user, wired with least-privilege inline policies stored as external JSON documents.
|
||||
- **Orchestration**: Terragrunt dependencies guarantee the apply order (state → lock → identity) and propagate outputs (bucket name, region, lock table) automatically.
|
||||
|
||||
## Execution Order
|
||||
|
||||
1. `state`: Creates the S3 backend bucket and exports `bucket_name`, `bucket_arn`, and `region`.
|
||||
2. `lock`: Creates the DynamoDB lock table in the same region and exports `dynamodb_table_name` and `region`.
|
||||
3. `identity`: Uses dependency outputs to bind IAM policies to the created state and lock resources.
|
||||
|
||||
Terragrunt `run-all` handles the ordering; no manual sequencing is required.
|
||||
|
||||
## Security Model
|
||||
|
||||
- **Data plane**: S3 bucket enforces AES256 SSE, public access block, and versioning. DynamoDB enables server-side encryption and PITR for forensic recovery.
|
||||
- **Control plane**: IAM policies are externalized in `identity/policies/*.json` and rendered via `aws_iam_policy_document` to keep Terraform code lean and auditable.
|
||||
- **Config source of truth**: `config/accounts/bootstrap.yaml` defines canonical names, regions, and tags. Terragrunt passes outputs between modules to avoid drift.
|
||||
|
||||
## How to Run with Terragrunt
|
||||
|
||||
```bash
|
||||
cd iac-template/terraform-hcl-standard/aws-cloud/bootstrap
|
||||
|
||||
# Plan everything in dependency order
|
||||
terragrunt run-all plan
|
||||
|
||||
# Apply everything (state -> lock -> identity)
|
||||
terragrunt run-all apply
|
||||
```
|
||||
|
||||
### Targeting a Single Module
|
||||
|
||||
```bash
|
||||
terragrunt plan --terragrunt-working-dir state
|
||||
terragrunt apply --terragrunt-working-dir identity
|
||||
```
|
||||
|
||||
Terragrunt injects dependency outputs automatically; you do not need to pass bucket or table names manually.
|
||||
|
||||
### Decommissioning Bootstrap Resources
|
||||
|
||||
Bootstrap is intentionally outside day-to-day state management. Avoid `terragrunt destroy` and use the AWS CLI for teardown to keep lifecycle control explicit and auditable.
|
||||
|
||||
```bash
|
||||
# Remove automation user and deploy role (customize to your account IDs)
|
||||
aws iam delete-access-key --user-name terraform-automation --access-key-id <key-id>
|
||||
aws iam delete-user-policy --user-name terraform-automation --policy-name terraform-automation-inline
|
||||
aws iam delete-user --user-name terraform-automation
|
||||
aws iam detach-role-policy --role-name terraform-deploy --policy-arn arn:aws:iam::<account-id>:policy/terraform-deploy-inline
|
||||
aws iam delete-role --role-name terraform-deploy
|
||||
|
||||
# Remove lock + state once no stacks depend on them
|
||||
aws dynamodb delete-table --table-name <bootstrap-lock-table>
|
||||
aws s3 rb s3://<bootstrap-state-bucket> --force
|
||||
```
|
||||
|
||||
Document the teardown in your change log for auditability.
|
||||
|
||||
## CloudNeutral Bootstrap Principles
|
||||
|
||||
- **Separation of concerns**: State, locking, and identity are isolated modules with explicit interfaces.
|
||||
- **Least privilege by default**: IAM policies grant the minimal scope required for bootstrap lifecycle operations.
|
||||
- **Idempotent automation**: All configurations are declarative, version-controlled, and runnable via Terragrunt without manual steps.
|
||||
- **Auditability**: Policies live in external JSON files; DynamoDB PITR and S3 versioning preserve history for compliance.
|
||||
- **Portability**: Inputs are read from YAML configuration and Terragrunt dependencies, making the stack reusable across accounts and regions.
|
||||
@ -1,15 +1,17 @@
|
||||
locals {
|
||||
bootstrap = yamldecode(file("${path.module}/../../config/accounts/bootstrap.yaml"))
|
||||
|
||||
config_account_name = coalesce(var.account_name, local.bootstrap.account_name)
|
||||
config_region = coalesce(var.region, local.bootstrap.region)
|
||||
config_role_name = coalesce(var.role_name, local.bootstrap.iam.role_name)
|
||||
config_terraform_user = coalesce(var.terraform_user_name, local.bootstrap.iam.terraform_user_name)
|
||||
environment = coalesce(try(local.bootstrap.environment, null), try(local.bootstrap.iam.environment, null), "bootstrap")
|
||||
extra_tags = try(local.bootstrap.tags, {})
|
||||
config_account_name = coalesce(var.account_name, local.bootstrap.account_name)
|
||||
config_region = coalesce(var.region, local.bootstrap.region)
|
||||
config_role_name = coalesce(var.role_name, local.bootstrap.iam.role_name)
|
||||
config_terraform_user = coalesce(var.terraform_user_name, local.bootstrap.iam.terraform_user_name)
|
||||
environment = coalesce(try(local.bootstrap.environment, null), try(local.bootstrap.iam.environment, null), "bootstrap")
|
||||
extra_tags = try(local.bootstrap.tags, {})
|
||||
|
||||
role_name = coalesce(var.existing_role_name, local.config_role_name)
|
||||
role_name = coalesce(var.existing_role_name, local.config_role_name)
|
||||
terraform_user_name = coalesce(var.existing_user_name, local.config_terraform_user)
|
||||
state_bucket_name = coalesce(var.state_bucket_name, try(local.bootstrap.state.bucket_name, null))
|
||||
lock_table_name = coalesce(var.state_lock_table_name, try(local.bootstrap.state.dynamodb_table_name, null))
|
||||
}
|
||||
|
||||
locals {
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
#
|
||||
# IAM Role: Terraform Deploy Role
|
||||
# ----------------------------------------
|
||||
data "aws_iam_policy_document" "terraform_deploy_assume_role" {
|
||||
source_json = templatefile(
|
||||
"${path.module}/policies/terraform-deploy-assume-role.json",
|
||||
{
|
||||
account_id = local.account.account_id
|
||||
terraform_user_name = local.config_terraform_user
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "terraform_deploy_role" {
|
||||
count = var.create_role ? 1 : 0
|
||||
|
||||
name = local.role_name
|
||||
|
||||
assume_role_policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [{
|
||||
Effect = "Allow"
|
||||
Principal = {
|
||||
AWS = "arn:aws:iam::${local.account.account_id}:user/${local.config_terraform_user}"
|
||||
}
|
||||
Action = "sts:AssumeRole"
|
||||
}]
|
||||
})
|
||||
name = local.role_name
|
||||
assume_role_policy = data.aws_iam_policy_document.terraform_deploy_assume_role.json
|
||||
|
||||
tags = merge(
|
||||
{
|
||||
@ -23,91 +23,29 @@ resource "aws_iam_role" "terraform_deploy_role" {
|
||||
Environment = coalesce(try(local.account.environment, null), local.environment)
|
||||
},
|
||||
try(local.account.tags, {}),
|
||||
local.extra_tags
|
||||
local.extra_tags,
|
||||
)
|
||||
}
|
||||
|
||||
data "aws_iam_policy_document" "terraform_deploy_inline" {
|
||||
source_json = templatefile(
|
||||
"${path.module}/policies/terraform-deploy-inline-policy.json",
|
||||
{
|
||||
account_id = local.account.account_id
|
||||
bucket_name = local.state_bucket_name
|
||||
region = local.config_region
|
||||
role_name = local.role_name
|
||||
table_name = local.lock_table_name
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "terraform_deploy_role_policy" {
|
||||
count = var.create_role ? 1 : 0
|
||||
|
||||
name = "${local.role_name}-bootstrap-minimal"
|
||||
role = aws_iam_role.terraform_deploy_role[0].id
|
||||
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17",
|
||||
Statement = [
|
||||
# Bootstrap S3 backend (state bucket)
|
||||
{
|
||||
Effect = "Allow",
|
||||
Action = [
|
||||
"s3:CreateBucket",
|
||||
"s3:GetBucketLocation",
|
||||
"s3:ListBucket",
|
||||
"s3:PutBucketVersioning",
|
||||
"s3:PutBucketPolicy",
|
||||
"s3:PutBucketTagging",
|
||||
"s3:PutEncryptionConfiguration",
|
||||
"s3:PutBucketPublicAccessBlock",
|
||||
],
|
||||
Resource = "arn:aws:s3:::${local.bootstrap.state.bucket_name}"
|
||||
},
|
||||
{
|
||||
Effect = "Allow",
|
||||
Action = [
|
||||
"s3:GetObject",
|
||||
"s3:PutObject",
|
||||
"s3:DeleteObject",
|
||||
"s3:PutObjectTagging"
|
||||
],
|
||||
Resource = "arn:aws:s3:::${local.bootstrap.state.bucket_name}/*"
|
||||
},
|
||||
|
||||
# DynamoDB state lock table
|
||||
{
|
||||
Effect = "Allow",
|
||||
Action = [
|
||||
"dynamodb:CreateTable",
|
||||
"dynamodb:DescribeTable",
|
||||
"dynamodb:UpdateTable",
|
||||
"dynamodb:TagResource",
|
||||
"dynamodb:UntagResource"
|
||||
],
|
||||
Resource = "arn:aws:dynamodb:${local.config_region}:${local.account.account_id}:table/${local.bootstrap.state.dynamodb_table_name}"
|
||||
},
|
||||
|
||||
# IAM roles needed for bootstrap lifecycle
|
||||
{
|
||||
Effect = "Allow",
|
||||
Action = [
|
||||
"iam:GetRole",
|
||||
"iam:CreateRole",
|
||||
"iam:DeleteRole",
|
||||
"iam:UpdateAssumeRolePolicy",
|
||||
"iam:TagRole",
|
||||
"iam:UntagRole"
|
||||
],
|
||||
Resource = [
|
||||
"arn:aws:iam::${local.account.account_id}:role/${local.role_name}",
|
||||
"arn:aws:iam::${local.account.account_id}:role/bootstrap-*",
|
||||
"arn:aws:iam::${local.account.account_id}:role/terraform-*"
|
||||
]
|
||||
},
|
||||
{
|
||||
Effect = "Allow",
|
||||
Action = [
|
||||
"iam:PutRolePolicy",
|
||||
"iam:DeleteRolePolicy",
|
||||
"iam:AttachRolePolicy",
|
||||
"iam:DetachRolePolicy"
|
||||
],
|
||||
Resource = [
|
||||
"arn:aws:iam::${local.account.account_id}:role/${local.role_name}",
|
||||
"arn:aws:iam::${local.account.account_id}:role/bootstrap-*",
|
||||
"arn:aws:iam::${local.account.account_id}:role/terraform-*"
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
name = "${local.role_name}-bootstrap-minimal"
|
||||
role = aws_iam_role.terraform_deploy_role[0].id
|
||||
policy = data.aws_iam_policy_document.terraform_deploy_inline.json
|
||||
}
|
||||
|
||||
#
|
||||
@ -122,23 +60,20 @@ resource "aws_iam_user" "terraform_user" {
|
||||
#
|
||||
# IAM User Policy: 最小权限
|
||||
# ----------------------------------------
|
||||
data "aws_iam_policy_document" "terraform_user" {
|
||||
source_json = templatefile(
|
||||
"${path.module}/policies/terraform-user-assume-role.json",
|
||||
{
|
||||
account_id = local.account.account_id
|
||||
role_name = local.role_name
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
resource "aws_iam_user_policy" "terraform_user_policy" {
|
||||
count = var.create_user ? 1 : 0
|
||||
|
||||
name = "${local.terraform_user_name}-iac-policy"
|
||||
user = aws_iam_user.terraform_user[0].name
|
||||
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17",
|
||||
Statement = [
|
||||
# 允许 Assume TerraformDeployRole
|
||||
{
|
||||
Effect = "Allow",
|
||||
Action = [
|
||||
"sts:AssumeRole"
|
||||
],
|
||||
Resource = var.create_role ? aws_iam_role.terraform_deploy_role[0].arn : var.existing_role_arn
|
||||
}
|
||||
]
|
||||
})
|
||||
name = "${local.terraform_user_name}-iac-policy"
|
||||
user = aws_iam_user.terraform_user[0].name
|
||||
policy = data.aws_iam_policy_document.terraform_user.json
|
||||
}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": "arn:aws:iam::${account_id}:user/${terraform_user_name}"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "BootstrapStateBucketManagement",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:CreateBucket",
|
||||
"s3:GetBucketLocation",
|
||||
"s3:ListBucket",
|
||||
"s3:PutBucketVersioning",
|
||||
"s3:PutBucketPolicy",
|
||||
"s3:PutBucketTagging",
|
||||
"s3:PutEncryptionConfiguration",
|
||||
"s3:PutBucketPublicAccessBlock"
|
||||
],
|
||||
"Resource": "arn:aws:s3:::${bucket_name}"
|
||||
},
|
||||
{
|
||||
"Sid": "BootstrapStateObjectAccess",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetObject",
|
||||
"s3:PutObject",
|
||||
"s3:DeleteObject",
|
||||
"s3:PutObjectTagging"
|
||||
],
|
||||
"Resource": "arn:aws:s3:::${bucket_name}/*"
|
||||
},
|
||||
{
|
||||
"Sid": "TerraformLockTable",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"dynamodb:CreateTable",
|
||||
"dynamodb:DescribeTable",
|
||||
"dynamodb:UpdateTable",
|
||||
"dynamodb:TagResource",
|
||||
"dynamodb:UntagResource"
|
||||
],
|
||||
"Resource": "arn:aws:dynamodb:${region}:${account_id}:table/${table_name}"
|
||||
},
|
||||
{
|
||||
"Sid": "BootstrapIamRoleLifecycle",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"iam:GetRole",
|
||||
"iam:CreateRole",
|
||||
"iam:DeleteRole",
|
||||
"iam:UpdateAssumeRolePolicy",
|
||||
"iam:TagRole",
|
||||
"iam:UntagRole"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:iam::${account_id}:role/${role_name}",
|
||||
"arn:aws:iam::${account_id}:role/bootstrap-*",
|
||||
"arn:aws:iam::${account_id}:role/terraform-*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Sid": "BootstrapIamRolePolicies",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"iam:PutRolePolicy",
|
||||
"iam:DeleteRolePolicy",
|
||||
"iam:AttachRolePolicy",
|
||||
"iam:DetachRolePolicy"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:iam::${account_id}:role/${role_name}",
|
||||
"arn:aws:iam::${account_id}:role/bootstrap-*",
|
||||
"arn:aws:iam::${account_id}:role/terraform-*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"sts:AssumeRole"
|
||||
],
|
||||
"Resource": "arn:aws:iam::${account_id}:role/${role_name}"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
include "root" {
|
||||
path = find_in_parent_folders()
|
||||
}
|
||||
|
||||
locals {
|
||||
bootstrap_config = include.root.locals.bootstrap_config
|
||||
}
|
||||
|
||||
dependency "state" {
|
||||
config_path = "../state"
|
||||
|
||||
mock_outputs = {
|
||||
bucket_name = local.bootstrap_config.state.bucket_name
|
||||
region = local.bootstrap_config.region
|
||||
}
|
||||
|
||||
mock_outputs_allowed_terraform_commands = ["plan", "validate"]
|
||||
}
|
||||
|
||||
dependency "lock" {
|
||||
config_path = "../lock"
|
||||
|
||||
mock_outputs = {
|
||||
dynamodb_table_name = local.bootstrap_config.state.dynamodb_table_name
|
||||
region = local.bootstrap_config.region
|
||||
}
|
||||
|
||||
mock_outputs_allowed_terraform_commands = ["plan", "validate"]
|
||||
}
|
||||
|
||||
terraform {
|
||||
source = "./"
|
||||
}
|
||||
|
||||
inputs = {
|
||||
region = dependency.state.outputs.region
|
||||
state_bucket_name = dependency.state.outputs.bucket_name
|
||||
state_lock_table_name = dependency.lock.outputs.dynamodb_table_name
|
||||
}
|
||||
@ -61,3 +61,15 @@ variable "create_user" {
|
||||
error_message = "existing_user_name must be provided when create_user is false."
|
||||
}
|
||||
}
|
||||
|
||||
variable "state_bucket_name" {
|
||||
description = "Name of the Terraform state bucket (overrides bootstrap config when provided)"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "state_lock_table_name" {
|
||||
description = "Name of the DynamoDB state lock table (overrides bootstrap config when provided)"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
@ -9,6 +9,14 @@ resource "aws_dynamodb_table" "terraform_locks" {
|
||||
type = "S"
|
||||
}
|
||||
|
||||
point_in_time_recovery {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
server_side_encryption {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
tags = merge(
|
||||
{
|
||||
Name = local.dynamodb_table_name
|
||||
|
||||
@ -3,3 +3,7 @@ output "dynamodb_table_name" {
|
||||
value = aws_dynamodb_table.terraform_locks.name
|
||||
}
|
||||
|
||||
output "region" {
|
||||
description = "AWS region hosting the DynamoDB lock table"
|
||||
value = local.region
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
include "root" {
|
||||
path = find_in_parent_folders()
|
||||
}
|
||||
|
||||
locals {
|
||||
bootstrap_config = include.root.locals.bootstrap_config
|
||||
}
|
||||
|
||||
dependency "state" {
|
||||
config_path = "../state"
|
||||
|
||||
mock_outputs = {
|
||||
bucket_name = local.bootstrap_config.state.bucket_name
|
||||
region = local.bootstrap_config.region
|
||||
}
|
||||
|
||||
mock_outputs_allowed_terraform_commands = ["plan", "validate"]
|
||||
}
|
||||
|
||||
terraform {
|
||||
source = "./"
|
||||
}
|
||||
|
||||
inputs = {
|
||||
region = dependency.state.outputs.region
|
||||
}
|
||||
@ -27,3 +27,12 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "sse" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_public_access_block" "block" {
|
||||
bucket = aws_s3_bucket.state.id
|
||||
|
||||
block_public_acls = true
|
||||
block_public_policy = true
|
||||
ignore_public_acls = true
|
||||
restrict_public_buckets = true
|
||||
}
|
||||
|
||||
@ -1,3 +1,13 @@
|
||||
output "bucket_name" {
|
||||
value = aws_s3_bucket.state.bucket
|
||||
}
|
||||
|
||||
output "bucket_arn" {
|
||||
value = aws_s3_bucket.state.arn
|
||||
description = "ARN of the Terraform state bucket"
|
||||
}
|
||||
|
||||
output "region" {
|
||||
value = local.region
|
||||
description = "AWS region hosting the state bucket"
|
||||
}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
include "root" {
|
||||
path = find_in_parent_folders()
|
||||
}
|
||||
|
||||
locals {
|
||||
bootstrap_config = include.root.locals.bootstrap_config
|
||||
}
|
||||
|
||||
terraform {
|
||||
source = "./"
|
||||
}
|
||||
|
||||
inputs = {
|
||||
bucket_name = local.bootstrap_config.state.bucket_name
|
||||
region = local.bootstrap_config.region
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
terraform_version_constraint = ">= 1.2.0"
|
||||
terragrunt_version_constraint = ">= 0.67.14"
|
||||
|
||||
locals {
|
||||
bootstrap_config = yamldecode(file("${get_terragrunt_dir()}/../config/accounts/bootstrap.yaml"))
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user