Implement IAM instance profile associations (#3482)
* Add associate_iam_instance_profile describe_iam_instance_profile_associations, disassociate_iam_instance_profile, replace_iam_instance_profile_association * More tests, removed type hints, filter fix * Ec2 fix
This commit is contained in:
parent
54e296eb53
commit
689cd8f285
7 changed files with 656 additions and 0 deletions
|
|
@ -99,6 +99,8 @@ from .exceptions import (
|
|||
RulesPerSecurityGroupLimitExceededError,
|
||||
TagLimitExceeded,
|
||||
InvalidParameterDependency,
|
||||
IncorrectStateIamProfileAssociationError,
|
||||
InvalidAssociationIDIamProfileAssociationError,
|
||||
)
|
||||
from .utils import (
|
||||
EC2_RESOURCE_TO_PREFIX,
|
||||
|
|
@ -136,6 +138,7 @@ from .utils import (
|
|||
random_vpc_id,
|
||||
random_vpc_cidr_association_id,
|
||||
random_vpc_peering_connection_id,
|
||||
random_iam_instance_profile_association_id,
|
||||
generic_filter,
|
||||
is_valid_resource_id,
|
||||
get_prefix,
|
||||
|
|
@ -143,6 +146,8 @@ from .utils import (
|
|||
is_valid_cidr,
|
||||
filter_internet_gateways,
|
||||
filter_reservations,
|
||||
filter_iam_instance_profile_associations,
|
||||
filter_iam_instance_profiles,
|
||||
random_network_acl_id,
|
||||
random_network_acl_subnet_association_id,
|
||||
random_vpn_gateway_id,
|
||||
|
|
@ -674,6 +679,16 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
|
|||
instance = reservation.instances[0]
|
||||
for tag in properties.get("Tags", []):
|
||||
instance.add_tag(tag["Key"], tag["Value"])
|
||||
|
||||
# Associating iam instance profile.
|
||||
# TODO: Don't forget to implement replace_iam_instance_profile_association once update_from_cloudformation_json
|
||||
# for ec2 instance will be implemented.
|
||||
if properties.get("IamInstanceProfile"):
|
||||
ec2_backend.associate_iam_instance_profile(
|
||||
instance_id=instance.id,
|
||||
iam_instance_profile_name=properties.get("IamInstanceProfile"),
|
||||
)
|
||||
|
||||
return instance
|
||||
|
||||
@classmethod
|
||||
|
|
@ -759,6 +774,15 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
|
|||
"Client.UserInitiatedShutdown",
|
||||
)
|
||||
|
||||
# Disassociate iam instance profile if associated, otherwise iam_instance_profile_associations will
|
||||
# be pointing to None.
|
||||
if self.ec2_backend.iam_instance_profile_associations.get(self.id):
|
||||
self.ec2_backend.disassociate_iam_instance_profile(
|
||||
association_id=self.ec2_backend.iam_instance_profile_associations[
|
||||
self.id
|
||||
].id
|
||||
)
|
||||
|
||||
def reboot(self, *args, **kwargs):
|
||||
self._state.name = "running"
|
||||
self._state.code = 16
|
||||
|
|
@ -5868,6 +5892,121 @@ class LaunchTemplateBackend(object):
|
|||
return generic_filter(filters, templates)
|
||||
|
||||
|
||||
class IamInstanceProfileAssociation(CloudFormationModel):
|
||||
def __init__(self, ec2_backend, association_id, instance, iam_instance_profile):
|
||||
self.ec2_backend = ec2_backend
|
||||
self.id = association_id
|
||||
self.instance = instance
|
||||
self.iam_instance_profile = iam_instance_profile
|
||||
self.state = "associated"
|
||||
|
||||
|
||||
class IamInstanceProfileAssociationBackend(object):
|
||||
def __init__(self):
|
||||
self.iam_instance_profile_associations = {}
|
||||
super(IamInstanceProfileAssociationBackend, self).__init__()
|
||||
|
||||
def associate_iam_instance_profile(
|
||||
self,
|
||||
instance_id,
|
||||
iam_instance_profile_name=None,
|
||||
iam_instance_profile_arn=None,
|
||||
):
|
||||
iam_association_id = random_iam_instance_profile_association_id()
|
||||
|
||||
instance_profile = filter_iam_instance_profiles(
|
||||
iam_instance_profile_arn, iam_instance_profile_name
|
||||
)
|
||||
|
||||
if instance_id in self.iam_instance_profile_associations.keys():
|
||||
raise IncorrectStateIamProfileAssociationError(instance_id)
|
||||
|
||||
iam_instance_profile_associations = IamInstanceProfileAssociation(
|
||||
self,
|
||||
iam_association_id,
|
||||
self.get_instance(instance_id) if instance_id else None,
|
||||
instance_profile,
|
||||
)
|
||||
# Regarding to AWS there can be only one association with ec2.
|
||||
self.iam_instance_profile_associations[
|
||||
instance_id
|
||||
] = iam_instance_profile_associations
|
||||
return iam_instance_profile_associations
|
||||
|
||||
def describe_iam_instance_profile_associations(
|
||||
self, association_ids, filters=None, max_results=100, next_token=None
|
||||
):
|
||||
associations_list = []
|
||||
if association_ids:
|
||||
for association in self.iam_instance_profile_associations.values():
|
||||
if association.id in association_ids:
|
||||
associations_list.append(association)
|
||||
else:
|
||||
# That's mean that no association id were given. Showing all.
|
||||
associations_list.extend(self.iam_instance_profile_associations.values())
|
||||
|
||||
associations_list = filter_iam_instance_profile_associations(
|
||||
associations_list, filters
|
||||
)
|
||||
|
||||
starting_point = int(next_token or 0)
|
||||
ending_point = starting_point + int(max_results or 100)
|
||||
associations_page = associations_list[starting_point:ending_point]
|
||||
new_next_token = (
|
||||
str(ending_point) if ending_point < len(associations_list) else None
|
||||
)
|
||||
|
||||
return associations_page, new_next_token
|
||||
|
||||
def disassociate_iam_instance_profile(self, association_id):
|
||||
iam_instance_profile_associations = None
|
||||
for association_key in self.iam_instance_profile_associations.keys():
|
||||
if (
|
||||
self.iam_instance_profile_associations[association_key].id
|
||||
== association_id
|
||||
):
|
||||
iam_instance_profile_associations = self.iam_instance_profile_associations[
|
||||
association_key
|
||||
]
|
||||
del self.iam_instance_profile_associations[association_key]
|
||||
# Deleting once and avoiding `RuntimeError: dictionary changed size during iteration`
|
||||
break
|
||||
|
||||
if not iam_instance_profile_associations:
|
||||
raise InvalidAssociationIDIamProfileAssociationError(association_id)
|
||||
|
||||
return iam_instance_profile_associations
|
||||
|
||||
def replace_iam_instance_profile_association(
|
||||
self,
|
||||
association_id,
|
||||
iam_instance_profile_name=None,
|
||||
iam_instance_profile_arn=None,
|
||||
):
|
||||
instance_profile = filter_iam_instance_profiles(
|
||||
iam_instance_profile_arn, iam_instance_profile_name
|
||||
)
|
||||
|
||||
iam_instance_profile_association = None
|
||||
for association_key in self.iam_instance_profile_associations.keys():
|
||||
if (
|
||||
self.iam_instance_profile_associations[association_key].id
|
||||
== association_id
|
||||
):
|
||||
self.iam_instance_profile_associations[
|
||||
association_key
|
||||
].iam_instance_profile = instance_profile
|
||||
iam_instance_profile_association = self.iam_instance_profile_associations[
|
||||
association_key
|
||||
]
|
||||
break
|
||||
|
||||
if not iam_instance_profile_association:
|
||||
raise InvalidAssociationIDIamProfileAssociationError(association_id)
|
||||
|
||||
return iam_instance_profile_association
|
||||
|
||||
|
||||
class EC2Backend(
|
||||
BaseBackend,
|
||||
InstanceBackend,
|
||||
|
|
@ -5897,6 +6036,7 @@ class EC2Backend(
|
|||
CustomerGatewayBackend,
|
||||
NatGatewayBackend,
|
||||
LaunchTemplateBackend,
|
||||
IamInstanceProfileAssociationBackend,
|
||||
):
|
||||
def __init__(self, region_name):
|
||||
self.region_name = region_name
|
||||
|
|
@ -5983,6 +6123,13 @@ class EC2Backend(
|
|||
self.describe_vpn_connections(vpn_connection_ids=[resource_id])
|
||||
elif resource_prefix == EC2_RESOURCE_TO_PREFIX["vpn-gateway"]:
|
||||
self.get_vpn_gateway(vpn_gateway_id=resource_id)
|
||||
elif (
|
||||
resource_prefix
|
||||
== EC2_RESOURCE_TO_PREFIX["iam-instance-profile-association"]
|
||||
):
|
||||
self.describe_iam_instance_profile_associations(
|
||||
association_ids=[resource_id]
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue