Skip to content

Commit

Permalink
Update css, resize images, add content to part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
Tehnix committed Oct 7, 2023
1 parent 1b153ee commit a7a57e8
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 9 deletions.
10 changes: 5 additions & 5 deletions posts/2023-10-07-the-stack-part-1.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "\"The Stack\" Part 1: Setting up your AWS Account Structure"
title: "The Stack Part 1: Setting up your AWS Account Structure"
tags: aws, cloud, infrastructure
---

Expand Down Expand Up @@ -119,15 +119,15 @@ We'll immediately start our good habit of adding billing tags whereever we can,
<details>
<summary>👈 Open the toggle for an example</summary>

<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="100%" /></a>
<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>

</details>

Skip through Step 3, Step 4, and click "Finish" on the review step.

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:

<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="100%" /></a>
<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>

1. Click on your newly created key.
2. Note down the Key ID (e.g. `12345678-1234-1234-1234-123456789012`).
Expand Down Expand Up @@ -224,7 +224,7 @@ Find the User portal URL:
3. Copy the **User portal URL** from the **Federated access management** card.
4. Bookmark this URL, you're gonna need it a lot.

<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="100%" /></a>
<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>

Once you're logged into the Control Tower account using the portal, jump into the Account Factory again:

Expand All @@ -249,7 +249,7 @@ For each account, in the **Create account** process, fill in:

Pick the appropriate Organizational unit according to the list above, and click **Create account**. You should end up with an overall structure like the following:

<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="100%" /></a>
<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="50%" /></a>


# Next Steps
Expand Down
195 changes: 191 additions & 4 deletions posts/2023-10-07-the-stack-part-2.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "\"The Stack\" Part 2: Automating Deployments via CI"
title: "The Stack Part 2: Automating Deployments via CI"
tags: aws, cloud, infrastructure, cdk
---

Expand Down Expand Up @@ -66,18 +66,205 @@ Repeat those steps with the relevant values for each of the environments `Integr

Your `Environment` overview will look like this:

<a href="/resources/images/the-stack-part-2-environment-overview.png" target="_blank" rel="noopener noreferrer"><img src="/resources/images/the-stack-part-2-environment-overview.thumbnail.png" loading="lazy" alt="Overview of Environments" title="Overview of Environments" width="100%" /></a>
<a href="/resources/images/the-stack-part-2-environment-overview.png" target="_blank" rel="noopener noreferrer"><img src="/resources/images/the-stack-part-2-environment-overview.thumbnail.png" loading="lazy" alt="Overview of Environments" title="Overview of Environments" width="65%" /></a>


And each environment will roughly look like this:

<a href="/resources/images/the-stack-part-2-environment-configuration.png" target="_blank" rel="noopener noreferrer"><img src="/resources/images/the-stack-part-2-environment-configuration.thumbnail.png" loading="lazy" alt="Configuration, secrets, and variables of an environment" title="Configuration, secrets, and variables of an environment" width="100%" /></a>
<a href="/resources/images/the-stack-part-2-environment-configuration.png" target="_blank" rel="noopener noreferrer"><img src="/resources/images/the-stack-part-2-environment-configuration.thumbnail.png" loading="lazy" alt="Configuration, secrets, and variables of an environment" title="Configuration, secrets, and variables of an environment" width="65%" /></a>


# CDK: Infrastructure as Code
[CDK](https://github.com/aws/aws-cdk) is our tool of choice for Infrastructure as Code. We'll start from the default template and adjust it to use [Bun](https://bun.sh/) which simplifies the process of running CDK commands while using TypeScript.

Instead of setting this up from scratch, start from the template for this step in the [GitHub repository](https://github.com/codetalkio/the-stack/tree/part-2-automatic-deployments).
Instead of setting this up from scratch, start from the template for this step in the [GitHub repository](https://github.com/codetalkio/the-stack/tree/part-2-automatic-deployments). We'll go through what this contains in the next two sections.

## Explanation: CDK Stack

We structure our CDK stack as follows:
- `deployment/`: The root folder for all things CDK
- `bin/`: The entry point for all our stacks
- `deployment.ts`: Our "executable" that CDK run run (defined in `cdk.json`)
- `helpers.ts`: Helper functions to make our CDK code more readable and safe to run
- `lib/`: The actual logic of our CDK stacks
- `base/`: Our base layer containing all the resources that are shared across all stacks
- `stack.ts`: Gathers all of our base stacks into one stack
- `domain.ts`: Sets up our Hosted Zone and ACM certificates

This might seem like overkill right now, but will benefit us quite quickly as we start adding more stacks to our project.

In `deployment.ts` you'll see the root of our CDK stack:

```typescript
// ...imports
const app = new cdk.App();

