Skip to content

Commit

Permalink
feat: Create construct for cross account role
Browse files Browse the repository at this point in the history
Co-Authored-By: Jacob Winch <[email protected]>
  • Loading branch information
DavidLawes and jacobwinch committed Oct 10, 2023
1 parent aa5f363 commit 077464a
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`The GuCrossAccountRoleExperimental construct can create a cross account role 1`] = `
{
"Metadata": {
"gu:cdk:constructs": [
"GuStack",
"GuCrossAccountRoleExperimental",
],
"gu:cdk:version": "TEST",
},
"Resources": {
"testCrossAccountRole975BA7C0": {
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::1234:role/nameOfRoleInOtherAccountWhichCanAssumeThis-STAGE",
},
},
],
"Version": "2012-10-17",
},
"RoleName": "crossAccountRole",
"Tags": [
{
"Key": "gu:cdk:version",
"Value": "TEST",
},
{
"Key": "gu:repo",
"Value": "guardian/cdk",
},
{
"Key": "Stack",
"Value": "test-stack",
},
{
"Key": "Stage",
"Value": "TEST",
},
],
},
"Type": "AWS::IAM::Role",
},
},
}
`;
59 changes: 59 additions & 0 deletions src/experimental/constructs/iam/roles/cross-account-role.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Template } from "aws-cdk-lib/assertions";
import { ServicePrincipal } from "aws-cdk-lib/aws-iam";
import { GuRole } from "../../../../constructs/iam";
import { simpleGuStackForTesting } from "../../../../utils/test";
import { GuCrossAccountRoleExperimental } from "./cross-account-role";

describe("The GuCrossAccountRoleExperimental construct", () => {
it("can create a cross account role", () => {
const stack = simpleGuStackForTesting();
new GuCrossAccountRoleExperimental(stack, "testCrossAccountRole", {
nameOfRoleWhichCanAssumeThisRole: "nameOfRoleInOtherAccountWhichCanAssumeThis-STAGE",
roleName: "crossAccountRole",
accountId: "1234"
});
expect(Template.fromStack(stack).toJSON()).toMatchSnapshot();
});

it("can create a cross account role that can be assumed by a service in another account", () => {
const stackThatCreatesTheRole = simpleGuStackForTesting();
new GuCrossAccountRoleExperimental(stackThatCreatesTheRole, "testCrossAccountRole", {
nameOfRoleWhichCanAssumeThisRole: "nameOfRoleInOtherAccountWhichCanAssumeThisNewlyCreatedOne-CODE",
roleName: "crossAccountRole",
accountId: "1234"
});

Template.fromStack(stackThatCreatesTheRole).hasResourceProperties("AWS::IAM::Role", {
RoleName: "crossAccountRole",
AssumeRolePolicyDocument: {
Statement: [{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: {
AWS: "arn:aws:iam::1234:role/nameOfRoleInOtherAccountWhichCanAssumeThisNewlyCreatedOne-CODE"
}
}]
}
})

const stackThatAssumesTheRole = simpleGuStackForTesting();
new GuRole(stackThatAssumesTheRole, "idForRole", {
assumedBy: new ServicePrincipal("ec2.amazonaws.com"),
roleName: "nameOfRoleInOtherAccountWhichCanAssumeThisNewlyCreatedOne-CODE"
});

Template.fromStack(stackThatAssumesTheRole).hasResourceProperties("AWS::IAM::Role", {
RoleName: "nameOfRoleInOtherAccountWhichCanAssumeThisNewlyCreatedOne-CODE",
AssumeRolePolicyDocument: {
Statement: [{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: {
Service: "ec2.amazonaws.com"
}
}]
}
});
})
});

42 changes: 42 additions & 0 deletions src/experimental/constructs/iam/roles/cross-account-role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ArnPrincipal } from "aws-cdk-lib/aws-iam";
import type { GuStack } from "../../../../constructs/core";
import type { GuRoleProps} from "../../../../constructs/iam";
import {GuRole} from "../../../../constructs/iam";

export interface GuCrossAccountRoleExperimentalProps extends Omit<GuRoleProps, "assumedBy"> {
/**
* The name of the role that will assume this new GuCrossAccountRoleExperimental.
* It should be the full name including the stage (if appropriate).
*/
nameOfRoleWhichCanAssumeThisRole: string;

/**
* The AWS account ID associated with the resource that will consume this new GuCrossAccountRoleExperimental.
*
* If this construct is being used on a public repo it would be advisable to use
* [private-infrastructure-config](https://github.com/guardian/private-infrastructure-config) so that any account ID
* is not publicly available.
*/
accountId: string;
}

/**
* A construct to create a cross account role.
*
* In order to use this construct the name of the role that will assume this cross account role must be provided, along
* with the corresponding AWS account ID of the assuming role.
*
* The resulting role can only be assumed by the specified role in the given AWS account. When this role has been
* created it can be extended to grant permissions for specific actions, allowing a resource in one AWS account to
* perform actions in another.
*/
export class GuCrossAccountRoleExperimental extends GuRole {
constructor(scope: GuStack, id: string, props: GuCrossAccountRoleExperimentalProps) {
const { nameOfRoleWhichCanAssumeThisRole, accountId } = props;

super(scope, id, {
assumedBy: new ArnPrincipal(`arn:aws:iam::${accountId}:role/${nameOfRoleWhichCanAssumeThisRole}`),
...props
});
}
}

0 comments on commit 077464a

Please sign in to comment.