Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New module: manage Amazon CloudFront origin access identities (cloud/amazon/cloudfront_origin_access_identity) #35540

Merged
merged 15 commits into from
Feb 1, 2018
272 changes: 272 additions & 0 deletions lib/ansible/modules/cloud/amazon/cloudfront_origin_access_identity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
#!/usr/bin/python
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'}

DOCUMENTATION = '''
---

module: cloudfront_origin_access_identity

short_description: create, update and delete origin access identities for a
cloudfront distribution.

description:
- Allows for easy creation, updating and deletion of origin access
identities.

requirements:
- boto3 >= 1.0.0
- python >= 2.6

version_added: "2.5"

author: Willem van Ketwich (@wilvk)

extends_documentation_fragment:
- aws
- ec2

options:
state:
description: If the named resource should exist.
choices:
- present
- absent
default: update_origin_access_identity
origin_access_identity_id:
description:
- The origin_access_identity_id of the cloudfront distribution.
required: false
comment:
description:
- A comment to describe the cloudfront origin access identity.
required: false
caller_reference:
description:
- A unique identifier to reference the origin access identity by.
required: false

notes:
- does not support check mode

'''

EXAMPLES = '''

- name: create an origin access identity
cloudfront_origin_access_identity:
state: present
caller_reference: this is an example reference
comment: this is an example comment

- name: update an existing origin access identity using caller_reference as an identifier
cloudfront_origin_access_identity:
origin_access_identity_id: E17DRN9XUOAHZX
caller_reference: this is an example reference
comment: this is a new comment

- name: delete an existing origin access identity using caller_reference as an identifier
cloudfront_origin_access_identity:
state: absent
caller_reference: this is an example reference
comment: this is a new comment

'''

RETURN = '''

cloud_front_origin_access_identity_config:
description: describes a url specifying the origin access identity.
returned: always
type: complex
contains:
caller_reference:
description: a caller reference for the oai
returned: always
type: str
comment:
description: a comment describing the oai
returned: always
type: str
id:
description: a unique identifier of the oai
returned: always
type: str
s3_canonical_user_id:
description: the cannonical user id of the user who created the oai
returned: always
type: str

'''

from ansible.module_utils.ec2 import get_aws_connection_info, ec2_argument_spec
from ansible.module_utils.ec2 import boto3_conn
from ansible.module_utils.aws.cloudfront_facts import CloudFrontFactsServiceManager
from ansible.module_utils.ec2 import camel_dict_to_snake_dict
from ansible.module_utils.aws.core import AnsibleAWSModule
import datetime
from functools import partial
import json
import traceback

try:
import botocore
from botocore.signers import CloudFrontSigner
from botocore.exceptions import ClientError, BotoCoreError
except ImportError:
pass # caught by imported AnsibleAWSModule


class CloudFrontOriginAccessIdentityServiceManager(object):
"""
Handles cloudfront origin access identity service calls to aws
"""

def __init__(self, module):
self.module = module
self.create_client('cloudfront')

def create_client(self, resource):
try:
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(self.module, boto3=True)
self.client = boto3_conn(self.module, conn_type='client', resource=resource, region=region, endpoint=ec2_url, **aws_connect_kwargs)
except (ClientError, BotoCoreError) as e:
self.module.fail_json_aws(e, msg="Unable to establish connection.")

def create_origin_access_identity(self, caller_reference, comment):
try:
return self.client.create_cloud_front_origin_access_identity(
CloudFrontOriginAccessIdentityConfig={
'CallerReference': caller_reference,
'Comment': comment
}
)
except (ClientError, BotoCoreError) as e:
self.module.fail_json_aws(e, msg="Error creating cloud front origin access identity.")

def delete_origin_access_identity(self, origin_access_identity_id, e_tag):
try:
return self.client.delete_cloud_front_origin_access_identity(Id=origin_access_identity_id, IfMatch=e_tag)
except (ClientError, BotoCoreError) as e:
self.module.fail_json_aws(e, msg="Error updating Origin Access Identity.")

def update_origin_access_identity(self, caller_reference, comment, origin_access_identity_id, e_tag):
changed = False
new_config = {
'CallerReference': caller_reference,
'Comment': comment
}

