From b385e3d725c04f49d71549a70e2b8cb842023593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Saltvik?= Date: Tue, 19 Dec 2017 20:23:51 +0100 Subject: [PATCH] Add support for adding CORS to S3 bucket on AWS - fixes #29238 (#22284) * Add aws_s3_cors module for adding/removing CORS to S3 bucket on AWS Based on code found here but fixes some of the mentioned issues. https://github.com/ansible/ansible-modules-extras/pull/1177/files --- .../modules/cloud/amazon/aws_s3_cors.py | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 lib/ansible/modules/cloud/amazon/aws_s3_cors.py diff --git a/lib/ansible/modules/cloud/amazon/aws_s3_cors.py b/lib/ansible/modules/cloud/amazon/aws_s3_cors.py new file mode 100644 index 0000000000..4a0f55fa65 --- /dev/null +++ b/lib/ansible/modules/cloud/amazon/aws_s3_cors.py @@ -0,0 +1,193 @@ +#!/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.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: aws_s3_cors +short_description: Manage CORS for S3 buckets in AWS +description: + - Manage CORS for S3 buckets in AWS +version_added: "2.5" +author: "Oyvind Saltvik (@fivethreeo)" +options: + name: + description: + - Name of the s3 bucket + required: true + default: null + rules: + description: + - Cors rules to put on the s3 bucket + required: false + state: + description: + - Create or remove cors on the s3 bucket + required: false + default: present + choices: [ 'present', 'absent' ] +extends_documentation_fragment: + - aws +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Create a simple cors for s3 bucket +- aws_s3_cors: + name: mys3bucket + state: present + rules: + - allowed_origins: + - http://www.example.com/ + allowed_methods: + - GET + - POST + allowed_headers: + - Authorization + expose_headers: + - x-amz-server-side-encryption + - x-amz-request-id + max_age_seconds: 30000 + +# Remove cors for s3 bucket +- aws_s3_cors: + name: mys3bucket + state: absent +''' + +RETURN = ''' +changed: + description: check to see if a change was made to the rules + returned: always + type: boolean + sample: true +name: + description: name of bucket + returned: always + type: string + sample: 'bucket-name' +rules: + description: list of current rules + returned: always + type: list + sample: [ + { + "allowed_headers": [ + "Authorization" + ], + "allowed_methods": [ + "GET" + ], + "allowed_origins": [ + "*" + ], + "max_age_seconds": 30000 + } + ] +''' + +try: + from botocore.exceptions import ClientError, BotoCoreError +except: + # handled by HAS_BOTO3 check in main + pass + +import traceback + +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.ec2 import (HAS_BOTO3, boto3_conn, ec2_argument_spec, get_aws_connection_info, + camel_dict_to_snake_dict, snake_dict_to_camel_dict, compare_policies) + + +def create_or_update_bucket_cors(connection, module): + + name = module.params.get("name") + rules = module.params.get("rules", []) + changed = False + + try: + current_camel_rules = connection.get_bucket_cors(Bucket=name)['CORSRules'] + except ClientError: + current_camel_rules = [] + + new_camel_rules = snake_dict_to_camel_dict(rules, capitalize_first=True) + # compare_policies() takes two dicts and makes them hashable for comparison + if compare_policies(new_camel_rules, current_camel_rules): + changed = True + + if changed: + try: + cors = connection.put_bucket_cors(Bucket=name, CORSConfiguration={'CORSRules': new_camel_rules}) + except ClientError as e: + module.fail_json( + msg="Unable to update CORS for bucket {0}: {1}".format(name, to_native(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response) + ) + except BotoCoreError as e: + module.fail_json( + msg=to_native(e), + exception=traceback.format_exc() + ) + + module.exit_json(changed=changed, name=name, rules=rules) + + +def destroy_bucket_cors(connection, module): + + name = module.params.get("name") + changed = False + + try: + cors = connection.delete_bucket_cors(Bucket=name) + changed = True + except ClientError as e: + module.fail_json( + msg="Unable to delete CORS for bucket {0}: {1}".format(name, to_native(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response) + ) + except BotoCoreError as e: + module.fail_json( + msg=to_native(e), + exception=traceback.format_exc() + ) + + module.exit_json(changed=changed) + + +def main(): + + argument_spec = ec2_argument_spec() + argument_spec.update( + dict( + name=dict(required=True, type='str'), + rules=dict(type='list'), + state=dict(type='str', choices=['present', 'absent'], required=True) + ) + ) + + module = AnsibleModule(argument_spec=argument_spec) + + if not HAS_BOTO3: + module.fail_json(msg='boto3 is required.') + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) + client = boto3_conn(module, conn_type='client', resource='s3', + region=region, endpoint=ec2_url, **aws_connect_kwargs) + + state = module.params.get("state") + + if state == 'present': + create_or_update_bucket_cors(client, module) + elif state == 'absent': + destroy_bucket_cors(client, module) + +if __name__ == '__main__': + main()