Multi-Region Terraform Deployments with Amazon CodePipeline using Terraform Built CI/CD

海外精选
海外精选的内容汇集了全球优质的亚马逊云科技相关技术内容。同时,内容中提到的“AWS” 是 “Amazon Web Services” 的缩写,在此网站不作为商标展示。
0
0
{"value":"As of February 2022, the AWS Cloud spans 84 Availability Zones within 26 geographic Regions, with announced plans for more Availability Zones and Regions. Customers can leverage this global infrastructure to expand their presence to their primary target of users, satisfying data residency requirements, and implementing disaster recovery strategy to make sure of business continuity. Although leveraging multi-Region architecture would address these requirements, deploying and configuring consistent infrastructure stacks across multi-Regions could be challenging, as AWS Regions are designed to be autonomous in nature. Multi-region deployments with Terraform and [AWS CodePipeline](https://aws.amazon.com/codepipeline/) can help customers with these challenges.\n\nIn this post, we’ll demonstrate the best practice for multi-Region deployments using [HashiCorp Terraform](https://www.terraform.io/) as infrastructure as code (IaC), and [AWS CodeBuild](https://aws.amazon.com/codebuild/) , CodePipeline as continuous integration and continuous delivery (CI/CD) for consistency and repeatability of deployments into multiple AWS Regions and AWS Accounts. We’ll dive deep on the IaC deployment pipeline architecture and the best practices for structuring the Terraform project and configuration for multi-Region deployment of multiple AWS target accounts.\n\nYou can find the sample code for this solution [here](https://github.com/aws-samples/aws-multi-region-cicd-with-terraform)\n\n### **Solutions Overview**\n#### **Architecture**\nThe following architecture diagram illustrates the main components of the multi-Region Terraform deployment pipeline with all of the resources built using IaC.\n\nDevOps engineer initially works against the infrastructure repo in a short-lived branch. Once changes in the short-lived branch are ready, DevOps engineer gets them reviewed and merged into the main branch. Then, DevOps engineer git tags the repo. For any future changes in the infra repo, DevOps engineer repeats this same process.\n\n![image.png](https://dev-media.amazoncloud.cn/fc38edce3a114dd2b692a2aa6d204f0e_image.png)\n\nFig 1. Tagging to release from the main branch.\n\n1. The deployment is triggered from DevOps engineer git tagging the repo, which contains the Terraform code to be deployed. This action starts the deployment pipeline execution.\nTagging with ‘dev_us-east-1/research/1.0’ triggers a pipeline to deploy the research dev account to us-east-1. In our example git tag ‘dev_us-east-1/research/1.0’ contains the target environment (i.e., dev), AWS Region (i.e. us-east-1), team (i.e., research), and a version number (i.e., 1.0) that maps to an annotated tag on a commit ID. The target workload account aliases (i.e., research dev, risk qa) are mapped to AWS account numbers in the environment configuration files of the infra repo in [AWS CodeCommit](https://aws.amazon.com/codecommit/).\n\n![image.png](https://dev-media.amazoncloud.cn/9a85465389ae43e3a9a9925baed4c8ac_image.png)\n\nFig 2. Multi-Region AWS deployment with IaC and CI/CD pipelines.\n\n2. To capture the exact git tag that starts a pipeline, we use an [Amazon EventBridge](https://aws.amazon.com/eventbridge/) rule. The rule is triggered when the tag is created with an environment prefix for deploying to a respective environment (i.e., dev). The rule kicks off an [AWS CodeBuild](https://aws.amazon.com/cn/codebuild/?trk=cndc-detail) project that takes the git tag from the [AWS CodeCommit](https://aws.amazon.com/cn/codecommit/?trk=cndc-detail) event and stores it with a full clone of the repo into a versioned [Amazon Simple Storage Service (Amazon S3)](https://aws.amazon.com/s3/) bucket for the corresponding environment.\n3. We have a continuous delivery pipeline defined in [AWS CodePipeline](https://aws.amazon.com/cn/codepipeline/?trk=cndc-detail). To make sure that the pipelines for each environment run independent of each other, we use a separate pipeline per environment. Each pipeline consists of three stages in addition to the Source stage:\n\n\ta.IaC linting stage – A stage for linting Terraform code. For illustration purposes, we’ll use the open source tool tflint.\n\n\tb.IaC security scanning stage – A stage for static security scanning of Terraform code. There are many tooling choices when it comes to the security scanning of Terraform code. Checkov, TFSec, and Terrascan are the commonly used tools. For illustration purposes, we’ll use the open source tool Checkov.\n\n\tc.IaC build stage – A stage for Terraform build. This includes an action for the Terraform execution plan followed by an action to apply the plan to deploy the stack to a specific Region in the target workload account.\n\n4. Once the Terraform apply is triggered, it deploys the infrastructure components in the target workload account to the AWS Region based on the git tag. In turn, you have the flexibility to point the deployment to any AWS Region or account configured in the repo.\n5. The sample infrastructure in the target workload account consists of an [AWS Identity and Access Management (IAM) role](https://aws.amazon.com/iam/), an external facing Application Load Balancer (ALB), as well as all of the required resources down to the Amazon Virtual Private Cloud ([Amazon VPC](https://aws.amazon.com/cn/vpc/?trk=cndc-detail)). Upon successful deployment, browsing to the external facing ALB DNS Name URL displays a very simple message including the location of the Region.\n\n### **Architectural considerations**\n#### **Multi-account strategy**\nLeveraging well-architected multi-account strategy, we have a separate central tooling account for housing the code repository and infrastructure pipeline, and a separate target workload account to house our sample workload infra-architecture. The clean account separation lets us easily control the IAM permission for granular access and have different guardrails and security controls applied. Ultimately, this enforces the separation of concerns as well as minimizes the blast radius.\n\n![image.png](https://dev-media.amazoncloud.cn/2f3469ccb6144a7aad11a4513434c271_image.png)\n\n\nFig 3. A separate pipeline per environment.\n\nThe sample architecture shown above contained a pipeline per environment (DEV, QA, STAGING, PROD) in the tooling account deploying to the target workload account for the respective environment. At scale, you can consider having multiple infrastructure deployment pipelines for multiple business units in the central tooling account, thereby targeting workload accounts per environment and business unit. If your organization has a complex business unit structure and is bound to have different levels of compliance and security controls, then the central tooling account can be further divided into the central tooling accounts per business unit.\n\n### **Pipeline considerations**\nThe infrastructure deployment pipeline is hosted in a central tooling account and targets workload accounts. The pipeline is the **authoritative source** managing the full lifecycle of resources. The goal is to decrease the risk of ad hoc changes (e.g., manual changes made directly via the console) that can’t be easily reproduced at a future date. The pipeline and the build step each run as their own IAM role that adheres to the **principle of least privilege**. The pipeline is configured with a stage to lint the Terraform code, as well as a static security scan of the Terraform resources following the principle of **shifting security left** in the SDLC.\n\nAs a further improvement for resiliency and applying the cell architecture principle to the CI/CD deployment, we can consider having multi-Region deployment of the [AWS CodePipeline](https://aws.amazon.com/cn/codepipeline/?trk=cndc-detail) pipeline and [AWS CodeBuild](https://aws.amazon.com/cn/codebuild/?trk=cndc-detail) build resources, in addition to a clone of the [AWS CodeCommit](https://aws.amazon.com/cn/codecommit/?trk=cndc-detail) repository. We can use the approach detailed in this [post](https://aws.amazon.com/blogs/devops/replicate-aws-codecommit-repository-between-regions-using-aws-fargate/) to sync the repo across multiple regions. This means that both the workload architecture and the deployment infrastructure are multi-Region. However, it’s important to note that the business continuity requirements of the infrastructure deployment pipeline are most likely different than the requirements of the workloads themselves.\n\n![image.png](https://dev-media.amazoncloud.cn/e89f6f336ea948a8b1159930f0207582_image.png)\n\nFig 4. Multi-Region CI/CD dev pipelines targeting the dev workload account resources in the respective Region.\n\n### **Deeper dive into Terraform code**\n#### **Backend configuration and state**\nAs a prerequisite, we created [Amazon S3](https://aws.amazon.com/cn/s3/?trk=cndc-detail) buckets to store the Terraform state files and [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) tables for the state file locks. The latter is a best practice to prevent concurrent operations on the same state file. For naming the buckets and tables, our code expects the use of the same prefix (i.e., <tf_backend_config_prefix>-<env> for buckets and <tf_backend_config_prefix>-lock-<env> for tables). The value of this prefix must be passed in as an input param (i.e., “tf_backend_config_prefix”). Then, it’s fed into AWS CodeBuild actions for Terraform as an environment variable. Separation of remote state management resources (Amazon S3 bucket and Amazon DynamoDB table) across environments makes sure that we’re minimizing the blast radius.\\n\\n```\\n-backend-config=\\"bucket=\${TF_BACKEND_CONFIG_PREFIX}-\${ENV}\\" \\n-backend-config=\\"dynamodb_table=\${TF_BACKEND_CONFIG_PREFIX}-lock-\${ENV}\\"\\n```\\n\\n![image.png](https://dev-media.amazoncloud.cn/57dc9222ddfd45b095520c8ca37d7b55_image.png)\\n\\nFig 5. Terraform state file buckets and state lock tables per environment in the central tooling account.\\n\\nThe git tag that kicks off the pipeline is named with the following convention of “<env>_<region>/<team>/<version>” for regional deployments and “<env>_global/<team>/<version>” for global resource deployments. The stage following the source stage in our pipeline, tflint stage, is where we parse the git tag. From the tag, we derive the values of environment, deployment scope (i.e., Region or global), and team to determine the Terraform state Amazon S3 object key uniquely identifying the Terraform state file for the deployment. The values of environment, deployment scope, and team are passed as environment variables to the subsequent AWS CodeBuild Terraform plan and apply actions.\\n\\n```\\n-backend-config=\\"key=\${TEAM}/\${ENV}-\${TARGET_DEPLOYMENT_SCOPE}/terraform.tfstate\\"\\n```\\n\\nWe set the Region to the value of [AWS_REGION env variable](https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html) that is made available by AWS CodeBuild, and it’s the Region in which our build is running.\\n\\n```\\n-backend-config=\\"region=\$AWS_REGION\\"\\n```\\nThe following is how the Terraform backend config initialization looks in our AWS CodeBuild buildspec files for Terraform actions, such as tflint, plan, and apply.\\n\\n```\\nterraform init -backend-config=\\"key=\${TEAM}/\${ENV}-\\n\${TARGET_DEPLOYMENT_SCOPE}/terraform.tfstate\\" -backend-config=\\"region=\$AWS_REGION\\"\\n-backend-config=\\"bucket=\${TF_BACKEND_CONFIG_PREFIX}-\${ENV}\\" \\n-backend-config=\\"dynamodb_table=\${TF_BACKEND_CONFIG_PREFIX}-lock-\${ENV}\\"\\n-backend-config=\\"encrypt=true\\"\\n```\\nUsing this approach, the Terraform states for each combination of account and Region are kept in their own distinct state file. This means that if there is an issue with one Terraform state file, then the rest of the state files aren’t impacted.\\n\\n![image.png](https://dev-media.amazoncloud.cn/f047952b05a5417791f584e1abfb3333_image.png)\\n\\nFig 6. Terraform state files per account and Region for each environment in the central tooling account\\n\\nFollowing the example, a git tag of the form “dev_us-east-1/research/1.0” that kicks off the dev pipeline works against the research team’s dev account’s state file containing us-east-1 Regional resources (i.e., Amazon S3 object key “research/dev-us-east-1/terraform.tfstate” in the S3 bucket <tf_backend_config_prefix>-dev), and a git tag of the form “dev_ap-southeast-1/risk/1.0” that kicks off the dev pipeline works against the risk team’s dev account’s Terraform state file containing ap-southeast-1 Regional resources (i.e., Amazon S3 object key “risk/dev-ap-southeast-1/terraform.tfstate”). For global resources, we use a git tag of the form “dev_global/research/1.0” that kicks off a dev pipeline and works against the research team’s dev account’s global resources as they are at account level (i.e., “research/dev-global/terraform.tfstate).\\n\\n![image.png](https://dev-media.amazoncloud.cn/42b206a214114e219b136de6233ac94b_image.png)\\n\\nFig 7. Git tags and the respective Terraform state files.\\n\\nThis backend configuration makes sure that the state file for one account and Region is independent of the state file for the same account but different Region. Adding or expanding the workload to additional Regions would have no impact on the state files of existing Regions.\\n\\nIf we look at the further improvement where we make our deployment infrastructure also multi-Region, then we can consider each Region’s CI/CD deployment to be the **authoritative source** for its local Region’s deployments and Terraform state files. In this case, tagging against the repo triggers a pipeline within the local CI/CD Region to deploy resources in the Region. The Terraform state files in the local Region are used for keeping track of state for the account’s deployment within the Region. This further decreases cross-regional dependencies.\\n\\n![image.png](https://dev-media.amazoncloud.cn/0f59c2349cfe4afe90230d08ef70fad3_image.png)\\n\\nFig 8. Multi-Region CI/CD with Terraform state resources stored in the same Region as the workload account resources for the respective Region\\n\\n### **Provider**\\nFor deployments, we use the default Terraform AWS provider. The provider is **parametrized** with the value of the region passed in as an input parameter.\\n\\n```\\nprovider \\"aws\\" {\\n region = var.region\\n ...\\n}\\n```\\nOnce the provider knows which Region to target, we can refer to the current AWS Region in the rest of the code.\\n\\n```\\n# The value of the current AWS region is the name of the AWS region configured on the provider\\n# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region\\ndata \\"aws_region\\" \\"current\\" {} \\n\\nlocals {\\n region = data.aws_region.current.name # then use local.region where region is needed\\n}\\n```\\nProvider is configured to assume a cross account IAM role defined in the workload account. The value of the account ID is fed as an input parameter.\\n\\n```\\nprovider \\"aws\\" {\\n region = var.region\\n assume_role {\\n role_arn = \\"arn:aws:iam::\${var.account}:role/InfraBuildRole\\"\\n session_name = \\"INFRA_BUILD\\"\\n }\\n}\\n```\\nThis InfraBuildRole IAM role could be created as part of the account creation process. The [AWS Control Tower Terraform Account Factory](https://aws.amazon.com/blogs/aws/new-aws-control-tower-account-factory-for-terraform/) could be used to automate this.\\n\\n### **Code**\\n#### **Minimize cross-regional dependencies**\\nWe keep the Regional resources and the global resources (e.g., IAM role or policy) in distinct namespaces following the **cell architecture** principle. We treat each Region as one cell, with the goal of decreasing cross-regional dependencies. Regional resources are created once in each Region. On the other hand, global resources are created once globally and may have cross-regional dependencies (e.g., DynamoDB global table with a replica table in multiple Regions). There’s no “global” Terraform AWS provider since the AWS provider requires a Region. This means that we pick a specific Region from which to deploy our global resources (i.e., global_resource_deploy_from_region input param). By creating a distinct Terraform namespace for Regional resources (e.g., module.regional) and a distinct namespace for global resources (e.g., module.global), we can target a deployment for each using pipelines scoped to the respective namespace (e.g., module.global or module.regional).\\n\\n![image.png](https://dev-media.amazoncloud.cn/220e83e076de4887a54843a8ba86d1a8_image.png)\\n\\nFig 9. Deploying regional and global resources scoped to the Terraform namespace\\n\\nAs global resources have a scope of the whole account regardless of Region while Regional resources are scoped for the respective Region in the account, one point of consideration and a trade-off with having to pick a Region to deploy global resources is that this introduces a dependency on that region for the deployment of the global resources. In addition, in the case of a misconfiguration of a global resource, there may be an impact to each Region in which we deployed our workloads. Let’s consider a scenario where an IAM role has access to an S3 bucket. If the IAM role is misconfigured as a result of one of the deployments, then this may impact access to the S3 bucket in each Region.\\n\\nThere are alternate approaches, such as creating an IAM role per Region (myrole-use1 with access to the S3 bucket in us-east-1, myrole-apse1 with access to the S3 bucket in ap-southeast-1, etc.). This would make sure that if the respective IAM role is misconfigured, then the impact is scoped to the Region. Another approach is versioning our global resources (e.g., myrole-v1, myrole-v2) with the ability to move to a new version and roll back to a previous version if needed. Each of these approaches has different drawbacks, such as the duplication of global resources that may make auditing more cumbersome with the tradeoff of minimizing cross Regional dependencies.\\n\\nWe recommend looking at the pros and cons of each approach and selecting the approach that best suits the requirements for your workloads regarding the flexibility to deploy to multiple Regions.\\n\\n### **Consistency**\\nWe keep one copy of the infrastructure code and deploy the resources targeted for each Region using this same copy. Our code is built using [versioned module composition](https://www.terraform.io/docs/language/modules/develop/composition.html) as the “lego blocks”. This follows the **DRY (Don’t Repeat Yourself)** principle and decreases the risk of code drift per Region. We may deploy to any Region independently, including any Regions added at a future date with zero code changes and minimal additional configuration for that Region. We can see three advantages with this approach.\\n\\n1. The total deployment time per Region remains the same regardless of the addition of Regions. This helps for restrictions, such as tight release windows due to business requirements.\\n2. If there’s an issue with one of the regional deployments, then the remaining Regions and their deployment pipelines aren’t affected.\\n3. It allows the ability to stagger deployments or the possibility of not deploying to every region in non-critical environments (e.g., dev) to minimize costs and remain in line with the [Well Architected Sustainability pillar](https://aws.amazon.com/blogs/aws/sustainability-pillar-well-architected-framework/).\\n\\n### **Conclusion**\\nIn this post, we demonstrated a multi-account, multi-region deployment approach, along with sample code, with a focus on architecture using IaC tool Terraform and CI/CD services AWS CodeBuild and AWS CodePipeline to help customers in their journey through multi-Region deployments.\\n\\n*Thanks to Welly Siauw, Kenneth Jackson, Andy Taylor, Rodney Bozo, Craig Edwards and Curtis Rissi for their contributions reviewing this post and its artifacts.*\\n\\n#### **Author:**\\n\\n![image.png](https://dev-media.amazoncloud.cn/2b37b89b78fc4bd5ac08566d8877a629_image.png)\\n\\n**Lerna Ekmekcioglu**\\nLerna Ekmekcioglu is a Senior Solutions Architect with AWS where she helps Global Financial Services customers build secure, scalable and highly available workloads.\\nShe brings over 17 years of platform engineering experience including authentication systems, distributed caching, and multi region deployments using IaC and CI/CD to name a few.\\nIn her spare time, she enjoys hiking, sight seeing and backyard astronomy.\\n\\n![image.png](https://dev-media.amazoncloud.cn/ae8a67b297c240049f617e2dc2a6a8be_image.png)\\n\\n**Jack Iu**\\nJack is a Global Solutions Architect at AWS Financial Services. Jack is based in New York City, where he works with Financial Services customers to help them design, deploy, and scale applications to achieve their business goals. In his spare time, he enjoys badminton and loves to spend time with his wife and Shiba Inu.\\n\\n","render":"<p>As of February 2022, the AWS Cloud spans 84 Availability Zones within 26 geographic Regions, with announced plans for more Availability Zones and Regions. Customers can leverage this global infrastructure to expand their presence to their primary target of users, satisfying data residency requirements, and implementing disaster recovery strategy to make sure of business continuity. Although leveraging multi-Region architecture would address these requirements, deploying and configuring consistent infrastructure stacks across multi-Regions could be challenging, as AWS Regions are designed to be autonomous in nature. Multi-region deployments with Terraform and <a href=\\"https://aws.amazon.com/codepipeline/\\" target=\\"_blank\\">AWS CodePipeline</a> can help customers with these challenges.</p>\\n<p>In this post, we’ll demonstrate the best practice for multi-Region deployments using <a href=\\"https://www.terraform.io/\\" target=\\"_blank\\">HashiCorp Terraform</a> as infrastructure as code (IaC), and <a href=\\"https://aws.amazon.com/codebuild/\\" target=\\"_blank\\">AWS CodeBuild</a> , CodePipeline as continuous integration and continuous delivery (CI/CD) for consistency and repeatability of deployments into multiple AWS Regions and AWS Accounts. We’ll dive deep on the IaC deployment pipeline architecture and the best practices for structuring the Terraform project and configuration for multi-Region deployment of multiple AWS target accounts.</p>\\n<p>You can find the sample code for this solution <a href=\\"https://github.com/aws-samples/aws-multi-region-cicd-with-terraform\\" target=\\"_blank\\">here</a></p>\\n<h3><a id=\\"Solutions_Overview_6\\"></a><strong>Solutions Overview</strong></h3>\\n<h4><a id=\\"Architecture_7\\"></a><strong>Architecture</strong></h4>\\n<p>The following architecture diagram illustrates the main components of the multi-Region Terraform deployment pipeline with all of the resources built using IaC.</p>\n<p>DevOps engineer initially works against the infrastructure repo in a short-lived branch. Once changes in the short-lived branch are ready, DevOps engineer gets them reviewed and merged into the main branch. Then, DevOps engineer git tags the repo. For any future changes in the infra repo, DevOps engineer repeats this same process.</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/fc38edce3a114dd2b692a2aa6d204f0e_image.png\\" alt=\\"image.png\\" /></p>\n<p>Fig 1. Tagging to release from the main branch.</p>\n<ol>\\n<li>The deployment is triggered from DevOps engineer git tagging the repo, which contains the Terraform code to be deployed. This action starts the deployment pipeline execution.<br />\\nTagging with ‘dev_us-east-1/research/1.0’ triggers a pipeline to deploy the research dev account to us-east-1. In our example git tag ‘dev_us-east-1/research/1.0’ contains the target environment (i.e., dev), AWS Region (i.e. us-east-1), team (i.e., research), and a version number (i.e., 1.0) that maps to an annotated tag on a commit ID. The target workload account aliases (i.e., research dev, risk qa) are mapped to AWS account numbers in the environment configuration files of the infra repo in <a href=\\"https://aws.amazon.com/codecommit/\\" target=\\"_blank\\">AWS CodeCommit</a>.</li>\\n</ol>\n<p><img src=\\"https://dev-media.amazoncloud.cn/9a85465389ae43e3a9a9925baed4c8ac_image.png\\" alt=\\"image.png\\" /></p>\n<p>Fig 2. Multi-Region AWS deployment with IaC and CI/CD pipelines.</p>\n<ol start=\\"2\\">\\n<li>\\n<p>To capture the exact git tag that starts a pipeline, we use an <a href=\\"https://aws.amazon.com/eventbridge/\\" target=\\"_blank\\">Amazon EventBridge</a> rule. The rule is triggered when the tag is created with an environment prefix for deploying to a respective environment (i.e., dev). The rule kicks off an [AWS CodeBuild](https://aws.amazon.com/cn/codebuild/?trk=cndc-detail) project that takes the git tag from the [AWS CodeCommit](https://aws.amazon.com/cn/codecommit/?trk=cndc-detail) event and stores it with a full clone of the repo into a versioned <a href=\\"https://aws.amazon.com/s3/\\" target=\\"_blank\\">Amazon Simple Storage Service (Amazon S3)</a> bucket for the corresponding environment.</p>\\n</li>\n<li>\\n<p>We have a continuous delivery pipeline defined in AWS CodePipeline. To make sure that the pipelines for each environment run independent of each other, we use a separate pipeline per environment. Each pipeline consists of three stages in addition to the Source stage:</p>\n<p>a.IaC linting stage – A stage for linting Terraform code. For illustration purposes, we’ll use the open source tool tflint.</p>\n<p>b.IaC security scanning stage – A stage for static security scanning of Terraform code. There are many tooling choices when it comes to the security scanning of Terraform code. Checkov, TFSec, and Terrascan are the commonly used tools. For illustration purposes, we’ll use the open source tool Checkov.</p>\n<p>c.IaC build stage – A stage for Terraform build. This includes an action for the Terraform execution plan followed by an action to apply the plan to deploy the stack to a specific Region in the target workload account.</p>\n</li>\\n<li>\\n<p>Once the Terraform apply is triggered, it deploys the infrastructure components in the target workload account to the AWS Region based on the git tag. In turn, you have the flexibility to point the deployment to any AWS Region or account configured in the repo.</p>\n</li>\\n<li>\\n<p>The sample infrastructure in the target workload account consists of an <a href=\\"https://aws.amazon.com/iam/\\" target=\\"_blank\\">AWS Identity and Access Management (IAM) role</a>, an external facing Application Load Balancer (ALB), as well as all of the required resources down to the Amazon Virtual Private Cloud ([Amazon VPC](https://aws.amazon.com/cn/vpc/?trk=cndc-detail)). Upon successful deployment, browsing to the external facing ALB DNS Name URL displays a very simple message including the location of the Region.</p>\\n</li>\n</ol>\\n<h3><a id=\\"Architectural_considerations_35\\"></a><strong>Architectural considerations</strong></h3>\\n<h4><a id=\\"Multiaccount_strategy_36\\"></a><strong>Multi-account strategy</strong></h4>\\n<p>Leveraging well-architected multi-account strategy, we have a separate central tooling account for housing the code repository and infrastructure pipeline, and a separate target workload account to house our sample workload infra-architecture. The clean account separation lets us easily control the IAM permission for granular access and have different guardrails and security controls applied. Ultimately, this enforces the separation of concerns as well as minimizes the blast radius.</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/2f3469ccb6144a7aad11a4513434c271_image.png\\" alt=\\"image.png\\" /></p>\n<p>Fig 3. A separate pipeline per environment.</p>\n<p>The sample architecture shown above contained a pipeline per environment (DEV, QA, STAGING, PROD) in the tooling account deploying to the target workload account for the respective environment. At scale, you can consider having multiple infrastructure deployment pipelines for multiple business units in the central tooling account, thereby targeting workload accounts per environment and business unit. If your organization has a complex business unit structure and is bound to have different levels of compliance and security controls, then the central tooling account can be further divided into the central tooling accounts per business unit.</p>\n<h3><a id=\\"Pipeline_considerations_46\\"></a><strong>Pipeline considerations</strong></h3>\\n<p>The infrastructure deployment pipeline is hosted in a central tooling account and targets workload accounts. The pipeline is the <strong>authoritative source</strong> managing the full lifecycle of resources. The goal is to decrease the risk of ad hoc changes (e.g., manual changes made directly via the console) that can’t be easily reproduced at a future date. The pipeline and the build step each run as their own IAM role that adheres to the <strong>principle of least privilege</strong>. The pipeline is configured with a stage to lint the Terraform code, as well as a static security scan of the Terraform resources following the principle of <strong>shifting security left</strong> in the SDLC.</p>\\n<p>As a further improvement for resiliency and applying the cell architecture principle to the CI/CD deployment, we can consider having multi-Region deployment of the AWS CodePipeline pipeline and AWS CodeBuild build resources, in addition to a clone of the AWS CodeCommit repository. We can use the approach detailed in this <a href=\\"https://aws.amazon.com/blogs/devops/replicate-aws-codecommit-repository-between-regions-using-aws-fargate/\\" target=\\"_blank\\">post</a> to sync the repo across multiple regions. This means that both the workload architecture and the deployment infrastructure are multi-Region. However, it’s important to note that the business continuity requirements of the infrastructure deployment pipeline are most likely different than the requirements of the workloads themselves.</p>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/e89f6f336ea948a8b1159930f0207582_image.png\\" alt=\\"image.png\\" /></p>\n<p>Fig 4. Multi-Region CI/CD dev pipelines targeting the dev workload account resources in the respective Region.</p>\n<h3><a id=\\"Deeper_dive_into_Terraform_code_55\\"></a><strong>Deeper dive into Terraform code</strong></h3>\\n<h4><a id=\\"Backend_configuration_and_state_56\\"></a><strong>Backend configuration and state</strong></h4>\\n<p>As a prerequisite, we created Amazon S3 buckets to store the Terraform state files and <a href=\\"https://aws.amazon.com/dynamodb/\\" target=\\"_blank\\">Amazon DynamoDB</a> tables for the state file locks. The latter is a best practice to prevent concurrent operations on the same state file. For naming the buckets and tables, our code expects the use of the same prefix (i.e., &lt;tf_backend_config_prefix&gt;-&lt;env&gt; for buckets and &lt;tf_backend_config_prefix&gt;-lock-&lt;env&gt; for tables). The value of this prefix must be passed in as an input param (i.e., “tf_backend_config_prefix”). Then, it’s fed into [AWS CodeBuild](https://aws.amazon.com/cn/codebuild/?trk=cndc-detail) actions for Terraform as an environment variable. Separation of remote state management resources ([Amazon S3](https://aws.amazon.com/cn/s3/?trk=cndc-detail) bucket and [Amazon DynamoDB](https://aws.amazon.com/cn/dynamodb/?trk=cndc-detail) table) across environments makes sure that we’re minimizing the blast radius.</p>\\n<pre><code class=\\"lang-\\">-backend-config=&quot;bucket=\${TF_BACKEND_CONFIG_PREFIX}-\${ENV}&quot; \\n-backend-config=&quot;dynamodb_table=\${TF_BACKEND_CONFIG_PREFIX}-lock-\${ENV}&quot;\\n</code></pre>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/57dc9222ddfd45b095520c8ca37d7b55_image.png\\" alt=\\"image.png\\" /></p>\n<p>Fig 5. Terraform state file buckets and state lock tables per environment in the central tooling account.</p>\n<p>The git tag that kicks off the pipeline is named with the following convention of “&lt;env&gt;_&lt;region&gt;/&lt;team&gt;/&lt;version&gt;” for regional deployments and “&lt;env&gt;_global/&lt;team&gt;/&lt;version&gt;” for global resource deployments. The stage following the source stage in our pipeline, tflint stage, is where we parse the git tag. From the tag, we derive the values of environment, deployment scope (i.e., Region or global), and team to determine the Terraform state Amazon S3 object key uniquely identifying the Terraform state file for the deployment. The values of environment, deployment scope, and team are passed as environment variables to the subsequent AWS CodeBuild Terraform plan and apply actions.</p>\n<pre><code class=\\"lang-\\">-backend-config=&quot;key=\${TEAM}/\${ENV}-\${TARGET_DEPLOYMENT_SCOPE}/terraform.tfstate&quot;\\n</code></pre>\\n<p>We set the Region to the value of <a href=\\"https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html\\" target=\\"_blank\\">AWS_REGION env variable</a> that is made available by [AWS CodeBuild](https://aws.amazon.com/cn/codebuild/?trk=cndc-detail), and it’s the Region in which our build is running.</p>\\n<pre><code class=\\"lang-\\">-backend-config=&quot;region=\$AWS_REGION&quot;\\n</code></pre>\\n<p>The following is how the Terraform backend config initialization looks in our AWS CodeBuild buildspec files for Terraform actions, such as tflint, plan, and apply.</p>\n<pre><code class=\\"lang-\\">terraform init -backend-config=&quot;key=\${TEAM}/\${ENV}-\\n\${TARGET_DEPLOYMENT_SCOPE}/terraform.tfstate&quot; -backend-config=&quot;region=\$AWS_REGION&quot;\\n-backend-config=&quot;bucket=\${TF_BACKEND_CONFIG_PREFIX}-\${ENV}&quot; \\n-backend-config=&quot;dynamodb_table=\${TF_BACKEND_CONFIG_PREFIX}-lock-\${ENV}&quot;\\n-backend-config=&quot;encrypt=true&quot;\\n</code></pre>\\n<p>Using this approach, the Terraform states for each combination of account and Region are kept in their own distinct state file. This means that if there is an issue with one Terraform state file, then the rest of the state files aren’t impacted.</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/f047952b05a5417791f584e1abfb3333_image.png\\" alt=\\"image.png\\" /></p>\n<p>Fig 6. Terraform state files per account and Region for each environment in the central tooling account</p>\n<p>Following the example, a git tag of the form “dev_us-east-1/research/1.0” that kicks off the dev pipeline works against the research team’s dev account’s state file containing us-east-1 Regional resources (i.e., Amazon S3 object key “research/dev-us-east-1/terraform.tfstate” in the S3 bucket &lt;tf_backend_config_prefix&gt;-dev), and a git tag of the form “dev_ap-southeast-1/risk/1.0” that kicks off the dev pipeline works against the risk team’s dev account’s Terraform state file containing ap-southeast-1 Regional resources (i.e., Amazon S3 object key “risk/dev-ap-southeast-1/terraform.tfstate”). For global resources, we use a git tag of the form “dev_global/research/1.0” that kicks off a dev pipeline and works against the research team’s dev account’s global resources as they are at account level (i.e., “research/dev-global/terraform.tfstate).</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/42b206a214114e219b136de6233ac94b_image.png\\" alt=\\"image.png\\" /></p>\n<p>Fig 7. Git tags and the respective Terraform state files.</p>\n<p>This backend configuration makes sure that the state file for one account and Region is independent of the state file for the same account but different Region. Adding or expanding the workload to additional Regions would have no impact on the state files of existing Regions.</p>\n<p>If we look at the further improvement where we make our deployment infrastructure also multi-Region, then we can consider each Region’s CI/CD deployment to be the <strong>authoritative source</strong> for its local Region’s deployments and Terraform state files. In this case, tagging against the repo triggers a pipeline within the local CI/CD Region to deploy resources in the Region. The Terraform state files in the local Region are used for keeping track of state for the account’s deployment within the Region. This further decreases cross-regional dependencies.</p>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/0f59c2349cfe4afe90230d08ef70fad3_image.png\\" alt=\\"image.png\\" /></p>\n<p>Fig 8. Multi-Region CI/CD with Terraform state resources stored in the same Region as the workload account resources for the respective Region</p>\n<h3><a id=\\"Provider_108\\"></a><strong>Provider</strong></h3>\\n<p>For deployments, we use the default Terraform AWS provider. The provider is <strong>parametrized</strong> with the value of the region passed in as an input parameter.</p>\\n<pre><code class=\\"lang-\\">provider &quot;aws&quot; {\\n region = var.region\\n ...\\n}\\n</code></pre>\\n<p>Once the provider knows which Region to target, we can refer to the current AWS Region in the rest of the code.</p>\n<pre><code class=\\"lang-\\"># The value of the current AWS region is the name of the AWS region configured on the provider\\n# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region\\ndata &quot;aws_region&quot; &quot;current&quot; {} \\n\\nlocals {\\n region = data.aws_region.current.name # then use local.region where region is needed\\n}\\n</code></pre>\\n<p>Provider is configured to assume a cross account IAM role defined in the workload account. The value of the account ID is fed as an input parameter.</p>\n<pre><code class=\\"lang-\\">provider &quot;aws&quot; {\\n region = var.region\\n assume_role {\\n role_arn = &quot;arn:aws:iam::\${var.account}:role/InfraBuildRole&quot;\\n session_name = &quot;INFRA_BUILD&quot;\\n }\\n}\\n</code></pre>\\n<p>This InfraBuildRole IAM role could be created as part of the account creation process. The <a href=\\"https://aws.amazon.com/blogs/aws/new-aws-control-tower-account-factory-for-terraform/\\" target=\\"_blank\\">AWS Control Tower Terraform Account Factory</a> could be used to automate this.</p>\\n<h3><a id=\\"Code_141\\"></a><strong>Code</strong></h3>\\n<h4><a id=\\"Minimize_crossregional_dependencies_142\\"></a><strong>Minimize cross-regional dependencies</strong></h4>\\n<p>We keep the Regional resources and the global resources (e.g., IAM role or policy) in distinct namespaces following the <strong>cell architecture</strong> principle. We treat each Region as one cell, with the goal of decreasing cross-regional dependencies. Regional resources are created once in each Region. On the other hand, global resources are created once globally and may have cross-regional dependencies (e.g., DynamoDB global table with a replica table in multiple Regions). There’s no “global” Terraform AWS provider since the AWS provider requires a Region. This means that we pick a specific Region from which to deploy our global resources (i.e., global_resource_deploy_from_region input param). By creating a distinct Terraform namespace for Regional resources (e.g., module.regional) and a distinct namespace for global resources (e.g., module.global), we can target a deployment for each using pipelines scoped to the respective namespace (e.g., module.global or module.regional).</p>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/220e83e076de4887a54843a8ba86d1a8_image.png\\" alt=\\"image.png\\" /></p>\n<p>Fig 9. Deploying regional and global resources scoped to the Terraform namespace</p>\n<p>As global resources have a scope of the whole account regardless of Region while Regional resources are scoped for the respective Region in the account, one point of consideration and a trade-off with having to pick a Region to deploy global resources is that this introduces a dependency on that region for the deployment of the global resources. In addition, in the case of a misconfiguration of a global resource, there may be an impact to each Region in which we deployed our workloads. Let’s consider a scenario where an IAM role has access to an S3 bucket. If the IAM role is misconfigured as a result of one of the deployments, then this may impact access to the S3 bucket in each Region.</p>\n<p>There are alternate approaches, such as creating an IAM role per Region (myrole-use1 with access to the S3 bucket in us-east-1, myrole-apse1 with access to the S3 bucket in ap-southeast-1, etc.). This would make sure that if the respective IAM role is misconfigured, then the impact is scoped to the Region. Another approach is versioning our global resources (e.g., myrole-v1, myrole-v2) with the ability to move to a new version and roll back to a previous version if needed. Each of these approaches has different drawbacks, such as the duplication of global resources that may make auditing more cumbersome with the tradeoff of minimizing cross Regional dependencies.</p>\n<p>We recommend looking at the pros and cons of each approach and selecting the approach that best suits the requirements for your workloads regarding the flexibility to deploy to multiple Regions.</p>\n<h3><a id=\\"Consistency_155\\"></a><strong>Consistency</strong></h3>\\n<p>We keep one copy of the infrastructure code and deploy the resources targeted for each Region using this same copy. Our code is built using <a href=\\"https://www.terraform.io/docs/language/modules/develop/composition.html\\" target=\\"_blank\\">versioned module composition</a> as the “lego blocks”. This follows the <strong>DRY (Don’t Repeat Yourself)</strong> principle and decreases the risk of code drift per Region. We may deploy to any Region independently, including any Regions added at a future date with zero code changes and minimal additional configuration for that Region. We can see three advantages with this approach.</p>\\n<ol>\\n<li>The total deployment time per Region remains the same regardless of the addition of Regions. This helps for restrictions, such as tight release windows due to business requirements.</li>\n<li>If there’s an issue with one of the regional deployments, then the remaining Regions and their deployment pipelines aren’t affected.</li>\n<li>It allows the ability to stagger deployments or the possibility of not deploying to every region in non-critical environments (e.g., dev) to minimize costs and remain in line with the <a href=\\"https://aws.amazon.com/blogs/aws/sustainability-pillar-well-architected-framework/\\" target=\\"_blank\\">Well Architected Sustainability pillar</a>.</li>\\n</ol>\n<h3><a id=\\"Conclusion_162\\"></a><strong>Conclusion</strong></h3>\\n<p>In this post, we demonstrated a multi-account, multi-region deployment approach, along with sample code, with a focus on architecture using IaC tool Terraform and CI/CD services AWS CodeBuild and AWS CodePipeline to help customers in their journey through multi-Region deployments.</p>\n<p><em>Thanks to Welly Siauw, Kenneth Jackson, Andy Taylor, Rodney Bozo, Craig Edwards and Curtis Rissi for their contributions reviewing this post and its artifacts.</em></p>\\n<h4><a id=\\"Author_167\\"></a><strong>Author:</strong></h4>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/2b37b89b78fc4bd5ac08566d8877a629_image.png\\" alt=\\"image.png\\" /></p>\n<p><strong>Lerna Ekmekcioglu</strong><br />\\nLerna Ekmekcioglu is a Senior Solutions Architect with AWS where she helps Global Financial Services customers build secure, scalable and highly available workloads.<br />\\nShe brings over 17 years of platform engineering experience including authentication systems, distributed caching, and multi region deployments using IaC and CI/CD to name a few.<br />\\nIn her spare time, she enjoys hiking, sight seeing and backyard astronomy.</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/ae8a67b297c240049f617e2dc2a6a8be_image.png\\" alt=\\"image.png\\" /></p>\n<p><strong>Jack Iu</strong><br />\\nJack is a Global Solutions Architect at AWS Financial Services. Jack is based in New York City, where he works with Financial Services customers to help them design, deploy, and scale applications to achieve their business goals. In his spare time, he enjoys badminton and loves to spend time with his wife and Shiba Inu.</p>\n"}
目录
亚马逊云科技解决方案 基于行业客户应用场景及技术领域的解决方案
联系亚马逊云科技专家
亚马逊云科技解决方案
基于行业客户应用场景及技术领域的解决方案
联系专家
0
目录
关闭