try:
current_config = self.client.get_cloud_front_origin_access_identity_config(
Id=origin_access_identity_id)['CloudFrontOriginAccessIdentityConfig']
except (ClientError, BotoCoreError) as e:
self.module.fail_json_aws(e, msg="Error getting Origin Access Identity config.")

if new_config != current_config:
changed = True

try:
# If the CallerReference is a value already sent in a previous identity request
# the returned value is that of the original request
result = self.client.update_cloud_front_origin_access_identity(
CloudFrontOriginAccessIdentityConfig=new_config,
Id=origin_access_identity_id,
IfMatch=e_tag,
)
except (ClientError, BotoCoreError) as e:
self.module.fail_json_aws(e, msg="Error updating Origin Access Identity.")

return result, changed


class CloudFrontOriginAccessIdentityValidationManager(object):
"""
Manages Cloudfront Origin Access Identities
"""

def __init__(self, module):
self.module = module
self.__cloudfront_facts_mgr = CloudFrontFactsServiceManager(module)

def validate_etag_from_origin_access_identity_id(self, origin_access_identity_id):
try:
if origin_access_identity_id is None:
return
oai = self.__cloudfront_facts_mgr.get_origin_access_identity(origin_access_identity_id)
if oai is not None:
return oai.get('ETag')
except (ClientError, BotoCoreError) as e:
self.module.fail_json_aws(e, msg="Error getting etag from origin_access_identity.")

def validate_origin_access_identity_id_from_caller_reference(
self, caller_reference):
try:
origin_access_identities = self.__cloudfront_facts_mgr.list_origin_access_identities()
origin_origin_access_identity_ids = [oai.get('Id') for oai in origin_access_identities]
for origin_access_identity_id in origin_origin_access_identity_ids:
oai_config = (self.__cloudfront_facts_mgr.get_origin_access_identity_config(origin_access_identity_id))
temp_caller_reference = oai_config.get('CloudFrontOriginAccessIdentityConfig').get('CallerReference')
if temp_caller_reference == caller_reference:
return origin_access_identity_id
except (ClientError, BotoCoreError) as e:
self.module.fail_json_aws(e, msg="Error getting Origin Access Identity from caller_reference.")

def validate_comment(self, comment):
if comment is None:
return "origin access identity created by Ansible with datetime " + datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')
return comment


def main():
argument_spec = ec2_argument_spec()

argument_spec.update(dict(
state=dict(choices=['present', 'absent'], default='present'),
origin_access_identity_id=dict(),
caller_reference=dict(),
comment=dict(),
))

result = {}
e_tag = None
changed = False

module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=False)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If checkmode isn't supported please add this in the
notes: section in DOCUMENTATION

Copy link
Contributor Author

@wilvk wilvk Jan 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have added to documentation under notes: section. let me know if this is incorrect

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, appologies, I think this needs to be a list

service_mgr = CloudFrontOriginAccessIdentityServiceManager(module)
validation_mgr = CloudFrontOriginAccessIdentityValidationManager(module)

state = module.params.get('state')
caller_reference = module.params.get('caller_reference')

comment = module.params.get('comment')
origin_access_identity_id = module.params.get('origin_access_identity_id')

if origin_access_identity_id is None and caller_reference is not None:
origin_access_identity_id = validation_mgr.validate_origin_access_identity_id_from_caller_reference(caller_reference)

e_tag = validation_mgr.validate_etag_from_origin_access_identity_id(origin_access_identity_id)
comment = validation_mgr.validate_comment(comment)

if state == 'present':
if origin_access_identity_id is not None and e_tag is not None:
result, changed = service_mgr.update_origin_access_identity(caller_reference, comment, origin_access_identity_id, e_tag)
else:
result = service_mgr.create_origin_access_identity(caller_reference, comment)
changed = True
elif(state == 'absent' and origin_access_identity_id is not None and
e_tag is not None):
result = service_mgr.delete_origin_access_identity(origin_access_identity_id, e_tag)
changed = True

result.pop('ResponseMetadata', None)

module.exit_json(changed=changed, **camel_dict_to_snake_dict(result))


if __name__ == '__main__':
main()