/**
* Define our base stack that provisions the infrastructure for our application, such
* as domain names, certificates, and other resources that are shared across all.
*/
const baseStackName = "Base";
if (matchesStack(app, baseStackName)) {
new BaseStack(app, baseStackName, {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT || process.env.AWS_ACCOUNT_ID,
region: process.env.CDK_DEFAULT_REGION || process.env.AWS_REGION,
},
domain: validateEnv("DOMAIN", baseStackName),
});
}
```

We've set up some conveniences to easily run a single stack, via `matchesStack`, and to validate our environment variables, via `validateEnv`.

Our `BaseStack` is then defined in `lib/base/stack.ts`, and more or less just pieces together the types and the sub-stacks in the `base/` directory:

```typescript
// ...imports
interface StackProps extends cdk.StackProps, domain.StackProps {}

export class Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);

// Set up our domain stack.
new domain.Stack(this, "Domain", props);
}
}
```

And finally, to the meat of things, our `lib/base/domain.ts` is currently where the magic happens:

```typescript
// ...imports
export interface StackProps extends cdk.StackProps {
/**
* The domain name the application is hosted under.
*/
readonly domain: string;
}

/**
* Set up a Hosted Zone to manage our domain names.
*
* https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_route53.HostedZone.html
*/
export class Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);

// Set up the hosted zone.
const hostedZone = new route53.HostedZone(this, "HostedZone", {
zoneName: props.domain,
});

// Set up an ACM certificate for the domain, and validate it using DNS.
new acm.Certificate(this, "Certificate", {
domainName: props.domain,
validation: acm.CertificateValidation.fromDns(hostedZone),
});
}
}
```

This sets up a Hosted Zone and an ACM certificate for our domain, and configures it to validate the Certificate via DNS validation.

## Explanation: Automated Deployments via GitHub Actions

We have two workflows to deploy things, they share much of the same logic, so let's focus on the commonalities first.

Both of them do a few things:
- Sets up a trigger on `workflow_dispatch` so that we can manually trigger the workflow.
- Set up a matrix of environments to deploy to.
- Configures and `environment` and a `concurrency` group. The `concurrency` group is important, since we don't want to deploy to the same environment at the same time.
- Installs `bun` and our dependencies.

All of this looks like this, taken from the `cd-bootstrap` workflow:

```yaml
name: "Deployment: Bootstrap"

on:
workflow_dispatch:

defaults:
run:
shell: bash # Set the default shell to bash.

jobs:
bootstrap:
name: bootstrap
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Add new environments to this list to run bootstrap on them when manually triggered.
environment:
- "Integration Test"
- "Production Single-tenant"
- "Production Multi-tenant"
environment: ${{ matrix.environment }}
# Limit to only one concurrent deployment per environment.
concurrency:
group: ${{ matrix.environment }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1

- name: Install dependencies
working-directory: ./deployment
run: bun install
```
We now diverge (beyond the naming in the above example) for each workflow. The bootstrap flow is quite simple, it just runs `bun run cdk bootstrap` for each environment:

```yaml
- name: Bootstrap account
working-directory: ./deployment
env:
AWS_REGION: ${{ vars.AWS_REGION }}
AWS_DEFAULT_REGION: ${{ vars.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: bun run cdk bootstrap
```

Our deployment workflow does a bit more, we also synthesize our stacks and run any tests we have:

```yaml
- name: Synthesize the whole stack
working-directory: ./deployment
env:
DOMAIN: ${{ vars.DOMAIN }}
AWS_REGION: ${{ vars.AWS_REGION }}
AWS_DEFAULT_REGION: ${{ vars.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: bun run cdk synth --all
- name: Run tests
working-directory: ./deployment
env:
DOMAIN: ${{ vars.DOMAIN }}
AWS_REGION: ${{ vars.AWS_REGION }}
AWS_DEFAULT_REGION: ${{ vars.AWS_REGION }}
run: bun test
- name: Deploy to ${{ matrix.environment }}
working-directory: ./deployment
env:
DOMAIN: ${{ vars.DOMAIN }}
AWS_REGION: ${{ vars.AWS_REGION }}
AWS_DEFAULT_REGION: ${{ vars.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: bun run cdk deploy --all --require-approval never
```

The synth and test steps ensure we have a minimum of sanity checking in place.

## Trigger the Workflows

Push your project to GitHub. You now have access to the workflows and can trigger them manually.

Expand Down
1 change: 1 addition & 0 deletions resources/scss/_post.scss
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
ul,
ol,
blockquote,
details,
pre:not(.mermaid),
div.figure {
@include center-content();
Expand Down

0 comments on commit a7a57e8

Please sign in to comment.