Skip to content

Commit

Permalink
compute the manager prefix
Browse files Browse the repository at this point in the history
  • Loading branch information
Oliver Schoenborn authored and Oliver Schoenborn committed Feb 7, 2023
1 parent 7fd3778 commit 071cdbf
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 90 deletions.
95 changes: 38 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,65 +56,46 @@ module.

## Renaming the tfstates bucket

It may happen that the backends bucket name needs changing, eg following some naming policy
changes in your organization. AWS does not provide a means of doing this directly.
Here is the procedure that I use:

1. set `this_tfstate_in_s3` to false and comment out the items in `stacks_map`
2. run `terraform apply` in your root module that calls the `multi_stack_backends` module. This will
delete all the `backend.tf` files that had been created by this root module.
3. run `terraform init -migrate-state` (optionally with the `-force-copy` arg) to migrate the
manager's tfstate out of s3
4. run that same command for all the sub-stacks (ie root modules) that you commented out. I use a
loop
like `for ss in paths-to-substacks; do echo $ss; cd $ss; terraform init ...; cd - > /dev/null; done`
5. create a local backup of the bucket: `aws s3 cp s3://BACKENDS_BUCKET_NAME . --recursive`
6. now that all tfstates have been copied from the bucket to localhost, and the bucket has been
backed up just in case, you can change the value of `backends_bucket_name` given to this module
and run `terraform apply`
7. undo step 1: set `this_tfstate_in_s3` to true
8. undo step 2: uncomment the items from that step
9. undo step 3 by re-running that exact same command `terraform init -migrate-state`
10. undo step 4 by re-running that exact same command for all sub-stacks
11. If you had any `terraform_remote_state`, point them to the new location
12. running `terraform apply` in any of the sub-stacks should show no init and no changes needed

Here is a different procedure that has fewer steps, some are simpler than previous procedure, and
you don't need to delete the original bucket until the very end. It will be especially useful if you
have many sub-stacks. HOWEVER I have not tested yet, so please test it first.

1. WARN your team that no one can use terraform on this code, and ENSURE THAT TERRAFORM PLAN SHOWS
NO CHANGES NEEDED
It may happen that the backends bucket (and therefore its replica) need to be renamed, eg following
some naming policy changes in your organization. AWS does not provide a means of doing this
directly. The following procedure is one that I use.

