Skip to content

Commit 7bbb415

Browse files
committed
Add support for creating an IAM role
This adds `stacker_blueprints.iam_roles.IAMRole`, which creates an IAM Role, accepting a list of principals allowed to assume the role, and attached policies. It's expected that this would be used with an existing user and policy.
1 parent 7013151 commit 7bbb415

File tree

4 files changed

+188
-1
lines changed

4 files changed

+188
-1
lines changed

stacker_blueprints/iam_roles.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
iam,
99
)
1010

11-
from awacs.aws import Policy
11+
from awacs.aws import Policy, Statement, Principal
12+
from awacs import sts
1213
from awacs.helpers.trust import (
1314
get_default_assumerole_policy,
1415
get_lambda_assumerole_policy
@@ -227,3 +228,73 @@ def create_template(self):
227228
self.create_lambda_role(role)
228229

229230
self.create_policy()
231+
232+
233+
class IAMRole(Blueprint):
234+
"""
235+
Blueprint to create an IAM role.
236+
237+
- class_path: stacker_blueprints.iam_roles.IAMRole
238+
name: my-role
239+
variables:
240+
Name: myRole
241+
Path: /
242+
AttachedPolicies:
243+
- arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
244+
AssumeRole:
245+
- arn:aws:iam::123456789012:user/JohnDoe
246+
"""
247+
VARIABLES = {
248+
"Name": {
249+
"type": str,
250+
"description": "The name of the role",
251+
"default": "Role",
252+
},
253+
"Path": {
254+
"type": str,
255+
"description": "Provide the path",
256+
"default": "/",
257+
},
258+
"AttachedPolicies": {
259+
"type": list,
260+
"description": "List of ARNs of policies to attach",
261+
"default": [],
262+
},
263+
"AssumeRole": {
264+
"type": list,
265+
"description": "List of ARNs of entities allowed to assume this role",
266+
"default": [],
267+
},
268+
}
269+
270+
def create_template(self):
271+
variables = self.get_variables()
272+
273+
ar_policy = Policy(
274+
Statement=[
275+
Statement(
276+
Effect='Allow',
277+
Principal=Principal("AWS", p),
278+
Action=[
279+
sts.AssumeRole,
280+
],
281+
) for p in variables['AssumeRole']
282+
]
283+
)
284+
285+
role = self.template.add_resource(
286+
iam.Role(
287+
variables['Name'],
288+
Path=variables['Path'],
289+
ManagedPolicyArns=variables['AttachedPolicies'],
290+
AssumeRolePolicyDocument=ar_policy,
291+
)
292+
)
293+
294+
self.template.add_output(
295+
Output("RoleName", Value=Ref(role))
296+
)
297+
298+
self.template.add_output(
299+
Output("RoleArn", Value=GetAtt(role.title, "Arn"))
300+
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"Outputs": {
3+
"RoleArn": {
4+
"Value": {
5+
"Fn::GetAtt": [
6+
"Role",
7+
"Arn"
8+
]
9+
}
10+
},
11+
"RoleName": {
12+
"Value": {
13+
"Ref": "Role"
14+
}
15+
}
16+
},
17+
"Resources": {
18+
"Role": {
19+
"Properties": {
20+
"AssumeRolePolicyDocument": {
21+
"Statement": [
22+
{
23+
"Action": [
24+
"sts:AssumeRole"
25+
],
26+
"Effect": "Allow",
27+
"Principal": {
28+
"AWS": "arn:aws:iam::123456789012:user/JohnDoe"
29+
}
30+
}
31+
]
32+
},
33+
"ManagedPolicyArns": [
34+
"arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
35+
],
36+
"Path": "/"
37+
},
38+
"Type": "AWS::IAM::Role"
39+
}
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"Outputs": {
3+
"RoleArn": {
4+
"Value": {
5+
"Fn::GetAtt": [
6+
"myRole",
7+
"Arn"
8+
]
9+
}
10+
},
11+
"RoleName": {
12+
"Value": {
13+
"Ref": "myRole"
14+
}
15+
}
16+
},
17+
"Resources": {
18+
"myRole": {
19+
"Properties": {
20+
"AssumeRolePolicyDocument": {
21+
"Statement": [
22+
{
23+
"Action": [
24+
"sts:AssumeRole"
25+
],
26+
"Effect": "Allow",
27+
"Principal": {
28+
"AWS": "arn:aws:iam::123456789012:user/JohnDoe"
29+
}
30+
}
31+
]
32+
},
33+
"ManagedPolicyArns": [
34+
"arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
35+
],
36+
"Path": "/"
37+
},
38+
"Type": "AWS::IAM::Role"
39+
}
40+
}
41+
}

tests/test_iam_roles.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,37 @@ def test_role_name(self):
8888
blueprint.resolve_variables(self.generate_variables())
8989
blueprint.create_template()
9090
self.assertRenderedBlueprint(blueprint)
91+
92+
93+
class TestIamRoleBlueprint(TestIamRolesCommon):
94+
95+
def test_role(self):
96+
self.common_variables = {
97+
'Path': '/',
98+
'AttachedPolicies': [
99+
'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess',
100+
],
101+
'AssumeRole': [
102+
'arn:aws:iam::123456789012:user/JohnDoe',
103+
],
104+
}
105+
blueprint = self.create_blueprint('test_iam_roles_iam_role', class_name=iam_roles.IAMRole)
106+
blueprint.resolve_variables(self.generate_variables())
107+
blueprint.create_template()
108+
self.assertRenderedBlueprint(blueprint)
109+
110+
def test_role_name(self):
111+
self.common_variables = {
112+
'Name': 'myRole',
113+
'Path': '/',
114+
'AttachedPolicies': [
115+
'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess',
116+
],
117+
'AssumeRole': [
118+
'arn:aws:iam::123456789012:user/JohnDoe',
119+
],
120+
}
121+
blueprint = self.create_blueprint('test_iam_roles_iam_role_name', class_name=iam_roles.IAMRole)
122+
blueprint.resolve_variables(self.generate_variables())
123+
blueprint.create_template()
124+
self.assertRenderedBlueprint(blueprint)

0 commit comments

Comments
 (0)