[cloud] Add support for updating IAM role with ec2_instance module (#38812)
* [cloud] Add support for updating IAM role with ec2_instance module * Add test for updating IAM role
This commit is contained in:
parent
4117b2dd29
commit
44d06f8858
2 changed files with 75 additions and 3 deletions
|
@ -737,6 +737,47 @@ def build_volume_spec(params):
|
|||
return [ec2_utils.snake_dict_to_camel_dict(v, capitalize_first=True) for v in volumes]
|
||||
|
||||
|
||||
def add_or_update_instance_profile(instance, desired_profile_name):
|
||||
instance_profile_setting = instance.get('IamInstanceProfile')
|
||||
if instance_profile_setting and desired_profile_name:
|
||||
if desired_profile_name in (instance_profile_setting.get('Name'), instance_profile_setting.get('Arn')):
|
||||
# great, the profile we asked for is what's there
|
||||
return False
|
||||
else:
|
||||
desired_arn = determine_iam_role(desired_profile_name)
|
||||
if instance_profile_setting.get('Arn') == desired_arn:
|
||||
return False
|
||||
# update association
|
||||
ec2 = module.client('ec2')
|
||||
try:
|
||||
association = ec2.describe_iam_instance_profile_associations(Filters=[{'Name': 'instance-id', 'Values': [instance['InstanceId']]}])
|
||||
except botocore.exceptions.ClientError as e:
|
||||
# check for InvalidAssociationID.NotFound
|
||||
module.fail_json_aws(e, "Could not find instance profile association")
|
||||
try:
|
||||
resp = ec2.replace_iam_instance_profile_association(
|
||||
AssociationId=association['IamInstanceProfileAssociations'][0]['AssociationId'],
|
||||
IamInstanceProfile={'Arn': determine_iam_role(desired_profile_name)}
|
||||
)
|
||||
return True
|
||||
except botocore.exceptions.ClientError as e:
|
||||
module.fail_json_aws(e, "Could not associate instance profile")
|
||||
|
||||
if not instance_profile_setting and desired_profile_name:
|
||||
# create association
|
||||
ec2 = module.client('ec2')
|
||||
try:
|
||||
resp = ec2.associate_iam_instance_profile(
|
||||
IamInstanceProfile={'Arn': determine_iam_role(desired_profile_name)},
|
||||
InstanceId=instance['InstanceId']
|
||||
)
|
||||
return True
|
||||
except botocore.exceptions.ClientError as e:
|
||||
module.fail_json_aws(e, "Could not associate new instance profile")
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def build_network_spec(params, ec2=None):
|
||||
"""
|
||||
Returns list of interfaces [complex]
|
||||
|
@ -1292,9 +1333,9 @@ def pretty_instance(i):
|
|||
def determine_iam_role(name_or_arn):
|
||||
if re.match(r'^arn:aws:iam::\d+:instance-profile/[\w+=/,.@-]+$', name_or_arn):
|
||||
return name_or_arn
|
||||
iam = module.client('iam')
|
||||
iam = module.client('iam', retry_decorator=AWSRetry.jittered_backoff())
|
||||
try:
|
||||
role = iam.get_instance_profile(InstanceProfileName=name_or_arn)
|
||||
role = iam.get_instance_profile(InstanceProfileName=name_or_arn, aws_retry=True)
|
||||
return role['InstanceProfile']['Arn']
|
||||
except botocore.exceptions.ClientError as e:
|
||||
if e.response['Error']['Code'] == 'NoSuchEntity':
|
||||
|
@ -1313,6 +1354,8 @@ def handle_existing(existing_matches, changed, ec2, state):
|
|||
changes = diff_instance_and_params(existing_matches[0], module.params)
|
||||
for c in changes:
|
||||
ec2.modify_instance_attribute(**c)
|
||||
changed |= bool(changes)
|
||||
changed |= add_or_update_instance_profile(existing_matches[0], module.params.get('instance_role'))
|
||||
changed |= change_network_attachments(existing_matches[0], module.params, ec2)
|
||||
altered = find_instances(ec2, ids=[i['InstanceId'] for i in existing_matches])
|
||||
module.exit_json(
|
||||
|
|
|
@ -19,6 +19,17 @@
|
|||
<<: *aws_connection_info
|
||||
register: iam_role
|
||||
|
||||
- name: Create second IAM role for test
|
||||
iam_role:
|
||||
name: "{{ resource_prefix }}-test-policy-2"
|
||||
assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}"
|
||||
state: present
|
||||
create_instance_profile: yes
|
||||
managed_policy:
|
||||
- AmazonEC2ContainerServiceRole
|
||||
<<: *aws_connection_info
|
||||
register: iam_role_2
|
||||
|
||||
- name: Wait for IAM role to be available, otherwise the next step will fail (Invalid IAM Instance Profile name)
|
||||
command: sleep 10
|
||||
|
||||
|
@ -36,6 +47,21 @@
|
|||
that:
|
||||
- 'instance_with_role.instances[0].iam_instance_profile.arn == iam_role.arn.replace(":role/", ":instance-profile/")'
|
||||
|
||||
- name: Update instance with new instance_role
|
||||
ec2_instance:
|
||||
name: "{{ resource_prefix }}-test-default-vpc"
|
||||
image_id: "{{ ec2_ami_image[aws_region] }}"
|
||||
security_groups: "{{ sg.group_id }}"
|
||||
instance_type: t2.micro
|
||||
instance_role: "{{ resource_prefix }}-test-policy-2"
|
||||
<<: *aws_connection_info
|
||||
register: instance_with_updated_role
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- 'instance_with_updated_role.instances[0].iam_instance_profile.arn == iam_role_2.arn.replace(":role/", ":instance-profile/")'
|
||||
- 'instance_with_updated_role.instances[0].instance_id == instance_with_role.instances[0].instance_id'
|
||||
|
||||
always:
|
||||
- name: Terminate instance
|
||||
ec2:
|
||||
|
@ -49,13 +75,16 @@
|
|||
|
||||
- name: Delete IAM role for test
|
||||
iam_role:
|
||||
name: "{{ resource_prefix }}-test-policy"
|
||||
name: "{{ item }}"
|
||||
assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}"
|
||||
state: absent
|
||||
create_instance_profile: yes
|
||||
managed_policy:
|
||||
- AmazonEC2ContainerServiceRole
|
||||
<<: *aws_connection_info
|
||||
loop:
|
||||
- "{{ resource_prefix }}-test-policy"
|
||||
- "{{ resource_prefix }}-test-policy-2"
|
||||
register: removed
|
||||
until: removed is not failed
|
||||
ignore_errors: yes
|
||||
|
|
Loading…
Reference in a new issue