Now that you understand how the Reference Architecture works, the next step is to begin migrating your apps to it. The
process we recommend is as follows:
Most "big bang" migrations—where you try to pick up everything and move it over to a new cloud (AWS), new tools
(Terraform, Docker, Packer), and new processes (CI, CD, DevOps), all in one huge step—almost always fail. The only way
to succeed at a big migration is to do it incrementally. Note that "incrementally" doesn't mean you just chop up the
work into small parts; it means you chop it up into small parts so that each part is worth doing, even if the other
parts never happen.
If the only value you get from the migration is at the very end of a long process (and lets be honest, migrations
always take longer than you expect), then there's a good chance management will lose patience, developers will get
frustrated, funding for the project will be cut, corners will be cut, and the project will be a disaster.
Therefore, you want to arrange the work so that you can complete one small piece at a time and get value out of that
piece immediately. That way, management stays happy, developers stay motivated, and the project is worth doing, even
if the end of the migration is still a long ways off.
You'll find a number of suggestions in this document on how to do the migration incrementally. The short version is:
Migrate one or a small number of apps.
Allow the apps to keep using the data stores and other dependencies in your original data center via a VPN
connection.
Test.
Once things are working, switch over the DNS config to point to the apps running in AWS.
Repeat the previous steps with the rest of your apps.
Once all apps have been migrated, begin migrating your data stores.
Read on for the long version!
Set up account access
The first step to migrating to the Reference Architecture is to make sure everyone on your team has access to your AWS
accounts. This consists of the following items:
We strongly recommend that everyone on your team reads through the following:
Gruntwork Security Best
Practices,
doc, which covers critical concepts such as how to securely store secrets, how to lock down servers, and how to
securely use AWS.
Each of your AWS accounts has a root user. Whoever
created the account should have access to the root user. If you created the account using the gruntwork
CLI or AWS Organizations, then
you will know the email address associated with the root user, but not the password. To get the password:
If you had previously signed into some other AWS account, click "Sign-in using root account credentials."
Enter the email address and click "Forgot your password" to reset the password.
Check the email address associated with the root user account for a link you can use to create a new password.
Please note that the root user account can do just about anything in your AWS account, bypassing almost all security
restrictions you put in place, so you need to take extra care with protecting this account. We very strongly
recommend that you:
Use a strong password. Preferably 30+ characters, randomly generated, and stored in a secrets manager.
Only use the root user to create an IAM User for yourself and then switch to the IAM User for all other operations
thereafter. You should NOT use the root user account on a day-to-day basis; it's there only for very rare
circumstances (e.g., if you get locked out of your IAM User account).
IAM users
You should create an IAM User for each
developer in the security account. The Reference Architecture enforces a
password policy: see
the iam-user-password-policy module in infrastructure-live-multi-account-acme for the details. We also strongly recommend
requiring every developer to enable multi-factor auth
for their IAM users.
IAM groups
Give each IAM User permissions by adding them to the appropriate IAM
Groups. The following IAM Groups may be of especial
interest (see the iam-groups module
for a more complete list):
full-access: This gives full admin privileges within this AWS account. Use this only for a small number of
trusted admins.
read-only: This gives read-only access within this AWS account. This is especially useful for production
accounts where you want to be able to debug, but not necessarily make changes.
_account.xxx-full-access: This gives full admin privileges within the AWS account xxx. Use this only for a
small number of trusted admins. See Accounts and Auth for how to switch between AWS
accounts.
_account.xxx-read-only: This gives read-only access to the AWS account xxx. This is especially useful for
production accounts where you want to be able to debug, but not necessarily make changes.
_account.xxx-openvpn-users: This allows users to use openvpn-admin
to request VPN certificates in account xxx. See SSH and VPN for more info.
ssh-grunt-users: Users in this group will be able to access servers via SSH. See SSH and VPN for
more info.
ssh-grunt-sudo-users: Users in this group will be able to access servers via SSH with sudo permissions. See
SSH and VPN for more info.
SSH Access
We have configured ssh-grunt on every
EC2 Instance, so developers will be able to SSH to the servers using their own usernames and SSH keys. See
SSH and VPN for instructions.
VPN Access
All the EC2 Instances run in private subnets, so to access them, you must first connect via VPN. See SSH and
VPN for instructions.
This will involve setting up Virtual Private Gateway,
and you will need to configure the VPCs to propagate routes for these Gateways using the private_propagating_vgws
and persistence_propagating_vgws variables.
Deploy your apps
Once account access is set up, the next step is to migrate your apps—or even just a single app—to the Reference
Architecture, while continuing to use whatever data stores and other dependencies you already have deployed in your old
data center (e.g., via a VPN connection). Once you can get one app running in AWS successfully, you can use it as a
template to move over the others, getting value from the migration right away (i.e., more security, scalability, etc)
without having to wait weeks or months to move everything over (incrementalism!).
To move over an app, you'll need to do the steps listed below. The Reference Architecture includes two sample apps
(sample-app-frontend-multi-account-acme and sample-app-backend-multi-account-acme) that show how to implement all of these steps, so use them as a
reference!
Note that you do NOT need to do all of these steps at once! You can take care of just a few of them, deploy the app
into the Reference Architecture, see that some basic things work, move on to the next few steps, and so on.
Each app should expose a health check endpoint (e.g., /health) that the load balancer can use to see if the app is
running and ready to receive requests. The health check endpoint should return a 200 OK when the app is healthy.
The definition of "healthy" depends on the app; for some apps, healthy might mean the app has booted; for others,
healthy may mean the app has booted, can talk to databases, and has warmed up its cache.
Update the load balancer config
Now that your app is packaged and has a health check endpoint, you can try deploying it! The sample apps are deployed
via Terraform code in the infrastructure-live-multi-account-acme repo, under the services folder (e.g.,
stage/services/sample-app-frontend-multi-account-acme). Make a copy of that folder and customize the terragrunt.hcl file within it
to deploy your app instead. This will include specifying which paths in the load balancer your app should claim.
This is because the Reference Architecture uses a single Application Load Balancer
(ALB) for several apps in
each environment, and each of those apps can claim certain paths (e.g., /foo goes to app foo, /bar goes to app bar)
and/or certain domain names (e.g., foo.acme.com goes to app foo, bar.acme.com goes to app bar). If you want your
app to handle all paths, you can use regular expressions (e.g., *).
Set up config files for each environment
If your app needs to be configured differently in each environment (e.g., in stage and prod), we recommend creating
config files for each environment, checking those configs into version control, and packaging them with the app. That
way, config is updated, versioned, tested, and deployed exactly like the rest of your code. This tends to be easier
to reason about and far less error prone than storing configs externally (e.g., in a DB).
The sample apps use JSON files for config, but any format supported by your apps will be fine, so long as it is
file-based. See the config folder in the sample apps for an example, and check out the bin/run-app.sh script for
how the sample apps select the proper config file for the current environment.
Encrypt secrets
If your apps need access to secrets, such as the password to a database, we recommend encrypting those secrets with
KMS and adding the ciphertext to your config files. Your apps can then use IAM Roles to
decrypt the secrets just before booting.
If your apps talk to a relational database, you will need a way to evolve the database schema over time. The best way
to do that is to use a schema migration tool such as Flyway or
Liquibase to define the schema migrations as code, check that code into version control,
package it with your apps, and have your apps apply the schema migrations just before booting (Flyway and Liquibase
will use locks to ensure there are no concurrency issues).
See the sql folder and bin/run-app.sh of sample-app-backend-multi-account-acme for an example.
Configure service discovery
If you need to run multiple apps ("microservices") that talk to each other, those apps will need a way to discover each
other's IP and port. In the dynamic world of AWS, IPs and ports change all the time, so you don't want to hard code
them. Instead, use one of the following options:
Use an internal load balancer. Each of your apps can register at a different path with the load balancer, and each
app gets the load balancer domain name as an environment variable (via Terraform), so, for example, you can talk to
app foo by sending a request to <LOAD_BALANCER_DOMAIN_NAME>/foo and to app bar by sending a request to
<LOAD_BALANCER_DOMAIN_NAME>/bar. This is how sample-app-frontend-multi-account-acme talks to sample-app-backend-multi-account-acme.
For Docker containers running in ECS, you can use ECS Service
Discovery. As of May, 2018,
Gruntwork has not yet added support for this, but it should be added to the ECS
modules soon.
Use a tool such as Consul. This does require running extra infrastructure, which is made
easier via the Gruntwork Consul module (not currently part of
the Reference Architecture).
Set up static content
The best way to serve CSS, JS, images, and fonts is by putting them in an S3 bucket and
using CloudFront as a CDN in front of it. Your Reference Architecture may already
contain an example of this under the services/static-website folder. Your CloudFront distribution will have its own
domain name (e.g., static.<your-company>.com), so you'll need to ensure that any code of yours that links to static
content (e.g., server-side rendered HTML) makes it possible to configure a different static content domain name in
each environment.
Configure CI and CD
The sample apps have Continuous Integration (CI) and Continuous Delivery (CD) configured for them. A CI job will run
after every commit to build, test, and package the apps, and, for certain commits (e.g., to specific branches or with
specific tags), the CI job will automatically deploy the app to stage or prod.
Once you have your apps running in AWS, the next step is to migrate the data stores they depend on. We again recommend
doing this incrementally, moving over one data store at a time, if possible. There are two ways to move over your data
stores:
Take a downtime
Do a zero-downtime migration
Option #2 typically takes 100x longer (this is NOT an exaggeration) than option #1 and has a much higher risk of
data loss or data corruption. If at all possible, we very strongly recommend taking a brief downtime to do the
migration, as it will make your life much easier.
If you are migrating to the Reference Architecture from another AWS account, and in that account you are using an
AWS-managed data store such as RDS or ElastiCache,
the migration process (with downtime!) is:
Put your app in read-only mode or take a downtime.
Share the snapshot with the prod account where the Reference Architecture is deployed (e.g., see Sharing a DB
Snapshot).
Deploy a new data store in the Reference Architecture account from the snapshot (e.g., set the
snapshot_identifier parameter in the rds module).
Bring your app back up.
Migrating from external data stores
If you are migrating data stores from non-AWS managed systems (e.g., from your own data center), the process is the
same as in the previous section, except instead of using the snapshotting mechanism built into AWS, you will need to
use one of these mechanisms instead:
Use your data store's native snapshot functionality (e.g., mysqldump)
to make a backup of your database, copy the backup into AWS (e.g., into an S3 bucket), and fire up an EC2 Instance
that connects to the data store and loads the backup into it.
Note that all of these assume at least some amount of downtime!
If you absolutely must do a zero-downtime migration
If you absolutely cannot take any downtime whatsoever (unlikely, as you most likely have unplanned outages anyway!),
things get a lot more complicated. The typical strategy is:
Take an initial snapshot of your data store and load it into AWS. The idea is to move most of the data over early on
and then "catch up" whatever new data is written in the following steps.
Find a way to send all new writes to both your original data store and the new one in AWS.
Once the new data store has "caught up", start using it directly instead of the old database.
Step #2 is the tricky one, as dual writes are notoriously error prone. For example, what happens if the process doing
dual writes succeeds with the first write, but crashes before the second? What if it sends the requests to both data
stores, but one of them is temporarily down? What if both requests make it to the data stores, but then a transaction
gets rolled back? See why dual writes are a bad
idea
for more info.
The most common solutions to these problems are:
Use a change capture system. This is a system that effectively replicates your data store's underlying log: you
write to the original data store, and if this write has succeeded and been committed, it triggers an "event" in the
change capture system, which an event processor then writes to the second data store. For example,
see databus and bottledwater-pg.
Send all writes initially to a log system such as Apache Kafka. You then have one
Kafka consumer that writes the messages to your original data store and another that writes it to the new data store.
Kafka guarantees at-least once delivery, so as long as the database writes are idempotent, this ensures no data
will be lost.
Whatever option you go with, we strongly recommend adding "integrity checking" to verify no data was lost or corrupted.
Some example integrity checks:
Count the number of rows in each table and make sure they are identical in both data stores.
Perform a checksum or hash on all the data in a table (or some randomly selected subset if there's too much data)
and make sure you get identical values in both data stores.
Configure monitoring and alerting
Once your apps and data stores are working, you'll want to make sure you have the following monitoring and alerting in
place:
Make sure all the metrics you expect show up in CloudWatch. All AWS services
automatically send metrics to CloudWatch and the Reference Architecture includes a few important ones that are not
available by default (e.g., memory and disk space usage on EC2 Instances). Create
Dashboards for the
most important metrics, such as CPU usage in your ECS cluster or request count in the ALB.
Note that the Reference Architecture uses CloudWatch for metrics as it's built-into AWS, very inexpensive, and requires
very little work to maintain. However, the CloudWatch UI is fairly basic, so if you need more powerful tools for slicing
and dicing your metrics, consider tools such as DataDog,
New Relic, or Prometheus. If you'd like help with this, please reach
out to support@gruntwork.io.
Alerts
Configure alerts to go off for key metrics. All the services in the Reference Architecture have basic alarms
configured directly in the Terraform code, such as low disk space alarms on RDS DBs and high CPU usage alarms for the
ECS cluster. All of the Terraform modules allow you to tweak the settings for these alarms (e.g., should the CPU
usage alarm go off at 80% or 90%?), so take some time to go through each module in infrastructure-modules-multi-account-acme and
make sure the settings match your use cases so you don't have too many false positives or negatives.
Logs
The logs from all servers get automatically sent to CloudWatch
Logs. The user-data.sh scripts
in infrastructure-modules-multi-account-acme call run-cloudwatch-logs-agent.sh (from the cloudwatch-log-aggregation-scripts
module)
to configure which log files get sent to CloudWatch. By default, syslog is sent to CloudWatch on every server, and
ECS Services are configured to send their logs to syslog. Make sure you can find the logs for your apps there
before you go live!
Note that the Reference Architecture uses CloudWatch Logs as it's built-into AWS, very inexpensive, and requires
very little work to maintain. However, the CloudWatch UI is fairly basic, so if you need more powerful tools for slicing
and dicing your log data, consider tools such as ELK (as of May, 2018, the ELK
support is under development in the package-elk repo and should be
ready soon), Loggly, and Papertrail. If you'd like help with
this, please reach out to support@gruntwork.io.
Test
As you go through the process of migrating your apps and data stores, you should continuously be testing your
infrastructure to make sure things are working. Here are the key types of tests to perform:
Manual testing: Hit URLs by hand, look at log files, and check the metrics. You already know how to do this!
Automated testing: We strongly recommend writing automated tests that (a) deploy your infrastructure into an
AWS account dedicated for testing, (b) check the infrastructure works as expected, and (c) tears the infrastructure
back down at the end of the test. You can use the Terratest library
to write these tests.
Load testing: You should make sure your code performs well under the kind of loads you expect in production.
Use tools such as Apache Bench,
JMeter to throw traffic at your infrastructure and when things fall over, make sure
your monitoring and alerting can help you find the bottlenecks! Load testing across several different EC2 Instance
Types can also be a good way to find the sweet spot between price and performance (see How Netflix Tunes EC2
Instances for Performance).
Security testing: Bring in an outside firm to pen test and audit your code to look for security vulnerabilities.
Gruntwork customers have done this several times with the Reference Architecture already, and we've fixed all issues
that were found, but if you find any new vulnerabilities, please report them to support@gruntwork.io!
Switch over DNS
The Reference Architecture is typically deployed with "placeholder" domain names (e.g., your-company-aws.com) to make
testing easy and safe. Once your apps start passing tests, you can switch over your real domain names to point to
the Reference Architecture as follows:
If you have multiple AWS accounts (e.g., dev, stage, prod), we recommend that each one use its own, completely
separate domain names (e.g., my-company-dev.com, my-company-stage.com, etc.). This reduces the chances of
accidental errors (e.g., breaking prod DNS entries while trying to make changes to stage) and makes it harder for
attackers to do damage (e.g., using cookies from pre-prod environment in a prod environment).
Before switching the domain names on your apps, make to use AWS Certificate Manager
(ACM) to request TLS certificates for your prod domain names and
associate those certificates with your Load Balancers and CloudFront distributions. ACM is a great choice for
TLS certificates, because they are (a) free and (b) issue in 1-2 minutes for domain names you own in Route 53, and
(c) renew automatically!
To switch over your ALBs or ECS services to use different domain names, simply update the domain_name variables in
the corresponding terragrunt.hcl.
{"treedata":{"name":"root","toggled":true,"children":[{"name":".gitignore","path":".gitignore","sha":"1c27fc6013cba46cd301a7c8bf951694670153a3"},{"name":"CODEOWNERS","path":"CODEOWNERS","sha":"6bddb3ff6e1b3dfaba7cf180e56bca12c245be56"},{"name":"README.md","path":"README.md","sha":"45d75f99aefaa9a2d008b223da04bc26453ef651"},{"name":"_docs","children":[{"name":"01-architecture-overview.md","path":"_docs/01-architecture-overview.md","sha":"115a05d08f3a431a19e5aa2596c079619ae66dab"},{"name":"02-whats-deployed.md","path":"_docs/02-whats-deployed.md","sha":"9dc8a401caf24896ce00a8087bfe32c7af99d2d2"},{"name":"03-security-compliance-compatibility.md","path":"_docs/03-security-compliance-compatibility.md","sha":"9342617f42adb28e440cc2161f3fee56205c150e"},{"name":"04-how-code-is-organized.md","path":"_docs/04-how-code-is-organized.md","sha":"3b340de506525633e1f7333a1e9ac9a5565a88e3"},{"name":"05-dev-environment.md","path":"_docs/05-dev-environment.md","sha":"c8b494aed802b623f7891047b6cba633d8ab5fa7"},{"name":"06-ci-cd.md","path":"_docs/06-ci-cd.md","sha":"b6c2a7d7cde7471fb08bff5dcf68c40156db68d5"},{"name":"07-monitoring-alerting-logging.md","path":"_docs/07-monitoring-alerting-logging.md","sha":"619c810c6e60418b3a46fa3d903bc76dc6d48e41"},{"name":"08-ssh-vpn.md","path":"_docs/08-ssh-vpn.md","sha":"9fe83afbd3d6116a4f3faff8923a81cd37ff91c7"},{"name":"09-accounts-and-auth.md","path":"_docs/09-accounts-and-auth.md","sha":"6b0472241644ffc79556e60d582ff1edb80f0554"},{"name":"10-gruntwork-tools.md","path":"_docs/10-gruntwork-tools.md","sha":"d08b1fe7cfbb9ad91155bfff9e3a05525c39c127"},{"name":"11-deploying-a-docker-service.md","path":"_docs/11-deploying-a-docker-service.md","sha":"c735be4ee94e76cc55b48a21039dfec44e6a5d51"},{"name":"12-migration.md","path":"_docs/12-migration.md","sha":"464cadf6e05d5ffd44e569c0d866b5c2cf5f42e9","toggled":true},{"name":"13-deploying-the-reference-architecture-from-scratch.md","path":"_docs/13-deploying-the-reference-architecture-from-scratch.md","sha":"fa362071f460f7df7331645d6e5052e8cd20c30b"},{"name":"14-undeploying-the-reference-architecture.md","path":"_docs/14-undeploying-the-reference-architecture.md","sha":"c6dcaae7266ead56d539b1816a5cfe2988412fe1"},{"name":"15-adding-new-environments-regions-and-accounts.md","path":"_docs/15-adding-new-environments-regions-and-accounts.md","sha":"c01188a1539e93ed2773a1b799b3b0f8e7b2045e"},{"name":"README.md","path":"_docs/README.md","sha":"ddb9fe83eb2fcad91e82771ad276dd0bdba40cb2"},{"name":"_images","children":[{"name":"cw-logs-1.png","path":"_docs/_images/cw-logs-1.png","sha":"84c86f014751844fbd777b5139ed61f749b5ed32"},{"name":"cw-logs-2.png","path":"_docs/_images/cw-logs-2.png","sha":"9a0a80b20490fdc1b9014040cc0bbc87c9cf6f68"},{"name":"cw-logs-3.png","path":"_docs/_images/cw-logs-3.png","sha":"bda49dc4e947658e0ceb9ba592b4e314d9db61e9"},{"name":"cw-logs-4.png","path":"_docs/_images/cw-logs-4.png","sha":"54bcc44c4b0701620b7f20c4e6fc0a9fd8f38049"},{"name":"ecs-console-1.png","path":"_docs/_images/ecs-console-1.png","sha":"afe452278d5f107e6ec225a235c587de7cb53510"},{"name":"ecs-console-2.png","path":"_docs/_images/ecs-console-2.png","sha":"40609b98015d781b9e1de801c131fadc323337ae"},{"name":"ecs-console-3.png","path":"_docs/_images/ecs-console-3.png","sha":"87ad40d291b7e9e6f6caa0389b846392bdb93ee0"},{"name":"ref-arch-full.png","path":"_docs/_images/ref-arch-full.png","sha":"8c17eef52be06757553a1f3ee4e387e6dc820016"},{"name":"ref-arch-icon.png","path":"_docs/_images/ref-arch-icon.png","sha":"05876962e6877df911674237ca1b793d9f4f04b3"},{"name":"terraform-code-provenance.png","path":"_docs/_images/terraform-code-provenance.png","sha":"e2a9d6bfbd8b963b057d4341dd0ec93e3823d834"}]}],"toggled":true},{"name":"dev","children":[{"name":"_global","children":[{"name":"README.md","path":"dev/_global/README.md","sha":"d1b8a96c00211751f079fa13cac1b3417d29bf09"},{"name":"cloudtrail","children":[{"name":"README.md","path":"dev/_global/cloudtrail/README.md","sha":"65aac5742b3dc183d11a7d83a31ae69afe5df2e5"},{"name":"terragrunt.hcl","path":"dev/_global/cloudtrail/terragrunt.hcl","sha":"fb19b4438de2ba919d17f3e4a6ccb3c9f2517f26"}]},{"name":"iam-cross-account","children":[{"name":"README.md","path":"dev/_global/iam-cross-account/README.md","sha":"6bf06985be74cf68085a78728c41cf88fc354141"},{"name":"terragrunt.hcl","path":"dev/_global/iam-cross-account/terragrunt.hcl","sha":"96f6b058d3db5871b45cd9e9c05d2387f7fa8340"}]},{"name":"iam-user-password-policy","children":[{"name":"README.md","path":"dev/_global/iam-user-password-policy/README.md","sha":"d3240490ed6005924706f6dd8a1718747ebcd8d9"},{"name":"terragrunt.hcl","path":"dev/_global/iam-user-password-policy/terragrunt.hcl","sha":"47b669ba52099812a6d52ed4fcdad48c5e32e91e"}]},{"name":"region.yaml","path":"dev/_global/region.yaml","sha":"18b7823ed017b97431d58da7bcb9a4e31299272a"},{"name":"route53-public","children":[{"name":"README.md","path":"dev/_global/route53-public/README.md","sha":"69a24d1bb0eff2a66ca9be44c0dfc864f7086960"},{"name":"terragrunt.hcl","path":"dev/_global/route53-public/terragrunt.hcl","sha":"68ed9958a62546160f9007660857c5baa95ce12b"}]}]},{"name":"empty.yaml","path":"dev/empty.yaml","sha":"5aa66daa40faeaef37eccb7b4b0fcc792233cd7b"},{"name":"terragrunt.hcl","path":"dev/terragrunt.hcl","sha":"a63152bc683cf06815f93c001fc4e96d498db325"},{"name":"us-east-1","children":[{"name":"_global","children":[{"name":"README.md","path":"dev/us-east-1/_global/README.md","sha":"37b828b038945a50e2e571ef1e755c4f9170e7cf"},{"name":"kms-master-key","children":[{"name":"README.md","path":"dev/us-east-1/_global/kms-master-key/README.md","sha":"9f97f59f9d03bc3cef6ecb0e97af8ffdfd7c0334"},{"name":"terragrunt.hcl","path":"dev/us-east-1/_global/kms-master-key/terragrunt.hcl","sha":"d5c556e7eb743da62e0191243a2d4ee9ec2cf828"}]},{"name":"sns-topics","children":[{"name":"README.md","path":"dev/us-east-1/_global/sns-topics/README.md","sha":"7926797c3094f0e708bd761d297589bd94be873e"},{"name":"terragrunt.hcl","path":"dev/us-east-1/_global/sns-topics/terragrunt.hcl","sha":"225cf4b3fe49af57c85a1e25b7942c72cf9e6853"}]}]},{"name":"dev","children":[{"name":"README.md","path":"dev/us-east-1/dev/README.md","sha":"30eef7620895f3ad23174f5f2c8772ab7f8880a8"},{"name":"cloudwatch-dashboard","children":[{"name":"README.md","path":"dev/us-east-1/dev/cloudwatch-dashboard/README.md","sha":"01e60cd5e9f63892e09b1d7edfa7bea8fd7d0a3d"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/cloudwatch-dashboard/terragrunt.hcl","sha":"f3533abbe42145ab1b29f235701892621c9d63d3"}]},{"name":"data-stores","children":[{"name":"elk-single-cluster","children":[{"name":"README.md","path":"dev/us-east-1/dev/data-stores/elk-single-cluster/README.md","sha":"a90283b9240f67c38dd1bd77755a8162fa6f3999"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/data-stores/elk-single-cluster/terragrunt.hcl","sha":"97fc6b6c810137e559da50b3684a5f6383c77c72"}]},{"name":"kafka","children":[{"name":"README.md","path":"dev/us-east-1/dev/data-stores/kafka/README.md","sha":"72582bdb047da4f8820f45716977d5b962c17028"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/data-stores/kafka/terragrunt.hcl","sha":"1c40ceaf50ae178033dda665553598a3f7548cd1"}]},{"name":"mysql","children":[{"name":"README.md","path":"dev/us-east-1/dev/data-stores/mysql/README.md","sha":"625773572c620dcddf722d0d0d206576f57f4af7"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/data-stores/mysql/terragrunt.hcl","sha":"40313bc9ada285fe59d77067728f6cf208b04668"}]},{"name":"redis","children":[{"name":"README.md","path":"dev/us-east-1/dev/data-stores/redis/README.md","sha":"5d82990da39b55e6ac7b3bbb442d4209e62dba1f"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/data-stores/redis/terragrunt.hcl","sha":"bfa22ef95f443e06ccf0624c414d6f9f6bde5b88"}]},{"name":"zookeeper","children":[{"name":"README.md","path":"dev/us-east-1/dev/data-stores/zookeeper/README.md","sha":"87b4dda769ae63cffa851b6ccf2086b617bf989d"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/data-stores/zookeeper/terragrunt.hcl","sha":"76af3451b7a52fd7d59cd8744ff3836da3c1826d"}]}]},{"name":"env.yaml","path":"dev/us-east-1/dev/env.yaml","sha":"c38dd83256fc4206be3afc24972cd7f3b6712b19"},{"name":"lambda","children":[{"name":"long-running-scheduled","children":[{"name":"README.md","path":"dev/us-east-1/dev/lambda/long-running-scheduled/README.md","sha":"274c405a65d60c6a253ca2cf24863e1025402874"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/lambda/long-running-scheduled/terragrunt.hcl","sha":"72518b7089bd107c05c281372f3cccd6d7a6a628"}]},{"name":"s3-image-processing","children":[{"name":"README.md","path":"dev/us-east-1/dev/lambda/s3-image-processing/README.md","sha":"d7e48256e90edb0896769e4bd537c22e34c42f22"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/lambda/s3-image-processing/terragrunt.hcl","sha":"9be5024cab87f495dd8ac275b9807d086b971da5"}]}]},{"name":"networking","children":[{"name":"alb-internal","children":[{"name":"README.md","path":"dev/us-east-1/dev/networking/alb-internal/README.md","sha":"6d7b1a62e65dab5e908d2fd2ba291a98bc639a1d"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/networking/alb-internal/terragrunt.hcl","sha":"3226f2f8cc966d88abe6ffe7b1d912735a638b80"}]},{"name":"alb-public","children":[{"name":"README.md","path":"dev/us-east-1/dev/networking/alb-public/README.md","sha":"6d7b1a62e65dab5e908d2fd2ba291a98bc639a1d"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/networking/alb-public/terragrunt.hcl","sha":"039a02514db5e62d5ec21d3131579c821dcb6283"}]},{"name":"route53-private","children":[{"name":"README.md","path":"dev/us-east-1/dev/networking/route53-private/README.md","sha":"c93f222ed15cae75ec411a4b005ad4da32548c42"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/networking/route53-private/terragrunt.hcl","sha":"f7e7c4b437c11b0340c682314deb9b08aca3a854"}]}]},{"name":"services","children":[{"name":"ecs-cluster","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/ecs-cluster/README.md","sha":"775dc4b2d530ce3ef27d0e9ad22e282a52bcffd3"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/ecs-cluster/terragrunt.hcl","sha":"64c60f67c5ce0a965a2e636015dcb9ff4e22344a"}]},{"name":"eks-cluster","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/eks-cluster/README.md","sha":"8666da2d140182a504efdfedb45554623de0f642"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/eks-cluster/terragrunt.hcl","sha":"791a66ce8376fbec9a019d0cab0ac0ead3aac1ac"}]},{"name":"eks-core-services","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/eks-core-services/README.md","sha":"4c2ea353f337fa34827a56b6a2230f6c8c690deb"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/eks-core-services/terragrunt.hcl","sha":"b9a002b3992e384e57015587693b3e7fe8eddc48"}]},{"name":"k8s-applications-namespace","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/k8s-applications-namespace/README.md","sha":"4475bc76ebcba0b26acff53ccaf827375d74bf7c"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/k8s-applications-namespace/terragrunt.hcl","sha":"dd0fbec59f588fe88fc6c7f59382c0ab6d20ff9c"}]},{"name":"k8s-sample-app-backend-multi-account-acme","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/k8s-sample-app-backend-multi-account-acme/README.md","sha":"62e7efec668cc4effc669c4070dcb968fa0bdc1d"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/k8s-sample-app-backend-multi-account-acme/terragrunt.hcl","sha":"953bef0317447c080e801343e586b0d1b3cbfbfa"}]},{"name":"k8s-sample-app-frontend-multi-account-acme","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/k8s-sample-app-frontend-multi-account-acme/README.md","sha":"63c1026d7c1e936b30a82f90fae754cde0cc6897"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/k8s-sample-app-frontend-multi-account-acme/terragrunt.hcl","sha":"40060b5d96f21608eb30d259cef65289f6169a6f"}]},{"name":"sample-app-backend-multi-account-acme-asg","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/sample-app-backend-multi-account-acme-asg/README.md","sha":"211044e0160f812cc6a5c984ea2dc71d769eece7"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/sample-app-backend-multi-account-acme-asg/terragrunt.hcl","sha":"cf84fcfa70c6d966803b70e29f549f3bd057f206"}]},{"name":"sample-app-backend-multi-account-acme","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/sample-app-backend-multi-account-acme/README.md","sha":"caa8ba67485807d6d463fd7573aa8b9808f7e20b"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/sample-app-backend-multi-account-acme/terragrunt.hcl","sha":"c88b0d61edddbf9337c414cfae2d970735be2b6f"}]},{"name":"sample-app-beanstalk","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/sample-app-beanstalk/README.md","sha":"2cd92b79c23a86d7759b8074de2158ab762ab01d"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/sample-app-beanstalk/terragrunt.hcl","sha":"77ac87dca4c64fbf5108231a6faf807fca3d1fee"}]},{"name":"sample-app-frontend-multi-account-acme-asg","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/sample-app-frontend-multi-account-acme-asg/README.md","sha":"fcd0809e1c9a0a824ed30aca8746a383838b5745"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/sample-app-frontend-multi-account-acme-asg/terragrunt.hcl","sha":"17d10295d106fbde6b0de3bf6c5d3badc97c0ecd"}]},{"name":"sample-app-frontend-multi-account-acme","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/sample-app-frontend-multi-account-acme/README.md","sha":"70cacba00f9ed9f2b11ef2615fd9d27ef24558e6"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/sample-app-frontend-multi-account-acme/terragrunt.hcl","sha":"c423cc7a2fe3630db7c75a72ef212ba186ab3739"}]},{"name":"static-website","children":[{"name":"README.md","path":"dev/us-east-1/dev/services/static-website/README.md","sha":"c39ed7c607eb4d1313ea3892768bad09e7f39fd9"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/services/static-website/terragrunt.hcl","sha":"3329db273624fe437ef031ee4e42583aa13c2312"}]}]},{"name":"vpc","children":[{"name":"README.md","path":"dev/us-east-1/dev/vpc/README.md","sha":"9f2704ce60e42e8f3a603a376abb15135183d887"},{"name":"terragrunt.hcl","path":"dev/us-east-1/dev/vpc/terragrunt.hcl","sha":"aea95690d1f2f5d37e6781f718f4b132c7f509b1"}]}]},{"name":"mgmt","children":[{"name":"README.md","path":"dev/us-east-1/mgmt/README.md","sha":"8a131a11632b97fec18a5e344d5c721fce24b652"},{"name":"env.yaml","path":"dev/us-east-1/mgmt/env.yaml","sha":"b514ab3187ebfb5bf467c632f27a21f5a9611bfc"},{"name":"openvpn-server","children":[{"name":"README.md","path":"dev/us-east-1/mgmt/openvpn-server/README.md","sha":"aa6f4262ab6e2e98c4cc2bb76e0f53953dbefc86"},{"name":"terragrunt.hcl","path":"dev/us-east-1/mgmt/openvpn-server/terragrunt.hcl","sha":"c9373ed801250a4a3f727b77bb7ef026738226ac"}]},{"name":"vpc","children":[{"name":"README.md","path":"dev/us-east-1/mgmt/vpc/README.md","sha":"758f74748caa3cabf6230c214784445e0c1f7c97"},{"name":"terragrunt.hcl","path":"dev/us-east-1/mgmt/vpc/terragrunt.hcl","sha":"20f97f225446751b79f2283837d0d9d225ea3833"}]}]},{"name":"region.yaml","path":"dev/us-east-1/region.yaml","sha":"d56afa3d82e6cea0d792e84748de56dafb0bad70"}]}]},{"name":"master","children":[{"name":"_global","children":[{"name":"README.md","path":"master/_global/README.md","sha":"d1b8a96c00211751f079fa13cac1b3417d29bf09"},{"name":"cloudtrail","children":[{"name":"README.md","path":"master/_global/cloudtrail/README.md","sha":"65aac5742b3dc183d11a7d83a31ae69afe5df2e5"},{"name":"terragrunt.hcl","path":"master/_global/cloudtrail/terragrunt.hcl","sha":"fb19b4438de2ba919d17f3e4a6ccb3c9f2517f26"}]},{"name":"iam-cross-account","children":[{"name":"README.md","path":"master/_global/iam-cross-account/README.md","sha":"6bf06985be74cf68085a78728c41cf88fc354141"},{"name":"terragrunt.hcl","path":"master/_global/iam-cross-account/terragrunt.hcl","sha":"0f540eaa0fa2fddb3c7afdead9cd4d4b71e77b11"}]},{"name":"iam-user-password-policy","children":[{"name":"README.md","path":"master/_global/iam-user-password-policy/README.md","sha":"d3240490ed6005924706f6dd8a1718747ebcd8d9"},{"name":"terragrunt.hcl","path":"master/_global/iam-user-password-policy/terragrunt.hcl","sha":"47b669ba52099812a6d52ed4fcdad48c5e32e91e"}]},{"name":"region.yaml","path":"master/_global/region.yaml","sha":"18b7823ed017b97431d58da7bcb9a4e31299272a"}]},{"name":"empty.yaml","path":"master/empty.yaml","sha":"5aa66daa40faeaef37eccb7b4b0fcc792233cd7b"},{"name":"terragrunt.hcl","path":"master/terragrunt.hcl","sha":"5d9ca2068ff75ac0532e01f3ec52b4051e419053"},{"name":"us-east-1","children":[{"name":"_global","children":[{"name":"README.md","path":"master/us-east-1/_global/README.md","sha":"37b828b038945a50e2e571ef1e755c4f9170e7cf"}]},{"name":"region.yaml","path":"master/us-east-1/region.yaml","sha":"d56afa3d82e6cea0d792e84748de56dafb0bad70"}]}]},{"name":"prod","children":[{"name":"_global","children":[{"name":"README.md","path":"prod/_global/README.md","sha":"d1b8a96c00211751f079fa13cac1b3417d29bf09"},{"name":"cloudtrail","children":[{"name":"README.md","path":"prod/_global/cloudtrail/README.md","sha":"65aac5742b3dc183d11a7d83a31ae69afe5df2e5"},{"name":"terragrunt.hcl","path":"prod/_global/cloudtrail/terragrunt.hcl","sha":"fb19b4438de2ba919d17f3e4a6ccb3c9f2517f26"}]},{"name":"iam-cross-account","children":[{"name":"README.md","path":"prod/_global/iam-cross-account/README.md","sha":"6bf06985be74cf68085a78728c41cf88fc354141"},{"name":"terragrunt.hcl","path":"prod/_global/iam-cross-account/terragrunt.hcl","sha":"96f6b058d3db5871b45cd9e9c05d2387f7fa8340"}]},{"name":"iam-user-password-policy","children":[{"name":"README.md","path":"prod/_global/iam-user-password-policy/README.md","sha":"d3240490ed6005924706f6dd8a1718747ebcd8d9"},{"name":"terragrunt.hcl","path":"prod/_global/iam-user-password-policy/terragrunt.hcl","sha":"47b669ba52099812a6d52ed4fcdad48c5e32e91e"}]},{"name":"region.yaml","path":"prod/_global/region.yaml","sha":"18b7823ed017b97431d58da7bcb9a4e31299272a"},{"name":"route53-public","children":[{"name":"README.md","path":"prod/_global/route53-public/README.md","sha":"69a24d1bb0eff2a66ca9be44c0dfc864f7086960"},{"name":"terragrunt.hcl","path":"prod/_global/route53-public/terragrunt.hcl","sha":"68ed9958a62546160f9007660857c5baa95ce12b"}]}]},{"name":"empty.yaml","path":"prod/empty.yaml","sha":"5aa66daa40faeaef37eccb7b4b0fcc792233cd7b"},{"name":"terragrunt.hcl","path":"prod/terragrunt.hcl","sha":"ba18847c5969b30159af5c87a05655d3e7dc7eb8"},{"name":"us-east-1","children":[{"name":"_global","children":[{"name":"README.md","path":"prod/us-east-1/_global/README.md","sha":"37b828b038945a50e2e571ef1e755c4f9170e7cf"},{"name":"kms-master-key","children":[{"name":"README.md","path":"prod/us-east-1/_global/kms-master-key/README.md","sha":"2de4ac69670b2bf16a3da13f4bff76b5b85891b7"},{"name":"terragrunt.hcl","path":"prod/us-east-1/_global/kms-master-key/terragrunt.hcl","sha":"5e19caaa42e506f2797048979d773f2fac5bce8a"}]},{"name":"sns-topics","children":[{"name":"README.md","path":"prod/us-east-1/_global/sns-topics/README.md","sha":"7926797c3094f0e708bd761d297589bd94be873e"},{"name":"terragrunt.hcl","path":"prod/us-east-1/_global/sns-topics/terragrunt.hcl","sha":"225cf4b3fe49af57c85a1e25b7942c72cf9e6853"}]}]},{"name":"mgmt","children":[{"name":"README.md","path":"prod/us-east-1/mgmt/README.md","sha":"8a131a11632b97fec18a5e344d5c721fce24b652"},{"name":"env.yaml","path":"prod/us-east-1/mgmt/env.yaml","sha":"b514ab3187ebfb5bf467c632f27a21f5a9611bfc"},{"name":"openvpn-server","children":[{"name":"README.md","path":"prod/us-east-1/mgmt/openvpn-server/README.md","sha":"271cbe2b6f59de9ce438ed4516393fd01d64b072"},{"name":"terragrunt.hcl","path":"prod/us-east-1/mgmt/openvpn-server/terragrunt.hcl","sha":"00af46905196d2216ca0e7d7e2767a86900514ea"}]},{"name":"vpc","children":[{"name":"README.md","path":"prod/us-east-1/mgmt/vpc/README.md","sha":"758f74748caa3cabf6230c214784445e0c1f7c97"},{"name":"terragrunt.hcl","path":"prod/us-east-1/mgmt/vpc/terragrunt.hcl","sha":"a3dbfb5bb3b55e6fcf452338c69e2fdab5aa6204"}]}]},{"name":"prod","children":[{"name":"README.md","path":"prod/us-east-1/prod/README.md","sha":"f15da18661ef3624d5f63deb288bad072e93df57"},{"name":"cloudwatch-dashboard","children":[{"name":"README.md","path":"prod/us-east-1/prod/cloudwatch-dashboard/README.md","sha":"01e60cd5e9f63892e09b1d7edfa7bea8fd7d0a3d"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/cloudwatch-dashboard/terragrunt.hcl","sha":"fe0a59a0885dd19898acfd6b65b290e5b6bf279c"}]},{"name":"data-stores","children":[{"name":"elk-multi-cluster","children":[{"name":"README.md","path":"prod/us-east-1/prod/data-stores/elk-multi-cluster/README.md","sha":"b59860709b8ef7bc7746c0bd9e9a1a21d02142c8"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/data-stores/elk-multi-cluster/terragrunt.hcl","sha":"ad3c96134476a81696c73807021c875283c3dfd1"}]},{"name":"kafka","children":[{"name":"README.md","path":"prod/us-east-1/prod/data-stores/kafka/README.md","sha":"72582bdb047da4f8820f45716977d5b962c17028"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/data-stores/kafka/terragrunt.hcl","sha":"c9e85c9678126c48a4cd6fc16cc55d523194b4d8"}]},{"name":"mysql","children":[{"name":"README.md","path":"prod/us-east-1/prod/data-stores/mysql/README.md","sha":"625773572c620dcddf722d0d0d206576f57f4af7"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/data-stores/mysql/terragrunt.hcl","sha":"e88eea81774f2ff43c6ba0f4929593b2adf80e20"}]},{"name":"redis","children":[{"name":"README.md","path":"prod/us-east-1/prod/data-stores/redis/README.md","sha":"5d82990da39b55e6ac7b3bbb442d4209e62dba1f"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/data-stores/redis/terragrunt.hcl","sha":"2ab5abad0edb89d10ffebb7c279a94b5e344bce6"}]},{"name":"zookeeper","children":[{"name":"README.md","path":"prod/us-east-1/prod/data-stores/zookeeper/README.md","sha":"87b4dda769ae63cffa851b6ccf2086b617bf989d"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/data-stores/zookeeper/terragrunt.hcl","sha":"e04f530614ac4913a702c2a18e38b4fa5a1f7848"}]}]},{"name":"env.yaml","path":"prod/us-east-1/prod/env.yaml","sha":"90e2d18e481b6e35ddc57391f752874ffc0058cf"},{"name":"lambda","children":[{"name":"long-running-scheduled","children":[{"name":"README.md","path":"prod/us-east-1/prod/lambda/long-running-scheduled/README.md","sha":"274c405a65d60c6a253ca2cf24863e1025402874"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/lambda/long-running-scheduled/terragrunt.hcl","sha":"72518b7089bd107c05c281372f3cccd6d7a6a628"}]},{"name":"s3-image-processing","children":[{"name":"README.md","path":"prod/us-east-1/prod/lambda/s3-image-processing/README.md","sha":"d7e48256e90edb0896769e4bd537c22e34c42f22"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/lambda/s3-image-processing/terragrunt.hcl","sha":"87d070e374ab2b7a6860167650e26c0fc73b486a"}]}]},{"name":"networking","children":[{"name":"alb-internal","children":[{"name":"README.md","path":"prod/us-east-1/prod/networking/alb-internal/README.md","sha":"21e850d1bbe25ca3b16114b8f58d08086a687916"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/networking/alb-internal/terragrunt.hcl","sha":"cc5feacf99eebd2cd695507d9169037599c763e5"}]},{"name":"alb-public","children":[{"name":"README.md","path":"prod/us-east-1/prod/networking/alb-public/README.md","sha":"21e850d1bbe25ca3b16114b8f58d08086a687916"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/networking/alb-public/terragrunt.hcl","sha":"03af1d8d3f8a860dc443addbf610f5934f7869eb"}]},{"name":"route53-private","children":[{"name":"README.md","path":"prod/us-east-1/prod/networking/route53-private/README.md","sha":"c93f222ed15cae75ec411a4b005ad4da32548c42"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/networking/route53-private/terragrunt.hcl","sha":"f7e7c4b437c11b0340c682314deb9b08aca3a854"}]}]},{"name":"services","children":[{"name":"ecs-cluster","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/ecs-cluster/README.md","sha":"d9e015edbbcd8f9e200fb00a5dde7fc7f466cd3c"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/ecs-cluster/terragrunt.hcl","sha":"b0eef93fabce97dfe098d6e18677fd3b1839d009"}]},{"name":"eks-cluster","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/eks-cluster/README.md","sha":"239bc9a333f256845a90c786c00cd6789000bd9d"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/eks-cluster/terragrunt.hcl","sha":"6cd702f65352efa6710f63842666cd214b9e4109"}]},{"name":"eks-core-services","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/eks-core-services/README.md","sha":"66fbf40d9982d63707eb1e7c60d78bc4aa659a0b"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/eks-core-services/terragrunt.hcl","sha":"3ba3473cbb4c7cd83fb23f3fa18ca624bc9de5aa"}]},{"name":"k8s-applications-namespace","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/k8s-applications-namespace/README.md","sha":"39887806598180d75b0b0b0a46ea4418d75af8f1"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/k8s-applications-namespace/terragrunt.hcl","sha":"c9d638ac7e6367e974db2dbdf2ffc8f17473fb91"}]},{"name":"k8s-sample-app-backend-multi-account-acme","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/k8s-sample-app-backend-multi-account-acme/README.md","sha":"b7524c6453a2e3bf441c953de7236ab6273e2c0f"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/k8s-sample-app-backend-multi-account-acme/terragrunt.hcl","sha":"e8b8df48ed310a08be55d5d3d07af9dc20fe7c12"}]},{"name":"k8s-sample-app-frontend-multi-account-acme","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/k8s-sample-app-frontend-multi-account-acme/README.md","sha":"17ee9c75e9fdea8d4295ad45f2fb8a46d9241e47"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/k8s-sample-app-frontend-multi-account-acme/terragrunt.hcl","sha":"cab54c75aa28d2d0a1df465e3e4272fc7a154adb"}]},{"name":"sample-app-backend-multi-account-acme-asg","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/sample-app-backend-multi-account-acme-asg/README.md","sha":"b848e55fea0f16a9b7ccf9c1366c6b56c9966a7a"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/sample-app-backend-multi-account-acme-asg/terragrunt.hcl","sha":"3bc70a7302b813667b0d9b9a19d888ce7a53e711"}]},{"name":"sample-app-backend-multi-account-acme","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/sample-app-backend-multi-account-acme/README.md","sha":"f0910e01d79209f26d65fdb1e6de428fa26f9eb6"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/sample-app-backend-multi-account-acme/terragrunt.hcl","sha":"f6f9ecf019df9635b4a68f42816d37f1ef4722c3"}]},{"name":"sample-app-beanstalk","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/sample-app-beanstalk/README.md","sha":"a6f2e03ce14753bb39e48fbf350c4aeb3aa3e6c9"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/sample-app-beanstalk/terragrunt.hcl","sha":"aa974300d961214d4a76f729b87e3d6d1dc90107"}]},{"name":"sample-app-frontend-multi-account-acme-asg","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/sample-app-frontend-multi-account-acme-asg/README.md","sha":"a5291ba90a48e7087a2fa7e24f41f1e4f4d43a79"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/sample-app-frontend-multi-account-acme-asg/terragrunt.hcl","sha":"c7cdbcf4cc8effe73a0130cf07a7f5da8cca0f23"}]},{"name":"sample-app-frontend-multi-account-acme","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/sample-app-frontend-multi-account-acme/README.md","sha":"691ef675dbcdb807391b0e2b8547c9f260cc12c6"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/sample-app-frontend-multi-account-acme/terragrunt.hcl","sha":"f0137a766733b3575863a74230dfc92ec0c94cd0"}]},{"name":"static-website","children":[{"name":"README.md","path":"prod/us-east-1/prod/services/static-website/README.md","sha":"c39ed7c607eb4d1313ea3892768bad09e7f39fd9"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/services/static-website/terragrunt.hcl","sha":"d5879453a53db71287dad837ed32e7d9b59d120e"}]}]},{"name":"vpc","children":[{"name":"README.md","path":"prod/us-east-1/prod/vpc/README.md","sha":"934095e18b4fcdb217ba8a0192c2c65e6fafb989"},{"name":"terragrunt.hcl","path":"prod/us-east-1/prod/vpc/terragrunt.hcl","sha":"521968dd6fb563a01f26d8b4662fbf4624cd21a0"}]}]},{"name":"region.yaml","path":"prod/us-east-1/region.yaml","sha":"d56afa3d82e6cea0d792e84748de56dafb0bad70"}]}]},{"name":"security","children":[{"name":"_global","children":[{"name":"README.md","path":"security/_global/README.md","sha":"d1b8a96c00211751f079fa13cac1b3417d29bf09"},{"name":"cloudtrail","children":[{"name":"README.md","path":"security/_global/cloudtrail/README.md","sha":"65aac5742b3dc183d11a7d83a31ae69afe5df2e5"},{"name":"terragrunt.hcl","path":"security/_global/cloudtrail/terragrunt.hcl","sha":"880d875fa813a2ec594b9a8ef15eb431bf540fa4"}]},{"name":"iam-cross-account","children":[{"name":"README.md","path":"security/_global/iam-cross-account/README.md","sha":"6bf06985be74cf68085a78728c41cf88fc354141"},{"name":"terragrunt.hcl","path":"security/_global/iam-cross-account/terragrunt.hcl","sha":"fef1fc1992f71bf1e0707301a038457d70ec918c"}]},{"name":"iam-groups","children":[{"name":"README.md","path":"security/_global/iam-groups/README.md","sha":"93a0e35d82a328def8047539ce7d987ab062d292"},{"name":"terragrunt.hcl","path":"security/_global/iam-groups/terragrunt.hcl","sha":"d1398a63fe6180c591186343e390e4ca1ab45085"}]},{"name":"iam-user-password-policy","children":[{"name":"README.md","path":"security/_global/iam-user-password-policy/README.md","sha":"d3240490ed6005924706f6dd8a1718747ebcd8d9"},{"name":"terragrunt.hcl","path":"security/_global/iam-user-password-policy/terragrunt.hcl","sha":"47b669ba52099812a6d52ed4fcdad48c5e32e91e"}]},{"name":"region.yaml","path":"security/_global/region.yaml","sha":"18b7823ed017b97431d58da7bcb9a4e31299272a"}]},{"name":"empty.yaml","path":"security/empty.yaml","sha":"5aa66daa40faeaef37eccb7b4b0fcc792233cd7b"},{"name":"terragrunt.hcl","path":"security/terragrunt.hcl","sha":"6cb54cf9410ee6cec86c946669e5cbe8348e7e5b"}]},{"name":"shared-services","children":[{"name":"_global","children":[{"name":"README.md","path":"shared-services/_global/README.md","sha":"d1b8a96c00211751f079fa13cac1b3417d29bf09"},{"name":"cloudtrail","children":[{"name":"README.md","path":"shared-services/_global/cloudtrail/README.md","sha":"65aac5742b3dc183d11a7d83a31ae69afe5df2e5"},{"name":"terragrunt.hcl","path":"shared-services/_global/cloudtrail/terragrunt.hcl","sha":"fb19b4438de2ba919d17f3e4a6ccb3c9f2517f26"}]},{"name":"iam-cross-account","children":[{"name":"README.md","path":"shared-services/_global/iam-cross-account/README.md","sha":"6bf06985be74cf68085a78728c41cf88fc354141"},{"name":"terragrunt.hcl","path":"shared-services/_global/iam-cross-account/terragrunt.hcl","sha":"0f540eaa0fa2fddb3c7afdead9cd4d4b71e77b11"}]},{"name":"iam-user-password-policy","children":[{"name":"README.md","path":"shared-services/_global/iam-user-password-policy/README.md","sha":"d3240490ed6005924706f6dd8a1718747ebcd8d9"},{"name":"terragrunt.hcl","path":"shared-services/_global/iam-user-password-policy/terragrunt.hcl","sha":"47b669ba52099812a6d52ed4fcdad48c5e32e91e"}]},{"name":"region.yaml","path":"shared-services/_global/region.yaml","sha":"18b7823ed017b97431d58da7bcb9a4e31299272a"},{"name":"route53-public","children":[{"name":"README.md","path":"shared-services/_global/route53-public/README.md","sha":"69a24d1bb0eff2a66ca9be44c0dfc864f7086960"},{"name":"terragrunt.hcl","path":"shared-services/_global/route53-public/terragrunt.hcl","sha":"68ed9958a62546160f9007660857c5baa95ce12b"}]}]},{"name":"empty.yaml","path":"shared-services/empty.yaml","sha":"5aa66daa40faeaef37eccb7b4b0fcc792233cd7b"},{"name":"terragrunt.hcl","path":"shared-services/terragrunt.hcl","sha":"69470dec0813da115863dbffed34fe6f1ecc3c8e"},{"name":"us-east-1","children":[{"name":"_global","children":[{"name":"README.md","path":"shared-services/us-east-1/_global/README.md","sha":"37b828b038945a50e2e571ef1e755c4f9170e7cf"},{"name":"ecr-repos","children":[{"name":"README.md","path":"shared-services/us-east-1/_global/ecr-repos/README.md","sha":"050f270033456c0a3285056f41a4427ec4ca1db1"},{"name":"terragrunt.hcl","path":"shared-services/us-east-1/_global/ecr-repos/terragrunt.hcl","sha":"0aebd1fe76c536af522f7af783dae529637f5a2b"}]},{"name":"kms-master-key","children":[{"name":"README.md","path":"shared-services/us-east-1/_global/kms-master-key/README.md","sha":"8923e2581297b44e53b3a50b23505b7e0bf85f9e"},{"name":"terragrunt.hcl","path":"shared-services/us-east-1/_global/kms-master-key/terragrunt.hcl","sha":"e3d3456c830e33007018dfa0444e1bb20950e327"}]},{"name":"sns-topics","children":[{"name":"README.md","path":"shared-services/us-east-1/_global/sns-topics/README.md","sha":"7926797c3094f0e708bd761d297589bd94be873e"},{"name":"terragrunt.hcl","path":"shared-services/us-east-1/_global/sns-topics/terragrunt.hcl","sha":"225cf4b3fe49af57c85a1e25b7942c72cf9e6853"}]}]},{"name":"mgmt","children":[{"name":"README.md","path":"shared-services/us-east-1/mgmt/README.md","sha":"8a131a11632b97fec18a5e344d5c721fce24b652"},{"name":"env.yaml","path":"shared-services/us-east-1/mgmt/env.yaml","sha":"b514ab3187ebfb5bf467c632f27a21f5a9611bfc"},{"name":"jenkins","children":[{"name":"README.md","path":"shared-services/us-east-1/mgmt/jenkins/README.md","sha":"c67253232ed9a8a685ca1659c887ae60416f2998"},{"name":"terragrunt.hcl","path":"shared-services/us-east-1/mgmt/jenkins/terragrunt.hcl","sha":"f75d4c6b36c07a20f69863e69e41b2902658fd47"}]},{"name":"openvpn-server","children":[{"name":"README.md","path":"shared-services/us-east-1/mgmt/openvpn-server/README.md","sha":"523ded2b928a312657e689db78730315ad66633b"},{"name":"terragrunt.hcl","path":"shared-services/us-east-1/mgmt/openvpn-server/terragrunt.hcl","sha":"c4f4e0a9021527bf53fe18a62814ca30ce726649"}]},{"name":"vpc","children":[{"name":"README.md","path":"shared-services/us-east-1/mgmt/vpc/README.md","sha":"758f74748caa3cabf6230c214784445e0c1f7c97"},{"name":"terragrunt.hcl","path":"shared-services/us-east-1/mgmt/vpc/terragrunt.hcl","sha":"29ff68537a532e119786f868bb2a709839792e90"}]}]},{"name":"region.yaml","path":"shared-services/us-east-1/region.yaml","sha":"d56afa3d82e6cea0d792e84748de56dafb0bad70"}]}]},{"name":"stage","children":[{"name":"_global","children":[{"name":"README.md","path":"stage/_global/README.md","sha":"d1b8a96c00211751f079fa13cac1b3417d29bf09"},{"name":"cloudtrail","children":[{"name":"README.md","path":"stage/_global/cloudtrail/README.md","sha":"65aac5742b3dc183d11a7d83a31ae69afe5df2e5"},{"name":"terragrunt.hcl","path":"stage/_global/cloudtrail/terragrunt.hcl","sha":"fb19b4438de2ba919d17f3e4a6ccb3c9f2517f26"}]},{"name":"iam-cross-account","children":[{"name":"README.md","path":"stage/_global/iam-cross-account/README.md","sha":"6bf06985be74cf68085a78728c41cf88fc354141"},{"name":"terragrunt.hcl","path":"stage/_global/iam-cross-account/terragrunt.hcl","sha":"96f6b058d3db5871b45cd9e9c05d2387f7fa8340"}]},{"name":"iam-user-password-policy","children":[{"name":"README.md","path":"stage/_global/iam-user-password-policy/README.md","sha":"d3240490ed6005924706f6dd8a1718747ebcd8d9"},{"name":"terragrunt.hcl","path":"stage/_global/iam-user-password-policy/terragrunt.hcl","sha":"47b669ba52099812a6d52ed4fcdad48c5e32e91e"}]},{"name":"region.yaml","path":"stage/_global/region.yaml","sha":"18b7823ed017b97431d58da7bcb9a4e31299272a"},{"name":"route53-public","children":[{"name":"README.md","path":"stage/_global/route53-public/README.md","sha":"69a24d1bb0eff2a66ca9be44c0dfc864f7086960"},{"name":"terragrunt.hcl","path":"stage/_global/route53-public/terragrunt.hcl","sha":"68ed9958a62546160f9007660857c5baa95ce12b"}]}]},{"name":"empty.yaml","path":"stage/empty.yaml","sha":"5aa66daa40faeaef37eccb7b4b0fcc792233cd7b"},{"name":"terragrunt.hcl","path":"stage/terragrunt.hcl","sha":"e2506d916b6362dfc1aa0192b71f388a72e79568"},{"name":"us-east-1","children":[{"name":"_global","children":[{"name":"README.md","path":"stage/us-east-1/_global/README.md","sha":"37b828b038945a50e2e571ef1e755c4f9170e7cf"},{"name":"kms-master-key","children":[{"name":"README.md","path":"stage/us-east-1/_global/kms-master-key/README.md","sha":"2ce8a7cdc4864091ba55cc930ad4e6f8dc29d535"},{"name":"terragrunt.hcl","path":"stage/us-east-1/_global/kms-master-key/terragrunt.hcl","sha":"c0c2f65c3cf60fc59e6646347d4458001a116e6b"}]},{"name":"sns-topics","children":[{"name":"README.md","path":"stage/us-east-1/_global/sns-topics/README.md","sha":"7926797c3094f0e708bd761d297589bd94be873e"},{"name":"terragrunt.hcl","path":"stage/us-east-1/_global/sns-topics/terragrunt.hcl","sha":"225cf4b3fe49af57c85a1e25b7942c72cf9e6853"}]}]},{"name":"mgmt","children":[{"name":"README.md","path":"stage/us-east-1/mgmt/README.md","sha":"8a131a11632b97fec18a5e344d5c721fce24b652"},{"name":"env.yaml","path":"stage/us-east-1/mgmt/env.yaml","sha":"b514ab3187ebfb5bf467c632f27a21f5a9611bfc"},{"name":"openvpn-server","children":[{"name":"README.md","path":"stage/us-east-1/mgmt/openvpn-server/README.md","sha":"5dcede127b26b8cfc2ed78cd1450ec7dd7d66d18"},{"name":"terragrunt.hcl","path":"stage/us-east-1/mgmt/openvpn-server/terragrunt.hcl","sha":"af54f724d3ee040688035af9ae0f41154a7dbc1c"}]},{"name":"vpc","children":[{"name":"README.md","path":"stage/us-east-1/mgmt/vpc/README.md","sha":"758f74748caa3cabf6230c214784445e0c1f7c97"},{"name":"terragrunt.hcl","path":"stage/us-east-1/mgmt/vpc/terragrunt.hcl","sha":"b2016650621679f7b9f99b93806cc0b8efb149ac"}]}]},{"name":"region.yaml","path":"stage/us-east-1/region.yaml","sha":"d56afa3d82e6cea0d792e84748de56dafb0bad70"},{"name":"stage","children":[{"name":"README.md","path":"stage/us-east-1/stage/README.md","sha":"b24ba21bf01baf19ff84a2de457697a757d905c5"},{"name":"cloudwatch-dashboard","children":[{"name":"README.md","path":"stage/us-east-1/stage/cloudwatch-dashboard/README.md","sha":"01e60cd5e9f63892e09b1d7edfa7bea8fd7d0a3d"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/cloudwatch-dashboard/terragrunt.hcl","sha":"01ed95b4b404eda346293dbe5eb78b9a74f2f5bb"}]},{"name":"data-stores","children":[{"name":"elk-single-cluster","children":[{"name":"README.md","path":"stage/us-east-1/stage/data-stores/elk-single-cluster/README.md","sha":"a90283b9240f67c38dd1bd77755a8162fa6f3999"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/data-stores/elk-single-cluster/terragrunt.hcl","sha":"7818782091e335360279369b391a3401703ddedd"}]},{"name":"kafka","children":[{"name":"README.md","path":"stage/us-east-1/stage/data-stores/kafka/README.md","sha":"72582bdb047da4f8820f45716977d5b962c17028"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/data-stores/kafka/terragrunt.hcl","sha":"26d778dddca07ead27d37e481a897659d7cec7d2"}]},{"name":"mysql","children":[{"name":"README.md","path":"stage/us-east-1/stage/data-stores/mysql/README.md","sha":"625773572c620dcddf722d0d0d206576f57f4af7"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/data-stores/mysql/terragrunt.hcl","sha":"35991621a16fb4cc8443fdf2695c23d3e658c5ac"}]},{"name":"redis","children":[{"name":"README.md","path":"stage/us-east-1/stage/data-stores/redis/README.md","sha":"5d82990da39b55e6ac7b3bbb442d4209e62dba1f"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/data-stores/redis/terragrunt.hcl","sha":"79ef1e07fde2d0dcb9673d653089da8fb3fa449a"}]},{"name":"zookeeper","children":[{"name":"README.md","path":"stage/us-east-1/stage/data-stores/zookeeper/README.md","sha":"87b4dda769ae63cffa851b6ccf2086b617bf989d"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/data-stores/zookeeper/terragrunt.hcl","sha":"045e1d8802b8b1dc93070a80d98da5b917dec655"}]}]},{"name":"env.yaml","path":"stage/us-east-1/stage/env.yaml","sha":"5767506e27e978f52524dadbbd8fb9f8ad115599"},{"name":"lambda","children":[{"name":"long-running-scheduled","children":[{"name":"README.md","path":"stage/us-east-1/stage/lambda/long-running-scheduled/README.md","sha":"274c405a65d60c6a253ca2cf24863e1025402874"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/lambda/long-running-scheduled/terragrunt.hcl","sha":"72518b7089bd107c05c281372f3cccd6d7a6a628"}]},{"name":"s3-image-processing","children":[{"name":"README.md","path":"stage/us-east-1/stage/lambda/s3-image-processing/README.md","sha":"d7e48256e90edb0896769e4bd537c22e34c42f22"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/lambda/s3-image-processing/terragrunt.hcl","sha":"02a23cc6f220d8301d40f7616d1e17f9e17448a5"}]}]},{"name":"networking","children":[{"name":"alb-internal","children":[{"name":"README.md","path":"stage/us-east-1/stage/networking/alb-internal/README.md","sha":"9ee564202c79754713107e2c3644c9cb3815922e"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/networking/alb-internal/terragrunt.hcl","sha":"9537dc7cb6dc7c9eb929a0b97a6f89025e042e57"}]},{"name":"alb-public","children":[{"name":"README.md","path":"stage/us-east-1/stage/networking/alb-public/README.md","sha":"9ee564202c79754713107e2c3644c9cb3815922e"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/networking/alb-public/terragrunt.hcl","sha":"8ea93d9b8dafad1fc2044a677dd9d0db8d768a55"}]},{"name":"route53-private","children":[{"name":"README.md","path":"stage/us-east-1/stage/networking/route53-private/README.md","sha":"c93f222ed15cae75ec411a4b005ad4da32548c42"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/networking/route53-private/terragrunt.hcl","sha":"f7e7c4b437c11b0340c682314deb9b08aca3a854"}]}]},{"name":"services","children":[{"name":"ecs-cluster","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/ecs-cluster/README.md","sha":"a4970def6876c873e8f722cbb4ff168d5b95d97d"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/ecs-cluster/terragrunt.hcl","sha":"a42cc57f67f8a9ee2bf5bfce1fb58ea96f5e48f5"}]},{"name":"eks-cluster","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/eks-cluster/README.md","sha":"3c63107a058eeb5b3118b4c569ee867c366c2762"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/eks-cluster/terragrunt.hcl","sha":"ec2a56580f4fbe9d5ea53b4a7bc0768a7ae90029"}]},{"name":"eks-core-services","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/eks-core-services/README.md","sha":"bdfb014e5d96aff7bfcb36d6b07bd20a30ddccf1"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/eks-core-services/terragrunt.hcl","sha":"4c1aecc3ec52837b98978ec6fc1cd8d5564763ae"}]},{"name":"k8s-applications-namespace","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/k8s-applications-namespace/README.md","sha":"41a7a39dea2f5278e9cc05d7c52eb0144fac15d1"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/k8s-applications-namespace/terragrunt.hcl","sha":"3665bac8be17e73ab034e2f32017c69ef209b2af"}]},{"name":"k8s-sample-app-backend-multi-account-acme","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/k8s-sample-app-backend-multi-account-acme/README.md","sha":"184f8a011d291db3558dbdbb0f9d8f0f12d5d888"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/k8s-sample-app-backend-multi-account-acme/terragrunt.hcl","sha":"9ea86835ea625bbc341b4d8cbfbcd9b954c4feb5"}]},{"name":"k8s-sample-app-frontend-multi-account-acme","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/k8s-sample-app-frontend-multi-account-acme/README.md","sha":"bf4f271d1b7d4e933a0919ee373289b03c9380fb"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/k8s-sample-app-frontend-multi-account-acme/terragrunt.hcl","sha":"b53766ca7b1cf18f27c115d086e3aebf697ee5f3"}]},{"name":"sample-app-backend-multi-account-acme-asg","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/sample-app-backend-multi-account-acme-asg/README.md","sha":"3b3779fb30149fb17d27b9d07ff46890645744b1"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/sample-app-backend-multi-account-acme-asg/terragrunt.hcl","sha":"2b1ddfe35699ecff6ee4e8c164ad4c9e00d224da"}]},{"name":"sample-app-backend-multi-account-acme","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/sample-app-backend-multi-account-acme/README.md","sha":"bc6fda688883bd02ec06de1dbf3abd0d7537970b"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/sample-app-backend-multi-account-acme/terragrunt.hcl","sha":"1dd22075941f49b593bc9bfb03f616ac8004a06d"}]},{"name":"sample-app-beanstalk","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/sample-app-beanstalk/README.md","sha":"ca960ca9cf3f71d783b3fd4478503e3bdbdb5690"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/sample-app-beanstalk/terragrunt.hcl","sha":"4360debb24de5f877a88782858260326f9d72dbd"}]},{"name":"sample-app-frontend-multi-account-acme-asg","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/sample-app-frontend-multi-account-acme-asg/README.md","sha":"ebd62ba824756666250d5a5c062c21a01a6a30cb"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/sample-app-frontend-multi-account-acme-asg/terragrunt.hcl","sha":"e0efa40fe276041d8bea4a543144a5591cfcead5"}]},{"name":"sample-app-frontend-multi-account-acme","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/sample-app-frontend-multi-account-acme/README.md","sha":"0ce40b08cc42cacd1d1b5652d7a98c07e3a17172"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/sample-app-frontend-multi-account-acme/terragrunt.hcl","sha":"2507d6e2e44be9e18a120c45b083b901f98a4cf9"}]},{"name":"static-website","children":[{"name":"README.md","path":"stage/us-east-1/stage/services/static-website/README.md","sha":"c39ed7c607eb4d1313ea3892768bad09e7f39fd9"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/services/static-website/terragrunt.hcl","sha":"5dcb8cc0d74456bfa9756c29cbc70118b7c894ed"}]}]},{"name":"vpc","children":[{"name":"README.md","path":"stage/us-east-1/stage/vpc/README.md","sha":"e037370db683520d7d593adddc2125ef3798d801"},{"name":"terragrunt.hcl","path":"stage/us-east-1/stage/vpc/terragrunt.hcl","sha":"96c7ebd950aea8d3e7d6767609e9419ef407db4f"}]}]}]}]}]},"detailsContent":"<h1 class=\"preview__body--title\" id=\"migration\">Migration</h1><div class=\"preview__body--border\"></div><p>Now that you understand how the Reference Architecture works, the next step is to begin migrating your apps to it. The\nprocess we recommend is as follows:</p>\n<ol>\n<li><a href=\"#do-the-migration-incrementally\" class=\"preview__body--description--blue\">Do the migration incrementally</a></li>\n<li><a href=\"#set-up-account-access\" class=\"preview__body--description--blue\">Set up account access</a></li>\n<li><a href=\"#deploy-your-apps\" class=\"preview__body--description--blue\">Deploy your apps</a></li>\n<li><a href=\"#migrate-your-data-stores\" class=\"preview__body--description--blue\">Migrate your data stores</a></li>\n<li><a href=\"#configure-monitoring-and-alerting\" class=\"preview__body--description--blue\">Configure monitoring and alerting</a></li>\n<li><a href=\"#test\" class=\"preview__body--description--blue\">Test</a></li>\n<li><a href=\"#switch-over-dns\" class=\"preview__body--description--blue\">Switch over DNS</a></li>\n</ol>\n<h2 class=\"preview__body--subtitle\" id=\"do-the-migration-incrementally\">Do the migration incrementally</h2>\n<p>Most "big bang" migrations—where you try to pick up everything and move it over to a new cloud (AWS), new tools\n(Terraform, Docker, Packer), and new processes (CI, CD, DevOps), all in one huge step—almost always fail. The only way\nto succeed at a big migration is to do it <em>incrementally</em>. Note that "incrementally" doesn't mean you just chop up the\nwork into small parts; it means you chop it up into small parts so that <em>each part is worth doing, even if the other\nparts never happen</em>.</p>\n<p>If the only value you get from the migration is at the very end of a long process (and lets be honest, migrations\n<em>always</em> take longer than you expect), then there's a good chance management will lose patience, developers will get\nfrustrated, funding for the project will be cut, corners will be cut, and the project will be a disaster.</p>\n<p>Therefore, you want to arrange the work so that you can complete one small piece at a time and get value out of that\npiece immediately. That way, management stays happy, developers stay motivated, and the project is worth doing, even\nif the end of the migration is still a long ways off.</p>\n<p>You'll find a number of suggestions in this document on how to do the migration incrementally. The short version is:</p>\n<ol>\n<li>Migrate one or a small number of apps.</li>\n<li>Allow the apps to keep using the data stores and other dependencies in your original data center via a VPN\nconnection.</li>\n<li>Test.</li>\n<li>Once things are working, switch over the DNS config to point to the apps running in AWS.</li>\n<li>Repeat the previous steps with the rest of your apps.</li>\n<li>Once all apps have been migrated, begin migrating your data stores.</li>\n</ol>\n<p>Read on for the long version!</p>\n<h2 class=\"preview__body--subtitle\" id=\"set-up-account-access\">Set up account access</h2>\n<p>The first step to migrating to the Reference Architecture is to make sure everyone on your team has access to your AWS\naccounts. This consists of the following items:</p>\n<ol>\n<li><a href=\"#security-primer\" class=\"preview__body--description--blue\">Security primer</a></li>\n<li><a href=\"#root-user\" class=\"preview__body--description--blue\">Root user</a></li>\n<li><a href=\"#iam-users\" class=\"preview__body--description--blue\">IAM users</a></li>\n<li><a href=\"#iam-groups\" class=\"preview__body--description--blue\">IAM groups</a></li>\n<li><a href=\"#ssh-access\" class=\"preview__body--description--blue\">SSH access</a></li>\n<li><a href=\"#vpn-access\" class=\"preview__body--description--blue\">VPN access</a></li>\n</ol>\n<h3 class=\"preview__body--subtitle\" id=\"security-primer\">Security primer</h3>\n<p>We <strong>strongly</strong> recommend that everyone on your team reads through the following:</p>\n<ol>\n<li><a href=\"https://docs.google.com/document/u/1/d/e/2PACX-1vTikva7hXPd2h1SSglJWhlW8W6qhMlZUxl0qQ9rUJ0OX22CQNeM-91w4lStRk9u2zQIn6lPejUbe-dl/pub\" class=\"preview__body--description--blue\" target=\"_blank\">Gruntwork Security Best\nPractices</a>,\ndoc, which covers critical concepts such as how to securely store secrets, how to lock down servers, and how to\nsecurely use AWS.</li>\n<li><a href=\"https://blog.gruntwork.io/a-comprehensive-guide-to-authenticating-to-aws-on-the-command-line-63656a686799\" class=\"preview__body--description--blue\" target=\"_blank\">A Comprehensive Guide to Authenticating to AWS on the Command\nLine</a>,\nwhich covers all the basics of AWS auth, including all the different options for how to authenticate on the CLI.</li>\n</ol>\n<h3 class=\"preview__body--subtitle\" id=\"root-user\">Root user</h3>\n<p>Each of your AWS accounts has a <a href=\"https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html\" class=\"preview__body--description--blue\" target=\"_blank\">root user</a>. Whoever\ncreated the account should have access to the root user. If you created the account using the <a href=\"/repos/gruntwork\" class=\"preview__body--description--blue\">gruntwork\nCLI</a> or <a href=\"https://aws.amazon.com/organizations/\" class=\"preview__body--description--blue\" target=\"_blank\">AWS Organizations</a>, then\nyou will know the email address associated with the root user, but not the password. To get the password:</p>\n<ol>\n<li>Go to the <a href=\"https://console.aws.amazon.com/console/home\" class=\"preview__body--description--blue\" target=\"_blank\">AWS Console</a></li>\n<li>If you had previously signed into some other AWS account, click "Sign-in using root account credentials."</li>\n<li>Enter the email address and click "Forgot your password" to reset the password.</li>\n<li>Check the email address associated with the root user account for a link you can use to create a new password.</li>\n</ol>\n<p>Please note that the root user account can do just about <em>anything</em> in your AWS account, bypassing almost all security\nrestrictions you put in place, so you need to take extra care with protecting this account. We <strong>very strongly</strong>\nrecommend that you:</p>\n<ol>\n<li>Use a strong password. Preferably 30+ characters, randomly generated, and stored in a secrets manager.</li>\n<li><a href=\"https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html#enable-virt-mfa-for-root\" class=\"preview__body--description--blue\" target=\"_blank\">Enable Multi-Factor Auth</a>\nfor the root user.</li>\n<li><em>Only</em> use the root user to create an IAM User for yourself and then switch to the IAM User for all other operations\nthereafter. You should NOT use the root user account on a day-to-day basis; it's there only for very rare\ncircumstances (e.g., if you get locked out of your IAM User account).</li>\n</ol>\n<h3 class=\"preview__body--subtitle\" id=\"iam-users\">IAM users</h3>\n<p>You should create an <a href=\"https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html\" class=\"preview__body--description--blue\" target=\"_blank\">IAM User</a> for each\ndeveloper in the <strong>security account</strong>. The Reference Architecture enforces a\n<a href=\"https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_account-policy.html\" class=\"preview__body--description--blue\" target=\"_blank\">password policy</a>: see\nthe <code>iam-user-password-policy</code> module in <code>infrastructure-live-multi-account-acme</code> for the details. We also <strong>strongly</strong> recommend\nrequiring every developer to <a href=\"https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa.html\" class=\"preview__body--description--blue\" target=\"_blank\">enable multi-factor auth</a>\nfor their IAM users.</p>\n<h3 class=\"preview__body--subtitle\" id=\"iam-groups\">IAM groups</h3>\n<p>Give each IAM User permissions by adding them to the appropriate <a href=\"https://docs.aws.amazon.com/IAM/latest/UserGuide/id_groups.html\" class=\"preview__body--description--blue\" target=\"_blank\">IAM\nGroups</a>. The following IAM Groups may be of especial\ninterest (see the <a href=\"/repos/module-security/modules/iam-groups\" class=\"preview__body--description--blue\">iam-groups</a> module\nfor a more complete list):</p>\n<ol>\n<li><strong>full-access</strong>: This gives full admin privileges within this AWS account. Use this only for a small number of\ntrusted admins.</li>\n<li><strong>read-only</strong>: This gives read-only access within this AWS account. This is especially useful for production\naccounts where you want to be able to debug, but not necessarily make changes.</li>\n<li><strong>_account.xxx-full-access</strong>: This gives full admin privileges within the AWS account <code>xxx</code>. Use this only for a\nsmall number of trusted admins. See <a href=\"/repos/v0.0.1-06112020/infrastructure-live-multi-account-acme/_docs/09-accounts-and-auth.md\" class=\"preview__body--description--blue\">Accounts and Auth</a> for how to switch between AWS\naccounts.</li>\n<li><strong>_account.xxx-read-only</strong>: This gives read-only access to the AWS account <code>xxx</code>. This is especially useful for\nproduction accounts where you want to be able to debug, but not necessarily make changes.</li>\n<li><strong>_account.xxx-openvpn-users</strong>: This allows users to use <a href=\"/repos/package-openvpn/modules/openvpn-admin\" class=\"preview__body--description--blue\">openvpn-admin</a>\nto request VPN certificates in account <code>xxx</code>. See <a href=\"/repos/v0.0.1-06112020/infrastructure-live-multi-account-acme/_docs/08-ssh-vpn.md\" class=\"preview__body--description--blue\">SSH and VPN</a> for more info.</li>\n<li><strong>ssh-grunt-users</strong>: Users in this group will be able to access servers via SSH. See <a href=\"/repos/v0.0.1-06112020/infrastructure-live-multi-account-acme/_docs/08-ssh-vpn.md\" class=\"preview__body--description--blue\">SSH and VPN</a> for\nmore info.</li>\n<li><strong>ssh-grunt-sudo-users</strong>: Users in this group will be able to access servers via SSH with sudo permissions. See\n<a href=\"/repos/v0.0.1-06112020/infrastructure-live-multi-account-acme/_docs/08-ssh-vpn.md\" class=\"preview__body--description--blue\">SSH and VPN</a> for more info.</li>\n</ol>\n<h3 class=\"preview__body--subtitle\" id=\"ssh-access\">SSH Access</h3>\n<p>We have configured <a href=\"/repos/module-security/modules/ssh-grunt\" class=\"preview__body--description--blue\">ssh-grunt</a> on every\nEC2 Instance, so developers will be able to SSH to the servers using their own usernames and SSH keys. See\n<a href=\"/repos/v0.0.1-06112020/infrastructure-live-multi-account-acme/_docs/08-ssh-vpn.md\" class=\"preview__body--description--blue\">SSH and VPN</a> for instructions.</p>\n<h3 class=\"preview__body--subtitle\" id=\"vpn-access\">VPN Access</h3>\n<p>All the EC2 Instances run in private subnets, so to access them, you must first connect via VPN. See <a href=\"/repos/v0.0.1-06112020/infrastructure-live-multi-account-acme/_docs/08-ssh-vpn.md\" class=\"preview__body--description--blue\">SSH and\nVPN</a> for instructions.</p>\n<p>Note that if you are migrating to AWS from some other data center, you may also want to set up VPN access back to that\ndata center. See <a href=\"https://aws.amazon.com/getting-started/projects/connect-data-center-to-aws/\" class=\"preview__body--description--blue\" target=\"_blank\">How to connect your data center to\nAWS</a> and <a href=\"https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/SetUpVPNConnections.html\" class=\"preview__body--description--blue\" target=\"_blank\">Setting Up an AWS VPN\nConnection</a> for instructions.</p>\n<p>This will involve setting up <a href=\"https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_VPN.html#VPNGateway\" class=\"preview__body--description--blue\" target=\"_blank\">Virtual Private Gateway</a>,\nand you will need to configure the VPCs to propagate routes for these Gateways using the <code>private_propagating_vgws</code>\nand <code>persistence_propagating_vgws</code> variables.</p>\n<h2 class=\"preview__body--subtitle\" id=\"deploy-your-apps\">Deploy your apps</h2>\n<p>Once account access is set up, the next step is to migrate your apps—or even just a single app—to the Reference\nArchitecture, while continuing to use whatever data stores and other dependencies you already have deployed in your old\ndata center (e.g., via a VPN connection). Once you can get one app running in AWS successfully, you can use it as a\ntemplate to move over the others, getting value from the migration right away (i.e., more security, scalability, etc)\nwithout having to wait weeks or months to move everything over (incrementalism!).</p>\n<p>To move over an app, you'll need to do the steps listed below. The Reference Architecture includes two sample apps\n(<code>sample-app-frontend-multi-account-acme</code> and <code>sample-app-backend-multi-account-acme</code>) that show how to implement all of these steps, so use them as a\nreference!</p>\n<p>Note that you do NOT need to do all of these steps at once! You can take care of just a few of them, deploy the app\ninto the Reference Architecture, see that some basic things work, move on to the next few steps, and so on.</p>\n<ol>\n<li><a href=\"#package-the-app\" class=\"preview__body--description--blue\">Package the app</a></li>\n<li><a href=\"#add-a-health-check-endpoint\" class=\"preview__body--description--blue\">Add a health check endpoint</a></li>\n<li><a href=\"#update-the-load-balancer-config\" class=\"preview__body--description--blue\">Update the load balancer config</a></li>\n<li><a href=\"#set-up-config-files-for-each-environment\" class=\"preview__body--description--blue\">Set up config files for each environment</a></li>\n<li><a href=\"#encrypt-secrets\" class=\"preview__body--description--blue\">Encrypt secrets</a></li>\n<li><a href=\"#configure-schema-migrations\" class=\"preview__body--description--blue\">Configure schema migrations</a></li>\n<li><a href=\"#configure-service-discovery\" class=\"preview__body--description--blue\">Configure service discovery</a></li>\n<li><a href=\"#set-up-static-content\" class=\"preview__body--description--blue\">Set up static content</a></li>\n<li><a href=\"#configure-ci-and-cd\" class=\"preview__body--description--blue\">Configure CI and CD</a></li>\n</ol>\n<h3 class=\"preview__body--subtitle\" id=\"package-the-app\">Package the app</h3>\n<p>To deploy into the Reference Architecture, you will need to package your app using one of the following tools:</p>\n<ol>\n<li>\n<p><a href=\"https://www.packer.io/\" class=\"preview__body--description--blue\" target=\"_blank\">Packer</a>: If you are deploying your apps directly on EC2 Instances, use Packer to package\nyour apps as <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html\" class=\"preview__body--description--blue\" target=\"_blank\">Amazon Machine Images</a>.</p>\n</li>\n<li>\n<p><a href=\"https://www.docker.com/\" class=\"preview__body--description--blue\" target=\"_blank\">Docker</a>: If you are deploying your apps as Docker containers, create a <code>Dockerfile</code> to\npackage your app as a Docker image.</p>\n</li>\n</ol>\n<p>See the <a href=\"/repos/sample-app-frontend-multi-account-acme\" class=\"preview__body--description--blue\">sample-app-frontend-multi-account-acme</a> and\n<a href=\"/repos/sample-app-backend-multi-account-acme\" class=\"preview__body--description--blue\">sample-app-backend-multi-account-acme</a> repos for examples.</p>\n<h3 class=\"preview__body--subtitle\" id=\"add-a-health-check-endpoint\">Add a health check endpoint</h3>\n<p>Each app should expose a health check endpoint (e.g., <code>/health</code>) that the load balancer can use to see if the app is\nrunning and ready to receive requests. The health check endpoint should return a <code>200 OK</code> when the app is healthy.\nThe definition of "healthy" depends on the app; for some apps, healthy might mean the app has booted; for others,\nhealthy may mean the app has booted, can talk to databases, and has warmed up its cache.</p>\n<h3 class=\"preview__body--subtitle\" id=\"update-the-load-balancer-config\">Update the load balancer config</h3>\n<p>Now that your app is packaged and has a health check endpoint, you can try deploying it! The sample apps are deployed\nvia Terraform code in the <code>infrastructure-live-multi-account-acme</code> repo, under the <code>services</code> folder (e.g.,\n<code>stage/services/sample-app-frontend-multi-account-acme</code>). Make a copy of that folder and customize the <code>terragrunt.hcl</code> file within it\nto deploy your app instead. This will include specifying which paths in the load balancer your app should claim.</p>\n<p>This is because the Reference Architecture uses a single <a href=\"https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html\" class=\"preview__body--description--blue\" target=\"_blank\">Application Load Balancer\n(ALB)</a> for several apps in\neach environment, and each of those apps can claim certain paths (e.g., <code>/foo</code> goes to app foo, <code>/bar</code> goes to app bar)\nand/or certain domain names (e.g., <code>foo.acme.com</code> goes to app foo, <code>bar.acme.com</code> goes to app bar). If you want your\napp to handle all paths, you can use regular expressions (e.g., <code>*</code>).</p>\n<h3 class=\"preview__body--subtitle\" id=\"set-up-config-files-for-each-environment\">Set up config files for each environment</h3>\n<p>If your app needs to be configured differently in each environment (e.g., in stage and prod), we recommend creating\nconfig files for each environment, checking those configs into version control, and packaging them with the app. That\nway, config is updated, versioned, tested, and deployed exactly like the rest of your code. This tends to be easier\nto reason about and far less error prone than storing configs externally (e.g., in a DB).</p>\n<p>The sample apps use JSON files for config, but any format supported by your apps will be fine, so long as it is\nfile-based. See the <code>config</code> folder in the sample apps for an example, and check out the <code>bin/run-app.sh</code> script for\nhow the sample apps select the proper config file for the current environment.</p>\n<h3 class=\"preview__body--subtitle\" id=\"encrypt-secrets\">Encrypt secrets</h3>\n<p>If your apps need access to secrets, such as the password to a database, we recommend encrypting those secrets with\n<a href=\"https://aws.amazon.com/kms/\" class=\"preview__body--description--blue\" target=\"_blank\">KMS</a> and adding the ciphertext to your config files. Your apps can then use IAM Roles to\ndecrypt the secrets just before booting.</p>\n<p>See <a href=\"/repos/v0.0.1-06112020/infrastructure-live-multi-account-acme/_docs/05-dev-environment.md#encrypt-a-secret\" class=\"preview__body--description--blue\">encrypt a secret</a> for instructions.</p>\n<h3 class=\"preview__body--subtitle\" id=\"configure-schema-migrations\">Configure schema migrations</h3>\n<p>If your apps talk to a relational database, you will need a way to evolve the database schema over time. The best way\nto do that is to use a schema migration tool such as <a href=\"https://flywaydb.org/\" class=\"preview__body--description--blue\" target=\"_blank\">Flyway</a> or\n<a href=\"https://www.liquibase.org/\" class=\"preview__body--description--blue\" target=\"_blank\">Liquibase</a> to define the schema migrations as code, check that code into version control,\npackage it with your apps, and have your apps apply the schema migrations just before booting (Flyway and Liquibase\nwill use locks to ensure there are no concurrency issues).</p>\n<p>See the <code>sql</code> folder and <code>bin/run-app.sh</code> of <code>sample-app-backend-multi-account-acme</code> for an example.</p>\n<h3 class=\"preview__body--subtitle\" id=\"configure-service-discovery\">Configure service discovery</h3>\n<p>If you need to run multiple apps ("microservices") that talk to each other, those apps will need a way to discover each\nother's IP and port. In the dynamic world of AWS, IPs and ports change all the time, so you don't want to hard code\nthem. Instead, use one of the following options:</p>\n<ol>\n<li>\n<p>Use an internal load balancer. Each of your apps can register at a different path with the load balancer, and each\napp gets the load balancer domain name as an environment variable (via Terraform), so, for example, you can talk to\napp <code>foo</code> by sending a request to <code><LOAD_BALANCER_DOMAIN_NAME>/foo</code> and to app <code>bar</code> by sending a request to\n<code><LOAD_BALANCER_DOMAIN_NAME>/bar</code>. This is how <code>sample-app-frontend-multi-account-acme</code> talks to <code>sample-app-backend-multi-account-acme</code>.</p>\n</li>\n<li>\n<p>For Docker containers running in ECS, you can use <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-discovery.html\" class=\"preview__body--description--blue\" target=\"_blank\">ECS Service\nDiscovery</a>. As of May, 2018,\nGruntwork has not yet added support for this, but it should be added to the <a href=\"/repos/module-ecs\" class=\"preview__body--description--blue\">ECS\nmodules</a> soon.</p>\n</li>\n<li>\n<p>Use a tool such as <a href=\"https://www.consul.io/\" class=\"preview__body--description--blue\" target=\"_blank\">Consul</a>. This does require running extra infrastructure, which is made\neasier via the Gruntwork <a href=\"/repos/terraform-aws-consul\" class=\"preview__body--description--blue\">Consul module</a> (not currently part of\nthe Reference Architecture).</p>\n</li>\n</ol>\n<h3 class=\"preview__body--subtitle\" id=\"set-up-static-content\">Set up static content</h3>\n<p>The best way to serve CSS, JS, images, and fonts is by putting them in an <a href=\"https://aws.amazon.com/s3/\" class=\"preview__body--description--blue\" target=\"_blank\">S3 bucket</a> and\nusing <a href=\"https://aws.amazon.com/cloudfront/\" class=\"preview__body--description--blue\" target=\"_blank\">CloudFront</a> as a CDN in front of it. Your Reference Architecture may already\ncontain an example of this under the <code>services/static-website</code> folder. Your CloudFront distribution will have its own\ndomain name (e.g., <code>static.<your-company>.com</code>), so you'll need to ensure that any code of yours that links to static\ncontent (e.g., server-side rendered HTML) makes it possible to configure a different static content domain name in\neach environment.</p>\n<h3 class=\"preview__body--subtitle\" id=\"configure-ci-and-cd\">Configure CI and CD</h3>\n<p>The sample apps have Continuous Integration (CI) and Continuous Delivery (CD) configured for them. A CI job will run\nafter every commit to build, test, and package the apps, and, for certain commits (e.g., to specific branches or with\nspecific tags), the CI job will automatically deploy the app to stage or prod.</p>\n<p>See <a href=\"/repos/v0.0.1-06112020/infrastructure-live-multi-account-acme/_docs/06-ci-cd.md\" class=\"preview__body--description--blue\">Build, tests, and deployment (CI/CD)</a> and the sample app repos for details on how to set up the same\nCI/CD workflow for your real apps.</p>\n<h2 class=\"preview__body--subtitle\" id=\"migrate-your-data-stores\">Migrate your data stores</h2>\n<p>Once you have your apps running in AWS, the next step is to migrate the data stores they depend on. We again recommend\ndoing this incrementally, moving over one data store at a time, if possible. There are two ways to move over your data\nstores:</p>\n<ol>\n<li>Take a downtime</li>\n<li>Do a zero-downtime migration</li>\n</ol>\n<p>Option #2 typically takes 100x longer (this is NOT an exaggeration) than option #1 and has a much higher risk of\ndata loss or data corruption. If at all possible, we <strong>very strongly</strong> recommend taking a brief downtime to do the\nmigration, as it will make your life much easier.</p>\n<p>A few guidelines about migrating data stores:</p>\n<ol>\n<li><a href=\"#migrating-from-another-aws-managed-data-store\" class=\"preview__body--description--blue\">Migrating from another AWS-managed data store</a></li>\n<li><a href=\"#migrating-from-external-data-stores\" class=\"preview__body--description--blue\">Migrating from external data stores</a></li>\n<li><a href=\"#if-you-absolutely-must-do-a-zero-downtime-migration\" class=\"preview__body--description--blue\">If you absolutely must do a zero-downtime migration</a></li>\n</ol>\n<h3 class=\"preview__body--subtitle\" id=\"migrating-from-another-aws-managed-data-store\">Migrating from another AWS-managed data store</h3>\n<p>If you are migrating to the Reference Architecture from another AWS account, and in that account you are using an\nAWS-managed data store such as <a href=\"https://aws.amazon.com/rds/\" class=\"preview__body--description--blue\" target=\"_blank\">RDS</a> or <a href=\"https://aws.amazon.com/elasticache/\" class=\"preview__body--description--blue\" target=\"_blank\">ElastiCache</a>,\nthe migration process (with downtime!) is:</p>\n<ol>\n<li>Put your app in read-only mode or take a downtime.</li>\n<li>Use the built-in snapshotting mechanism to take a snapshot of the data store (e.g., <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_CreateSnapshot.html\" class=\"preview__body--description--blue\" target=\"_blank\">RDS\nsnapshots</a> or\n<a href=\"https://docs.aws.amazon.com/AmazonElastiCache/latest/APIReference/API_Snapshot.html\" class=\"preview__body--description--blue\" target=\"_blank\">ElastiCache snapshots</a>).</li>\n<li>Share the snapshot with the prod account where the Reference Architecture is deployed (e.g., see <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ShareSnapshot.html\" class=\"preview__body--description--blue\" target=\"_blank\">Sharing a DB\nSnapshot</a>).</li>\n<li>Deploy a new data store in the Reference Architecture account from the snapshot (e.g., set the\n<code>snapshot_identifier</code> parameter in the <a href=\"/repos/module-data-storage/modules/rds\" class=\"preview__body--description--blue\">rds module</a>).</li>\n<li>Bring your app back up.</li>\n</ol>\n<h3 class=\"preview__body--subtitle\" id=\"migrating-from-external-data-stores\">Migrating from external data stores</h3>\n<p>If you are migrating data stores from non-AWS managed systems (e.g., from your own data center), the process is the\nsame as in the previous section, except instead of using the snapshotting mechanism built into AWS, you will need to\nuse one of these mechanisms instead:</p>\n<ol>\n<li>Use the <a href=\"https://aws.amazon.com/dms/\" class=\"preview__body--description--blue\" target=\"_blank\">AWS database migration service</a> to migrate relational databases.</li>\n<li>Use a mechanism supported by RDS and your database, such as <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/MySQL.Procedural.Importing.html\" class=\"preview__body--description--blue\" target=\"_blank\">restoring MySQL into RDS using Percona\nXtraBackup</a>.</li>\n<li>Use your data store's native snapshot functionality (e.g., <a href=\"https://dev.mysql.com/doc/refman/5.5/en/mysqldump.html\" class=\"preview__body--description--blue\" target=\"_blank\">mysqldump</a>)\nto make a backup of your database, copy the backup into AWS (e.g., into an S3 bucket), and fire up an EC2 Instance\nthat connects to the data store and loads the backup into it.</li>\n</ol>\n<p>Note that all of these assume at least some amount of downtime!</p>\n<h3 class=\"preview__body--subtitle\" id=\"if-you-absolutely-must-do-a-zero-downtime-migration\">If you absolutely must do a zero-downtime migration</h3>\n<p>If you absolutely cannot take any downtime whatsoever (unlikely, as you most likely have unplanned outages anyway!),\nthings get a lot more complicated. The typical strategy is:</p>\n<ol>\n<li>Take an initial snapshot of your data store and load it into AWS. The idea is to move most of the data over early on\nand then "catch up" whatever new data is written in the following steps.</li>\n<li>Find a way to send all new writes to both your original data store and the new one in AWS.</li>\n<li>Once the new data store has "caught up", start using it directly instead of the old database.</li>\n</ol>\n<p>Step #2 is the tricky one, as dual writes are notoriously error prone. For example, what happens if the process doing\ndual writes succeeds with the first write, but crashes before the second? What if it sends the requests to both data\nstores, but one of them is temporarily down? What if both requests make it to the data stores, but then a transaction\ngets rolled back? See <a href=\"https://www.confluent.io/blog/using-logs-to-build-a-solid-data-infrastructure-or-why-dual-writes-are-a-bad-idea/\" class=\"preview__body--description--blue\" target=\"_blank\">why dual writes are a bad\nidea</a>\nfor more info.</p>\n<p>The most common solutions to these problems are:</p>\n<ol>\n<li>\n<p>Use a change capture system. This is a system that effectively replicates your data store's underlying log: you\nwrite to the original data store, and if this write has succeeded and been committed, it triggers an "event" in the\nchange capture system, which an event processor then writes to the second data store. For example,\nsee <a href=\"https://github.com/linkedin/databus\" class=\"preview__body--description--blue\" target=\"_blank\">databus</a> and <a href=\"https://github.com/confluentinc/bottledwater-pg\" class=\"preview__body--description--blue\" target=\"_blank\">bottledwater-pg</a>.</p>\n</li>\n<li>\n<p>Send all writes initially to a log system such as <a href=\"https://kafka.apache.org/\" class=\"preview__body--description--blue\" target=\"_blank\">Apache Kafka</a>. You then have one\nKafka consumer that writes the messages to your original data store and another that writes it to the new data store.\nKafka guarantees at-least once delivery, so as long as the database writes are idempotent, this ensures no data\nwill be lost.</p>\n</li>\n</ol>\n<p>Whatever option you go with, we strongly recommend adding "integrity checking" to verify no data was lost or corrupted.\nSome example integrity checks:</p>\n<ol>\n<li>Count the number of rows in each table and make sure they are identical in both data stores.</li>\n<li>Perform a checksum or hash on all the data in a table (or some randomly selected subset if there's too much data)\nand make sure you get identical values in both data stores.</li>\n</ol>\n<h2 class=\"preview__body--subtitle\" id=\"configure-monitoring-and-alerting\">Configure monitoring and alerting</h2>\n<p>Once your apps and data stores are working, you'll want to make sure you have the following monitoring and alerting in\nplace:</p>\n<ol>\n<li><a href=\"#metrics\" class=\"preview__body--description--blue\">Metrics</a></li>\n<li><a href=\"#alerts\" class=\"preview__body--description--blue\">Alerts</a></li>\n<li><a href=\"#logs\" class=\"preview__body--description--blue\">Logs</a></li>\n</ol>\n<h3 class=\"preview__body--subtitle\" id=\"metrics\">Metrics</h3>\n<p>Make sure all the metrics you expect show up in <a href=\"https://aws.amazon.com/cloudwatch/\" class=\"preview__body--description--blue\" target=\"_blank\">CloudWatch</a>. All AWS services\nautomatically send metrics to CloudWatch and the Reference Architecture includes a few important ones that are not\navailable by default (e.g., memory and disk space usage on EC2 Instances). Create\n<a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Dashboards.html\" class=\"preview__body--description--blue\" target=\"_blank\">Dashboards</a> for the\nmost important metrics, such as CPU usage in your ECS cluster or request count in the ALB.</p>\n<p>Note that the Reference Architecture uses CloudWatch for metrics as it's built-into AWS, very inexpensive, and requires\nvery little work to maintain. However, the CloudWatch UI is fairly basic, so if you need more powerful tools for slicing\nand dicing your metrics, consider tools such as <a href=\"https://www.datadoghq.com/\" class=\"preview__body--description--blue\" target=\"_blank\">DataDog</a>,\n<a href=\"https://newrelic.com/\" class=\"preview__body--description--blue\" target=\"_blank\">New Relic</a>, or <a href=\"https://prometheus.io/\" class=\"preview__body--description--blue\" target=\"_blank\">Prometheus</a>. If you'd like help with this, please reach\nout to support@gruntwork.io.</p>\n<h3 class=\"preview__body--subtitle\" id=\"alerts\">Alerts</h3>\n<p>Configure alerts to go off for key metrics. All the services in the Reference Architecture have basic alarms\nconfigured directly in the Terraform code, such as low disk space alarms on RDS DBs and high CPU usage alarms for the\nECS cluster. All of the Terraform modules allow you to tweak the settings for these alarms (e.g., should the CPU\nusage alarm go off at 80% or 90%?), so take some time to go through each module in <code>infrastructure-modules-multi-account-acme</code> and\nmake sure the settings match your use cases so you don't have too many false positives or negatives.</p>\n<h3 class=\"preview__body--subtitle\" id=\"logs\">Logs</h3>\n<p>The logs from all servers get automatically sent to <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html\" class=\"preview__body--description--blue\" target=\"_blank\">CloudWatch\nLogs</a>. The <code>user-data.sh</code> scripts\nin <code>infrastructure-modules-multi-account-acme</code> call <code>run-cloudwatch-logs-agent.sh</code> (from the <a href=\"/repos/module-aws-monitoring/modules/logs/cloudwatch-log-aggregation-scripts\" class=\"preview__body--description--blue\">cloudwatch-log-aggregation-scripts\nmodule</a>)\nto configure which log files get sent to CloudWatch. By default, <code>syslog</code> is sent to CloudWatch on every server, and\nECS Services are configured to send their logs to <code>syslog</code>. Make sure you can find the logs for your apps there\n<em>before</em> you go live!</p>\n<p>Note that the Reference Architecture uses CloudWatch Logs as it's built-into AWS, very inexpensive, and requires\nvery little work to maintain. However, the CloudWatch UI is fairly basic, so if you need more powerful tools for slicing\nand dicing your log data, consider tools such as <a href=\"https://www.elastic.co/elk-stack\" class=\"preview__body--description--blue\" target=\"_blank\">ELK</a> (as of May, 2018, the ELK\nsupport is under development in the <a href=\"/repos/package-elk\" class=\"preview__body--description--blue\">package-elk</a> repo and should be\nready soon), <a href=\"https://www.loggly.com/\" class=\"preview__body--description--blue\" target=\"_blank\">Loggly</a>, and <a href=\"https://papertrailapp.com/\" class=\"preview__body--description--blue\" target=\"_blank\">Papertrail</a>. If you'd like help with\nthis, please reach out to support@gruntwork.io.</p>\n<h2 class=\"preview__body--subtitle\" id=\"test\">Test</h2>\n<p>As you go through the process of migrating your apps and data stores, you should continuously be testing your\ninfrastructure to make sure things are working. Here are the key types of tests to perform:</p>\n<ol>\n<li>\n<p><strong>Manual testing</strong>: Hit URLs by hand, look at log files, and check the metrics. You already know how to do this!</p>\n</li>\n<li>\n<p><strong>Automated testing</strong>: We strongly recommend writing automated tests that (a) deploy your infrastructure into an\nAWS account dedicated for testing, (b) check the infrastructure works as expected, and (c) tears the infrastructure\nback down at the end of the test. You can use the <a href=\"/repos/terratest\" class=\"preview__body--description--blue\">Terratest</a> library\nto write these tests.</p>\n</li>\n<li>\n<p><strong>Load testing</strong>: You should make sure your code performs well under the kind of loads you expect in production.\nUse tools such as <a href=\"https://httpd.apache.org/docs/2.4/programs/ab.html\" class=\"preview__body--description--blue\" target=\"_blank\">Apache Bench</a>,\n<a href=\"https://jmeter.apache.org/\" class=\"preview__body--description--blue\" target=\"_blank\">JMeter</a> to throw traffic at your infrastructure and when things fall over, make sure\nyour monitoring and alerting can help you find the bottlenecks! Load testing across several different EC2 Instance\nTypes can also be a good way to find the sweet spot between price and performance (see <a href=\"https://www.slideshare.net/brendangregg/how-netflix-tunes-ec2-instances-for-performance\" class=\"preview__body--description--blue\" target=\"_blank\">How Netflix Tunes EC2\nInstances for Performance</a>).</p>\n</li>\n<li>\n<p><strong>Security testing</strong>: Bring in an outside firm to pen test and audit your code to look for security vulnerabilities.\nGruntwork customers have done this several times with the Reference Architecture already, and we've fixed all issues\nthat were found, but if you find any new vulnerabilities, please report them to support@gruntwork.io!</p>\n</li>\n</ol>\n<h2 class=\"preview__body--subtitle\" id=\"switch-over-dns\">Switch over DNS</h2>\n<p>The Reference Architecture is typically deployed with "placeholder" domain names (e.g., <code>your-company-aws.com</code>) to make\ntesting easy and safe. Once your apps start passing tests, you can switch over your real domain names to point to\nthe Reference Architecture as follows:</p>\n<ol>\n<li>\n<p>We recommend managing all DNS entries using <a href=\"https://aws.amazon.com/route53/\" class=\"preview__body--description--blue\" target=\"_blank\">Route 53</a>. If you bought your\nproduction domain name using some other registrar, you can either configure <a href=\"https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/MigratingDNS.html\" class=\"preview__body--description--blue\" target=\"_blank\">Route 53 as the DNS\nService</a> for your existing domain name\nor <a href=\"https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-transfer-to-route-53.html\" class=\"preview__body--description--blue\" target=\"_blank\">transfer the domain name entirely to Route\n53</a>.</p>\n</li>\n<li>\n<p>If you have multiple AWS accounts (e.g., dev, stage, prod), we recommend that each one use its own, completely\nseparate domain names (e.g., <code>my-company-dev.com</code>, <code>my-company-stage.com</code>, etc.). This reduces the chances of\naccidental errors (e.g., breaking prod DNS entries while trying to make changes to stage) and makes it harder for\nattackers to do damage (e.g., using cookies from pre-prod environment in a prod environment).</p>\n</li>\n<li>\n<p>Before switching the domain names on your apps, make to use <a href=\"https://aws.amazon.com/certificate-manager/\" class=\"preview__body--description--blue\" target=\"_blank\">AWS Certificate Manager\n(ACM)</a> to request TLS certificates for your prod domain names and\nassociate those certificates with your Load Balancers and CloudFront distributions. ACM is a great choice for\nTLS certificates, because they are (a) free and (b) issue in 1-2 minutes for domain names you own in Route 53, and\n(c) renew automatically!</p>\n</li>\n<li>\n<p>To switch over your ALBs or ECS services to use different domain names, simply update the <code>domain_name</code> variables in\nthe corresponding <code>terragrunt.hcl</code>.</p>\n</li>\n</ol>\n<h2 class=\"preview__body--subtitle\" id=\"next-steps\">Next steps</h2>\n<p>Now that you know how to migrate to the Reference Architecture, the next thing to learn is <a href=\"/repos/v0.0.1-06112020/infrastructure-live-multi-account-acme/_docs/13-deploying-the-reference-architecture-from-scratch.md\" class=\"preview__body--description--blue\">how to deploy the\nReference Architecture from scratch</a>.</p>\n","repoName":"infrastructure-live-multi-account-acme","repoRef":"v0.0.1-01172020","serviceDescriptor":{"serviceName":"Multi-account Reference Architecture","serviceRepoName":"infrastructure-live-multi-account-acme","serviceRepoOrg":"gruntwork-io","cloudProviders":["aws"],"description":"End-to-end tech stack designed to deploy into multiple AWS accounts. Includes VPCs, EKS, ALBs, CI / CD, monitoring, alerting, VPN, DNS, and more.","imageUrl":"grunt.png","licenseType":"subscriber","technologies":["Terraform","Go","Bash","Python"],"compliance":[],"tags":[""]},"serviceCategoryName":"Reference Architecture","fileName":"12-migration.md","filePath":"/_docs/12-migration.md","title":"Repo Browser: Multi-account Reference Architecture","description":"Browse the repos in the Gruntwork Infrastructure as Code Library."}