Skip to content

Commit

Permalink
feat(cloud9-alpha): add support for federated-user and `assumed-rol…
Browse files Browse the repository at this point in the history
…e` for Cloud9 environment ownership (#27001)

Currently, the cloud9-alpha module only supports two IAM entities as the owners of a Cloud9 environment

- Account Root
- User

However, in many environments, access to an AWS account is gained via Federation. To use Cloud9 via the CDK in such environments, workarounds like the following one where required:

```
const cloud9 = new Ec2Environment(this, 'Cloud9', {..});

const cfnC9 = cloud9.node.findChild('Resource') as CfnEnvironmentEC2;
cfnC9.ownerArn = 'arn:sts:..;
```

This merge request adds support for assumed roles and federated users to be owners of C9 environments directly in the CDK construct.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
markusz authored Nov 1, 2023
1 parent 24ffb6a commit 00d2ff2
Show file tree
Hide file tree
Showing 12 changed files with 1,756 additions and 4 deletions.
46 changes: 43 additions & 3 deletions packages/@aws-cdk/aws-cloud9-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,16 @@ Every Cloud9 Environment has an **owner**. An owner has full control over the en

By default, the owner will be the identity that creates the Environment, which is most likely your CloudFormation Execution Role when the Environment is created using CloudFormation. Provider a value for the `owner` property to assign a different owner, either a specific IAM User or the AWS Account Root User.

`Owner` is a user that owns a Cloud9 environment . `Owner` has their own access permissions, resources. And we can specify an `Owner`in an Ec2 environment which could be of two types, 1. AccountRoot and 2. Iam User. It allows AWS to determine who has permissions to manage the environment, either an IAM user or the account root user (but using the account root user is not recommended, see [environment sharing best practices](https://docs.aws.amazon.com/cloud9/latest/user-guide/share-environment.html#share-environment-best-practices)).
`Owner` is an IAM entity that owns a Cloud9 environment. `Owner` has their own access permissions, and resources. You can specify an `Owner`in an EC2 environment which could be of the following types:

1. Account Root
2. IAM User
3. IAM Federated User
4. IAM Assumed Role

The ARN of the owner must satisfy the following regular expression: `^arn:(aws|aws-cn|aws-us-gov|aws-iso|aws-iso-b):(iam|sts)::\d+:(root|(user\/[\w+=/:,.@-]{1,64}|federated-user\/[\w+=/:,.@-]{2,32}|assumed-role\/[\w+=:,.@-]{1,64}\/[\w+=,.@-]{1,64}))$`

Note: Using the account root user is not recommended, see [environment sharing best practices](https://docs.aws.amazon.com/cloud9/latest/user-guide/share-environment.html#share-environment-best-practices).

To specify the AWS Account Root User as the environment owner, use `Owner.accountRoot()`

Expand All @@ -114,13 +123,14 @@ declare const vpc: ec2.Vpc;
new cloud9.Ec2Environment(this, 'C9Env', {
vpc,
imageId: cloud9.ImageId.AMAZON_LINUX_2,

owner: cloud9.Owner.accountRoot('111111111')
})
```

To specify a specific IAM User as the environment owner, use `Owner.user()`. The user should have the `AWSCloud9Administrator` managed policy

The user should have the `AWSCloud9User` (preferred) or `AWSCloud9Administrator` managed policy attached.

```ts
import * as iam from 'aws-cdk-lib/aws-iam';

Expand All @@ -135,9 +145,39 @@ new cloud9.Ec2Environment(this, 'C9Env', {
})
```

To specify a specific IAM Federated User as the environment owner, use `Owner.federatedUser(accountId, userName)`.

The user should have the `AWSCloud9User` (preferred) or `AWSCloud9Administrator` managed policy attached.

```ts
import * as iam from 'aws-cdk-lib/aws-iam';

declare const vpc: ec2.Vpc;
new cloud9.Ec2Environment(this, 'C9Env', {
vpc,
imageId: cloud9.ImageId.AMAZON_LINUX_2,
owner: cloud9.Owner.federatedUser(Stack.of(this).account, "Admin/johndoe")
})
```

To specify an IAM Assumed Role as the environment owner, use `Owner.assumedRole(accountId: string, roleName: string)`.

The role should have the `AWSCloud9User` (preferred) or `AWSCloud9Administrator` managed policy attached.

```ts
import * as iam from 'aws-cdk-lib/aws-iam';

declare const vpc: ec2.Vpc;
new cloud9.Ec2Environment(this, 'C9Env', {
vpc,
imageId: cloud9.ImageId.AMAZON_LINUX_2,
owner: cloud9.Owner.assumedRole(Stack.of(this).account, "Admin/johndoe-role")
})
```

## Auto-Hibernation

A Cloud9 environemnt can automatically start and stop the associated EC2 instance to reduce costs.
A Cloud9 environment can automatically start and stop the associated EC2 instance to reduce costs.

Use `automaticStop` to specify the number of minutes until the running instance is shut down after the environment was last used.

Expand Down
20 changes: 20 additions & 0 deletions packages/@aws-cdk/aws-cloud9-alpha/lib/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,26 @@ export class Owner {
return { ownerArn: user.userArn };
}

/**
* Make an IAM assumed role the environment owner
*
* @param accountId The account id of the target account
* @param roleName The name of the assumed role
*/
public static assumedRole(accountId: string, roleName: string): Owner {
return { ownerArn: `arn:${cdk.Aws.PARTITION}:sts::${accountId}:assumed-role/${roleName}` };
}

/**
* Make an IAM federated user the environment owner
*
* @param accountId The AccountId of the target account
* @param userName The name of the federated user
*/
public static federatedUser(accountId: string, userName: string): Owner {
return { ownerArn: `arn:${cdk.Aws.PARTITION}:sts::${accountId}:federated-user/${userName}` };
}

/**
* Make the Account Root User the environment owner (not recommended)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,43 @@ test('environment owner can be an IAM user', () => {
imageId: cloud9.ImageId.AMAZON_LINUX_2,
owner: Owner.user(user),
});

// THEN
const userLogicalId = stack.getLogicalId(user.node.defaultChild as iam.CfnUser);
Template.fromStack(stack).hasResourceProperties('AWS::Cloud9::EnvironmentEC2', {
OwnerArn: {
'Fn::GetAtt': ['User00B015A1', 'Arn'],
'Fn::GetAtt': [userLogicalId, 'Arn'],
},
});
});

test('environment owner can be an IAM Assumed Role', () => {
// WHEN
new cloud9.Ec2Environment(stack, 'C9Env', {
vpc,
imageId: cloud9.ImageId.AMAZON_LINUX_2,
owner: Owner.assumedRole('123456789098', 'Admin'),
});
// THEN

Template.fromStack(stack).hasResourceProperties('AWS::Cloud9::EnvironmentEC2', {
OwnerArn: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':sts::123456789098:assumed-role/Admin']] },
});
});

test('environment owner can be an IAM Federated User', () => {
// WHEN
new cloud9.Ec2Environment(stack, 'C9Env', {
vpc,
imageId: cloud9.ImageId.AMAZON_LINUX_2,
owner: Owner.federatedUser('123456789098', 'Admin'),
});
// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Cloud9::EnvironmentEC2', {
OwnerArn: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':sts::123456789098:federated-user/Admin']] },
});
});

test('environment owner can be account root', () => {
// WHEN
new cloud9.Ec2Environment(stack, 'C9Env', {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 00d2ff2

Please sign in to comment.