1. WARN your team that no one can use terraform on this terraform module, and ENSURE THAT
TERRAFORM PLAN SHOWS NO CHANGES NEEDED
2. change the value of `backends_bucket_name` (this new value is referred to here
as `NEW_BACKENDS_BUCKET_NAME`)
3. determine the path to the manager module in your tfstate (look at your code or output of
3. determine the path to the manager module in your tfstate. The easiest is to run `terraform state list | grep aws_s3_bucket look at your code or output of
terraform state list)
4. run `script/rename-backends-manager-bucket.sh NEW_BUCKET_NAME MODULE_PATH`
5. If you had any `terraform_remote_state` in your sub-stacks, point them to the new location
6. running `terraform apply` in any of the sub-stacks should show no init and no changes needed
7. manually delete the 2 old buckets
4. run `script/rename-backends-manager-bucket.sh NEW_BUCKET_NAME MODULE_PATH` (per the license
terms, this is provided as-is without any warranty - you assume all responsibility!)
5. If you had any `terraform_remote_state` in your sub-stacks, point them to the new bucket name
6. run `terraform apply` in any of the sub-stacks, this should show no init and no changes needed
7. manually delete the 2 old buckets when you are satisfied

## Upgrades

### 0.6.x to 1.0

- The module no longer assumes that IAM policies for the tfstate access are required. These policies
are not needed by the module, they are merely a convenience (making it easy for you to control
access to the tfstates stored in the backend bucket). To generate the policies as in 0.6, set one
or both `create_tfstate_access_polic*` variables to true, depending on your needs.
- The module no longer assumes a default set of tags. Only `var.extra_tags` is used, and
by default it is empty. The terraform plan will clearly say what the old values were, if you
need some of them.
- The module now requires a value for `backends_bucket_name`. This won't likely affect you because
you almost certainly had to specify a name anyway, due
to [AWS uniqueness constraints on S3 bucket names](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html).
But if you were able to not specify it (indicating you were probably the first to use that default
name in yours AWS partition), you must now add `backends_bucket_name = "tfstate-s3-backends"`.
However, I highly recommend that you rename your backends bucket to something unique to you or
your organization / employer. Eg `ORG_NAME-GROUP_NAME-tfstate-backends`. The procedure to do this
is in a separate section of this readme.
- The module no longer defines providers, as recommended in the terraform documentation. Therefore,
before applying,
1. (Optional) The module no longer assumes that IAM policies for the tfstate access are required.
These policies are not needed by the module, they are merely a convenience (making it easy for
you to control access to the tfstates stored in the backend bucket). To generate the policies as
in 0.6, set one or both `create_tfstate_access_polic*` variables to true, depending on your
needs.
2. (Optional) The module no longer assumes a default set of tags. Only `var.extra_tags` is used, and
by default it is empty. The terraform plan will clearly say what the old values were, if you
need some of them.
3. Rename variable `this_tfstate_in_s3` to `manager_tfstate_in_s3`
4. The module now requires a value for `backends_bucket_name` (it no longer provides a default).
This won't likely affect you because you almost certainly had to specify a bucket name anyway,
due
to [AWS uniqueness constraints on S3 bucket names](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html).
But if you were able to not specify it (eg if you were the first to use that default bucket name
in yours AWS partition), you must now add `backends_bucket_name = "tfstate-s3-backends"`.
However, I highly recommend that you rename your backends bucket to something unique to you or
your organization / employer. Eg `ORG_NAME-GROUP_NAME-tfstate-backends`. The procedure to do this
is in a separate section of this readme.
5. The module itself no longer defines providers, as recommended in the terraform documentation.
Rather, it requires they be passed in to the module. Therefore, before applying,
- ensure you have these two blocks (as done
in `examples/simple/tfstate-s3-manager/terraform.tf`):
```hcl
Expand All @@ -136,12 +117,12 @@ have many sub-stacks. HOWEVER I have not tested yet, so please test it first.
aws.replica = aws.tfstate_backends_replica
}
```
- The module now uses the `aws_s3_bucket_*` resources instead of the inline blocks that the AWS
provider has deprecated, like acl, server-side encryption, etc. This will cause terraform to
plan generating those resources, unless you import them into your tfstate. I use the bash script
`scripts/upgrade-to-1.0.sh`. I recommend echoing the terraform command that will be run to
verify that it makes sense, as there are too many possibilities to say for sure that the
script will work as-is.
6. (Optional) The module now uses the `aws_s3_bucket_*` resources instead of the inline blocks that
the AWS provider has deprecated, like acl, server-side encryption, etc. This will cause terraform
to plan generating those resources, unless you import them into your tfstate. I use the bash
script `scripts/upgrade-to-1.0.sh`. I recommend testing it first: open the script in an editor
and add an "echo" in front of the terraform commands to verify that it makes sense, as there are
too many possibilities to say for sure that the script will work as-is.
## Acknowledgements
Expand Down
68 changes: 35 additions & 33 deletions scripts/rename-backends-manager-bucket.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,16 @@ usage() {
echo
echo "ERROR: missing command line arguments"
echo
echo "Usage: $(basename $0) NEW_BUCKET_NAME BACKENDS_MODULE_PATH [CURRENT_BUCKET]"
echo "Usage: $(basename "$0") NEW_BUCKET_NAME [CURRENT_BUCKET]"
echo
echo "- BACKENDS_MODULE_PATH: can be determined by looking at the terraform state list"
echo "- CURRENT_BUCKET: only needed if there is no backend.tf (eg it has already been deleted)"
echo "- NEW_BUCKET_NAME: name for new backends bucket (must match 'backends_bucket_name'"
echo " that you specified in your tf code)"
echo "- CURRENT_BUCKET: current name of backends bucket (only needed if there is "
echo " no backend.tf, eg it has already been deleted)"
echo
echo "Example:"
echo
echo " $(basename $0) new_bucket module.tfstate_manager"
echo
echo "which assumes you have a 'module \"tfstate_manager\"' block in your"
echo "root module, with its 'source' pointing to schollii/multi-stack-backends/aws"
echo "(or its equivalent github URL)"
echo " $(basename "$0") new_bucket"
echo
}

Expand All @@ -26,47 +24,51 @@ if [[ -z $new_bucket_name ]]; then
usage
exit 1
fi
manager_state_prefix=$2 # ex: "module.tfstate_backends.module.multi_stack_backends"
if [[ -z $manager_state_prefix ]]; then
usage
exit 2
fi

current_bucket=$3
if [[ -z $current_bucket ]]; then
current_bucket_name=$2
if [[ -z $current_bucket_name ]]; then
if [[ ! -f ./backend.tf ]]; then
echo "ERROR: No ./backend.tf file found. You must specify the bucket name as third arg."
exit 8
fi

current_bucket=$(sed -En 's/ *bucket *= *"([a-z0-9][a-z0-9.-]+[a-z0-9])"/\1/p' backend.tf)
if [[ -z $current_bucket ]]; then
current_bucket_name=$(sed -En 's/ *bucket *= *"([a-z0-9][a-z0-9.-]+[a-z0-9])"/\1/p' backend.tf)
if [[ -z $current_bucket_name ]]; then
echo "ERROR: Could not determine current bucket from ./backend.tf."
exit 10
fi

echo "Current bucket is: $current_bucket"
read -p "Is this correct (y/n)? " -n 1 -r
echo
if [[ ! $REPLY =~ ^(y|Y)$ ]]; then
exit 0
fi
fi

if [[ $current_bucket == $new_bucket_name ]]; then
if [[ $current_bucket_name == "$new_bucket_name" ]]; then
echo "ERROR: current and new bucket names are the same!!"
exit 5
fi

manager_state_prefix=$(terraform state list | sed -En 's/(.*)\.aws_s3_bucket\.tfstate_backends/\1/p')
if [[ -z $manager_state_prefix ]]; then
echo "ERROR: could not determine which tf resource is backends bucket"
exit 2
fi

# Confirm:
echo "Current bucket: $current_bucket_name"
echo "New bucket: $new_bucket_name"
echo "Manager tfstate prefix: $manager_state_prefix"
read -p "Is this correct (y/n)? " -n 1 -r
echo
if [[ ! $REPLY =~ ^(y|Y)$ ]]; then
exit 0
fi

echo
echo "Moving manager tfstate to local host, without confirmation from user"
rm -f backend.tf
terraform init -migrate-state -force-copy

echo
echo "Making terraform forget about the 2 current buckets:"
terraform state rm $manager_state_prefix.aws_s3_bucket.tfstate_backends
terraform state rm $manager_state_prefix.aws_s3_bucket.replica
terraform state rm "$manager_state_prefix.aws_s3_bucket.tfstate_backends"
terraform state rm "$manager_state_prefix.aws_s3_bucket.replica"

echo
echo "Running terraform apply"
Expand All @@ -78,16 +80,16 @@ terraform apply

# copy tfstates to the new bucket just created, except the old manager state:
echo "Copying the stacks tfstates"
aws s3 cp "s3://$current_bucket" "s3://$new_bucket_name" --recursive
aws s3 rm "s3://$new_bucket_name/_manager_" --recursive
aws s3 cp "s3://$current_bucket_name/" "s3://$new_bucket_name/" --recursive
aws s3 rm "s3://$new_bucket_name/_manager_/" --recursive

# move the manager's tfstate back into s3
terraform init -migrate-state -force-copy

echo
echo "DONE!"
echo "NOTE: VERIFY that all tfstates have been properly transfered. Eg, run 'terraform apply'"
echo "in all substacks: no changes should be planned."
echo "NOTE: VERIFY that all tfstates have been properly transfered. Eg, run"
echo "'terraform plan' in all substacks: no changes should be planned."
echo "ONCE YOU ARE SATISFIED, YOU CAN MANUALLY DELETE THE TWO PREVIOUS BUCKETS."
echo "Example: aws s3 rb \"s3://$current_bucket\" --force"
echo "Example: aws s3 rb \"s3://${current_bucket}-replica\" --force"
echo "Example: aws s3 rb \"s3://$current_bucket_name\" --force"
echo "Example: aws s3 rb \"s3://${current_bucket_name}-replica\" --force"
1 change: 1 addition & 0 deletions test/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,5 @@ func TestTerraformBasicExample(t *testing.T) {
// website::tag::3::Check the output against expected values.
// Verify we're getting back the outputs we expect
assert.Equal(t, "us-east-1", outputs.Tfstate_backends_bucket.Region)
assert.Equal(t, "us-west-1", outputs.Replica_bucket.Region)
}

0 comments on commit 071cdbf

Please sign in to comment.