Skip to content

Commit 4d496e7

Browse files
committed
v2.0.0 - Destroying Bootstraps FTW
1 parent fdfacd9 commit 4d496e7

55 files changed

Lines changed: 1010 additions & 118 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
### Terraform ###
2+
3+
# Transient backends
4+
components/**/backend_tfscaffold.tf
5+
26
# Compiled files
37
**/*.tfstate
48
**/*.tfplan
59
**/*.tfstate.backup
610
**/.terraform
11+
**/.terraform.lock.hcl
712
**/.terraform/*
8-
9-
# Terraform Output
10-
**/.terraform.output.json
13+
**/build/*
14+
**/work/*
15+
**/*tfstate.lock.info
16+
**/*.tfplan.json
1117

1218
# Scaffold Plugin Cache
1319
plugin-cache/*
@@ -37,3 +43,20 @@ Icon
3743
Network Trash Folder
3844
Temporary Items
3945
.apdisk
46+
47+
# Security scan reports
48+
*.sarif
49+
50+
# Checkov module directory
51+
.external_modules
52+
53+
# Node modules
54+
node_modules/
55+
56+
*.swp
57+
.nyc_output
58+
59+
# Python
60+
**/.venv
61+
**/__pycache__
62+
**/.pytest_cache

CHANGELOG.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,41 @@
1+
## 2.0.0 (02/12/2024)
2+
3+
BREAKING CHANGES:
4+
5+
* It is now possible, with a validation check, to destroy the bootstrap.
6+
* Bootstrap and the example component have been completely rewritten.
7+
* `.terraform` and `backend_tfscaffold.tf` no longer removed during bootstrap cleanup
8+
* Bootstrap and examples now configured to use terraform >= 1.10.0.
9+
* Bootstrap and examples now require terraform > 1.0.0.
10+
* Bootstrap and examples now require AWS Provider ~> 5.79.0.
11+
* tfscaffold tagging default have changed to prefix tag keys with tfscaffold:
12+
13+
FEATURES:
14+
15+
* Bootstraps can now be fully and cleanly destroyed, however only interactively,
16+
requiring a manual text input to confirm.
17+
* A DynamoDB lock table has been added to Bootstrap.
18+
* Bootstrap now uses AWS Provider v4+ S3 Bucket property resources instead
19+
of declaring all configuration in a single bucket resource.
20+
* The example component has been rewritten to reflect modern naming standards,
21+
and idempotency structures.
22+
* An example module has been added, which is called from the example component.
23+
* Example region changed to eu-west-2.
24+
* `bin/docs.sh` has been added to recursively apply terraform-docs
25+
(https://github.com/terraform-docs/terraform-docs) to all directories beneath
26+
the project root that contain a variables.tf file.
27+
* Remove unnecessary compatability log entry for auto-approve.
28+
* Remove unnecessary non-current version transitions from the bootstrap bucket.
29+
30+
BUG FIXES:
31+
32+
* Do not write `.terraform.output.json` after a destroy.
33+
* `-compact-warnings` is no longer passed to `terraform init`
34+
35+
CHORES:
36+
37+
* `.gitignore` updated
38+
139
## 1.10.2 (02/12/2024)
240

341
BUG FIXES:

bin/docs.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
declare script_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";
6+
declare base_path="${script_path%%\/bin}";
7+
find "${base_path}" -name 'variables.tf' -exec sh -c 'terraform-docs markdown table --output-file README.md $(dirname "$1")' sh {} \;

bin/terraform.sh

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
##
99
# Set Script Version
1010
##
11-
readonly script_ver='1.10.2';
11+
readonly script_ver='2.0.0';
1212

1313
##
1414
# Standardised failure function
@@ -438,7 +438,11 @@ declare tf_var_params;
438438

439439
if [ "${bootstrap}" == 'true' ]; then
440440
if [ "${action}" == "destroy" ]; then
441-
error_and_die 'You cannot destroy a bootstrap bucket using tfscaffold, it's just too dangerous. If you're absolutely certain that you want to delete the bucket and all contents, including any possible state files environments and components within this project, then you will need to do it from the AWS Console. Note you cannot do this from the CLI because the bootstrap bucket is versioned, and even the --force CLI parameter will not empty the bucket of versions';
441+
echo -en "\n#####################\n# ALERT ALERT ALERT #\n#####################\n\nDo you *really* want to destroy this bootstrap?\n\nPerforming this action will delete your WHOLE STATE BUCKET (${bucket}) AND ALL ITS CONTENTS FOR ALL ENVIRONMENTS.\nAny state files you have created as part of this tfscaffold project will be IRRECOVERABLY DELETED! Forever!\n\nAcknowledge by typing out this exact sentence, removing all + characters: \"I+am+not+an+idiot,+I+know+what+I+am+doing!\": ";
442+
read destroy_response;
443+
if [ "${destroy_response}" != 'I am not an idiot, I know what I am doing!' ]; then
444+
error_and_die "ABORT ABORT ABORT!! YOU ARE AN IDIOT!!";
445+
fi;
442446
fi;
443447

444448
# Bootstrap requires this parameter as explicit as it is constructed here
@@ -668,14 +672,18 @@ if [ "${bootstrapped}" == 'true' ]; then
668672
# Configure remote state storage
669673
echo "Setting up S3 remote state from s3://${bucket}/${backend_key}";
670674
[ "${lock_table}" == 'true' ] && echo "Using DynamoDB Table for state locking: ${bucket}";
671-
terraform init ${no_color} ${compact_warnings} ${lockfile_or_upgrade} \
675+
terraform init ${no_color} ${lockfile_or_upgrade} \
672676
|| error_and_die 'Terraform init failed';
677+
678+
if [ "${action}" == 'destroy' ] && [ "${destroy_response}" == 'I am not an idiot, I know what I am doing!' ]; then
679+
echo -e "terraform {\n backend \"local\" {}\n}" > backend_tfscaffold.tf;
680+
terraform init -migrate-state -force-copy;
681+
fi;
673682
else
674683
# We are bootstrapping. Download the providers, skip the backend config.
675684
terraform init \
676685
-backend=false \
677686
${no_color} \
678-
${compact_warnings} \
679687
${lockfile} \
680688
|| error_and_die 'Terraform init failed';
681689
fi;
@@ -740,7 +748,6 @@ case "${action}" in
740748

741749
# Support for terraform <0.10 is now deprecated
742750
if [ "${action}" == 'apply' ]; then
743-
echo 'Compatibility: Adding to terraform arguments: -auto-approve=true';
744751
extra_args+=' -auto-approve=true';
745752
else # action is `destroy`
746753
# Check terraform version - if pre-0.15, need to add `-force`; 0.15 and above instead use `-auto-approve`
@@ -795,9 +802,7 @@ case "${action}" in
795802
echo 'yes' | terraform init ${lockfile} || error_and_die 'Terraform init failed';
796803

797804
# Hard cleanup
798-
rm -f backend_tfscaffold.tf;
799805
rm -f terraform.tfstate # Prime not the backup
800-
rm -rf .terraform;
801806

802807
# This doesn't mean anything here, we're just celebrating!
803808
bootstrapped='true';
@@ -809,7 +814,7 @@ case "${action}" in
809814
error_and_die "Terraform ${action} failed with exit code ${exit_code}";
810815
fi;
811816

812-
if [ "${output_json}" == 'true' ]; then
817+
if [ "${output_json}" == 'true' ] && [ "${action}" != 'destroy' ]; then
813818
echo "Writing terraform output to $(pwd)/.terraform.output.json";
814819
terraform output -json -no-color > .terraform.output.json;
815820
fi;

bootstrap/.terraform-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.14.7
1+
latest:^1\.[1-9][0-9]*\.[0-9]\+$

bootstrap/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<!-- BEGIN_TF_DOCS -->
2+
## Requirements
3+
4+
| Name | Version |
5+
|------|---------|
6+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
7+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.79.0 |
8+
9+
## Providers
10+
11+
| Name | Version |
12+
|------|---------|
13+
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.79.0 |
14+
15+
## Modules
16+
17+
No modules.
18+
19+
## Resources
20+
21+
| Name | Type |
22+
|------|------|
23+
| [aws_dynamodb_table.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table) | resource |
24+
| [aws_kms_alias.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
25+
| [aws_kms_key.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
26+
| [aws_s3_bucket.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
27+
| [aws_s3_bucket_lifecycle_configuration.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource |
28+
| [aws_s3_bucket_policy.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
29+
| [aws_s3_bucket_public_access_block.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
30+
| [aws_s3_bucket_server_side_encryption_configuration.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
31+
| [aws_s3_bucket_versioning.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
32+
| [aws_iam_policy_document.default_assumerole](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
33+
| [aws_iam_policy_document.kms_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
34+
| [aws_iam_policy_document.s3_main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
35+
36+
## Inputs
37+
38+
| Name | Description | Type | Default | Required |
39+
|------|-------------|------|---------|:--------:|
40+
| <a name="input_aws_account_id"></a> [aws\_account\_id](#input\_aws\_account\_id) | The AWS Account ID into which we are bootstrapping tfscaffold | `string` | n/a | yes |
41+
| <a name="input_bucket_name"></a> [bucket\_name](#input\_bucket\_name) | The name to use for the tfscaffold bucket. This should be provided from tfscaffold shell, not environment or group tfvars | `string` | n/a | yes |
42+
| <a name="input_component"></a> [component](#input\_component) | The name of the component for the bootstrapping process; which is always bootstrap | `string` | `"bootstrap"` | no |
43+
| <a name="input_environment"></a> [environment](#input\_environment) | The name of the environment for the bootstrapping process; which is always bootstrap | `string` | `"bootstrap"` | no |
44+
| <a name="input_project"></a> [project](#input\_project) | The name of the Project we are bootstrapping tfscaffold for | `string` | n/a | yes |
45+
| <a name="input_region"></a> [region](#input\_region) | The AWS Region into which we are bootstrapping tfscaffold | `string` | n/a | yes |
46+
| <a name="input_tfscaffold_ro_principals"></a> [tfscaffold\_ro\_principals](#input\_tfscaffold\_ro\_principals) | A list of Principals permitted to ListBucket and GetObject for Remote State purposes. Normally the root principal of the account | `list(string)` | `[]` | no |
47+
48+
## Outputs
49+
50+
| Name | Description |
51+
|------|-------------|
52+
| <a name="output_s3_bucket_arn"></a> [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | n/a |
53+
| <a name="output_s3_bucket_id"></a> [s3\_bucket\_id](#output\_s3\_bucket\_id) | n/a |
54+
| <a name="output_s3_bucket_name"></a> [s3\_bucket\_name](#output\_s3\_bucket\_name) | n/a |
55+
| <a name="output_s3_bucket_policy"></a> [s3\_bucket\_policy](#output\_s3\_bucket\_policy) | n/a |
56+
| <a name="output_s3_kms_key_arn"></a> [s3\_kms\_key\_arn](#output\_s3\_kms\_key\_arn) | n/a |
57+
| <a name="output_s3_kms_key_id"></a> [s3\_kms\_key\_id](#output\_s3\_kms\_key\_id) | n/a |
58+
| <a name="output_s3_kms_key_policy"></a> [s3\_kms\_key\_policy](#output\_s3\_kms\_key\_policy) | n/a |
59+
<!-- END_TF_DOCS -->
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
resource "aws_dynamodb_table" "main" {
2+
name = var.bucket_name
3+
hash_key = "LockID"
4+
billing_mode = "PAY_PER_REQUEST"
5+
6+
attribute {
7+
name = "LockID"
8+
type = "S"
9+
}
10+
11+
point_in_time_recovery {
12+
enabled = true
13+
}
14+
15+
server_side_encryption {
16+
enabled = true
17+
kms_key_arn = aws_kms_key.s3.arn
18+
}
19+
20+
tags = merge(
21+
local.default_tags,
22+
{
23+
Name = var.bucket_name
24+
},
25+
)
26+
}

bootstrap/aws_kms_alias.s3.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
resource "aws_kms_alias" "s3" {
2+
name = "alias/s3/${var.bucket_name}"
3+
target_key_id = aws_kms_key.s3.key_id
4+
}
Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ resource "aws_kms_key" "s3" {
33
deletion_window_in_days = 10
44
enable_key_rotation = true
55

6-
policy = data.aws_iam_policy_document.kms_key_s3.json
6+
policy = data.aws_iam_policy_document.kms_s3.json
77

88
# This does not use default tag map merging because bootstrapping is special
99
# You should use default tag map merging elsewhere
10-
tags = {
11-
Name = "tfscaffold Bootstrap S3 Bucket"
12-
Environment = var.environment
13-
Project = var.project
14-
Component = var.component
15-
Account = var.aws_account_id
16-
}
10+
tags = merge(
11+
local.default_tags,
12+
{
13+
Name = "tfscaffold Bootstrap S3 Bucket"
14+
}
15+
)
1716
}

bootstrap/aws_s3_bucket.main.tf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
resource "aws_s3_bucket" "main" {
2+
bucket = var.bucket_name
3+
4+
force_destroy = true
5+
6+
# This does not use default tag map merging because bootstrapping is special
7+
# You should use default tag map merging elsewhere
8+
tags = merge(
9+
local.default_tags,
10+
{
11+
Name = "Terraform Scaffold State File Bucket for account ${var.aws_account_id} in region ${var.region}"
12+
}
13+
)
14+
}

0 commit comments

Comments
 (0)