|
| 1 | +--- |
| 2 | +title: "The Stack Part 1: Setting up your AWS Account Structure" |
| 3 | +tags: aws, cloud, infrastructure |
| 4 | +date: 2023-10-07 |
| 5 | +--- |
| 6 | + |
| 7 | +In [the last post](/posts/2023-01-29-the-stack.html) we went over the overall goals of "The Stack" and what we will be building. In this post we'll be setting up our AWS Account structure. See the full overview of posts [here](/posts/2023-01-29-the-stack.html#what-will-we-be-covering). |
| 8 | + |
| 9 | +- [Set up Control Tower](#set-up-control-tower) |
| 10 | + - [Step 1](#step-1) |
| 11 | + - [Step 2](#step-2) |
| 12 | + - [Step 3](#step-3) |
| 13 | + - [Step 4](#step-4) |
| 14 | + - [Step 5](#step-5) |
| 15 | + - [Cleanup](#cleanup) |
| 16 | +- [Adjusting Account Factory defaults](#adjusting-account-factory-defaults) |
| 17 | +- [Set up our Development Organizational Unit](#set-up-our-development-organizational-unit) |
| 18 | +- [Set up our AWS Accounts](#set-up-our-aws-accounts) |
| 19 | +- [Next Steps](#next-steps) |
| 20 | + |
| 21 | +As a reminder, here is the structure we are aiming for: |
| 22 | + |
| 23 | +- **Control Tower**: This is your central place to control access and policies for all accounts in your organization |
| 24 | +- **Production Multi-tenant**: Your primary production account for multi-tenant setup, and most likely were the majority of users will be |
| 25 | +- **Production Single-tenant**: While desirable to avoid the operation overhead for single-tenant setups, its good to think in this from the get-go |
| 26 | +- **Integration Test**: This will be the account that IaC deployments get tested on to ensure rollout works |
| 27 | +- **Preview**: This will be used to spin up Preview Environments later on |
| 28 | +- **Individual Developer**: Individual developer accounts to allow easy testing of IaC testing and exploration |
| 29 | +- **Monitoring**: Centralize monitoring and observability into one account, allowing access to insights without access to sensitive logs or infrastructure from the other accounts |
| 30 | +- **Logs**: Centralized storage of logs, which may require different access considerations than metrics and traces |
| 31 | + |
| 32 | +<div></div><!--more--> |
| 33 | + |
| 34 | +Or, for the visual learners: |
| 35 | + |
| 36 | +<pre class="mermaid"> |
| 37 | +graph TD |
| 38 | + subgraph ControlTower[AWS: Control Tower] |
| 39 | + AuditLog[Audit Log] |
| 40 | + GuardRails[Guard Rails] |
| 41 | + end |
| 42 | + ControlTower-->AWSProdMultiTenantAccount |
| 43 | + ControlTower-->AWSProdSingleTenantAccount |
| 44 | + ControlTower-->AWSIntegrationTestAccount |
| 45 | + ControlTower-->AWSPreviewAccount |
| 46 | + ControlTower-->AWSIndividualDeveloperAccount |
| 47 | + ControlTower-->AWSMonitoringAccount |
| 48 | + ControlTower-->AWSLogsAccount |
| 49 | + |
| 50 | + subgraph AWSProdMultiTenantAccount[AWS: Production Multi-tenant] |
| 51 | + AccountFillerProdMultiTenant[...] |
| 52 | + end |
| 53 | + |
| 54 | + subgraph AWSProdSingleTenantAccount[AWS: Production Single-tenant] |
| 55 | + AccountFillerProdSingleTenant[...] |
| 56 | + end |
| 57 | + |
| 58 | + subgraph AWSIntegrationTestAccount[AWS: Integration Test] |
| 59 | + AccountFillerIntegrationTest[...] |
| 60 | + end |
| 61 | + |
| 62 | + subgraph AWSPreviewAccount[AWS: Preview] |
| 63 | + AccountFillerPreview[...] |
| 64 | + end |
| 65 | + |
| 66 | + subgraph AWSIndividualDeveloperAccount[AWS: Individual Developer] |
| 67 | + AccountFillerIndividualDeveloper[...] |
| 68 | + end |
| 69 | + |
| 70 | + subgraph AWSMonitoringAccount[AWS: Monitoring] |
| 71 | + direction LR |
| 72 | + CloudWatchDashboards[CloudWatch Dashboards] |
| 73 | + CloudWatchMetrics[CloudWatch Metrics/Alarms] |
| 74 | + XRay[XRay Analytics] |
| 75 | + end |
| 76 | + |
| 77 | + subgraph AWSLogsAccount[AWS: Logs] |
| 78 | + CloudWatchLogs[CloudWatch Logs] |
| 79 | + end |
| 80 | + |
| 81 | + classDef container stroke:#333,stroke-width:2px,fill:transparent,padding:8px |
| 82 | + class ControlTower,AWSProdMultiTenantAccount,AWSProdSingleTenantAccount,AWSIntegrationTestAccount,AWSPreviewAccount,AWSIndividualDeveloperAccount,AWSMonitoringAccount,AWSLogsAccount container; |
| 83 | +</pre> |
| 84 | + |
| 85 | +Let's jump into it! |
| 86 | + |
| 87 | +## Set up Control Tower |
| 88 | + |
| 89 | +AWS has an excellent [Getting Started Guide](https://docs.aws.amazon.com/controltower/latest/userguide/quick-start.html) which goes through setting up a new Control Tower at a high-level. We'll do a few adjustments to the defaults to make it fit our needs. |
| 90 | + |
| 91 | +Fist off, you will need an existing AWS Account. This is the one we are turning into our Control Tower, or also called our "Landing Zone". If you don't already have an account ready to use, then [go setup your AWS Account first](https://aws.amazon.com/). |
| 92 | + |
| 93 | +Now that we are ready, a high-level overview of the steps we will be taking are: |
| 94 | +1. Sign in to the AWS management console with your root user. |
| 95 | +3. Navigate to the "Control Tower" console [here](https://console.aws.amazon.com/controltower). |
| 96 | +4. Change to the region you want your base to be in (e.g. for me I prefer eu-west-1). |
| 97 | +5. Click "Set up landing zone" to start the process. |
| 98 | +6. Set up your landing zone, following the detailed steps below |
| 99 | + |
| 100 | +#### Step 1 |
| 101 | + |
| 102 | +The first screen you'll meet wants you to review various infomration and pricing as well as choose a few defaults. We are going to change some of the values: |
| 103 | + |
| 104 | +- **Region deny setting**: Choose `Enabled` for this. We want to make sure that Control Tower is governing our accounts and resources. |
| 105 | +- We will need `us-east-1` for certain "global" resources, so we'll add that to the list of allowed regions along with your desired region. |
| 106 | + |
| 107 | +#### Step 2 |
| 108 | + |
| 109 | +Now we need to create our Organizational Units (OUs). We want both a Foundation and an Additional OU, but we will rename them a bit to make more sense for our use-case: |
| 110 | + |
| 111 | +- **Foundation OU**: We'll call this `Compliance` since it contains our Logs as well as our Audit accounts. |
| 112 | +- **Additonal OU**: This is where we will put our Production workloads into, so we'll call this `Production`. |
| 113 | + |
| 114 | +#### Step 3 |
| 115 | + |
| 116 | +The Foundation OU, **Compliance**, creates two accounts for us. We to set up emails for these accounts, but we will keep the names. I recommend pointing this to an administrator email and using `+` to allow the same email to be used for multiple accounts: |
| 117 | + |
| 118 | +- **Log Archive**: `administrator+log-archive@example.com` (adjust to an account and domain you control) |
| 119 | +- **Audit**: `administrator+audit@example.com` (adjust to an account and domain you control) |
| 120 | + |
| 121 | +#### Step 4 |
| 122 | + |
| 123 | +Now that we have a location for our logs, we can configure CloudTrail as well as log retention. You can adjust to your needs, but I recommend the following: |
| 124 | + |
| 125 | +- **CloudTrail**: `Enabled` |
| 126 | +- **Amazon S3 bucket retention for logging**: `1 years` |
| 127 | +- **Amazon S3 bucket retention for access logging**: `10 years` |
| 128 | +- **KMS Encryption**: `Enable and custome encryption settings` |
| 129 | + |
| 130 | +When you check the box to enable KMS encryption you'll be asked for a key to use. Click "Create a KMS key" which will take us into the KMS Console: |
| 131 | + |
| 132 | +Key configuration |
| 133 | + |
| 134 | +- **Symmetric** |
| 135 | +- **Encrypt and decrypt** |
| 136 | + |
| 137 | +Key labels |
| 138 | + |
| 139 | +- **KMS Key Alias**: `control-tower-cloudtrail` |
| 140 | +- **Description**: `KMS key used for CloudTrail logs stored by Control Tower` |
| 141 | +- **Tags**: |
| 142 | + - Key: `billing`, Value: `cloudtrail-control-tower` |
| 143 | + - Key: `billing-group`, Value: `control-tower` |
| 144 | + |
| 145 | +We'll immediately start our good habit of adding billing tags whereever we can, which greatly simplifies diving into cloud expenses. |
| 146 | + |
| 147 | +<details> |
| 148 | +<summary>👈 Open the toggle for an example</summary> |
| 149 | + |
| 150 | +<div style="text-align:center;"> |
| 151 | +<a href="/resources/images/the-stack-part-1-kms-step-1.png" target="_blank" rel="noopener noreferrer"><img src="/resources/images/the-stack-part-1-kms-step-1.thumbnail.png" loading="lazy" alt="KMS Configuration" title="KMS Configuration" width="70%" /></a> |
| 152 | +</div> |
| 153 | + |
| 154 | +</details> |
| 155 | + |
| 156 | +Skip through Step 3, Step 4, and click "Finish" on the review step. |
| 157 | + |
| 158 | +Once the key is created we'll immediately edit it now that it has gotten a Key ID. If we don't we'll run into this nice error later on: |
| 159 | + |
| 160 | +<div style="text-align:center;"> |
| 161 | +<a href="/resources/images/the-stack-part-1-kms-error.png" target="_blank" rel="noopener noreferrer"><img src="/resources/images/the-stack-part-1-kms-error.thumbnail.png" loading="lazy" alt="KMS error" title="KMS error" width="70%" /></a> |
| 162 | +</div> |
| 163 | + |
| 164 | +To avoid this: |
| 165 | + |
| 166 | +1. Click on your newly created key. |
| 167 | +2. Note down the Key ID (e.g. `12345678-1234-1234-1234-123456789012`). |
| 168 | +3. Note down your AWS Account ID (e.g. `123456789012`). |
| 169 | +4. Click "Edit" and switch to the JSON policy editor. |
| 170 | +5. Add the following policies, with values replaced, at the end of the `Statement` list. |
| 171 | + |
| 172 | + |
| 173 | +Replace `AWS_REGION`, `AWS_ACCOUNT_ID`, and `KMS_KEY_ID` and insert the following: |
| 174 | + |
| 175 | +<script src="https://gist.github.com/Tehnix/7746c4a8f2340d563890a0db519d8d84.js?file=policy.json"></script> |
| 176 | + |
| 177 | +This ensures that both Config and CloudTrail can use the key for encryption and decryption. |
| 178 | + |
| 179 | +#### Step 5 |
| 180 | + |
| 181 | +Review and confirm the setup. |
| 182 | + |
| 183 | +#### Cleanup |
| 184 | + |
| 185 | +Finally, we'll also cleanup the VPCs that were created in our Control Tower: |
| 186 | + |
| 187 | +1. Go to the [AWS Console -> VPC](https://console.aws.amazon.com/vpc/). |
| 188 | +2. Click on **Your VPCs** in the menu on the left, and click on your Control Tower VPC. |
| 189 | +3. Choose **Actions** and then choose **Delete VPC** as well as confirming the choice. |
| 190 | + |
| 191 | +<div class="callout"> |
| 192 | + <div class="callout-bulb">💡</div> |
| 193 | + If you missed the KMS policy adjustment in Step 4 and the creation fails, you can go into CloudFormation and delete the stack called `AWSControlTowerBP-BASELINE-CLOUDTRAIL-MASTER`. Once that is done, you can go back to the Control Tower console and click "Retry" to try again. |
| 194 | +</div> |
| 195 | + |
| 196 | +## Adjusting Account Factory defaults |
| 197 | + |
| 198 | +By default the Account Factory will be creating VPCs and Subnets in newly provisioned accounts. We don't want this as we are focusing on serverless, so we'll disable this. |
| 199 | + |
| 200 | +1. Go to the [AWS Console -> Control Tower](https://console.aws.amazon.com/controltower/). |
| 201 | +2. Go into **Account Factory**. |
| 202 | +3. Click **Edit** on the **Network configuration** card. |
| 203 | +4. Set the **Maximum number of private subnets** to `0` |
| 204 | +5. Uncheck any checkboxes in the **Regions for VPC creation** list. |
| 205 | +6. Click **Save**. |
| 206 | + |
| 207 | +## Set up our Development Organizational Unit |
| 208 | + |
| 209 | +We've only set up two OUs so far, `Compliance` and `Production`, but we have one more we'd like to use. Jump into your Organization overview in Control Tower: |
| 210 | + |
| 211 | +1. Go to the [AWS Console -> Control Tower](https://console.aws.amazon.com/controltower/). |
| 212 | +2. Go into **Organization**. |
| 213 | +3. Click **Create resources** and choose **Create organizational unit**. |
| 214 | +4. Call it `Development` and set the Parent OU to `Root`. |
| 215 | + |
| 216 | +This will take a bit of time, and we cannot create accounts during this. |
| 217 | + |
| 218 | +## Set up our AWS Accounts |
| 219 | + |
| 220 | +For this step it's important that you are not logged in as the Root user anymore. Instead, jump to your new User portal URL which AWS has set up for us. |
| 221 | + |
| 222 | +Find the User portal URL: |
| 223 | + |
| 224 | +1. Go to the [AWS Console -> Control Tower](https://console.aws.amazon.com/controltower/). |
| 225 | +2. Go into **Users and access**. |
| 226 | +3. Copy the **User portal URL** from the **Federated access management** card. |
| 227 | +4. Bookmark this URL, you're gonna need it a lot. |
| 228 | + |
| 229 | +<div style="text-align:center;"> |
| 230 | +<a href="/resources/images/the-stack-part-1-user-portal-url.png" target="_blank" rel="noopener noreferrer"><img src="/resources/images/the-stack-part-1-user-portal-url.thumbnail.png" loading="lazy" alt="User portal URL" title="User portal URL" width="65%" /></a> |
| 231 | +</div> |
| 232 | + |
| 233 | +Once you're logged into the Control Tower account using the portal, jump into the Account Factory again: |
| 234 | + |
| 235 | +1. Go to the [AWS Console -> Control Tower](https://console.aws.amazon.com/controltower/). |
| 236 | +2. Go into **Account Factory**. |
| 237 | +3. Click **Create account**. |
| 238 | + |
| 239 | +We'll be creating the following accounts: |
| 240 | + |
| 241 | +- `Integration Test` in our Development OU |
| 242 | +- `Preview` in our Development OU |
| 243 | +- `Production Multi-tenant` in our Production OU |
| 244 | +- `Production Single-tenant` in our Production OU |
| 245 | +- `Monitoring` in our Production OU |
| 246 | +- `Logs` in our Production OU |
| 247 | + |
| 248 | +For each account, in the **Create account** process, fill in: |
| 249 | + |
| 250 | +- **Account email**: An unused email for the account, e.g. `administrator+integration@example.com` |
| 251 | +- **Display name**: The name of the account, e.g. `Integration Test` |
| 252 | +- **IAM Identity Center user email**: The email of the user that will be the administrator of the account, e.g. `administrator+integration@example.com` |
| 253 | +- **IAM Identity Center user first name**: The first name of the user that will be the administrator of the account, e.g. `Integration` |
| 254 | +- **IAM Identity Center user last name**: The last name of the user that will be the administrator of the account, e.g. `Test` |
| 255 | + |
| 256 | +Pick the appropriate Organizational unit according to the list above, and click **Create account**. |
| 257 | + |
| 258 | +You should end up with an overall structure like the following (Preview environment missing, but should be in the Development OU): |
| 259 | + |
| 260 | +<div style="text-align:center;"> |
| 261 | +<a href="/resources/images/the-stack-part-1-account-overview.png" target="_blank" rel="noopener noreferrer"><img src="/resources/images/the-stack-part-1-account-overview.thumbnail.png" loading="lazy" alt="Account overview" title="Account overview" width="40%" /></a> |
| 262 | +</div> |
| 263 | + |
| 264 | +## Next Steps |
| 265 | + |
| 266 | +Next up, we will be looking at how we can set up and automate our deployments to these environments so that once we start building it will update automatically. Follow along in [Part 2 of the series](/posts/2023-01-29-the-stack-part-2.html). |
0 commit comments