From 6e30c0b879618c3e740cfc197b4a24a714764e22 Mon Sep 17 00:00:00 2001 From: nom3ad <19239479+nom3ad@users.noreply.github.com> Date: Wed, 24 Nov 2021 20:42:08 +0530 Subject: [PATCH] feat(iam): support `fromGroupName()` for IAM groups (#17243) IAM Policies and Users already support import by name. Extending same for Groups ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-iam/README.md | 29 ++++++++++++++--- packages/@aws-cdk/aws-iam/lib/group.ts | 18 +++++++++++ packages/@aws-cdk/aws-iam/test/group.test.ts | 33 +++++++++++++++----- 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/packages/@aws-cdk/aws-iam/README.md b/packages/@aws-cdk/aws-iam/README.md index a81b25d53e6ba..66d720ba8a708 100644 --- a/packages/@aws-cdk/aws-iam/README.md +++ b/packages/@aws-cdk/aws-iam/README.md @@ -329,7 +329,7 @@ Identity Pools Developer Guide]. The following examples defines an OpenID Connect provider. Two client IDs (audiences) are will be able to send authentication requests to -https://openid/connect. +. ```ts const provider = new iam.OpenIdConnectProvider(this, 'MyProvider', { @@ -439,6 +439,26 @@ const user = iam.User.fromUserAttributes(this, 'MyImportedUserByAttributes', { }); ``` +## Groups + +An IAM user group is a collection of IAM users. User groups let you specify permissions for multiple users. + +```ts +const group = new iam.Group(this, 'MyGroup'); +``` + +To import an existing group by ARN: + +```ts +const group = iam.Group.fromGroupArn(this, 'MyImportedGroupByArn', 'arn:aws:iam::account-id:group/group-name'); +``` + +To import an existing group by name [with path](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-friendly-names): + +```ts +const group = iam.Group.fromGroupName(this, 'MyImportedGroupByName', 'group-name'); +``` + To add a user to a group (both for a new and imported user/group): ```ts @@ -450,12 +470,11 @@ user.addToGroup(group); group.addUser(user); ``` - ## Features - * Policy name uniqueness is enforced. If two policies by the same name are attached to the same +* Policy name uniqueness is enforced. If two policies by the same name are attached to the same principal, the attachment will fail. - * Policy names are not required - the CDK logical ID will be used and ensured to be unique. - * Policies are validated during synthesis to ensure that they have actions, and that policies +* Policy names are not required - the CDK logical ID will be used and ensured to be unique. +* Policies are validated during synthesis to ensure that they have actions, and that policies attached to IAM principals specify relevant resources, while policies attached to resources specify which IAM principals they apply to. diff --git a/packages/@aws-cdk/aws-iam/lib/group.ts b/packages/@aws-cdk/aws-iam/lib/group.ts index 1fb4fac6b2fe4..350a447b517f5 100644 --- a/packages/@aws-cdk/aws-iam/lib/group.ts +++ b/packages/@aws-cdk/aws-iam/lib/group.ts @@ -156,6 +156,24 @@ export class Group extends GroupBase { return new Import(scope, id); } + /** + * Import an existing group by given name (with path). + * This method has same caveats of `fromGroupArn` + * + * @param scope construct scope + * @param id construct id + * @param groupName the groupName (path included) of the existing group to import + */ + static fromGroupName(scope: Construct, id: string, groupName: string) { + const groupArn = Stack.of(scope).formatArn({ + service: 'iam', + region: '', + resource: 'group', + resourceName: groupName, + }); + return Group.fromGroupArn(scope, id, groupArn); + } + public readonly groupName: string; public readonly groupArn: string; diff --git a/packages/@aws-cdk/aws-iam/test/group.test.ts b/packages/@aws-cdk/aws-iam/test/group.test.ts index d8b7c6fb47913..6fc7129a3a1d9 100644 --- a/packages/@aws-cdk/aws-iam/test/group.test.ts +++ b/packages/@aws-cdk/aws-iam/test/group.test.ts @@ -27,15 +27,15 @@ describe('IAM groups', () => { { MyGroupCBA54B1B: { Type: 'AWS::IAM::Group' }, User1E278A736: - { - Type: 'AWS::IAM::User', - Properties: { Groups: [{ Ref: 'MyGroupCBA54B1B' }] }, - }, + { + Type: 'AWS::IAM::User', + Properties: { Groups: [{ Ref: 'MyGroupCBA54B1B' }] }, + }, User21F1486D1: - { - Type: 'AWS::IAM::User', - Properties: { Groups: [{ Ref: 'MyGroupCBA54B1B' }] }, - }, + { + Type: 'AWS::IAM::User', + Properties: { Groups: [{ Ref: 'MyGroupCBA54B1B' }] }, + }, }, }); }); @@ -56,4 +56,21 @@ describe('IAM groups', () => { ], }); }); + + test('groups imported by group name have valid arn', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const group1 = Group.fromGroupName(stack, 'imported-group1', 'MyGroupName1'); + const group2 = Group.fromGroupName(stack, 'imported-group2', 'division/MyGroupName2'); + + // THEN + expect(stack.resolve(group1.groupArn)).toStrictEqual({ + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::', { Ref: 'AWS::AccountId' }, ':group/MyGroupName1']], + }); + expect(stack.resolve(group2.groupArn)).toStrictEqual({ + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::', { Ref: 'AWS::AccountId' }, ':group/division/MyGroupName2']], + }); + }); });