-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Create construct for cross account role #2053
Conversation
Co-Authored-By: Jacob Winch <19384074+jacobwinch@users.noreply.github.com>
accountId: string; | ||
} | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please let me know if I should be adding documentation anywhere else!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the correct place to add the docs, thanks!
It looks like there may be something wrong with the documentation setup, because I can't see the docs for some of the other experimental constructs either.
I think we might need to update https://github.com/guardian/cdk/blob/main/src/experimental/constructs/index.ts and/or https://github.com/guardian/cdk/blob/main/src/experimental/constructs/iam/index.ts to fix this - perhaps one for another PR unless it's simple!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
npm run format
Running |
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", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this might be a bit easier to follow as:
nameOfRoleWhichCanAssumeThisRole: "nameOfRoleInOtherAccountWhichCanAssumeThisNewlyCreatedOne-CODE", | |
nameOfRoleWhichCanAssumeThisRole: "roleInAccountA-CODE", |
const stackThatCreatesTheRole = simpleGuStackForTesting(); | ||
new GuCrossAccountRoleExperimental(stackThatCreatesTheRole, "testCrossAccountRole", { | ||
nameOfRoleWhichCanAssumeThisRole: "nameOfRoleInOtherAccountWhichCanAssumeThisNewlyCreatedOne-CODE", | ||
roleName: "crossAccountRole", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
roleName: "crossAccountRole", | |
roleName: `roleinAccountB-CODE`, |
new GuCrossAccountRoleExperimental(stackThatCreatesTheRole, "testCrossAccountRole", { | ||
nameOfRoleWhichCanAssumeThisRole: "nameOfRoleInOtherAccountWhichCanAssumeThisNewlyCreatedOne-CODE", | ||
roleName: "crossAccountRole", | ||
accountId: "1234", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
accountId: "1234", | |
accountId: "idForAccountA", |
const stackThatAssumesTheRole = simpleGuStackForTesting(); | ||
new GuRole(stackThatAssumesTheRole, "idForRole", { | ||
assumedBy: new ServicePrincipal("ec2.amazonaws.com"), | ||
roleName: "nameOfRoleInOtherAccountWhichCanAssumeThisNewlyCreatedOne-CODE", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
roleName: "nameOfRoleInOtherAccountWhichCanAssumeThisNewlyCreatedOne-CODE", | |
roleName: "roleInAccountA-CODE", |
I think there is overlap with AWS CDK's const myRole = new Role(); // Or `new GuRole`, as `GuRole` extends from `Role`
// https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.ArnComponents.html
const externalRoleArn: string = Arn.from({
service: 'iam',
account: '123456789012',
resource: 'role',
resourceName: 'TheOtherRole',
});
// https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.Role.html#static-fromwbrrolewbrarnscope-id-rolearn-options
const externalRole: Role = Role.fromRoleArn(this, 'AnExternalRole', externalRoleArn)
// https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.Role.html#grantwbrassumewbrroleidentity
myRole.grantAssumeRole(externalRole); If Given the class name ( |
Thanks @akash1810 - I didn't know about In terms of using |
I think this the main thing that we're trying to add over When using that function you pass in an |
Hi there, thinking about the use of "The grantAssumeRole function is a bit misleading here in that it isn't updating the trust policy of the role but rather granting the principal passed in to this action sts:AssumeRole permission. This ends up not doing anything because the principal here is a service who doesn't need to be granted this action, but rather needs to be in the trust policy." So, perhaps for now, I could use something like assumeRolePolicy which results in code like this:
Giving a policy statement (for my specific tests):
^ I'm not sure if this is a preferable approach to what I have now ( |
Wow, that's pretty confusing/surprising, but thanks for looking into that!
Personally I think |
|
||
const stackInAccountA = simpleGuStackForTesting(); | ||
const role = new GuRole(stackInAccountA, "idForRole", { | ||
assumedBy: new ServicePrincipal("ec2.amazonaws.com"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: as we're adding a test for documentation purposes, it could be worth a comment here to explain that (IIUC!) this corresponds to the AWS service that our app will be running in.
In other words, ec2.amazonaws.com
is just an example and it could be something else depending on the user's runtime e.g. lambda.amazonaws.com
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me 👍
Co-authored-by: Jacob Winch <jacob.winch@guardian.co.uk>
Thanks for the exploration to What would you think about making this behaviour a function within
|
Thanks @akash1810 - the benefits you mentioned are very compelling! I've been looking at how this could work: I tried adding method I was thinking about updating the type definition for
Is this closer to what you had in mind? |
Placing the logic in the constructor limits export class GuRole extends Role {
constructor(scope: GuStack, props: GuRoleProps) {
super(scope, props);
}
/**
* Grants permission for an external role to assume this role.
* Favour over [[ grantAssumeRole ]] which doesn't behave as you'd expect.
*
* @see https://github.com/aws/aws-cdk/issues/24507
*/
grantCrossAccountAssumeRole(externalAccountId: string, externalIamRoleName: string) {
const externalRoleArn: string = Arn.from({
service: 'iam',
account: externalAccountId,
resource: 'role',
resourceName: externalIamRoleName,
});
// https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.Role.html#static-fromwbrrolewbrarnscope-id-rolearn-options
const externalRole: Role = Role.fromRoleArn(this, 'AnExternalRole', externalRoleArn);
this.grant(externalRole, "sts:AssumeRole");
}
} |
Thanks @akash1810 - I'm wondering if I haven't done a very good job of explaining the problem I'm trying to solve. The difficulty I had recently was around how to go about allowing a service in one aws account to perform actions in another. There was a chat in the devx channel and Adam highlighted some great best practice. If there was a change to be added to gu cdk then I thought a nice addition could be to define a new type of role that encapsulates this best practice and provides a structure (with associated docs). At the time I didn't think the confusing part was about the role assumption, it was primarily around how to create the role itself. With that in mind, I'm not convinced that defining a method on the existing
However, I could be missing something. If the implementation of method |
This PR is stale because it has been open 30 days with no activity. Unless a comment is added or the “stale” label removed, this will be closed in 3 days |
This PR was closed because it has been stalled for 3 days with no activity. |
What does this change?
This change adds a new experimental construct:
GuCrossAccountRoleExperimental
.The intention is to provide an abstraction to support the creation of cross account roles in accordance with (what we considered to be) best practice. Specifically, the role is scoped to a specific role ARN, tightly controlling exactly what resources can assume the cross account role.
Creating a construct for cross account roles should help in situations such as this.
How to test
-[x] Run the tests
How can we measure success?
Consumers can create and use a cross account role using the new construct.
Have we considered potential risks?
Hopefully the risks are low/minimal - we used an existing AWS cdk definition of a cross-account role to inspire this construct.
Checklist
I have listed any breaking changes, along with a migration path 1<-- I don't believe this is necessary because we're only adding a new (experimental) construct.Footnotes
Consider whether this is something that will mean changes to projects that have already been migrated, or to the CDK CLI tool. If changes are required, consider adding a checklist here and/or linking to related PRs. ↩
If you are adding a new construct or pattern, has new documentation been added? If you are amending defaults or changing behaviour, are the existing docs still valid? ↩