Skip to content

Commit 5f00a63

Browse files
authored
Merge pull request #156 from bweigel/cfn_intrinsic_functions
Add support for CloudFormation intrinsic function for role
2 parents 099dad3 + b3bd40c commit 5f00a63

File tree

3 files changed

+82
-12
lines changed

3 files changed

+82
-12
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,7 @@ $ sls invoke stepf --name <stepfunctionname> --data '{"foo":"bar"}'
626626
- --path or -p The path to a json file with input data to be passed to the invoked step function.
627627
628628
## IAM Role
629+
629630
The IAM roles required to run Statemachine are automatically generated. It is also possible to specify ARN directly.
630631
631632
Here's an example:
@@ -637,6 +638,28 @@ stepFunctions:
637638
role: arn:aws:iam::xxxxxxxx:role/yourRole
638639
definition:
639640
```
641+
642+
It is also possible to use the [CloudFormation intrinsic functions](https://docs.aws.amazon.com/en_en/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html) to reference resources from elsewhere:
643+
644+
```yml
645+
stepFunctions:
646+
stateMachines:
647+
hello:
648+
role:
649+
Ref: StateMachineRole
650+
definition:
651+
...
652+
653+
resources:
654+
Resources:
655+
StateMachineRole:
656+
Type: AWS::IAM::Role
657+
Properties:
658+
...
659+
```
660+
661+
The short form of the intrinsic functions (i.e. `!Sub`, `!Ref`) is not supported at the moment.
662+
640663
## Tips
641664
### How to specify the stateMachine ARN to environment variables
642665
Here is serverless.yml sample to specify the stateMachine ARN to environment variables.

lib/deploy/stepFunctions/compileStateMachines.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22
const _ = require('lodash');
33
const BbPromise = require('bluebird');
44

5+
function isIntrinsic(obj) {
6+
const isObject = typeof obj === 'object';
7+
return isObject && Object.keys(obj).some((k) => k.startsWith('Fn::') || k.startsWith('Ref'));
8+
}
9+
510
module.exports = {
11+
isIntrinsic,
612
compileStateMachines() {
713
if (this.isStateMachines()) {
814
this.getAllStateMachines().forEach((stateMachineName) => {
@@ -37,15 +43,18 @@ module.exports = {
3743
' Please check the README for more info.',
3844
].join('');
3945
throw new this.serverless.classes
40-
.Error(errorMessage);
46+
.Error(errorMessage);
4147
}
48+
} else if (isIntrinsic(stateMachineObj.role)) {
49+
RoleArn = stateMachineObj.role;
4250
} else {
4351
const errorMessage = [
44-
`role property in stateMachine "${stateMachineName}" is not an string`,
52+
`role property in stateMachine "${stateMachineName}" is neither a string`,
53+
' nor a CloudFormation intrinsic function',
4554
' Please check the README for more info.',
4655
].join('');
4756
throw new this.serverless.classes
48-
.Error(errorMessage);
57+
.Error(errorMessage);
4958
}
5059
} else {
5160
RoleArn = {
@@ -61,15 +70,14 @@ module.exports = {
6170
stateMachineObj);
6271
const stateMachineOutputLogicalId = this
6372
.getStateMachineOutputLogicalId(stateMachineName, stateMachineObj);
64-
const stateMachineTemplate =
65-
{
66-
Type: 'AWS::StepFunctions::StateMachine',
67-
Properties: {
68-
DefinitionString,
69-
RoleArn,
70-
},
71-
DependsOn,
72-
};
73+
const stateMachineTemplate = {
74+
Type: 'AWS::StepFunctions::StateMachine',
75+
Properties: {
76+
DefinitionString,
77+
RoleArn,
78+
},
79+
DependsOn,
80+
};
7381

7482
const newStateMachineObject = {
7583
[stateMachineLogicalId]: stateMachineTemplate,

lib/deploy/stepFunctions/compileStateMachines.test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,45 @@ describe('#compileStateMachines', () => {
234234
).to.equal('StateMachineBeta2');
235235
});
236236

237+
it('should respect CloudFormation intrinsic functions for role property', () => {
238+
serverless.service.stepFunctions = {
239+
stateMachines: {
240+
myStateMachine1: {
241+
name: 'stateMachineWithIntrinsicRole1',
242+
definition: 'definition1\n',
243+
role: { 'Fn::Attr': ['RoleID', 'Arn'] },
244+
},
245+
myStateMachine2: {
246+
name: 'stateMachineWithIntrinsicRole2',
247+
definition: 'definition1\n',
248+
role: { Ref: 'CloudformationId' },
249+
},
250+
},
251+
};
252+
serverlessStepFunctions.compileStateMachines();
253+
expect(serverlessStepFunctions.serverless.service
254+
.provider.compiledCloudFormationTemplate.Resources
255+
.StateMachineWithIntrinsicRole1.Properties.RoleArn
256+
).to.deep.equal({ 'Fn::Attr': ['RoleID', 'Arn'] });
257+
expect(serverlessStepFunctions.serverless.service
258+
.provider.compiledCloudFormationTemplate.Resources
259+
.StateMachineWithIntrinsicRole2.Properties.RoleArn
260+
).to.deep.equal({ Ref: 'CloudformationId' });
261+
});
262+
263+
it('should throw error if role property is neither string nor intrinsic functions', () => {
264+
serverless.service.stepFunctions = {
265+
stateMachines: {
266+
myStateMachine1: {
267+
name: 'stateMachineWithIntrinsicRole',
268+
definition: 'definition1\n',
269+
role: { XXX: ['RoleID', 'Arn'] },
270+
},
271+
},
272+
};
273+
expect(() => serverlessStepFunctions.compileStateMachines()).to.throw(Error);
274+
});
275+
237276
it('should throw error when definition property is not given', () => {
238277
serverless.service.stepFunctions = {
239278
stateMachines: {

0 commit comments

Comments
 (0)