Skip to content

Commit

Permalink
feat(aws-iam): add aws iam instance profile: fixes aws#1223
Browse files Browse the repository at this point in the history
  • Loading branch information
cmaurer committed Nov 21, 2018
1 parent 0904436 commit aa082b4
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 162 deletions.
37 changes: 18 additions & 19 deletions packages/@aws-cdk/aws-iam/lib/instance-profile.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Construct, Token } from '@aws-cdk/cdk';
import { Construct } from '@aws-cdk/cdk';
import { cloudformation } from './iam.generated';
import { ServicePrincipal } from './policy-document';
import { Role } from './role';
import { generateInstanceProfileName, undefinedIfEmpty } from './util';

export interface InstanceProfileProps {

Expand All @@ -14,8 +14,9 @@ export interface InstanceProfileProps {
/**
* The name of an existing IAM role to associate with this instance profile.
* Currently, you can assign a maximum of one role to an instance profile.
* @default Creates a default ec2.amazonaws.com Assume Role.
*/
roles: Role[];
role?: Role;

/**
* The name of the instance profile that you want to create.
Expand All @@ -34,27 +35,25 @@ export interface InstanceProfileProps {
*/
export class InstanceProfile extends Construct {

public readonly instanceProfileName: string;
public readonly roles: Role[];
public readonly roles = new Array<Role>();
public defaultRole = new Role(this, 'EC2Role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com')
});

constructor(parent: Construct, name: string, props: InstanceProfileProps) {
constructor(parent: Construct, name: string, props: InstanceProfileProps = {}) {
super(parent, name);
this.validateMaxSessionDuration(props.roles);
const resource = new cloudformation.InstanceProfileResource(this, 'Resource', {
instanceProfileName: new Token(() => this.instanceProfileName),
path: props.path,
roles: undefinedIfEmpty(() => this.roles.map(r => r.roleName))
});

this.instanceProfileName = props.instanceProfileName || generateInstanceProfileName(resource.logicalId);
this.roles = props.roles;
if (props.role) {
this.defaultRole = props.role;
}
this.roles.push(this.defaultRole);

}
new cloudformation.InstanceProfileResource(this, 'Resource', {
instanceProfileName: props.instanceProfileName,
roles: this.roles.map(r => r.roleName),
path: props.path
});

private validateMaxSessionDuration(roles: Role[]) {
if (roles.length > 1) {
throw new Error('Currently, you can assign a maximum of one role to an instance profile.');
}
}

}
7 changes: 0 additions & 7 deletions packages/@aws-cdk/aws-iam/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,6 @@ export function generatePolicyName(logicalId: string) {
return logicalId.substring(Math.max(logicalId.length - MAX_POLICY_NAME_LEN, 0), logicalId.length);
}

/**
* Used to generate a unique instance profile name based on the instance profile resource construct.
*/
export function generateInstanceProfileName(logicalId: string) {
return logicalId.substring(Math.max(logicalId.length - MAX_POLICY_NAME_LEN, 0), logicalId.length);
}

/**
* Helper class that maintains the set of attached policies for a principal.
*/
Expand Down
177 changes: 41 additions & 136 deletions packages/@aws-cdk/aws-iam/test/test.instance-profile.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from '@aws-cdk/assert';
import { expect, haveResource } from '@aws-cdk/assert';
import { Stack } from '@aws-cdk/cdk';
import { Test } from 'nodeunit';
import { InstanceProfile, Role, ServicePrincipal } from '../lib';
Expand All @@ -13,182 +13,87 @@ export = {

new InstanceProfile(stack, 'TestEC2InstanceProfile', {
instanceProfileName: "TestInstanceProfile",
roles: [ testEc2Role ],
role: testEc2Role,
path: "/"
});

expect(stack).toMatch({ Resources: {
TestEC2RoleBD27AEF4: {
Type: 'AWS::IAM::Role',
Properties: {
AssumeRolePolicyDocument: {
Statement: [{
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: 'ec2.amazonaws.com'
}
}],
Version: '2012-10-17'
}
}
},
TestEC2InstanceProfile8AF426F8: {
Type: 'AWS::IAM::InstanceProfile',
Properties: {
Roles: [
{ Ref: "TestEC2RoleBD27AEF4" }
],
InstanceProfileName: 'TestInstanceProfile',
Path: '/'
}
}
}
});
expect(stack).to(haveResource('AWS::IAM::InstanceProfile', {
Roles: [
{ Ref: "TestEC2RoleBD27AEF4" }
],
InstanceProfileName: 'TestInstanceProfile',
Path: '/'
}));
test.done();
},

