IAM - Implement ServiceLinkedRoles (#5089)
This commit is contained in:
parent
578de3d47f
commit
76fe578d95
@ -2903,7 +2903,7 @@
|
|||||||
|
|
||||||
## iam
|
## iam
|
||||||
<details>
|
<details>
|
||||||
<summary>71% implemented</summary>
|
<summary>73% implemented</summary>
|
||||||
|
|
||||||
- [ ] add_client_id_to_open_id_connect_provider
|
- [ ] add_client_id_to_open_id_connect_provider
|
||||||
- [X] add_role_to_instance_profile
|
- [X] add_role_to_instance_profile
|
||||||
@ -2922,7 +2922,7 @@
|
|||||||
- [X] create_policy_version
|
- [X] create_policy_version
|
||||||
- [X] create_role
|
- [X] create_role
|
||||||
- [X] create_saml_provider
|
- [X] create_saml_provider
|
||||||
- [ ] create_service_linked_role
|
- [X] create_service_linked_role
|
||||||
- [ ] create_service_specific_credential
|
- [ ] create_service_specific_credential
|
||||||
- [X] create_user
|
- [X] create_user
|
||||||
- [X] create_virtual_mfa_device
|
- [X] create_virtual_mfa_device
|
||||||
@ -2942,7 +2942,7 @@
|
|||||||
- [X] delete_role_policy
|
- [X] delete_role_policy
|
||||||
- [X] delete_saml_provider
|
- [X] delete_saml_provider
|
||||||
- [X] delete_server_certificate
|
- [X] delete_server_certificate
|
||||||
- [ ] delete_service_linked_role
|
- [X] delete_service_linked_role
|
||||||
- [ ] delete_service_specific_credential
|
- [ ] delete_service_specific_credential
|
||||||
- [X] delete_signing_certificate
|
- [X] delete_signing_certificate
|
||||||
- [X] delete_ssh_public_key
|
- [X] delete_ssh_public_key
|
||||||
@ -2978,7 +2978,7 @@
|
|||||||
- [X] get_server_certificate
|
- [X] get_server_certificate
|
||||||
- [ ] get_service_last_accessed_details
|
- [ ] get_service_last_accessed_details
|
||||||
- [ ] get_service_last_accessed_details_with_entities
|
- [ ] get_service_last_accessed_details_with_entities
|
||||||
- [ ] get_service_linked_role_deletion_status
|
- [X] get_service_linked_role_deletion_status
|
||||||
- [X] get_ssh_public_key
|
- [X] get_ssh_public_key
|
||||||
- [X] get_user
|
- [X] get_user
|
||||||
- [X] get_user_policy
|
- [X] get_user_policy
|
||||||
|
@ -42,7 +42,7 @@ iam
|
|||||||
- [X] create_policy_version
|
- [X] create_policy_version
|
||||||
- [X] create_role
|
- [X] create_role
|
||||||
- [X] create_saml_provider
|
- [X] create_saml_provider
|
||||||
- [ ] create_service_linked_role
|
- [X] create_service_linked_role
|
||||||
- [ ] create_service_specific_credential
|
- [ ] create_service_specific_credential
|
||||||
- [X] create_user
|
- [X] create_user
|
||||||
- [X] create_virtual_mfa_device
|
- [X] create_virtual_mfa_device
|
||||||
@ -64,7 +64,7 @@ iam
|
|||||||
- [X] delete_role_policy
|
- [X] delete_role_policy
|
||||||
- [X] delete_saml_provider
|
- [X] delete_saml_provider
|
||||||
- [X] delete_server_certificate
|
- [X] delete_server_certificate
|
||||||
- [ ] delete_service_linked_role
|
- [X] delete_service_linked_role
|
||||||
- [ ] delete_service_specific_credential
|
- [ ] delete_service_specific_credential
|
||||||
- [X] delete_signing_certificate
|
- [X] delete_signing_certificate
|
||||||
- [X] delete_ssh_public_key
|
- [X] delete_ssh_public_key
|
||||||
@ -106,7 +106,11 @@ iam
|
|||||||
- [X] get_server_certificate
|
- [X] get_server_certificate
|
||||||
- [ ] get_service_last_accessed_details
|
- [ ] get_service_last_accessed_details
|
||||||
- [ ] get_service_last_accessed_details_with_entities
|
- [ ] get_service_last_accessed_details_with_entities
|
||||||
- [ ] get_service_linked_role_deletion_status
|
- [X] get_service_linked_role_deletion_status
|
||||||
|
|
||||||
|
This method always succeeds for now - we do not yet keep track of deletions
|
||||||
|
|
||||||
|
|
||||||
- [X] get_ssh_public_key
|
- [X] get_ssh_public_key
|
||||||
- [X] get_user
|
- [X] get_user
|
||||||
- [X] get_user_policy
|
- [X] get_user_policy
|
||||||
|
@ -4,6 +4,7 @@ import os
|
|||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
@ -12,6 +13,7 @@ import time
|
|||||||
from cryptography import x509
|
from cryptography import x509
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
|
||||||
|
from jinja2 import Template
|
||||||
from urllib import parse
|
from urllib import parse
|
||||||
from moto.core.exceptions import RESTError
|
from moto.core.exceptions import RESTError
|
||||||
from moto.core import BaseBackend, BaseModel, ACCOUNT_ID, CloudFormationModel
|
from moto.core import BaseBackend, BaseModel, ACCOUNT_ID, CloudFormationModel
|
||||||
@ -47,6 +49,15 @@ from .utils import (
|
|||||||
from ..utilities.tagging_service import TaggingService
|
from ..utilities.tagging_service import TaggingService
|
||||||
|
|
||||||
|
|
||||||
|
# Map to convert service names used in ServiceLinkedRoles
|
||||||
|
# The PascalCase should be used as part of the RoleName
|
||||||
|
SERVICE_NAME_CONVERSION = {
|
||||||
|
"autoscaling": "AutoScaling",
|
||||||
|
"application-autoscaling": "ApplicationAutoScaling",
|
||||||
|
"elasticbeanstalk": "ElasticBeanstalk",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MFADevice(object):
|
class MFADevice(object):
|
||||||
"""MFA Device class."""
|
"""MFA Device class."""
|
||||||
|
|
||||||
@ -556,6 +567,7 @@ class Role(CloudFormationModel):
|
|||||||
description,
|
description,
|
||||||
tags,
|
tags,
|
||||||
max_session_duration,
|
max_session_duration,
|
||||||
|
linked_service=None,
|
||||||
):
|
):
|
||||||
self.id = role_id
|
self.id = role_id
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -568,6 +580,7 @@ class Role(CloudFormationModel):
|
|||||||
self.description = description
|
self.description = description
|
||||||
self.permissions_boundary = permissions_boundary
|
self.permissions_boundary = permissions_boundary
|
||||||
self.max_session_duration = max_session_duration
|
self.max_session_duration = max_session_duration
|
||||||
|
self._linked_service = linked_service
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_iso_8601(self):
|
def created_iso_8601(self):
|
||||||
@ -622,6 +635,8 @@ class Role(CloudFormationModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def arn(self):
|
def arn(self):
|
||||||
|
if self._linked_service:
|
||||||
|
return f"arn:aws:iam::{ACCOUNT_ID}:role/aws-service-role/{self._linked_service}/{self.name}"
|
||||||
return "arn:aws:iam::{0}:role{1}{2}".format(ACCOUNT_ID, self.path, self.name)
|
return "arn:aws:iam::{0}:role{1}{2}".format(ACCOUNT_ID, self.path, self.name)
|
||||||
|
|
||||||
def to_config_dict(self):
|
def to_config_dict(self):
|
||||||
@ -722,6 +737,41 @@ class Role(CloudFormationModel):
|
|||||||
|
|
||||||
return html.escape(self.description or "")
|
return html.escape(self.description or "")
|
||||||
|
|
||||||
|
def to_xml(self):
|
||||||
|
template = Template(
|
||||||
|
"""<Role>
|
||||||
|
<Path>{{ role.path }}</Path>
|
||||||
|
<Arn>{{ role.arn }}</Arn>
|
||||||
|
<RoleName>{{ role.name }}</RoleName>
|
||||||
|
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
|
||||||
|
{% if role.description is not none %}
|
||||||
|
<Description>{{ role.description_escaped }}</Description>
|
||||||
|
{% endif %}
|
||||||
|
<CreateDate>{{ role.created_iso_8601 }}</CreateDate>
|
||||||
|
<RoleId>{{ role.id }}</RoleId>
|
||||||
|
{% if role.max_session_duration %}
|
||||||
|
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
|
||||||
|
{% endif %}
|
||||||
|
{% if role.permissions_boundary %}
|
||||||
|
<PermissionsBoundary>
|
||||||
|
<PermissionsBoundaryType>PermissionsBoundaryPolicy</PermissionsBoundaryType>
|
||||||
|
<PermissionsBoundaryArn>{{ role.permissions_boundary }}</PermissionsBoundaryArn>
|
||||||
|
</PermissionsBoundary>
|
||||||
|
{% endif %}
|
||||||
|
{% if role.tags %}
|
||||||
|
<Tags>
|
||||||
|
{% for tag in role.get_tags() %}
|
||||||
|
<member>
|
||||||
|
<Key>{{ tag['Key'] }}</Key>
|
||||||
|
<Value>{{ tag['Value'] }}</Value>
|
||||||
|
</member>
|
||||||
|
{% endfor %}
|
||||||
|
</Tags>
|
||||||
|
{% endif %}
|
||||||
|
</Role>"""
|
||||||
|
)
|
||||||
|
return template.render(role=self)
|
||||||
|
|
||||||
|
|
||||||
class InstanceProfile(CloudFormationModel):
|
class InstanceProfile(CloudFormationModel):
|
||||||
def __init__(self, instance_profile_id, name, path, roles, tags=None):
|
def __init__(self, instance_profile_id, name, path, roles, tags=None):
|
||||||
@ -1707,6 +1757,7 @@ class IAMBackend(BaseBackend):
|
|||||||
description,
|
description,
|
||||||
tags,
|
tags,
|
||||||
max_session_duration,
|
max_session_duration,
|
||||||
|
linked_service=None,
|
||||||
):
|
):
|
||||||
role_id = random_resource_id()
|
role_id = random_resource_id()
|
||||||
if permissions_boundary and not self.policy_arn_regex.match(
|
if permissions_boundary and not self.policy_arn_regex.match(
|
||||||
@ -1733,6 +1784,7 @@ class IAMBackend(BaseBackend):
|
|||||||
description,
|
description,
|
||||||
clean_tags,
|
clean_tags,
|
||||||
max_session_duration,
|
max_session_duration,
|
||||||
|
linked_service=linked_service,
|
||||||
)
|
)
|
||||||
self.roles[role_id] = role
|
self.roles[role_id] = role
|
||||||
return role
|
return role
|
||||||
@ -2813,5 +2865,51 @@ class IAMBackend(BaseBackend):
|
|||||||
|
|
||||||
self.tagger.untag_resource_using_names(user.arn, tag_keys)
|
self.tagger.untag_resource_using_names(user.arn, tag_keys)
|
||||||
|
|
||||||
|
def create_service_linked_role(self, service_name, description, suffix):
|
||||||
|
# service.amazonaws.com -> Service
|
||||||
|
# some-thing.service.amazonaws.com -> Service_SomeThing
|
||||||
|
service = service_name.split(".")[-3]
|
||||||
|
prefix = service_name.split(".")[0]
|
||||||
|
if service != prefix:
|
||||||
|
prefix = "".join([x.capitalize() for x in prefix.split("-")])
|
||||||
|
service = SERVICE_NAME_CONVERSION.get(service, service) + "_" + prefix
|
||||||
|
else:
|
||||||
|
service = SERVICE_NAME_CONVERSION.get(service, service)
|
||||||
|
role_name = f"AWSServiceRoleFor{service}"
|
||||||
|
if suffix:
|
||||||
|
role_name = role_name + f"_{suffix}"
|
||||||
|
assume_role_policy_document = {
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Action": ["sts:AssumeRole"],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {"Service": [service_name]},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
path = f"/aws-service-role/{service_name}/"
|
||||||
|
return self.create_role(
|
||||||
|
role_name,
|
||||||
|
json.dumps(assume_role_policy_document),
|
||||||
|
path,
|
||||||
|
permissions_boundary=None,
|
||||||
|
description=description,
|
||||||
|
tags=[],
|
||||||
|
max_session_duration=None,
|
||||||
|
linked_service=service_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_service_linked_role(self, role_name):
|
||||||
|
self.delete_role(role_name)
|
||||||
|
deletion_task_id = str(uuid.uuid4())
|
||||||
|
return deletion_task_id
|
||||||
|
|
||||||
|
def get_service_linked_role_deletion_status(self):
|
||||||
|
"""
|
||||||
|
This method always succeeds for now - we do not yet keep track of deletions
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
iam_backend = IAMBackend()
|
iam_backend = IAMBackend()
|
||||||
|
@ -1088,6 +1088,32 @@ class IamResponse(BaseResponse):
|
|||||||
template = self.response_template(UNTAG_USER_TEMPLATE)
|
template = self.response_template(UNTAG_USER_TEMPLATE)
|
||||||
return template.render()
|
return template.render()
|
||||||
|
|
||||||
|
def create_service_linked_role(self):
|
||||||
|
service_name = self._get_param("AWSServiceName")
|
||||||
|
description = self._get_param("Description")
|
||||||
|
suffix = self._get_param("CustomSuffix")
|
||||||
|
|
||||||
|
role = iam_backend.create_service_linked_role(service_name, description, suffix)
|
||||||
|
|
||||||
|
template = self.response_template(CREATE_SERVICE_LINKED_ROLE_TEMPLATE)
|
||||||
|
return template.render(role=role)
|
||||||
|
|
||||||
|
def delete_service_linked_role(self):
|
||||||
|
role_name = self._get_param("RoleName")
|
||||||
|
|
||||||
|
deletion_task_id = iam_backend.delete_service_linked_role(role_name)
|
||||||
|
|
||||||
|
template = self.response_template(DELETE_SERVICE_LINKED_ROLE_TEMPLATE)
|
||||||
|
return template.render(deletion_task_id=deletion_task_id)
|
||||||
|
|
||||||
|
def get_service_linked_role_deletion_status(self):
|
||||||
|
iam_backend.get_service_linked_role_deletion_status()
|
||||||
|
|
||||||
|
template = self.response_template(
|
||||||
|
GET_SERVICE_LINKED_ROLE_DELETION_STATUS_TEMPLATE
|
||||||
|
)
|
||||||
|
return template.render()
|
||||||
|
|
||||||
|
|
||||||
LIST_ENTITIES_FOR_POLICY_TEMPLATE = """<ListEntitiesForPolicyResponse>
|
LIST_ENTITIES_FOR_POLICY_TEMPLATE = """<ListEntitiesForPolicyResponse>
|
||||||
<ListEntitiesForPolicyResult>
|
<ListEntitiesForPolicyResult>
|
||||||
@ -1399,34 +1425,7 @@ GET_INSTANCE_PROFILE_TEMPLATE = """<GetInstanceProfileResponse xmlns="https://ia
|
|||||||
|
|
||||||
CREATE_ROLE_TEMPLATE = """<CreateRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
CREATE_ROLE_TEMPLATE = """<CreateRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
<CreateRoleResult>
|
<CreateRoleResult>
|
||||||
<Role>
|
{{ role.to_xml() }}
|
||||||
<Path>{{ role.path }}</Path>
|
|
||||||
<Arn>{{ role.arn }}</Arn>
|
|
||||||
<RoleName>{{ role.name }}</RoleName>
|
|
||||||
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
|
|
||||||
{% if role.description is not none %}
|
|
||||||
<Description>{{ role.description_escaped }}</Description>
|
|
||||||
{% endif %}
|
|
||||||
<CreateDate>{{ role.created_iso_8601 }}</CreateDate>
|
|
||||||
<RoleId>{{ role.id }}</RoleId>
|
|
||||||
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
|
|
||||||
{% if role.permissions_boundary %}
|
|
||||||
<PermissionsBoundary>
|
|
||||||
<PermissionsBoundaryType>PermissionsBoundaryPolicy</PermissionsBoundaryType>
|
|
||||||
<PermissionsBoundaryArn>{{ role.permissions_boundary }}</PermissionsBoundaryArn>
|
|
||||||
</PermissionsBoundary>
|
|
||||||
{% endif %}
|
|
||||||
{% if role.tags %}
|
|
||||||
<Tags>
|
|
||||||
{% for tag in role.get_tags() %}
|
|
||||||
<member>
|
|
||||||
<Key>{{ tag['Key'] }}</Key>
|
|
||||||
<Value>{{ tag['Value'] }}</Value>
|
|
||||||
</member>
|
|
||||||
{% endfor %}
|
|
||||||
</Tags>
|
|
||||||
{% endif %}
|
|
||||||
</Role>
|
|
||||||
</CreateRoleResult>
|
</CreateRoleResult>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>4a93ceee-9966-11e1-b624-b1aEXAMPLE7c</RequestId>
|
<RequestId>4a93ceee-9966-11e1-b624-b1aEXAMPLE7c</RequestId>
|
||||||
@ -1444,6 +1443,33 @@ GET_ROLE_POLICY_TEMPLATE = """<GetRolePolicyResponse xmlns="https://iam.amazonaw
|
|||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</GetRolePolicyResponse>"""
|
</GetRolePolicyResponse>"""
|
||||||
|
|
||||||
|
CREATE_SERVICE_LINKED_ROLE_TEMPLATE = """<CreateServiceLinkedRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
|
<CreateServiceLinkedRoleResult>
|
||||||
|
{{ role.to_xml() }}
|
||||||
|
</CreateServiceLinkedRoleResult>
|
||||||
|
<ResponseMetadata>
|
||||||
|
<RequestId>4a93ceee-9966-11e1-b624-b1aEXAMPLE7c</RequestId>
|
||||||
|
</ResponseMetadata>
|
||||||
|
</CreateServiceLinkedRoleResponse>"""
|
||||||
|
|
||||||
|
DELETE_SERVICE_LINKED_ROLE_TEMPLATE = """<DeleteServiceLinkedRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
|
<DeleteServiceLinkedRoleResult>
|
||||||
|
<DeletionTaskId>{{ deletion_task_id }}</DeletionTaskId>
|
||||||
|
</DeleteServiceLinkedRoleResult>
|
||||||
|
<ResponseMetadata>
|
||||||
|
<RequestId>4a93ceee-9966-11e1-b624-b1aEXAMPLE7c</RequestId>
|
||||||
|
</ResponseMetadata>
|
||||||
|
</DeleteServiceLinkedRoleResponse>"""
|
||||||
|
|
||||||
|
GET_SERVICE_LINKED_ROLE_DELETION_STATUS_TEMPLATE = """<GetServiceLinkedRoleDeletionStatusResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
|
<GetServiceLinkedRoleDeletionStatusResult>
|
||||||
|
<Status>SUCCEEDED</Status>
|
||||||
|
</GetServiceLinkedRoleDeletionStatusResult>
|
||||||
|
<ResponseMetadata>
|
||||||
|
<RequestId>4a93ceee-9966-11e1-b624-b1aEXAMPLE7c</RequestId>
|
||||||
|
</ResponseMetadata>
|
||||||
|
</GetServiceLinkedRoleDeletionStatusResponse>"""
|
||||||
|
|
||||||
UPDATE_ROLE_TEMPLATE = """<UpdateRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
UPDATE_ROLE_TEMPLATE = """<UpdateRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
<UpdateRoleResult>
|
<UpdateRoleResult>
|
||||||
</UpdateRoleResult>
|
</UpdateRoleResult>
|
||||||
@ -1454,28 +1480,7 @@ UPDATE_ROLE_TEMPLATE = """<UpdateRoleResponse xmlns="https://iam.amazonaws.com/d
|
|||||||
|
|
||||||
UPDATE_ROLE_DESCRIPTION_TEMPLATE = """<UpdateRoleDescriptionResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
UPDATE_ROLE_DESCRIPTION_TEMPLATE = """<UpdateRoleDescriptionResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
<UpdateRoleDescriptionResult>
|
<UpdateRoleDescriptionResult>
|
||||||
<Role>
|
{{ role.to_xml() }}
|
||||||
<Path>{{ role.path }}</Path>
|
|
||||||
<Arn>{{ role.arn }}</Arn>
|
|
||||||
<RoleName>{{ role.name }}</RoleName>
|
|
||||||
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
|
|
||||||
{% if role.description is not none %}
|
|
||||||
<Description>{{ role.description_escaped }}</Description>
|
|
||||||
{% endif %}
|
|
||||||
<CreateDate>{{ role.created_iso_8601 }}</CreateDate>
|
|
||||||
<RoleId>{{ role.id }}</RoleId>
|
|
||||||
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
|
|
||||||
{% if role.tags %}
|
|
||||||
<Tags>
|
|
||||||
{% for tag in role.get_tags() %}
|
|
||||||
<member>
|
|
||||||
<Key>{{ tag['Key'] }}</Key>
|
|
||||||
<Value>{{ tag['Value'] }}</Value>
|
|
||||||
</member>
|
|
||||||
{% endfor %}
|
|
||||||
</Tags>
|
|
||||||
{% endif %}
|
|
||||||
</Role>
|
|
||||||
</UpdateRoleDescriptionResult>
|
</UpdateRoleDescriptionResult>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>df37e965-9967-11e1-a4c3-270EXAMPLE04</RequestId>
|
<RequestId>df37e965-9967-11e1-a4c3-270EXAMPLE04</RequestId>
|
||||||
@ -1484,34 +1489,7 @@ UPDATE_ROLE_DESCRIPTION_TEMPLATE = """<UpdateRoleDescriptionResponse xmlns="http
|
|||||||
|
|
||||||
GET_ROLE_TEMPLATE = """<GetRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
GET_ROLE_TEMPLATE = """<GetRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
<GetRoleResult>
|
<GetRoleResult>
|
||||||
<Role>
|
{{ role.to_xml() }}
|
||||||
<Path>{{ role.path }}</Path>
|
|
||||||
<Arn>{{ role.arn }}</Arn>
|
|
||||||
<RoleName>{{ role.name }}</RoleName>
|
|
||||||
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
|
|
||||||
{% if role.description is not none %}
|
|
||||||
<Description>{{ role.description_escaped }}</Description>
|
|
||||||
{% endif %}
|
|
||||||
<CreateDate>{{ role.created_iso_8601 }}</CreateDate>
|
|
||||||
<RoleId>{{ role.id }}</RoleId>
|
|
||||||
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
|
|
||||||
{% if role.permissions_boundary %}
|
|
||||||
<PermissionsBoundary>
|
|
||||||
<PermissionsBoundaryType>PermissionsBoundaryPolicy</PermissionsBoundaryType>
|
|
||||||
<PermissionsBoundaryArn>{{ role.permissions_boundary }}</PermissionsBoundaryArn>
|
|
||||||
</PermissionsBoundary>
|
|
||||||
{% endif %}
|
|
||||||
{% if role.tags %}
|
|
||||||
<Tags>
|
|
||||||
{% for tag in role.get_tags() %}
|
|
||||||
<member>
|
|
||||||
<Key>{{ tag['Key'] }}</Key>
|
|
||||||
<Value>{{ tag['Value'] }}</Value>
|
|
||||||
</member>
|
|
||||||
{% endfor %}
|
|
||||||
</Tags>
|
|
||||||
{% endif %}
|
|
||||||
</Role>
|
|
||||||
</GetRoleResult>
|
</GetRoleResult>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>df37e965-9967-11e1-a4c3-270EXAMPLE04</RequestId>
|
<RequestId>df37e965-9967-11e1-a4c3-270EXAMPLE04</RequestId>
|
||||||
|
@ -25,7 +25,7 @@ index 51e5d1c9c7..057446ae1d 100644
|
|||||||
Target: []string{iam.DeletionTaskStatusTypeSucceeded},
|
Target: []string{iam.DeletionTaskStatusTypeSucceeded},
|
||||||
Refresh: statusDeleteServiceLinkedRole(conn, deletionTaskID),
|
Refresh: statusDeleteServiceLinkedRole(conn, deletionTaskID),
|
||||||
- Timeout: 5 * time.Minute,
|
- Timeout: 5 * time.Minute,
|
||||||
+ Timeout: 5 * time.Second,
|
+ Timeout: 15 * time.Second,
|
||||||
Delay: 10 * time.Second,
|
Delay: 10 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +91,7 @@ iam:
|
|||||||
- TestAccIAMRolePolicy_
|
- TestAccIAMRolePolicy_
|
||||||
- TestAccIAMRolePolicyAttachment_
|
- TestAccIAMRolePolicyAttachment_
|
||||||
- TestAccIAMSessionContextDataSource_
|
- TestAccIAMSessionContextDataSource_
|
||||||
|
- TestAccIAMServiceLinkedRole
|
||||||
- TestAccIAMUserDataSource_
|
- TestAccIAMUserDataSource_
|
||||||
- TestAccIAMUserPolicy_
|
- TestAccIAMUserPolicy_
|
||||||
- TestAccIAMUserPolicyAttachment_
|
- TestAccIAMUserPolicyAttachment_
|
||||||
@ -100,6 +101,10 @@ iot:
|
|||||||
- TestAccIoTEndpointDataSource
|
- TestAccIoTEndpointDataSource
|
||||||
kms:
|
kms:
|
||||||
- TestAccKMSAlias
|
- TestAccKMSAlias
|
||||||
|
- TestAccKMSKey_Policy_basic
|
||||||
|
- TestAccKMSKey_Policy_iamRole
|
||||||
|
- TestAccKMSKey_Policy_iamRoleOrder
|
||||||
|
- TestAccKMSKey_Policy_iamServiceLinkedRole
|
||||||
- TestAccKMSSecretDataSource
|
- TestAccKMSSecretDataSource
|
||||||
- TestAccKMSSecretsDataSource
|
- TestAccKMSSecretsDataSource
|
||||||
meta:
|
meta:
|
||||||
|
@ -4407,3 +4407,82 @@ def test_untag_user_error_unknown_user_name():
|
|||||||
ex.response["Error"]["Message"].should.equal(
|
ex.response["Error"]["Message"].should.equal(
|
||||||
"The user with name {} cannot be found.".format(name)
|
"The user with name {} cannot be found.".format(name)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_iam
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"service,cased",
|
||||||
|
[
|
||||||
|
("autoscaling", "AutoScaling"),
|
||||||
|
("elasticbeanstalk", "ElasticBeanstalk"),
|
||||||
|
(
|
||||||
|
"custom-resource.application-autoscaling",
|
||||||
|
"ApplicationAutoScaling_CustomResource",
|
||||||
|
),
|
||||||
|
("other", "other"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_create_service_linked_role(service, cased):
|
||||||
|
client = boto3.client("iam", region_name="eu-central-1")
|
||||||
|
|
||||||
|
resp = client.create_service_linked_role(
|
||||||
|
AWSServiceName=f"{service}.amazonaws.com", Description="desc"
|
||||||
|
)["Role"]
|
||||||
|
|
||||||
|
resp.should.have.key("RoleName").equals(f"AWSServiceRoleFor{cased}")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_iam
|
||||||
|
def test_create_service_linked_role__with_suffix():
|
||||||
|
client = boto3.client("iam", region_name="eu-central-1")
|
||||||
|
|
||||||
|
resp = client.create_service_linked_role(
|
||||||
|
AWSServiceName="autoscaling.amazonaws.com",
|
||||||
|
CustomSuffix="suf",
|
||||||
|
Description="desc",
|
||||||
|
)["Role"]
|
||||||
|
|
||||||
|
resp.should.have.key("RoleName").match("_suf$")
|
||||||
|
resp.should.have.key("Description").equals("desc")
|
||||||
|
resp.should.have.key("AssumeRolePolicyDocument")
|
||||||
|
policy_doc = resp["AssumeRolePolicyDocument"]
|
||||||
|
policy_doc.should.have.key("Statement").equals(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Action": ["sts:AssumeRole"],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {"Service": ["autoscaling.amazonaws.com"]},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_iam
|
||||||
|
def test_delete_service_linked_role():
|
||||||
|
client = boto3.client("iam", region_name="eu-central-1")
|
||||||
|
|
||||||
|
role_name = client.create_service_linked_role(
|
||||||
|
AWSServiceName="autoscaling.amazonaws.com",
|
||||||
|
CustomSuffix="suf",
|
||||||
|
Description="desc",
|
||||||
|
)["Role"]["RoleName"]
|
||||||
|
|
||||||
|
# Role exists
|
||||||
|
client.get_role(RoleName=role_name)
|
||||||
|
|
||||||
|
# Delete role
|
||||||
|
resp = client.delete_service_linked_role(RoleName=role_name)
|
||||||
|
resp.should.have.key("DeletionTaskId")
|
||||||
|
|
||||||
|
# Role deletion should be successful
|
||||||
|
resp = client.get_service_linked_role_deletion_status(
|
||||||
|
DeletionTaskId=resp["DeletionTaskId"]
|
||||||
|
)
|
||||||
|
resp.should.have.key("Status").equals("SUCCEEDED")
|
||||||
|
|
||||||
|
# Role no longer exists
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
client.get_role(RoleName=role_name)
|
||||||
|
err = ex.value.response["Error"]
|
||||||
|
err["Code"].should.equal("NoSuchEntity")
|
||||||
|
err["Message"].should.contain("not found")
|
||||||
|
Loading…
Reference in New Issue
Block a user