Bootstrapping Infrastructure-as-Code
Objective
Deploy your first modules using Terragrunt and OpenTofu (Terraform).
Set Up State Management
To begin deploying infrastructure, you will first need both a state backend and a way to encrypt configuration values.
We provide two infrastructure modules that define both those resources:
- tf_bootstrap_resources (the state backend using AWS S3 and DynamoDB)
- aws_kms_encrypt_key (the encryption key using AWS KMS)
Deploying these modules for the first time is a bit cumbersome as they have circular references. 1
We provide a helper script pf-env-bootstrap
that takes care of the initial setup automatically.
For every environment:
-
Open its directory in your terminal (for example,
<environments_dir>/production
). -
Run
pf-env-bootstrap
. -
Confirm the prompt and wait for the bootstrap process to complete.
-
Note that each environment now has
tf_bootstrap_resources
andsops
(aws_kms_encrypt_key
) deployed in its global region.
sops
Terragrunt uses sops to store secret configuration values. This allows you to commit secrets in encrypted form directly to version control which comes with a host of benefits:
-
All of your settings for all infrastructure can now be found in a single location.
-
You do not need a separate change management or CI/CD process for secrets and non-secret values.
-
You will implicitly have an audit log of all changes.
-
You can utilize sops for other git ops activities in addition to deploying infrastructure modules (e.g., performing automatic rotations).
The above bootstrapping process sets up sops to use AWS KMS for the encryption keys (via .sops.yaml
).
KMS provides several benefits:
-
sops uses transit encryption. As a result, the encryption keys never leave the key store. By using KMS, no one in your organization will ever have access to the encryption keys which means you do not need to rotate these keys (and thus all secrets encrypted with them) every time you offboard a member of your organization.
-
KMS allows you to replicate the encryption keys across multiple regions, ensuring you will never lose access.
-
KMS will provide an audit log for every time a secret value is accessed which will augment the
git
commit history that records every time a secret is changed. -
Access to each KMS key will inherit our AWS role-based access control paradigm. As keys are scoped to each environment, access to secrets will automatically align with environment permissions. In other words, users with access to one environment will have access to that environment's secrets but not necessary secrets in other environments.
Let's perform a quick test to ensure sops is working properly.
-
Create a new file at
environments/management/test.yaml
:foo: bar
-
Run
sops -e -i test.yaml
((e)ncrypt (i)n-place). The file contents should be transformed into something like the following:foo: ENC[AES256_GCM,data:JJWK,iv:iF2zywZM3DWObiJCPsaPzETwnlQ1q2lh+zgHfCmk/PM=,tag:m2Ii1IlH05fT/TE4SStWIA==,type:str] sops: kms: - arn: arn:aws:kms:us-east-2:143003111016:key/mrk-955687aaf5124a07837ae4e2a442f8ec created_at: "2024-03-08T17:27:00Z" enc: AQICAHgMz35tnCYOcZgsSkZfKep5SPbKOCK5kzijAQLnZXO3TAHQ6ctemzPMzRenMG2LWQjAAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM4JXy9Gd99htGiu7aAgEQgDuxh107pk18bFU5Q8vzeu1rI+u6+/7s7Xao5DUSE/86Uyvo7USMny58KqJnqUdIvGmj3xYqV5dVMGJxAQ== aws_profile: management-superuser - arn: arn:aws:kms:us-west-2:143003111016:key/mrk-955687aaf5124a07837ae4e2a442f8ec created_at: "2024-03-08T17:27:00Z" enc: AQICAHgMz35tnCYOcZgsSkZfKep5SPbKOCK5kzijAQLnZXO3TAFwqRvjtwaFuBNp9ppYc58OAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMagf0c2ADsKLKXBFQAgEQgDsP5rqepGI3MvwhtRVxmS6/hyWWDyqEbOIxYS1WybNinBggkfKNRTw0Z8vob2tuSg5ZUeRpABry/C84PA== aws_profile: management-superuser gcp_kms: [] azure_kv: [] hc_vault: [] age: [] lastmodified: "2024-03-08T17:27:01Z" mac: ENC[AES256_GCM,data:JwzOMNKLn5ETSQY6QiGmxkxLqzX6buvs5OccPZ/BVOdfvgUW8vsgv34l6UiOidHq71GeGo5G9sPHeNiFGWPgqBMNU/qUugt9qldHs5k/oo6e4wFIzOxkIT2NZ1CmLw/9vxw8ZWskWX43XPC8tQlu9NmxE65G7NSDTaHBIFVVG44=,iv:L1pBiv+2y066RNr9qppBjuCjgLkaui6yOGJ6iPSCg8w=,tag:kmN5KBJ7gkhvcnkV67/xjw==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1
This file is now safe to commit to version control.
-
Run
sops -d test.yaml
((d)ecrypt). Notice the original file contents should have been output to your terminal. -
Delete the file.
Deploy the AWS Account Module
Now that you have both the state backends and encryption keys established, we can start deploying infrastructure. The first resource will be the aws_account module. This module adds some critical metadata to your account including setting up its alias and contact information.
Let's deploy it now:
-
In every environment's
global
region, create anaws_account
directory. -
Add a
terragrunt.hcl
to the directory that looks like this.Note the addition of this line
secrets = yamldecode(sops_decrypt_file("secrets.yaml"))
which demonstrates how terragrunt and sops integrate with one another.Replace the values as needed for your organization. Feel free to also change what values are secret or not.
-
Add a
secrets.yaml
to the directory:address_line_1: 1234 Platform Engineering Way city: Developer Junction district_or_county: Marion state_or_region: CA postal_code: "12345" phone_number: "+15555555555" email_address: spam@panfactum.com
Adjust the values as necessary.
-
Run
sops -e -i secrets.yaml
to encrypt the file. -
Run
pf-tf-init
in the module directory to enable the required providers. -
Run
terragrunt apply
in the module directory.
Next Steps
Now that you are set up to deploy infrastructure-as-code, we are ready to setup DNS.
Footnotes
-
Deploying infrastructure modules requires a state backend, and the state backend is itself an infrastructure module. ↩