'test generated instance profile name'(test: Test) {
'test without path'(test: Test) {
const stack = new Stack();

const testEc2Role = new Role(stack, 'TestEC2Role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com')
});

new InstanceProfile(stack, 'TestEC2InstanceProfile', {
roles: [ testEc2Role ],
path: "/"
instanceProfileName: 'InstanceProfileWithoutPath',
role: testEc2Role
});

expect(stack).toMatch({ Resources: {
TestEC2RoleBD27AEF4: {
Type: 'AWS::IAM::Role',
Properties: {
AssumeRolePolicyDocument: {
Statement: [{
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: 'ec2.amazonaws.com'
}
}],
Version: '2012-10-17'
}
}
},
TestEC2InstanceProfile8AF426F8: {
Type: 'AWS::IAM::InstanceProfile',
Properties: {
Roles: [
{ Ref: "TestEC2RoleBD27AEF4" }
],
InstanceProfileName: "TestEC2InstanceProfile8AF426F8",
Path: '/'
}
}
}
});
expect(stack).to(haveResource('AWS::IAM::InstanceProfile', {
Roles: [
{ Ref: "TestEC2RoleBD27AEF4" }
],
InstanceProfileName: 'InstanceProfileWithoutPath'
}));
test.done();
},

'test without path'(test: Test) {
'test role only'(test: Test) {
const stack = new Stack();

const testEc2Role = new Role(stack, 'TestEC2Role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com')
});

new InstanceProfile(stack, 'TestEC2InstanceProfile', {
instanceProfileName: 'InstanceProfileWithoutPath',
roles: [ testEc2Role ]
role: testEc2Role
});

expect(stack).toMatch({ Resources: {
TestEC2RoleBD27AEF4: {
Type: 'AWS::IAM::Role',
Properties: {
AssumeRolePolicyDocument: {
Statement: [{
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: 'ec2.amazonaws.com'
}
}],
Version: '2012-10-17'
}
}
},
TestEC2InstanceProfile8AF426F8: {
Type: 'AWS::IAM::InstanceProfile',
Properties: {
Roles: [
{ Ref: "TestEC2RoleBD27AEF4" }
],
InstanceProfileName: 'InstanceProfileWithoutPath'
}
}
}
});
expect(stack).to(haveResource('AWS::IAM::InstanceProfile', {
Roles: [
{ Ref: "TestEC2RoleBD27AEF4" }
]
}));
test.done();
},

'test role only'(test: Test) {
'test role other than ec2'(test: Test) {
const stack = new Stack();

const testEc2Role = new Role(stack, 'TestEC2Role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com')
const testEc2Role = new Role(stack, 'TestEC2SNSRole', {
assumedBy: new ServicePrincipal('sns.amazonaws.com')
});

new InstanceProfile(stack, 'TestEC2InstanceProfile', {
roles: [ testEc2Role ]
role: testEc2Role
});

expect(stack).toMatch({ Resources: {
TestEC2RoleBD27AEF4: {
Type: 'AWS::IAM::Role',
Properties: {
AssumeRolePolicyDocument: {
Statement: [{
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: 'ec2.amazonaws.com'
}
}],
Version: '2012-10-17'
}
}
},
TestEC2InstanceProfile8AF426F8: {
Type: 'AWS::IAM::InstanceProfile',
Properties: {
Roles: [
{ Ref: "TestEC2RoleBD27AEF4" }
],
InstanceProfileName: "TestEC2InstanceProfile8AF426F8"
}
}
}
});
expect(stack).to(haveResource('AWS::IAM::InstanceProfile', {
Roles: [
{ Ref: "TestEC2SNSRole3159CBC5" }
]
}));
test.done();
},

'test fails with multiple rolea'(test: Test) {
'default ec2 role created'(test: Test) {
const stack = new Stack();

const testEc2Role = new Role(stack, 'TestEC2Role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com')
});

const testAnotherEc2Role = new Role(stack, 'TestAnotherEC2Role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com')
});

test.throws(() =>
new InstanceProfile(stack, 'TestEC2InstanceProfile', { roles: [ testEc2Role, testAnotherEc2Role ] }),
'Currently, you can assign a maximum of one role to an instance profile.'
);
new InstanceProfile(stack, 'TestEC2InstanceProfile', {});
expect(stack).to(haveResource('AWS::IAM::InstanceProfile', {
Roles: [
{ Ref: "TestEC2InstanceProfileEC2Role5106A065" }
]
}));
test.done();
}
};

0 comments on commit aa082b4

Please sign in to comment.