[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]
|
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):
|
def build_network_spec(params, ec2=None):
|
||||||
"""
|
"""
|
||||||
Returns list of interfaces [complex]
|
Returns list of interfaces [complex]
|
||||||
|
@ -1292,9 +1333,9 @@ def pretty_instance(i):
|
||||||
def determine_iam_role(name_or_arn):
|
def determine_iam_role(name_or_arn):
|
||||||
if re.match(r'^arn:aws:iam::\d+:instance-profile/[\w+=/,.@-]+$', name_or_arn):
|
if re.match(r'^arn:aws:iam::\d+:instance-profile/[\w+=/,.@-]+$', name_or_arn):
|
||||||
return name_or_arn
|
return name_or_arn
|
||||||
iam = module.client('iam')
|
iam = module.client('iam', retry_decorator=AWSRetry.jittered_backoff())
|
||||||
try:
|
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']
|
return role['InstanceProfile']['Arn']
|
||||||
except botocore.exceptions.ClientError as e:
|
except botocore.exceptions.ClientError as e:
|
||||||
if e.response['Error']['Code'] == 'NoSuchEntity':
|
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)
|
changes = diff_instance_and_params(existing_matches[0], module.params)
|
||||||
for c in changes:
|
for c in changes:
|
||||||
ec2.modify_instance_attribute(**c)
|
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)
|
changed |= change_network_attachments(existing_matches[0], module.params, ec2)
|
||||||
altered = find_instances(ec2, ids=[i['InstanceId'] for i in existing_matches])
|
altered = find_instances(ec2, ids=[i['InstanceId'] for i in existing_matches])
|
||||||
module.exit_json(
|
module.exit_json(
|
||||||
|
|
|
@ -19,6 +19,17 @@
|
||||||
<<: *aws_connection_info
|
<<: *aws_connection_info
|
||||||
register: iam_role
|
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)
|
- name: Wait for IAM role to be available, otherwise the next step will fail (Invalid IAM Instance Profile name)
|
||||||
command: sleep 10
|
command: sleep 10
|
||||||
|
|
||||||
|
@ -36,6 +47,21 @@
|
||||||
that:
|
that:
|
||||||
- 'instance_with_role.instances[0].iam_instance_profile.arn == iam_role.arn.replace(":role/", ":instance-profile/")'
|
- '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:
|
always:
|
||||||
- name: Terminate instance
|
- name: Terminate instance
|
||||||
ec2:
|
ec2:
|
||||||
|
@ -49,13 +75,16 @@
|
||||||
|
|
||||||
- name: Delete IAM role for test
|
- name: Delete IAM role for test
|
||||||
iam_role:
|
iam_role:
|
||||||
name: "{{ resource_prefix }}-test-policy"
|
name: "{{ item }}"
|
||||||
assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}"
|
assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}"
|
||||||
state: absent
|
state: absent
|
||||||
create_instance_profile: yes
|
create_instance_profile: yes
|
||||||
managed_policy:
|
managed_policy:
|
||||||
- AmazonEC2ContainerServiceRole
|
- AmazonEC2ContainerServiceRole
|
||||||
<<: *aws_connection_info
|
<<: *aws_connection_info
|
||||||
|
loop:
|
||||||
|
- "{{ resource_prefix }}-test-policy"
|
||||||
|
- "{{ resource_prefix }}-test-policy-2"
|
||||||
register: removed
|
register: removed
|
||||||
until: removed is not failed
|
until: removed is not failed
|
||||||
ignore_errors: yes
|
ignore_errors: yes
|
||||||
|
|
Loading…
Reference in a new issue