diff --git a/stacker_blueprints/iam_roles.py b/stacker_blueprints/iam_roles.py index 46da7982..46b71527 100644 --- a/stacker_blueprints/iam_roles.py +++ b/stacker_blueprints/iam_roles.py @@ -8,7 +8,8 @@ iam, ) -from awacs.aws import Policy +from awacs.aws import Policy, Statement, Principal +from awacs import sts from awacs.helpers.trust import ( get_default_assumerole_policy, get_lambda_assumerole_policy @@ -227,3 +228,74 @@ def create_template(self): self.create_lambda_role(role) self.create_policy() + + +class IAMRole(Blueprint): + """ + Blueprint to create an IAM role. + + - class_path: stacker_blueprints.iam_roles.IAMRole + name: my-role + variables: + Name: myRole + Path: / + AttachedPolicies: + - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess + AssumeRole: + - arn:aws:iam::123456789012:user/JohnDoe + """ + VARIABLES = { + "Name": { + "type": str, + "description": "The name of the role", + "default": "", + }, + "Path": { + "type": str, + "description": "Provide the path", + "default": "/", + }, + "AttachedPolicies": { + "type": list, + "description": "List of ARNs of policies to attach", + "default": [], + }, + "AssumeRole": { + "type": list, + "description": "List of ARNs of entities allowed to assume this role", + "default": [], + }, + } + + def create_template(self): + variables = self.get_variables() + + ar_policy = Policy( + Statement=[ + Statement( + Effect='Allow', + Principal=Principal("AWS", p), + Action=[ + sts.AssumeRole, + ], + ) for p in variables['AssumeRole'] + ] + ) + + role = self.template.add_resource( + iam.Role( + variables['Name'] if variables['Name'] != "" else "Role", + RoleName=variables['Name'], + Path=variables['Path'], + ManagedPolicyArns=variables['AttachedPolicies'], + AssumeRolePolicyDocument=ar_policy, + ) + ) + + self.template.add_output( + Output("RoleName", Value=Ref(role)) + ) + + self.template.add_output( + Output("RoleArn", Value=GetAtt(role.title, "Arn")) + ) diff --git a/tests/fixtures/blueprints/test_iam_roles_iam_role.json b/tests/fixtures/blueprints/test_iam_roles_iam_role.json new file mode 100644 index 00000000..63245b89 --- /dev/null +++ b/tests/fixtures/blueprints/test_iam_roles_iam_role.json @@ -0,0 +1,41 @@ +{ + "Outputs": { + "RoleArn": { + "Value": { + "Fn::GetAtt": [ + "Role", + "Arn" + ] + } + }, + "RoleName": { + "Value": { + "Ref": "Role" + } + } + }, + "Resources": { + "Role": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::123456789012:user/JohnDoe" + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" + ], + "Path": "/" + }, + "Type": "AWS::IAM::Role" + } + } +} \ No newline at end of file diff --git a/tests/fixtures/blueprints/test_iam_roles_iam_role_name.json b/tests/fixtures/blueprints/test_iam_roles_iam_role_name.json new file mode 100644 index 00000000..1b3fe0b9 --- /dev/null +++ b/tests/fixtures/blueprints/test_iam_roles_iam_role_name.json @@ -0,0 +1,42 @@ +{ + "Outputs": { + "RoleArn": { + "Value": { + "Fn::GetAtt": [ + "myRole", + "Arn" + ] + } + }, + "RoleName": { + "Value": { + "Ref": "myRole" + } + } + }, + "Resources": { + "myRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::123456789012:user/JohnDoe" + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" + ], + "Path": "/", + "RoleName": "myRole" + }, + "Type": "AWS::IAM::Role" + } + } +} \ No newline at end of file diff --git a/tests/test_iam_roles.py b/tests/test_iam_roles.py index c2e718c7..199f3209 100644 --- a/tests/test_iam_roles.py +++ b/tests/test_iam_roles.py @@ -88,3 +88,37 @@ def test_role_name(self): blueprint.resolve_variables(self.generate_variables()) blueprint.create_template() self.assertRenderedBlueprint(blueprint) + + +class TestIamRoleBlueprint(TestIamRolesCommon): + + def test_role(self): + self.common_variables = { + 'Path': '/', + 'AttachedPolicies': [ + 'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess', + ], + 'AssumeRole': [ + 'arn:aws:iam::123456789012:user/JohnDoe', + ], + } + blueprint = self.create_blueprint('test_iam_roles_iam_role', class_name=iam_roles.IAMRole) + blueprint.resolve_variables(self.generate_variables()) + blueprint.create_template() + self.assertRenderedBlueprint(blueprint) + + def test_role_name(self): + self.common_variables = { + 'Name': 'myRole', + 'Path': '/', + 'AttachedPolicies': [ + 'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess', + ], + 'AssumeRole': [ + 'arn:aws:iam::123456789012:user/JohnDoe', + ], + } + blueprint = self.create_blueprint('test_iam_roles_iam_role_name', class_name=iam_roles.IAMRole) + blueprint.resolve_variables(self.generate_variables()) + blueprint.create_template() + self.assertRenderedBlueprint(blueprint)