AWS Multi-Account GitOps Deployment 2: GitHub Actions Setup

-

Welcome back to the second installment of our series on multi-account GitOps deployment on AWS. In the first part, we navigated through setting up a multi-account AWS organizational structure. Now, we will focus on integrating GitHub for our deployment processes and ensuring all the accounts are ready for deployments.

Integrating AWS Accounts with GitHub

1. Access Management Setup:

  • Log in to the Management Account: Navigate to AWS and sign in to your management account.
  • Go to IAM Identity Center: Once you are logged in, access the IAM Identity Center.
  • Access AWS Accounts: Under IAM Identity Center, find and click on AWS Accounts.

AWS IAM Identity Center AWS Organizations AWS Accounts Page

  • Assign Users or Groups: Select all the newly created accounts (deployment, dev, test, prod) and click on “Assign Users or Groups”.
  • Select the Main User: Choose the main user and click next.
  • Select the Permission Set: Now, select the Administrator Access permission set (or any other relevant permission set you have created) and click next.
  • Submission: Review your configurations and click submit

AWS IAM Identity Center AWS Organizations AWS Accounts Assign Users and Groups Review and Submit Page

  • Verify Account Access: You should now be able to see all the accounts listed in your AWS Access portal at https://lutku.awsapps.com/start#/

AWS Access Portal Accounts Page with multiple accounts

2. AWS CLI and SSO Configuration:

For all the newly created accounts, add them to the same session name using AWS CLI as we have done in the first instalment of the series:

aws configure sso

Follow the prompts and perform this action for all the accounts (deployment, dev, test, prod). Example session names could be:

  • lutku-deployment
  • lutku-dev
  • lutku-test
  • lutku-prod

3. GitHub Actions Setup:

Now, let’s set up our repository for AWS CDK and GitHub Actions:

mkdir actions-setup && cd actions-setup
git init
npx projen new awscdk-app-ts

Update the .projenrc.ts configuration file similar to following:

import { awscdk } from 'projen';

const project = new awscdk.AwsCdkTypeScriptApp({
   authorEmail: 'utku.demir@luminis.eu',
   authorName: 'Utku Demir',
   cdkVersion: '2.96.2',
   defaultReleaseBranch: 'main',
   name: 'actions-setup',
   description: 'A CDK project for GitOps Deployments',
   github: false,
   projenrcTs: true,
   keywords: [
      'AWS CDK',
      'projen',
      'Typescript',
      'Deployment',
   ],
   gitignore: ['.idea'],
   license: 'MIT',
   licensed: true,
});
project.synth();

After updating, generate the project:

yarn projen

Create a new file named actions_setup_stack.ts under src to include the necessary configuration for our stack:

import { Stack, StackProps } from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

export interface ActionsSetupStackProps extends StackProps {
  repositoryOwner: string;
  gitDeployableAccounts: string[];
}

export class ActionsSetupStack extends Stack {
  constructor(scope: Construct, id: string, props: ActionsSetupStackProps) {
    super(scope, id, props);

    const githubOidcProvider = new iam.OpenIdConnectProvider(this, 'github-oidc-provider', {
      url: 'https://token.actions.githubusercontent.com',
      clientIds: ['sts.amazonaws.com'],
      thumbprints: ['6938fd4d98bab03faadb97b34396831e3780aea1'],
    });

    const webIdentityPrincipal = new iam.WebIdentityPrincipal(githubOidcProvider.openIdConnectProviderArn, {
      StringEquals: {
        'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
      },
      StringLike: {
        'token.actions.githubusercontent.com:sub': `repo:${props.repositoryOwner}/*`,
      },
    });

    new iam.Role(this, 'github-actions-deploy-role', {
      roleName: 'github-actions-deploy-role',
      assumedBy: webIdentityPrincipal,
      inlinePolicies: {
        AllowCrossAccount: new iam.PolicyDocument({
          statements: [
            new iam.PolicyStatement({
              effect: iam.Effect.ALLOW,
              actions: ['sts:AssumeRole'],
              resources: [
                `arn:aws:iam::${this.account}:role/cdk-hnb659fds-*-role-${this.account}-*`,
                ...props.gitDeployableAccounts.map(appAccount => `arn:aws:iam::${appAccount}:role/cdk-hnb659fds-*-role-${appAccount}-*`),
              ],
            }),
          ],
        }),
      },
    },
    );
  }
}

The above stack, based on the work of Wojciech Matuszewski on Deploying AWS CDK apps using short-lived credentials and Github Actions, creates a web identity principal for the GitHub OIDC provider and a role for this web identity principal to assume to be able to conduct deployments.

Different from the stack that deployed the organization structure, I recommend creating a file named cdk.context.json in the root of the project to store the configuration necessary for this stack.

{
  "repositoryOwner": "<owner_of_the_repository>",
  "gitDeployableAccounts": [
    "<dev_account_id>",
    "<test_account_id>",
    "<prod_account_id>"
  ]
}

Lastly, edit the main.ts under src to include this stack as:

import { App } from 'aws-cdk-lib';
import { ActionsSetupStack } from './actions_setup_stack';

const devEnv = {
  account: process.env.CDK_DEFAULT_ACCOUNT,
  region: process.env.CDK_DEFAULT_REGION,
};
const app = new App();

const repositoryOwner = app.node.tryGetContext('repositoryOwner');
const gitDeployableAccounts = app.node.tryGetContext('gitDeployableAccounts');

new ActionsSetupStack(app, 'actions-setup-stack', {
  env: devEnv,
  repositoryOwner: repositoryOwner,
  gitDeployableAccounts: gitDeployableAccounts,
});

app.synth();

4. Bootstrapping and Deployment:

Now you’ll need to bootstrap your AWS accounts to prepare them for CDK deployments.

Start with the deployment account:

cdk bootstrap aws://<deployment_account_id>/eu-west-1 --profile lutku-deployment
yarn deploy --all --profile lutku-deployment

Navigate to the AWS Management Console, go to the IAM Roles in the deployment account, and find the github-actions-deploy-role. Copy the role’s ARN for the next steps.

AWS IAM Role for GitHub Actions

For each environment (dev, test, prod), run the following commands:

cdk bootstrap aws://<env_account_id>/eu-west-1 --profile lutku-<env> --trust <deployment_role_arn> --cloudformation-execution-policies 'arn:aws:iam::aws:policy/AdministratorAccess'

Replace <env_account_id> with the respective account ID, <env> with the environment (dev, test, prod), and <deployment_role_arn> with the role ARN copied earlier.

5. Verification:

After running the above commands, navigate to the CloudFormation console in each environment account (dev, test, prod). You should see a new stack created by the CDK bootstrap command. This confirms that the accounts are now ready for deployments using GitHub Actions.

CDK Toolkit assumable by another account

Conclusion

And that’s it! You have successfully set up and integrated your AWS accounts with GitHub for GitOps deployments. By following these steps, you have created a seamless and efficient workflow for deploying your applications across multiple AWS accounts.

Remember, while this setup provides a robust starting point, always continue to explore and adapt to the ever-evolving cloud landscape. Stay tuned for the final installment of this series, where we will delve deeper into advanced GitOps strategies and best practices.

References

Happy Cloud Engineering and until next time!