Managing multiple Application and Deployments in AWS CDK
Tags: aws, cdk, typescript
Published: 2023-03-26 11:18:43

In my previous post I talked about how we manage our AWS accounts in CDK, now it is time to see how we manage applications first, and after that, we have a look at the deployments. 
Just AwsAccounts class we use a class Application. One Application is one Stack in one Git Repository.
Here is an example for four applications.

```
interface ApplicationProps {
  name: string;
  repositoryName: string;
  stackName: string;
}

interface ApplicationObject {
  readonly name: string;
  readonly repositoryName: string;
  readonly stackName: string;
}

export default class Application {
  private static readonly applicationsArray: ApplicationObject[] = [];

  /* Account setup */
  public static readonly DNS = new Application({ name: 'dns', repositoryName: 'aws-dns', stackName: 'AwsDnsStack' });
  public static readonly SES = new Application({ name: 'ses', repositoryName: 'aws-ses', stackName: 'AwsSesPipelineStack' });

  /* Project */
  public static readonly COGNITO = new Application({ name: 'cognito', repositoryName: 'projectA-cognito-admin', stackName: 'CognitoAdminStack' });
  public static readonly API_GATEWAY = new Application({ name: 'apigateway', repositoryName: 'projectB-gateway', stackName: 'GatewayStack' });
  
  readonly name: string;
  readonly repositoryName: string;
  readonly stackName: string;

  private constructor({ name, repositoryName, stackName }: ApplicationProps) {
    this.name = name;
    this.repositoryName = repositoryName;
    this.stackName = stackName;

    Application.applicationsArray.push(this);
  }

  public static get applications(): ApplicationObject[] {
    return [...Application.applicationsArray];
  }
}
```

Here you can see the four applications; DNS, SES, COGNITO and API_GATEWAY.
Now it is time to put them into our Deployment class.

```
import Application from '../applicaitons/application';
import { AwsAccount } from '../aws-accounts/awsAccounts';

interface DeploymentProps {
  readonly name: string;
  readonly accounts: AwsAccount[];
  readonly applications: Application[];
}

interface DeploymentObject {
  readonly name: string;
  readonly accounts: AwsAccount[];
  readonly applications: Application[];
}

export default class Deployment {
  private static readonly deploymentsArray: DeploymentObject[] = [];

  public static readonly ACCOUNTS_SETUP = new Deployment({
    name: 'AccountSetup',
    accounts: [AwsAccount.PROJECT_A_TEST, AwsAccount.PROJECT_A_QA, AwsAccount.PROJECT_B_TEST, AwsAccount.PROJECT_B_QA, AwsAccount.PROJECT_B_PROD],
    applications: [Application.DNS, Application.SES],
  });

  public static readonly PROJECT_A = new Deployment({
    name: 'TheGame',
    accounts: [AwsAccount.PROJECT_A_TEST, AwsAccount.PROJECT_A_QA],
    applications: [Application.COGNITO],
  });

  public static readonly PROJECT_B = new Deployment({
    name: 'TheGame',
    accounts: [AwsAccount.PROJECT_B_TEST, AwsAccount.PROJECT_B_QA, AwsAccount.PROJECT_B_PROD],
    applications: [Application.API_GATEWAY],
  });

  readonly name: string;

  readonly accounts: AwsAccount[];

  readonly applications: Application[];

  private constructor({ name, accounts, applications }: DeploymentProps) {
    this.name = name;
    this.accounts = accounts;
    this.applications = applications;

    Deployment.deploymentsArray.push(this);
  }

  public static get deployments(): DeploymentObject[] {
    return [...Deployment.deploymentsArray];
  }
}
```

Still no strange stuff. 
DNS and SES should be deployed to all five accounts, while COGNITO goes to Project A´s TEST and QA accounts and API_GATEWAY will be deployed to Project B´s TEST, QA and PROD accounts. 
Now, how does our pipeline for this look like? 
This is our stack:
```
const app = new cdk.App();

new PipelinesStack(app, 'PipelinesSetup', {
  env: { account: AwsAccount.CICD.accountId, region: 'eu-north-1' },
});
```
```
export default class PipelinesStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    const deploymentsSetup = Deployment.ACCOUNT_SETUP;
    const deploymentsProjA = Deployment.PROJECT_A;
    const deploymentsProjB = Deployment.PROJECT_B;

    new DeploymentsStack(this, `Pipeline-${deploymentsSetup.name}-Pipelines`, {
      deployments: deploymentsSetup,
    });

    new DeploymentsStack(this, `Pipeline-${deploymentsGame.name}-Pipelines`, {
      deployments: deploymentsProjA,
    });

    new DeploymentsStack(this, `Pipeline-${deploymentsGame.name}-Pipelines`, {
      deployments: deploymentsProjB,
    });
  }
}
```
And for the DeployStack, it can be found here:
https://gist.github.com/kronis/de876999f725275f0ab92ed9f0ef5bf6