---
# General Tests:
# - s3_bucket_name required when state is 'present'
# - Creation / Deletion
# - Enable/Disable logging
# - Enable/Disable log file validation option
# - Manipulation of Global Event logging option
# - Manipulation of Multi-Region logging option
# - Manipulation of S3 bucket option
# - Manipulation of Encryption option
# - Manipulation of SNS options
# - Manipulation of CloudWatch Log group options
# - Manipulation of Tags
#
# Notes:
# - results include the updates, even when check_mode is true
# - Poor handling of disable global + enable multi-region
#       botocore.errorfactory.InvalidParameterCombinationException: An error
#       occurred (InvalidParameterCombinationException) when calling the
#       UpdateTrail operation: Multi-Region trail must include global service
#       events.
# - Using blank string for KMS ID doesn't remove encryption
# - Using blank string for SNS Topic doesn't remove it
# - Using blank string for CloudWatch Log Group / Role doesn't remove them
#
# Possible Bugs:
# - output.exists == false when creating
# - Changed reports true when using a KMS alias
# - Tags Keys are being lower-cased

- module_defaults:
    group/aws:
      aws_access_key: '{{ aws_access_key }}'
      aws_secret_key: '{{ aws_secret_key }}'
      security_token: '{{ security_token | default(omit) }}'
      region: '{{ aws_region }}'
    # Add this as a default because we (almost) always need it
    cloudtrail:
      s3_bucket_name: '{{ s3_bucket_name }}'
  collections:
    - amazon.aws
  block:

  # ============================================================
  #   Argument Tests
  # ============================================================
  - name: 'S3 Bucket required when state is "present"'
    module_defaults: { cloudtrail: {} }
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
    ignore_errors: yes
  - assert:
      that:
      - output is failed
      - '"s3_bucket_name" in output.msg'

  - name: 'CloudWatch cloudwatch_logs_log_group_arn required when cloudwatch_logs_role_arn passed'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      cloudwatch_logs_role_arn: 'SomeValue'
    register: output
    ignore_errors: yes
  - assert:
      that:
      - output is failed
      - '"parameters are required together" in output.msg'
      - '"cloudwatch_logs_log_group_arn" in output.msg'

  - name: 'CloudWatch cloudwatch_logs_role_arn required when cloudwatch_logs_log_group_arn passed'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      cloudwatch_logs_log_group_arn: 'SomeValue'
    register: output
    ignore_errors: yes
  - assert:
      that:
      - output is failed
      - '"parameters are required together" in output.msg'
      - '"cloudwatch_logs_role_arn" in output.msg'

  #- name: 'Global Logging must be enabled when enabling Multi-region'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    include_global_events: no
  #    is_multi_region_trail: yes
  #  register: output
  #  ignore_errors: yes
  #- assert:
  #    that:
  #    - output is failed

  # ============================================================
  #   Preparation
  # ============================================================
  - name: 'Retrieve caller facts'
    aws_caller_info: {}
    register: aws_caller_info

  - name: 'Create S3 bucket'
    vars:
      bucket_name: '{{ s3_bucket_name }}'
    s3_bucket:
      state: present
      name: '{{ bucket_name }}'
      policy: '{{ lookup("template", "s3-policy.j2") }}'
  - name: 'Create second S3 bucket'
    vars:
      bucket_name: '{{ s3_bucket_name }}-2'
    s3_bucket:
      state: present
      name: '{{ bucket_name }}'
      policy: '{{ lookup("template", "s3-policy.j2") }}'

  - name: 'Create SNS Topic'
    vars:
      sns_topic_name: '{{ sns_topic }}'
    sns_topic:
      state: present
      name: '{{ sns_topic_name }}'
      display_name: 'Used for testing SNS/CloudWatch integration'
      policy: "{{ lookup('template', 'sns-policy.j2') | to_json }}"
    register: output_sns_topic
  - name: 'Create second SNS Topic'
    vars:
      sns_topic_name: '{{ sns_topic }}-2'
    sns_topic:
      state: present
      name: '{{ sns_topic_name }}'
      display_name: 'Used for testing SNS/CloudWatch integration'
      policy: "{{ lookup('template', 'sns-policy.j2') | to_json }}"

  - name: 'Create KMS Key'
    aws_kms:
      state: present
      alias: '{{ kms_alias }}'
      enabled: yes
      policy: "{{ lookup('template', 'kms-policy.j2') | to_json }}"
    register: kms_key
  - name: 'Create second KMS Key'
    aws_kms:
      state: present
      alias: '{{ kms_alias }}-2'
      enabled: yes
      policy: "{{ lookup('template', 'kms-policy.j2') | to_json }}"
    register: kms_key2

  - name: 'Create CloudWatch IAM Role'
    iam_role:
      state: present
      name: '{{ cloudwatch_role }}'
      assume_role_policy_document: "{{ lookup('template', 'cloudwatch-assume-policy.j2') }}"
    register: output_cloudwatch_role
  - name: 'Create CloudWatch Log Group'
    cloudwatchlogs_log_group:
      state: present
      log_group_name: '{{ cloudwatch_log_group }}'
      retention: 1
    register: output_cloudwatch_log_group
  - name: 'Create second CloudWatch Log Group'
    cloudwatchlogs_log_group:
      state: present
      log_group_name: '{{ cloudwatch_log_group }}-2'
      retention: 1
    register: output_cloudwatch_log_group2
  - name: 'Add inline policy to CloudWatch Role'
    iam_policy:
      state: present
      iam_type: role
      iam_name: '{{ cloudwatch_role }}'
      policy_name: 'CloudWatch'
      policy_json: "{{ lookup('template', 'cloudwatch-policy.j2') | to_json }}"

  # ============================================================
  #   Tests
  # ============================================================

  - name: 'Create a trail (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Create a trail'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is changed
      # XXX This appears to be a bug...
      #- output.exists == True
      - output.trail.name == cloudtrail_name

  - name: 'No-op update to trail'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.exists == True
      # Check everything is what we expect before we start making changes
      - output.trail.name == cloudtrail_name
      - output.trail.home_region == aws_region
      - output.trail.include_global_service_events == True
      - output.trail.is_multi_region_trail == False
      - output.trail.is_logging == True
      - output.trail.log_file_validation_enabled == False
      - output.trail.s3_bucket_name == s3_bucket_name
      - output.trail.s3_key_prefix is none
      - output.trail.kms_key_id is none
      - output.trail.sns_topic_arn is none
      - output.trail.sns_topic_name is none
      - output.trail.tags | length == 0

  # ============================================================

  - name: 'Set S3 prefix (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '{{ cloudtrail_prefix }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Set S3 prefix'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '{{ cloudtrail_prefix }}'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.s3_key_prefix == cloudtrail_prefix

  - name: 'Set S3 prefix (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '{{ cloudtrail_prefix }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.s3_key_prefix == cloudtrail_prefix

  - name: 'No-op update to trail'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.s3_key_prefix == cloudtrail_prefix

  - name: 'Update S3 prefix (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '{{ cloudtrail_prefix }}-2'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Update S3 prefix'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '{{ cloudtrail_prefix }}-2'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - 'output.trail.s3_key_prefix ==  "{{ cloudtrail_prefix }}-2"'

  - name: 'Update S3 prefix (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '{{ cloudtrail_prefix }}-2'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - 'output.trail.s3_key_prefix ==  "{{ cloudtrail_prefix }}-2"'

  - name: 'Remove S3 prefix (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '/'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Remove S3 prefix'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '/'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.s3_key_prefix is none

  - name: 'Remove S3 prefix (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '/'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.s3_key_prefix is none

  # ============================================================

  - name: 'Add Tag (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      tags:
        tag1: Value1
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Add Tag'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      tags:
        tag1: Value1
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.tags | length == 1
      - '("tag1" in output.trail.tags) and (output.trail.tags["tag1"] == "Value1")'

  - name: 'Add Tag (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      tags:
        tag1: Value1
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.tags | length == 1
      - '("tag1" in output.trail.tags) and (output.trail.tags["tag1"] == "Value1")'

  - name: 'Change tags (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      tags:
        tag2: Value2
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Change tags'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      tags:
        tag2: Value2
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.tags | length == 1
      - '("tag2" in output.trail.tags) and (output.trail.tags["tag2"] == "Value2")'

  - name: 'Change tags (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      tags:
        tag2: Value2
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.tags | length == 1
      - '("tag2" in output.trail.tags) and (output.trail.tags["tag2"] == "Value2")'

  - name: 'Change tags (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      tags:
        tag2: Value2
        Tag3: Value3
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Change tags'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      tags:
        tag2: Value2
        Tag3: Value3
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.tags | length == 2
      - '("tag2" in output.trail.tags) and (output.trail.tags["tag2"] == "Value2")'
      #- '("Tag3" in output.trail.tags) and (output.trail.tags["Tag3"] == "Value3")'

  - name: 'Change tags (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      tags:
        tag2: Value2
        Tag3: Value3
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.tags | length == 2
      - '("tag2" in output.trail.tags) and (output.trail.tags["tag2"] == "Value2")'
      #- '("Tag3" in output.trail.tags) and (output.trail.tags["Tag3"] == "Value3")'

  - name: 'Remove tags (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Remove tags'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.tags | length == 0

  - name: 'Remove tags (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.tags | length == 0

  # ============================================================

  - name: 'Set SNS Topic (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      sns_topic_name: '{{ sns_topic }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Set SNS Topic'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      sns_topic_name: '{{ sns_topic }}'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.sns_topic_name == sns_topic

  - name: 'Set SNS Topic (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      sns_topic_name: '{{ sns_topic }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.sns_topic_name == sns_topic

  - name: 'No-op update to trail'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.sns_topic_name == sns_topic

  - name: 'Update SNS Topic (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      sns_topic_name: '{{ sns_topic }}-2'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Update SNS Topic'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      sns_topic_name: '{{ sns_topic }}-2'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - 'output.trail.sns_topic_name ==  "{{ sns_topic }}-2"'

  - name: 'Update SNS Topic (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      sns_topic_name: '{{ sns_topic }}-2'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - 'output.trail.sns_topic_name ==  "{{ sns_topic }}-2"'

  #- name: 'Remove SNS Topic (CHECK MODE)'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    sns_topic_name: ''
  #  register: output
  #  check_mode: yes
  #- assert:
  #    that:
  #    - output is changed

  #- name: 'Remove SNS Topic'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    sns_topic_name: ''
  #  register: output
  #- assert:
  #    that:
  #    - output is changed
  #    - output.trail.name == cloudtrail_name
  #    - output.trail.sns_topic_name is none

  #- name: 'Remove SNS Topic (no change)'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    sns_topic_name: ''
  #  register: output
  #- assert:
  #    that:
  #    - output is not changed
  #    - output.trail.name == cloudtrail_name
  #    - output.trail.sns_topic_name is none


  # ============================================================

  - name: 'Set CloudWatch Log Group (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      cloudwatch_logs_log_group_arn: '{{ output_cloudwatch_log_group.arn }}'
      cloudwatch_logs_role_arn: '{{ output_cloudwatch_role.arn }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Set CloudWatch Log Group'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      cloudwatch_logs_log_group_arn: '{{ output_cloudwatch_log_group.arn }}'
      cloudwatch_logs_role_arn: '{{ output_cloudwatch_role.arn }}'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.cloud_watch_logs_log_group_arn == output_cloudwatch_log_group.arn
      - output.trail.cloud_watch_logs_role_arn == output_cloudwatch_role.arn

  - name: 'Set CloudWatch Log Group (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      cloudwatch_logs_log_group_arn: '{{ output_cloudwatch_log_group.arn }}'
      cloudwatch_logs_role_arn: '{{ output_cloudwatch_role.arn }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.cloud_watch_logs_log_group_arn == output_cloudwatch_log_group.arn
      - output.trail.cloud_watch_logs_role_arn == output_cloudwatch_role.arn

  - name: 'No-op update to trail'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.cloud_watch_logs_log_group_arn == output_cloudwatch_log_group.arn
      - output.trail.cloud_watch_logs_role_arn == output_cloudwatch_role.arn

  - name: 'Update CloudWatch Log Group (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      cloudwatch_logs_log_group_arn: '{{ output_cloudwatch_log_group2.arn }}'
      cloudwatch_logs_role_arn: '{{ output_cloudwatch_role.arn }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed
      - output.trail.cloud_watch_logs_log_group_arn == output_cloudwatch_log_group2.arn
      - output.trail.cloud_watch_logs_role_arn == output_cloudwatch_role.arn

  - name: 'Update CloudWatch Log Group'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      cloudwatch_logs_log_group_arn: '{{ output_cloudwatch_log_group2.arn }}'
      cloudwatch_logs_role_arn: '{{ output_cloudwatch_role.arn }}'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.cloud_watch_logs_log_group_arn == output_cloudwatch_log_group2.arn
      - output.trail.cloud_watch_logs_role_arn == output_cloudwatch_role.arn

  - name: 'Update CloudWatch Log Group (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      cloudwatch_logs_log_group_arn: '{{ output_cloudwatch_log_group2.arn }}'
      cloudwatch_logs_role_arn: '{{ output_cloudwatch_role.arn }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.cloud_watch_logs_log_group_arn == output_cloudwatch_log_group2.arn
      - output.trail.cloud_watch_logs_role_arn == output_cloudwatch_role.arn

  #- name: 'Remove CloudWatch Log Group (CHECK MODE)'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    cloudwatch_logs_log_group_arn: ''
  #    cloudwatch_logs_role_arn: ''
  #  register: output
  #  check_mode: yes
  #- assert:
  #    that:
  #    - output is changed
  #    - output.trail.name == cloudtrail_name
  #    - output.trail.cloud_watch_logs_log_group_arn is none
  #    - output.trail.cloud_watch_logs_role_arn is none

  #- name: 'Remove CloudWatch Log Group'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    cloudwatch_logs_log_group_arn: ''
  #    cloudwatch_logs_role_arn: ''
  #  register: output
  #- assert:
  #    that:
  #    - output is changed
  #    - output.trail.name == cloudtrail_name
  #    - output.trail.cloud_watch_logs_log_group_arn is none
  #    - output.trail.cloud_watch_logs_role_arn is none

  #- name: 'Remove CloudWatch Log Group (no change)'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    cloudwatch_logs_log_group_arn: ''
  #    cloudwatch_logs_role_arn: ''
  #  register: output
  #- assert:
  #    that:
  #    - output is not changed
  #    - output.trail.name == cloudtrail_name
  #    - output.trail.cloud_watch_logs_log_group_arn is none
  #    - output.trail.cloud_watch_logs_role_arn is none

  # ============================================================

  - name: 'Update S3 bucket (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_bucket_name: '{{ s3_bucket_name }}-2'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Update S3 bucket'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_bucket_name: '{{ s3_bucket_name }}-2'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - 'output.trail.s3_bucket_name ==  "{{ s3_bucket_name }}-2"'

  - name: 'Update S3 bucket (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_bucket_name: '{{ s3_bucket_name }}-2'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - 'output.trail.s3_bucket_name ==  "{{ s3_bucket_name }}-2"'

  - name: 'Reset S3 bucket'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output.trail.name == cloudtrail_name
      - output.trail.s3_bucket_name == s3_bucket_name

  # ============================================================

  - name: 'Disable logging (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_logging: no
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Disable logging'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_logging: no
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.is_logging == False

  - name: 'Disable logging (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_logging: no
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.is_logging == False

  # Ansible Documentation lists logging as explicitly defaulting to enabled

  - name: 'Enable logging (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_logging: yes
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Enable logging'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_logging: yes
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.is_logging == True

  - name: 'Enable logging (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_logging: yes
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.is_logging == True

  # ============================================================

  - name: 'Disable global logging (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      include_global_events: no
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Disable global logging'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      include_global_events: no
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.include_global_service_events == False

  - name: 'Disable global logging (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      include_global_events: no
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.include_global_service_events == False

  # Ansible Documentation lists Global-logging as explicitly defaulting to enabled

  - name: 'Enable global logging (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      include_global_events: yes
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Enable global logging'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      include_global_events: yes
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.include_global_service_events == True

  - name: 'Enable global logging (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      include_global_events: yes
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.include_global_service_events == True

  # ============================================================

  - name: 'Enable multi-region logging (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      is_multi_region_trail: yes
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Enable multi-region logging'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      is_multi_region_trail: yes
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.is_multi_region_trail == True

  - name: 'Enable multi-region logging (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      is_multi_region_trail: yes
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.is_multi_region_trail == True

  # Ansible Documentation lists Multi-Region-logging as explicitly defaulting to disabled

  - name: 'Disable multi-region logging (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      is_multi_region_trail: no
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Disable multi-region logging'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      is_multi_region_trail: no
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.is_multi_region_trail == False

  - name: 'Disable multi-region logging (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      is_multi_region_trail: no
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.is_multi_region_trail == False

  # ============================================================

  - name: 'Enable logfile validation (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_log_file_validation: yes
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Enable logfile validation'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_log_file_validation: yes
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.log_file_validation_enabled == True

  - name: 'Enable logfile validation (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_log_file_validation: yes
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.log_file_validation_enabled == True

  - name: 'No-op update to trail'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.log_file_validation_enabled == True

  - name: 'Disable logfile validation (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_log_file_validation: no
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Disable logfile validation'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_log_file_validation: no
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.name == cloudtrail_name
      - output.trail.log_file_validation_enabled == False

  - name: 'Disable logfile validation (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      enable_log_file_validation: no
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.name == cloudtrail_name
      - output.trail.log_file_validation_enabled == False

  # ============================================================

  - name: 'Enable logging encryption (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      kms_key_id: '{{ kms_key.key_arn }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Enable logging encryption'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      kms_key_id: '{{ kms_key.key_arn }}'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.kms_key_id == kms_key.key_arn

  - name: 'Enable logging encryption (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      kms_key_id: '{{ kms_key.key_arn }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.kms_key_id == kms_key.key_arn

  - name: 'No-op update to trail'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.kms_key_id == kms_key.key_arn

  - name: 'Update logging encryption key (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      kms_key_id: '{{ kms_key2.key_arn }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Update logging encryption key'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      kms_key_id: '{{ kms_key2.key_arn }}'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.kms_key_id == kms_key2.key_arn

  - name: 'Update logging encryption key (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      kms_key_id: '{{ kms_key2.key_arn }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.trail.kms_key_id == kms_key2.key_arn

  - name: 'Update logging encryption to alias (CHECK MODE)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      kms_key_id: 'alias/{{ kms_alias }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Update logging encryption to alias'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      kms_key_id: 'alias/{{ kms_alias }}'
    register: output
  - assert:
      that:
      - output is changed
      - output.trail.kms_key_id == kms_key.key_arn

  - name: 'Update logging encryption to alias (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      kms_key_id: 'alias/{{ kms_alias }}'
    register: output
  - assert:
      that:
  #    - output is not changed
      - output.trail.kms_key_id == kms_key.key_arn

  #- name: 'Disable logging encryption (CHECK MODE)'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    kms_key_id: ''
  #  register: output
  #  check_mode: yes
  #- assert:
  #    that:
  #    - output is changed

  #- name: 'Disable logging encryption'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    kms_key_id: ''
  #  register: output
  #- assert:
  #    that:
  #    - output.trail.kms_key_id == None
  #    - output is changed

  #- name: 'Disable logging encryption (no change)'
  #  cloudtrail:
  #    state: present
  #    name: '{{ cloudtrail_name }}'
  #    kms_key_id: ''
  #  register: output
  #- assert:
  #    that:
  #    - output.kms_key_id == None
  #    - output is not changed

  # ============================================================

  - name: 'Delete a trail without providing bucket_name (CHECK MODE)'
    module_defaults: { cloudtrail: {} }
    cloudtrail:
      state: absent
      name: '{{ cloudtrail_name }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Delete a trail while providing bucket_name (CHECK MODE)'
    cloudtrail:
      state: absent
      name: '{{ cloudtrail_name }}'
    register: output
    check_mode: yes
  - assert:
      that:
      - output is changed

  - name: 'Delete a trail'
    cloudtrail:
      state: absent
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is changed
      - output.exists == False

  - name: 'Delete a non-existent trail'
    cloudtrail:
      state: absent
      name: '{{ cloudtrail_name }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.exists == False

  # ============================================================

  - name: 'Test creation of a complex Trail (all features)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '{{ cloudtrail_prefix }}'
      sns_topic_name: '{{ sns_topic }}'
      cloudwatch_logs_log_group_arn: '{{ output_cloudwatch_log_group.arn }}'
      cloudwatch_logs_role_arn: '{{ output_cloudwatch_role.arn }}'
      is_multi_region_trail: yes
      include_global_events: yes
      enable_log_file_validation: yes
      kms_key_id: '{{ kms_key.key_arn }}'
    register: output
  - assert:
      that:
      - output is changed
      #- output.exists == True
      - output.trail.name == cloudtrail_name
      - output.trail.home_region == aws_region
      - output.trail.include_global_service_events == True
      - output.trail.is_multi_region_trail == True
      - output.trail.is_logging == True
      - output.trail.log_file_validation_enabled ==  True
      - output.trail.s3_bucket_name == s3_bucket_name
      - output.trail.s3_key_prefix == cloudtrail_prefix
      - output.trail.kms_key_id == kms_key.key_arn
      - output.trail.sns_topic_arn == output_sns_topic.sns_arn
      - output.trail.sns_topic_name == sns_topic
      - output.trail.tags | length == 0

  - name: 'Test creation of a complex Trail (no change)'
    cloudtrail:
      state: present
      name: '{{ cloudtrail_name }}'
      s3_key_prefix: '{{ cloudtrail_prefix }}'
      sns_topic_name: '{{ sns_topic }}'
      cloudwatch_logs_log_group_arn: '{{ output_cloudwatch_log_group.arn }}'
      cloudwatch_logs_role_arn: '{{ output_cloudwatch_role.arn }}'
      is_multi_region_trail: yes
      include_global_events: yes
      enable_log_file_validation: yes
      kms_key_id: '{{ kms_key.key_arn }}'
    register: output
  - assert:
      that:
      - output is not changed
      - output.exists == True
      - output.trail.name == cloudtrail_name
      - output.trail.home_region == aws_region
      - output.trail.include_global_service_events == True
      - output.trail.is_multi_region_trail == True
      - output.trail.is_logging == True
      - output.trail.log_file_validation_enabled ==  True
      - output.trail.s3_bucket_name == s3_bucket_name
      - output.trail.s3_key_prefix == cloudtrail_prefix
      - output.trail.kms_key_id == kms_key.key_arn
      - output.trail.sns_topic_arn == output_sns_topic.sns_arn
      - output.trail.sns_topic_name == sns_topic
      - output.trail.tags | length == 0

  always:
  # ============================================================
  #   Cleanup
  # ============================================================
  - name: 'Delete test trail'
    cloudtrail:
      state: absent
      name: '{{ cloudtrail_name }}'
    ignore_errors: yes
  - name: 'Delete S3 bucket'
    s3_bucket:
      state: absent
      name: '{{ s3_bucket_name }}'
      force: yes
    ignore_errors: yes
  - name: 'Delete second S3 bucket'
    s3_bucket:
      state: absent
      name: '{{ s3_bucket_name }}-2'
      force: yes
    ignore_errors: yes
  - name: 'Delete KMS Key'
    aws_kms:
      state: absent
      alias: '{{ kms_alias }}'
    ignore_errors: yes
  - name: 'Delete second KMS Key'
    aws_kms:
      state: absent
      alias: '{{ kms_alias }}-2'
    ignore_errors: yes
  - name: 'Delete SNS Topic'
    sns_topic:
      state: absent
      name: '{{ sns_topic }}'
    ignore_errors: yes
  - name: 'Delete second SNS Topic'
    sns_topic:
      state: absent
      name: '{{ sns_topic }}-2'
    ignore_errors: yes
  - name: 'Delete CloudWatch Log Group'
    cloudwatchlogs_log_group:
      state: absent
      log_group_name: '{{ cloudwatch_log_group }}'
    ignore_errors: yes
  - name: 'Delete second CloudWatch Log Group'
    cloudwatchlogs_log_group:
      state: absent
      log_group_name: '{{ cloudwatch_log_group }}-2'
    ignore_errors: yes
  - name: 'Remove inline policy to CloudWatch Role'
    iam_policy:
      state: absent
      iam_type: role
      iam_name: '{{ cloudwatch_role }}'
      policy_name: 'CloudWatch'
    ignore_errors: yes
  - name: 'Delete CloudWatch IAM Role'
    iam_role:
      state: absent
      name: '{{ cloudwatch_role }}'
    ignore_errors: yes
