From 967d03ea0b998f8fd0ca92d87171a83351473ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Quatremain?= Date: Sun, 27 Oct 2024 10:06:13 +0100 Subject: [PATCH] New modules (#2) --- CHANGELOG.rst | 9 + README.md | 2 + changelogs/changelog.yaml | 9 + galaxy.yml | 2 +- meta/runtime.yml | 1 + plugins/module_utils/api_module.py | 121 ++- plugins/modules/rhacs_compliance_schedule.py | 699 +++++++++++++++ plugins/modules/rhacs_external_backup.py | 2 +- plugins/modules/rhacs_policy_notifier.py | 4 +- plugins/modules/rhacs_policy_status.py | 4 +- plugins/modules/rhacs_report_schedule.py | 825 ++++++++++++++++++ .../files/gcs_sa_key.json | 9 + .../rhacs_compliance_schedule/meta/main.yml | 4 + .../rhacs_compliance_schedule/tasks/main.yml | 547 ++++++++++++ .../rhacs_notifier_integration/tasks/main.yml | 4 +- .../files/gcs_sa_key.json | 9 + .../rhacs_report_schedule/meta/main.yml | 4 + .../rhacs_report_schedule/tasks/main.yml | 813 +++++++++++++++++ 18 files changed, 3058 insertions(+), 10 deletions(-) create mode 100644 plugins/modules/rhacs_compliance_schedule.py create mode 100644 plugins/modules/rhacs_report_schedule.py create mode 100644 tests/integration/targets/rhacs_compliance_schedule/files/gcs_sa_key.json create mode 100644 tests/integration/targets/rhacs_compliance_schedule/meta/main.yml create mode 100644 tests/integration/targets/rhacs_compliance_schedule/tasks/main.yml create mode 100644 tests/integration/targets/rhacs_report_schedule/files/gcs_sa_key.json create mode 100644 tests/integration/targets/rhacs_report_schedule/meta/main.yml create mode 100644 tests/integration/targets/rhacs_report_schedule/tasks/main.yml diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b210a1b..bbbb152 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,15 @@ Red Hat Advanced Cluster Security for Kubernetes Collection Release Notes .. contents:: Topics +v1.1.0 +====== + +New Modules +----------- + +- herve4m.rhacs_configuration.rhacs_compliance_schedule - Manage compliance schedule configurations. +- herve4m.rhacs_configuration.rhacs_report_schedule - Manage vulnerability reporting schedules. + v1.0.0 ====== diff --git a/README.md b/README.md index 0d5df92..4bdddc9 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Name | Description `rhacs_auth_provider` | Manage authentication providers `rhacs_cloud_management_platform` | Manage RHACS integration with cloud platforms `rhacs_collection` | Manage deployment collections +`rhacs_compliance_schedule` | Manage compliance schedule configurations `rhacs_config` | Manage RHACS configuration `rhacs_delegated_image_scan` | Manage delegated image scanning configuration `rhacs_exception` | Configure vulnerability exception expiration periods @@ -41,6 +42,7 @@ Name | Description `rhacs_policy_import` | Import security policies `rhacs_policy_notifier` | Associate notifiers to policies `rhacs_policy_status` | Enable or disable policies +`rhacs_report_schedule` | Manage vulnerability reporting schedules `rhacs_role` | Manage roles `rhacs_signature` | Manage RHACS integrations with Cosign signatures diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 19fc494..6852117 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -83,3 +83,12 @@ releases: name: rhacs_signature namespace: '' release_date: '2024-10-17' + 1.1.0: + modules: + - description: Manage compliance schedule configurations. + name: rhacs_compliance_schedule + namespace: '' + - description: Manage vulnerability reporting schedules. + name: rhacs_report_schedule + namespace: '' + release_date: '2024-10-27' diff --git a/galaxy.yml b/galaxy.yml index 0bcbf6b..6ef9eb5 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: herve4m name: rhacs_configuration -version: 1.0.0 +version: 1.1.0 readme: README.md authors: - Hervé Quatremain diff --git a/meta/runtime.yml b/meta/runtime.yml index a408793..afa5ab3 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -25,3 +25,4 @@ action_groups: - rhacs_policy_clone - rhacs_policy_export - rhacs_policy_import + - rhacs_compliance_schedule diff --git a/plugins/module_utils/api_module.py b/plugins/module_utils/api_module.py index b920679..d3aa572 100644 --- a/plugins/module_utils/api_module.py +++ b/plugins/module_utils/api_module.py @@ -714,7 +714,7 @@ def unconditional_update( return response.get("json", {}) - def get_item_from_resource_list(self, name_or_id, resource_list): + def get_item_from_resource_list(self, name_or_id, resource_list, name_attribute="name"): """Retrieve an RHACS object from a list or objects. :param name_or_id: Name or ID of the object to retrieve. @@ -722,6 +722,9 @@ def get_item_from_resource_list(self, name_or_id, resource_list): :param resource_list: List of objects. Each object is a dictionary and must have the ``name`` and ``id`` keys. :type resource_list: list + :param name_attribute: The attribute in the list that contains the + object name. + :type name_attribute: str :return: The object or None if the object is not found. :rtype: dict @@ -729,7 +732,7 @@ def get_item_from_resource_list(self, name_or_id, resource_list): if not name_or_id or not resource_list: return None for res in resource_list: - if name_or_id == res.get("name") or name_or_id == res.get("id"): + if name_or_id == res.get(name_attribute) or name_or_id == res.get("id"): return res return None @@ -995,3 +998,117 @@ def get_notifier_id(self, name_or_id): self.get_notifiers(), error_msg="the notifier method (in `notifiers') does not exist", ) + + def get_collections(self): + """Retrieve the list of the deployment collections. + + :return: The list of deployment collection objects + :rtype: list + """ + try: + return self.collections + except AttributeError: + # Retrieve the existing deployment collections + # + # GET /v1/collections + # { + # "collections": [ + # { + # "id": "7e4a265e-2d5a-4ff4-81a8-e426b102dbae", + # "name": "My collection", + # "description": "My description", + # "createdAt": "2024-10-03T14:07:18.562326152Z", + # "lastUpdated": "2024-10-03T14:07:18.562326152Z", + # "createdBy": { + # "id": "sso:4df1...b62d:admin", + # "name": "admin" + # }, + # "updatedBy": { + # "id": "sso:4df1...b62d:admin", + # "name": "admin" + # }, + # "resourceSelectors": [ + # { + # "rules": [ + # { + # "fieldName": "Namespace Label", + # "operator": "OR", + # "values": [ + # { + # "value": "team=payment", + # "matchType": "EXACT" + # }, + # { + # "value": "foo=bar", + # "matchType": "EXACT" + # } + # ] + # }, + # { + # "fieldName": "Namespace Label", + # "operator": "OR", + # "values": [ + # { + # "value": "toto=titi", + # "matchType": "EXACT" + # } + # ] + # }, + # { + # "fieldName": "Deployment", + # "operator": "OR", + # "values": [ + # { + # "value": "nginx-deployment", + # "matchType": "EXACT" + # }, + # { + # "value": "^nginx-deployment$", + # "matchType": "REGEX" + # } + # ] + # } + # ] + # } + # ], + # "embeddedCollections": [ + # { + # "id": "a7e188bb-f4f5-4023-a91f-4d4585809d17" + # } + # ] + # }, + # ... + # ] + # } + c = self.get_object_path( + "/v1/collections", query_params={"query.pagination.limit": 10000} + ) + self.collections = c.get("collections", []) + return self.collections + + def get_collection(self, name_or_id): + """Retrieve a deployment collection object. + + :param name_or_id: Name or ID of the collection to retrieve. + :type name_or_id: str + + :return: The collection object or None if the collection is not found. + :rtype: dict + """ + return self.get_item_from_resource_list(name_or_id, self.get_collections()) + + def get_collection_id(self, name_or_id): + """Return the ID of a deployment collection. + + :param name_or_id: Name or ID of the collection to retrieve. + :type name_or_id: str + + :return: The deployment collection ID. If the collection is not found, + then the module exists in error. + :rtype: str + """ + return self.get_id_from_resource_list( + name_or_id, + self.get_collections(), + error_msg="the deployment collection (in `collection') does not exist", + ) diff --git a/plugins/modules/rhacs_compliance_schedule.py b/plugins/modules/rhacs_compliance_schedule.py new file mode 100644 index 0000000..8c04b05 --- /dev/null +++ b/plugins/modules/rhacs_compliance_schedule.py @@ -0,0 +1,699 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2024 Hervé Quatremain +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: rhacs_compliance_schedule +short_description: Manage compliance schedule configurations +description: + - Create, delete, and update compliance schedule configurations. +version_added: '1.1.0' +author: Hervé Quatremain (@herve4m) +options: + name: + description: + - Name of the compliance schedule configuration. + - The name can contain only lowercase alphanumeric characters, hyphens + C(-), and periods C(.), and must start and end with an alphanumeric + character. + required: true + type: str + new_name: + description: + - New name for the compliance schedule configuration. + - Setting this option changes the name of the compliance schedule + configuration, which current name is provided in the O(name) parameter. + - The module returns an error if the destination configuration already + exists. + - The O(new_name) parameter is ignored when O(state=absent). + type: str + description: + description: + - Optional description for the compliance schedule configuration. + type: str + profiles: + description: + - List of compliance profiles to use for verifying the secured clusters. + - The compliance profiles come from the compliance operator. + - At least one profile is required when you create a compliance schedule + configuration. + - You can have only one compliance schedule that scans the same profile + on the same cluster. + type: list + elements: str + append_profiles: + description: + - If V(true), then the module adds the profiles listed in the + O(profiles) list to the existing list. + - If V(false), then the module replaces the current list of profiles + with the list from the O(profiles) parameter. + type: bool + default: true + interval: + description: + - Compliance scan frequency. + - If you set the O(interval) parameter to V(WEEKLY), then provide + your chosen week days for the scans in the O(week_days) parameter. + - If you set the O(interval) parameter to V(MONTHLY), then provide + your chosen days for the scans in the O(month_days) parameter. + - V(DAILY) by default. + type: str + choices: + - DAILY + - WEEKLY + - MONTHLY + week_days: + description: + - Name of the days for weekly scans when O(interval=WEEKLY). + - Mutually exclusive with O(month_days). + - V([Sunday]) by default. + type: list + elements: str + choices: + - Monday + - Tuesday + - Wednesday + - Thursday + - Friday + - Saturday + - Sunday + month_days: + description: + - Days of the month for monthly scans when O(interval=MONTHLY). + - The days in O(month_days) must be between 1 and 31. + - Mutually exclusive with O(week_days). + - V([1]) by default. + type: list + elements: int + hour: + description: + - Hour in the 24-hour notation at which the scan should start. + - O(hour) must be between 0 and 23. + - See the O(minute) parameter to specify the minute in the hour. + - V(18) by default. + type: int + minute: + description: + - Minute in the hour at which the scan should start. + - O(minute) must be between 0 and 59. + - V(0) by default. + type: int + email_notifiers: + description: + - Optional email delivery destinations for sending the compliance scan + reports. + type: list + elements: dict + suboptions: + notifier: + description: + - Name or identifier of the notification method to use for sending + emails. + - Only email notification methods can be used. + - See the M(herve4m.rhacs_configuration.rhacs_notifier_integration) + module to create and manage notification methods. + type: str + required: true + to: + description: + - Email addresses of the recipients who should receive the compliance + reports. + - By default, the module uses the recipient email address that the + notifier method defines. + type: list + elements: str + subject: + description: + - Subject line for the emails. + - If you omit the parameter, then a default subject is used. + type: str + body: + description: + - Text of the emails. + - If you omit the parameter, then a default text is used. + type: str + clusters: + description: + - List of the clusters to scan for compliance. + - You can specify the clusters by their names or their identifiers. + - At least one cluster is required when you create a compliance schedule + configuration. + type: list + elements: str + append_clusters: + description: + - If V(true), then the module adds the clusters listed in the O(clusters) + list to the existing list. + - If V(false), then the module replaces the current list of clusters + with the list from the O(clusters) parameter. + type: bool + default: true + state: + description: + - If V(absent), then the module deletes the configuration. + - The module does not fail if the configuration does not exist, because + the state is already as expected. + - If V(present), then the module creates the configuration if it does not + already exist. + - If the configuration already exists, then the module updates its state, + and renames the configuration if you provide the O(new_name) parameter. + type: str + default: present + choices: [absent, present] +attributes: + check_mode: + support: full + diff_mode: + support: none + platform: + support: full + platforms: all +extends_documentation_fragment: + - ansible.builtin.action_common_attributes + - herve4m.rhacs_configuration.auth +""" + +EXAMPLES = r""" +- name: Ensure the weekly compliance scan is scheduled + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + - ocp4-pci-dss + interval: WEEKLY + week_days: + - Monday + hour: 23 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS compliance scan report + clusters: + - production + - infra + state: present + rhacs_host: central.example.com + rhacs_username: admin + rhacs_password: vs9mrD55NP + +- name: Ensure the public cluster is added to the compliance scan report + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + clusters: + - public + append_clusters: true + state: present + rhacs_host: central.example.com + rhacs_username: admin + rhacs_password: vs9mrD55NP + +- name: Ensure the weekly compliance scan is renamed and sends monthly reports + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + new_name: monthly-reports + description: Monthly compliance scan + interval: MONTHLY + month_days: + - 1 + - 15 + state: present + rhacs_host: central.example.com + rhacs_username: admin + rhacs_password: vs9mrD55NP + +- name: Ensure the monthly compliance scan is removed + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: monthly-reports + state: absent + rhacs_host: central.example.com + rhacs_username: admin + rhacs_password: vs9mrD55NP +""" + +RETURN = r""" +id: + description: Internal identifier of the compliance schedule configuration. + type: str + returned: always + sample: b112af00-8256-43fe-82fd-ecafb62c5bea +""" + +import copy + +from ..module_utils.api_module import APIModule + +WEEK_DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + + +def main(): + + argument_spec = dict( + name=dict(required=True), + new_name=dict(), + description=dict(), + profiles=dict(type="list", elements="str"), + append_profiles=dict(type="bool", default=True), + interval=dict(choices=["DAILY", "WEEKLY", "MONTHLY"]), + hour=dict(type="int"), + minute=dict(type="int"), + week_days=dict(type="list", elements="str", choices=WEEK_DAYS), + month_days=dict(type="list", elements="int"), + email_notifiers=dict( + type="list", + elements="dict", + options=dict( + notifier=dict(required=True), + to=dict(type="list", elements="str"), + subject=dict(), + body=dict(), + ), + ), + clusters=dict(type="list", elements="str"), + append_clusters=dict(type="bool", default=True), + state=dict(choices=["present", "absent"], default="present"), + ) + + # Create a module for ourselves + module = APIModule( + argument_spec=argument_spec, + mutually_exclusive=[("week_days", "month_days")], + supports_check_mode=True, + ) + + # Extract our parameters + name = module.params.get("name") + new_name = module.params.get("new_name") + description = module.params.get("description") + profiles = module.params.get("profiles") + append_profiles = module.params.get("append_profiles") + interval = module.params.get("interval") + hour = module.params.get("hour") + minute = module.params.get("minute") + week_days = module.params.get("week_days") + month_days = module.params.get("month_days") + email_notifiers = module.params.get("email_notifiers") + clusters = module.params.get("clusters") + append_clusters = module.params.get("append_clusters") + state = module.params.get("state") + + if new_name == name: + new_name = None + + # Retrieve the existing compliance schedules + # + # GET /v2/compliance/scan/configurations + # { + # "configurations": [ + # { + # "id": "511df30c-123f-4979-8abc-8eb7a3c9f3e3", + # "scanName": "Monthly scan", + # "scanConfig": { + # "oneTimeScan": false, + # "profiles": [ + # "ocp4-cis", + # "ocp4-pci-dss-node-4-0" + # ], + # "scanSchedule": { + # "intervalType": "MONTHLY", + # "hour": 20, + # "minute": 42, + # "daysOfMonth": { + # "days": [ + # 1 + # ] + # } + # }, + # "description": "Perform a monthly scan", + # "notifiers": [] + # }, + # "clusterStatus": [ + # { + # "clusterId": "e3133d73-7f9a-438d-8099-b52ba68b33e7", + # "errors": [ + # "" + # ], + # "clusterName": "managed-cluster", + # "suiteStatus": { + # "phase": "DONE", + # "result": "NON-COMPLIANT", + # "errorMessage": "", + # "lastTransitionTime": "2024-10-18T13:52:13Z" + # } + # } + # ], + # "createdTime": "2024-10-18T13:51:00.219090285Z", + # "lastUpdatedTime": "2024-10-18T13:51:00.232759597Z", + # "modifiedBy": { + # "id": "sso:4df1b98c-24ed-4073-a9ad-356aec6bb62d:admin", + # "name": "admin" + # }, + # "lastExecutedTime": "2024-10-18T13:52:13Z" + # }, + # { + # "id": "d6a6b28e-9a64-41ef-b6f9-05bf57f1fefd", + # "scanName": "Weekly scan", + # "scanConfig": { + # "oneTimeScan": false, + # "profiles": [ + # "ocp4-cis-1-4" + # ], + # "scanSchedule": { + # "intervalType": "WEEKLY", + # "hour": 20, + # "minute": 45, + # "daysOfWeek": { + # "days": [ + # 0 + # ] + # } + # }, + # "description": "Perform a weekly scan", + # "notifiers": [ + # { + # "emailConfig": { + # "notifierId": "657a...74f9", + # "mailingLists": [ + # "foo1@example.com" + # ], + # "customSubject": "This is the subje", + # "customBody": "My body" + # }, + # "notifierName": "email" + # } + # ] + # }, + # "clusterStatus": [ + # { + # "clusterId": "e3133d73-7f9a-438d-8099-b52ba68b33e7", + # "errors": [ + # "" + # ], + # "clusterName": "managed-cluster", + # "suiteStatus": { + # "phase": "DONE", + # "result": "NON-COMPLIANT", + # "errorMessage": "", + # "lastTransitionTime": "2024-10-18T14:02:33Z" + # } + # } + # ], + # "createdTime": "2024-10-18T14:01:24.365418945Z", + # "lastUpdatedTime": "2024-10-18T14:01:24.378753158Z", + # "modifiedBy": { + # "id": "sso:4df1b98c-24ed-4073-a9ad-356aec6bb62d:admin", + # "name": "admin" + # }, + # "lastExecutedTime": "2024-10-18T14:02:33Z" + # } + # ], + # "totalCount": 2 + # } + + c = module.get_object_path( + "/v2/compliance/scan/configurations", query_params={"pagination.limit": 10000} + ) + schedules = c.get("configurations", []) + + # Retrieve the objects for the given names + config = module.get_item_from_resource_list(name, schedules, "scanName") + new_config = module.get_item_from_resource_list(new_name, schedules, "scanName") + + # Remove the object. For delete operations, the new_name parameter is + # ignored. + if state == "absent": + module.delete( + config, + "compliance schedule", + name, + "/v2/compliance/scan/configurations/{id}".format( + id=config.get("id", "") if config else "" + ), + ) + + # Validate some of the parameters + if hour and (hour < 0 or hour > 23): + module.fail_json( + msg="the `hour' parameter must be between 0 and 23: {hour}".format(hour=hour) + ) + + if minute and (minute < 0 or minute > 59): + module.fail_json( + msg="the `minute' parameter must be between 0 and 59: {minute}".format( + minute=minute + ) + ) + + if week_days: + wdays = set([WEEK_DAYS.index(d) for d in week_days]) + else: + wdays = set([0]) + + if month_days: + mdays = set() + for d in month_days: + if d < 1 or d > 31: + module.fail_json( + msg=( + "the days in the `month_days' parameter must be " + "between 1 and 31: {month_days}" + ).format(month_days=", ".join([str(i) for i in month_days])) + ) + mdays.add(d) + else: + mdays = set([1]) + + if clusters is not None: + req_clusters = set([module.get_cluster_id(c) for c in clusters]) + else: + req_clusters = set() + + if profiles is not None: + req_profiles = set(profiles) + else: + req_profiles = set() + + notifiers = [] + req_notifiers = set() + if email_notifiers is not None: + for n in email_notifiers: + obj = module.get_notifier(n["notifier"]) + if not obj: + module.fail_json( + msg=( + "the notifier method (in `email_notifiers') does not exist: {name}" + ).format(name=n["notifier"]) + ) + if obj.get("type") != "email": + module.fail_json( + msg=( + "the notifier method (in `email_notifiers') is not " + "an email notifier: {name}: {t}" + ).format(name=n["notifier"], t=obj.get("type")) + ) + id = obj.get("id") + notifier_name = obj.get("name") + subject = n.get("subject") or "" + body = n.get("body") or "" + dest = n.get("to") or [obj.get("labelDefault", "")] + notifiers.append( + { + "emailConfig": { + "notifierId": id, + "mailingLists": dest, + "customSubject": subject, + "customBody": body, + }, + "notifierName": notifier_name, + } + ) + req_notifiers.add((id, notifier_name, ",".join(sorted(dest)), subject, body)) + + # Create the object + if not config and not new_config: + + # Verify that the required parameters are provided + missing_args = [] + if not profiles: + missing_args.append("profiles") + if not clusters: + missing_args.append("clusters") + if missing_args: + module.fail_json( + msg="missing or empty required arguments: {args}".format( + args=", ".join(missing_args) + ) + ) + + # Build the data to send to the API to create the configuration + name = new_name if new_name else name + interval = interval if interval else "DAILY" + new_fields = { + "scanName": name, + "scanConfig": { + "description": description if description else "", + "oneTimeScan": False, + "profiles": list(req_profiles), + "scanSchedule": { + "hour": hour if hour is not None else 18, + "minute": minute if minute is not None else 0, + "intervalType": interval, + }, + "notifiers": notifiers, + }, + "clusters": list(req_clusters), + } + if interval == "WEEKLY": + new_fields["scanConfig"]["scanSchedule"]["daysOfWeek"] = {"days": list(wdays)} + elif interval == "MONTHLY": + new_fields["scanConfig"]["scanSchedule"]["daysOfMonth"] = {"days": list(mdays)} + + resp = module.create( + "compliance schedule", + name, + "/v2/compliance/scan/configurations", + new_fields, + auto_exit=False, + ) + module.exit_json(changed=True, id=resp.get("id", "")) + + if not config and new_config: + config = new_config + name = new_name + new_config = new_name = None + + # A compliance schedule with the new name already exists + if new_config: + module.fail_json( + msg="the compliance schedule (`new_name') already exists: {name}".format( + name=new_name + ) + ) + + # Verify whether a change is needed + scan_config = config.get("scanConfig", {}) + scan_schedule = scan_config.get("scanSchedule", {}) + + curr_clusters = set([c.get("clusterId") for c in config.get("clusterStatus", [])]) + clusters_to_add = req_clusters - curr_clusters + + curr_profiles = set(scan_config.get("profiles", [])) + profiles_to_add = req_profiles - curr_profiles + + curr_notifiers = set() + for n in scan_config.get("notifiers", []): + email_conf = n.get("emailConfig", {}) + curr_notifiers.add( + ( + email_conf.get("notifierId"), + n.get("notifierName"), + ",".join(sorted(email_conf.get("mailingLists", []))), + email_conf.get("customSubject", ""), + email_conf.get("customBody", ""), + ) + ) + + curr_wdays = set(scan_schedule.get("daysOfWeek", {}).get("days", [])) + curr_mdays = set(scan_schedule.get("daysOfMonth", {}).get("days", [])) + + if ( + not new_name + and (description is None or description == scan_config.get("description")) + and (interval is None or interval == scan_schedule.get("intervalType")) + and (hour is None or hour == scan_schedule.get("hour")) + and (minute is None or minute == scan_schedule.get("minute")) + and (week_days is None or wdays == curr_wdays) + and (month_days is None or mdays == curr_mdays) + and ( + clusters is None + or req_clusters == curr_clusters + or (append_clusters is True and not clusters_to_add) + ) + and ( + profiles is None + or req_profiles == curr_profiles + or (append_profiles is True and not profiles_to_add) + ) + and (email_notifiers is None or req_notifiers == curr_notifiers) + ): + module.exit_json(changed=False, id=config.get("id", "")) + + id = config.get("id", "") + name = new_name if new_name else name + new_fields = { + "id": id, + "scanName": name, + "scanConfig": copy.deepcopy(config.get("scanConfig", {})), + "clusters": list(curr_clusters), + } + + if description is not None: + new_fields["scanConfig"]["description"] = description + if interval is not None: + new_fields["scanConfig"]["scanSchedule"]["intervalType"] = interval + if interval == "WEEKLY": + new_fields["scanConfig"]["scanSchedule"].pop("daysOfMonth", None) + if "daysOfWeek" not in new_fields["scanConfig"]["scanSchedule"]: + new_fields["scanConfig"]["scanSchedule"]["daysOfWeek"] = {"days": list(wdays)} + elif interval == "MONTHLY": + new_fields["scanConfig"]["scanSchedule"].pop("daysOfWeek", None) + if "daysOfMonth" not in new_fields["scanConfig"]["scanSchedule"]: + new_fields["scanConfig"]["scanSchedule"]["daysOfMonth"] = { + "days": list(mdays) + } + else: # interval == "DAILY" + new_fields["scanConfig"]["scanSchedule"].pop("daysOfMonth", None) + new_fields["scanConfig"]["scanSchedule"].pop("daysOfWeek", None) + if hour is not None: + new_fields["scanConfig"]["scanSchedule"]["hour"] = hour + if minute is not None: + new_fields["scanConfig"]["scanSchedule"]["minute"] = minute + if ( + week_days is not None + and new_fields["scanConfig"]["scanSchedule"]["intervalType"] == "WEEKLY" + ): + new_fields["scanConfig"]["scanSchedule"]["daysOfWeek"] = {"days": list(wdays)} + if ( + month_days is not None + and new_fields["scanConfig"]["scanSchedule"]["intervalType"] == "MONTHLY" + ): + new_fields["scanConfig"]["scanSchedule"]["daysOfMonth"] = {"days": list(mdays)} + if email_notifiers is not None: + new_fields["scanConfig"]["notifiers"] = notifiers + if profiles is not None: + if append_profiles is True: + new_fields["scanConfig"]["profiles"].extend(list(profiles_to_add)) + else: + if not profiles: + module.fail_json(msg="at least one profile (in `profiles') is required") + new_fields["scanConfig"]["profiles"] = profiles + if clusters is not None: + if append_clusters is True: + new_fields["clusters"].extend(list(clusters_to_add)) + else: + if not clusters: + module.fail_json(msg="at least one cluster (in `clusters') is required") + new_fields["clusters"] = list(req_clusters) + + module.unconditional_update( + "compliance schedule", + name, + "/v2/compliance/scan/configurations/{id}".format(id=id), + new_fields, + ) + module.exit_json(changed=True, id=id) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/rhacs_external_backup.py b/plugins/modules/rhacs_external_backup.py index 58a34ff..9321944 100644 --- a/plugins/modules/rhacs_external_backup.py +++ b/plugins/modules/rhacs_external_backup.py @@ -180,7 +180,7 @@ you should choose a unique name for each configuration. - When several configurations have the same name, the module only considers the first configuration returned by the API for update operations. - - Also, the modules deletes all the configurations matching both the O(name) + - Also, the module deletes all the configurations matching both the O(name) and O(type) parameters for delete operations. attributes: check_mode: diff --git a/plugins/modules/rhacs_policy_notifier.py b/plugins/modules/rhacs_policy_notifier.py index 1f121a8..c2ee8d7 100644 --- a/plugins/modules/rhacs_policy_notifier.py +++ b/plugins/modules/rhacs_policy_notifier.py @@ -37,8 +37,8 @@ elements: str ignore_missing: description: - - ignore_missing the notifiers association even if some of the policies - listed in the O(policies) parameter do not exist. + - Whether to skip the security policies that do not exist in the + O(policies) parameter. - If O(ignore_missing=true), then the module associates the notifiers to the existing policies, and ignore the policies that do not exist. - If O(ignore_missing=false) and some policies do not exist, then the diff --git a/plugins/modules/rhacs_policy_status.py b/plugins/modules/rhacs_policy_status.py index 30e7145..9f36555 100644 --- a/plugins/modules/rhacs_policy_status.py +++ b/plugins/modules/rhacs_policy_status.py @@ -31,8 +31,8 @@ elements: str ignore_missing: description: - - ignore_missing the status change even if some of the policies listed in - the O(policies) parameter do not exist. + - Whether to skip the security policies that do not exist in the + O(policies) parameter. - If O(ignore_missing=true), then the module changes the status of the existing policies, and ignore the policies that do not exist. - If O(ignore_missing=false) and some policies do not exist, then the diff --git a/plugins/modules/rhacs_report_schedule.py b/plugins/modules/rhacs_report_schedule.py new file mode 100644 index 0000000..8fef956 --- /dev/null +++ b/plugins/modules/rhacs_report_schedule.py @@ -0,0 +1,825 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2024 Hervé Quatremain +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: rhacs_report_schedule +short_description: Manage vulnerability reporting schedules +description: + - Create, delete, and update vulnerability reporting schedules. +version_added: '1.1.0' +author: Hervé Quatremain (@herve4m) +options: + name: + description: + - Name of the vulnerability reporting schedule. + - Several schedules can have the same name. + - For a delete operation, the module removes all the schedules matching + the O(name) parameter. + - For update operations, the module updates only the configuration that + matches closely the provided parameters. + required: true + type: str + new_name: + description: + - New name for the vulnerability reporting schedule. + - Setting this option changes the name of the schedule, which current + name is provided in the O(name) parameter. + - The module returns an error if the destination schedule already exists. + - The O(new_name) parameter is ignored when O(state=absent). + type: str + description: + description: + - Optional description for the vulnerability reporting schedule. + type: str + fixability: + description: + - Whether to include CVEs that are fixable, not fixable, or both. + - V(BOTH) by default. + type: str + choices: + - FIXABLE + - NOT_FIXABLE + - BOTH + severities: + description: + - Severities of the CVEs to report. + - V(IMPORTANT) and V(CRITICAL) by default. + type: list + elements: str + choices: + - UNKNOWN + - LOW + - MODERATE + - IMPORTANT + - CRITICAL + image_types: + description: + - Whether to include CVEs from deployed images, watched images, or both. + - V(DEPLOYED) and V(WATCHED) by default. + type: list + elements: str + choices: + - DEPLOYED + - WATCHED + since: + description: + - Include CVEs based on their discovery date. + - If O(since=ALL_TIME), then the reports do not exclude CVEs based on + their date. + - If O(since=LAST_SENT), then the reports include only the CVEs + discovered after the last report. When O(since=LAST_SENT), the + O(email_notifiers) and O(interval) parameters are required. + - If O(since=DATE), then the reports include the CVEs discovered after + the date given in the O(date) parameter. + - V(ALL_TIME) by default. + type: str + choices: + - ALL_TIME + - LAST_SENT + - DATE + date: + description: + - The date after which the CVEs must be reported. The O(date) parameter + is used only when O(since=DATE). + - The format for the O(date) parameter is C(YYYY-MM-DD), such + as 2024-05-25. + type: str + collection: + description: + - Deployment collection to include in the reports. + - You can specify the collection by its name or by its identifier. + - See the M(herve4m.rhacs_configuration.rhacs_collection) module to + create and manage deployment collections. + type: str + interval: + description: + - Reporting frequency. + - If you set the O(interval) parameter to V(WEEKLY), then provide + your chosen week days for the reports in the O(week_days) parameter. + - If you set the O(interval) parameter to V(MONTHLY), then provide + your chosen days for the reports in the O(month_days) parameter. + - When O(interval=UNSET), then the reports are not scheduled. + - V(UNSET) by default. + type: str + choices: + - UNSET + - WEEKLY + - MONTHLY + week_days: + description: + - Name of the days for weekly reports when O(interval=WEEKLY). + - Mutually exclusive with O(month_days). + - V([Sunday]) by default. + type: list + elements: str + choices: + - Monday + - Tuesday + - Wednesday + - Thursday + - Friday + - Saturday + - Sunday + month_days: + description: + - Days of the month for monthly reports when O(interval=MONTHLY). + - The days in O(month_days) must be between 1 and 31. + - Mutually exclusive with O(week_days). + - V([1]) by default. + type: list + elements: int + hour: + description: + - Hour in the 24-hour notation at which the report should be created. + - O(hour) must be between 0 and 23. + - See the O(minute) parameter to specify the minute in the hour. + - V(18) by default. + type: int + minute: + description: + - Minute in the hour at which the report should be created. + - O(minute) must be between 0 and 59. + - V(0) by default. + type: int + email_notifiers: + description: + - Optional email delivery destinations for sending the vulnerability + reports. + type: list + elements: dict + suboptions: + notifier: + description: + - Name or identifier of the notification method to use for sending + emails. + - Only email notification methods can be used. + - See the M(herve4m.rhacs_configuration.rhacs_notifier_integration) + module to create and manage notification methods. + type: str + required: true + to: + description: + - Email addresses of the recipients who should receive the + vulnerability reports. + - By default, the module uses the recipient email address that the + notifier method defines. + type: list + elements: str + subject: + description: + - Subject line for the emails. + - If you omit the parameter, then a default subject is used. + type: str + body: + description: + - Text of the emails. + - If you omit the parameter, then a default text is used. + type: str + state: + description: + - If V(absent), then the module deletes all the schedules that match + the O(name) parameter. + - The module does not fail if the schedule does not exist, because + the state is already as expected. + - If V(present), then the module creates the schedule if it does not + already exist. + - If the schedule exists, then the module updates its state, and renames + the schedule if you provide the O(new_name) parameter. + type: str + default: present + choices: [absent, present] +notes: + - Although several vulnerability schedules can have the same name, + you should choose a unique name for each schedule. + - For update operations, when several schedules have the same name, the + module selects the schedule that matches most of the provided parameters. + - Also, the module deletes all the schedules matching the O(name) parameter + for delete operations. +attributes: + check_mode: + support: full + diff_mode: + support: none + platform: + support: full + platforms: all +extends_documentation_fragment: + - ansible.builtin.action_common_attributes + - herve4m.rhacs_configuration.auth +""" + +EXAMPLES = r""" +- name: Ensure the weekly vulnerability report is scheduled + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report + description: Bi-weekly vulnerability report of fixable CVEs + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + - WATCHED + collection: Sensitive data + since: DATE + date: "2024-05-25" + interval: WEEKLY + week_days: + - Sunday + - Wednesday + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS vulnerability report + state: present + rhacs_host: central.example.com + rhacs_username: admin + rhacs_password: vs9mrD55NP + +- name: Ensure the weekly vulnerability report is renamed + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report + new_name: Bi-weekly report + state: present + rhacs_host: central.example.com + rhacs_username: admin + rhacs_password: vs9mrD55NP + +- name: Ensure the bi-weekly vulnerability report is removed + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Bi-weekly report + state: absent + rhacs_host: central.example.com + rhacs_username: admin + rhacs_password: vs9mrD55NP +""" + +RETURN = r""" +id: + description: Internal identifier of the vulnerability reporting schedule. + type: str + returned: always + sample: b112af00-8256-43fe-82fd-ecafb62c5bea +""" + +from datetime import datetime +import copy + +from ..module_utils.api_module import APIModule + +WEEK_DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + + +def severity_to_API_type(severity): + if not severity: + return None + if severity == "UNKNOWN": + return "UNKNOWN_VULNERABILITY_SEVERITY" + if severity == "LOW": + return "LOW_VULNERABILITY_SEVERITY" + if severity == "MODERATE": + return "MODERATE_VULNERABILITY_SEVERITY" + if severity == "IMPORTANT": + return "IMPORTANT_VULNERABILITY_SEVERITY" + # if severity == "CRITICAL": + return "CRITICAL_VULNERABILITY_SEVERITY" + + +def main(): + + argument_spec = dict( + name=dict(required=True), + new_name=dict(), + description=dict(), + fixability=dict(choices=["BOTH", "FIXABLE", "NOT_FIXABLE"]), + severities=dict( + type="list", + elements="str", + choices=["UNKNOWN", "LOW", "MODERATE", "IMPORTANT", "CRITICAL"], + ), + image_types=dict(type="list", elements="str", choices=["DEPLOYED", "WATCHED"]), + since=dict(choices=["ALL_TIME", "LAST_SENT", "DATE"]), + date=dict(), + collection=dict(), + interval=dict(choices=["UNSET", "WEEKLY", "MONTHLY"]), + hour=dict(type="int"), + minute=dict(type="int"), + week_days=dict(type="list", elements="str", choices=WEEK_DAYS), + month_days=dict(type="list", elements="int"), + email_notifiers=dict( + type="list", + elements="dict", + options=dict( + notifier=dict(required=True), + to=dict(type="list", elements="str"), + subject=dict(), + body=dict(), + ), + ), + state=dict(choices=["present", "absent"], default="present"), + ) + + # Create a module for ourselves + module = APIModule( + argument_spec=argument_spec, + mutually_exclusive=[("week_days", "month_days")], + supports_check_mode=True, + ) + + # Extract our parameters + name = module.params.get("name") + new_name = module.params.get("new_name") + description = module.params.get("description") + fixability = module.params.get("fixability") + severities = module.params.get("severities") + image_types = module.params.get("image_types") + since = module.params.get("since") + date = module.params.get("date") + collection = module.params.get("collection") + interval = module.params.get("interval") + hour = module.params.get("hour") + minute = module.params.get("minute") + week_days = module.params.get("week_days") + month_days = module.params.get("month_days") + email_notifiers = module.params.get("email_notifiers") + state = module.params.get("state") + + if new_name == name: + new_name = None + + # Retrieve the existing vulnerability reporting schedules + # + # GET /v2/reports/configurations + # { + # "reportConfigs": [ + # { + # "id": "66211b6b-fcac-48e8-b583-78943b6862ef", + # "name": "myreport", + # "description": "my description", + # "type": "VULNERABILITY", + # "vulnReportFilters": { + # "fixability": "FIXABLE", + # "severities": [ + # "CRITICAL_VULNERABILITY_SEVERITY", + # "IMPORTANT_VULNERABILITY_SEVERITY" + # ], + # "imageTypes": [ + # "DEPLOYED", + # "WATCHED" + # ], + # "sinceStartDate": "2024-10-16T00:00:00Z" + # }, + # "schedule": { + # "intervalType": "MONTHLY", + # "hour": 0, + # "minute": 0, + # "daysOfMonth": { + # "days": [ + # 1 + # ] + # } + # }, + # "resourceScope": { + # "collectionScope": { + # "collectionId": "eebb...bb52", + # "collectionName": "mycol" + # } + # }, + # "notifiers": [ + # { + # "emailConfig": { + # "notifierId": "657a...74f9", + # "mailingLists": [ + # "foo@example.com" + # ], + # "customSubject": "", + # "customBody": "" + # }, + # "notifierName": "myemail" + # } + # ] + # }, + # { + # "id": "cdeaff52-e9ae-4b97-9de7-1cf926739166", + # "name": "myreport", + # "description": "My report", + # "type": "VULNERABILITY", + # "vulnReportFilters": { + # "fixability": "FIXABLE", + # "severities": [ + # "CRITICAL_VULNERABILITY_SEVERITY", + # "IMPORTANT_VULNERABILITY_SEVERITY" + # ], + # "imageTypes": [ + # "DEPLOYED", + # "WATCHED" + # ], + # "allVuln": true + # }, + # "schedule": null, + # "resourceScope": { + # "collectionScope": { + # "collectionId": "eebb...bb52", + # "collectionName": "mycol" + # } + # }, + # "notifiers": [] + # } + # ] + # } + + c = module.get_object_path( + "/v2/reports/configurations", query_params={"pagination.limit": 10000} + ) + reports = c.get("reportConfigs", []) + + # Retrieve the objects for the given names. Several configurations can + # have the same name. + configs = [] + new_configs = [] + for s in reports: + s_name = s.get("name") + if name == s_name: + configs.append(s) + elif new_name == s_name: + new_configs.append(s) + + # Remove all the objects matching the given name. For delete operations, + # the new_name parameter is ignored. + if state == "absent": + if not configs: + module.exit_json(changed=False) + for config in configs: + id = config.get("id", "") + module.delete( + config, + "vulnerability reporting schedule", + "{name} ({id})".format(name=name, id=id), + "/v2/reports/configurations/{id}".format(id=id), + auto_exit=False, + ) + module.exit_json(changed=True) + + # Validate and convert some of the parameters + if severities: + req_severities = set([severity_to_API_type(s) for s in severities]) + else: + req_severities = set() + + if image_types: + req_image_types = set(image_types) + else: + req_image_types = set(["DEPLOYED", "WATCHED"]) + + if date: + try: + d = datetime.strptime(date, "%Y-%m-%d") + except ValueError: + module.fail_json( + msg="the `date' parameter must have the `YYYY-MM-DD' format: {d}".format( + d=date + ) + ) + start_date = d.isoformat() + "Z" + else: + start_date = datetime.now().strftime("%Y-%m-%dT00:00:00Z") + + if hour and (hour < 0 or hour > 23): + module.fail_json( + msg="the `hour' parameter must be between 0 and 23: {hour}".format(hour=hour) + ) + + if minute and (minute < 0 or minute > 59): + module.fail_json( + msg="the `minute' parameter must be between 0 and 59: {minute}".format( + minute=minute + ) + ) + + if week_days: + wdays = set([WEEK_DAYS.index(d) for d in week_days]) + else: + wdays = set([0]) + + if month_days: + mdays = set() + for d in month_days: + if d < 1 or d > 31: + module.fail_json( + msg=( + "the days in the `month_days' parameter must be " + "between 1 and 31: {month_days}" + ).format(month_days=", ".join([str(i) for i in month_days])) + ) + mdays.add(d) + else: + mdays = set([1]) + + if collection is not None: + req_collection = module.get_collection(collection) + if not req_collection: + module.fail_json( + msg=( + "the deployment collection (in `collection') does not exist: {name}" + ).format(name=collection) + ) + else: + req_collection = None + + notifiers = [] + req_notifiers = set() + if email_notifiers is not None: + for n in email_notifiers: + obj = module.get_notifier(n["notifier"]) + if not obj: + module.fail_json( + msg=( + "the notifier method (in `email_notifiers') does not exist: {name}" + ).format(name=n["notifier"]) + ) + if obj.get("type") != "email": + module.fail_json( + msg=( + "the notifier method (in `email_notifiers') is not " + "an email notifier: {name}: {t}" + ).format(name=n["notifier"], t=obj.get("type")) + ) + id = obj.get("id") + notifier_name = obj.get("name") + subject = n.get("subject") or "" + body = n.get("body") or "" + dest = n.get("to") or [obj.get("labelDefault", "")] + notifiers.append( + { + "emailConfig": { + "notifierId": id, + "mailingLists": dest, + "customSubject": subject, + "customBody": body, + }, + "notifierName": notifier_name, + } + ) + req_notifiers.add((id, notifier_name, ",".join(sorted(dest)), subject, body)) + + # Create the object + if not configs and not new_configs: + # Verify that the collection parameter is provided + if not collection: + module.fail_json(msg="missing or empty required arguments: collection") + + # Build the data to send to the API to create the configuration + name = new_name if new_name else name + fixability = fixability if fixability else "BOTH" + severity_list = ( + list(req_severities) + if req_severities + else ["CRITICAL_VULNERABILITY_SEVERITY", "IMPORTANT_VULNERABILITY_SEVERITY"] + ) + since = since if since else "ALL_TIME" + interval = interval if interval else "UNSET" + if since == "LAST_SENT": + if not notifiers: + module.fail_json( + msg="when `since=LAST_SENT', the `email_notifiers' parameter is required" + ) + if interval == "UNSET": + module.fail_json( + msg=( + "when `since=LAST_SENT', the `interval' parameter is " + "required, and cannot be `UNSET'" + ) + ) + + new_fields = { + "name": name, + "description": description if description else "", + "type": "VULNERABILITY", + "vulnReportFilters": { + "fixability": fixability, + "severities": severity_list, + "imageTypes": list(req_image_types), + }, + "resourceScope": { + "collectionScope": { + "collectionId": req_collection.get("id", ""), + "collectionName": req_collection.get("name", ""), + } + }, + "notifiers": notifiers, + } + if since == "ALL_TIME": + new_fields["vulnReportFilters"]["allVuln"] = True + elif since == "LAST_SENT": + new_fields["vulnReportFilters"]["sinceLastSentScheduledReport"] = True + else: # since == "DATE" + new_fields["vulnReportFilters"]["sinceStartDate"] = start_date + + if interval == "UNSET": + new_fields["schedule"] = None + else: + new_fields["schedule"] = { + "intervalType": interval, + "hour": hour if hour is not None else 18, + "minute": minute if minute is not None else 0, + } + if interval == "WEEKLY": + new_fields["schedule"]["daysOfWeek"] = {"days": list(wdays)} + else: # interval == "MONTHLY": + new_fields["schedule"]["daysOfMonth"] = {"days": list(mdays)} + + resp = module.create( + "vulnerability reporting schedule", + name, + "/v2/reports/configurations", + new_fields, + auto_exit=False, + ) + module.exit_json(changed=True, id=resp.get("id", "")) + + if not configs and new_configs: + configs = new_configs + name = new_name + new_configs = new_name = None + + # A vulnerability reporting schedule with the new name already exists + if new_configs: + module.fail_json( + msg=( + "the vulnerability reporting schedule (`new_name') already exists: {name}" + ).format(name=new_name) + ) + + # Verify whether a change is needed. Go through all the configurations + # that match the given name, and select the one with the most matches. + min_changes = 100 + for config in configs: + vuln_report = config.get("vulnReportFilters", {}) + schedule = config.get("schedule") + schedule = schedule if schedule else {} + + curr_severities = set(vuln_report.get("severities", [])) + curr_image_types = set(vuln_report.get("imageTypes", [])) + + curr_notifiers = set() + for n in config.get("notifiers", []): + email_conf = n.get("emailConfig", {}) + curr_notifiers.add( + ( + email_conf.get("notifierId"), + n.get("notifierName"), + ",".join(sorted(email_conf.get("mailingLists", []))), + email_conf.get("customSubject", ""), + email_conf.get("customBody", ""), + ) + ) + + curr_wdays = set(schedule.get("daysOfWeek", {}).get("days", [])) + curr_mdays = set(schedule.get("daysOfMonth", {}).get("days", [])) + + changes = 0 + if new_name: + changes += 1 + if description is not None and description != config.get("description"): + changes += 1 + if fixability and fixability != vuln_report.get("fixability"): + changes += 1 + if severities and req_severities != curr_severities: + changes += 1 + if image_types and req_image_types != curr_image_types: + changes += 1 + if since == "ALL_TIME" and vuln_report.get("allVuln", False) is False: + changes += 1 + if ( + since == "LAST_SENT" + and vuln_report.get("sinceLastSentScheduledReport", False) is False + ): + changes += 1 + if (since == "DATE" or (since is None and date is not None)) and ( + not vuln_report.get("sinceStartDate") + or ( + date is not None + and start_date[0:10] != vuln_report.get("sinceStartDate")[0:10] + ) + ): + changes += 1 + if collection is not None and req_collection.get("id", "") != config.get( + "resourceScope", {} + ).get("collectionScope", {}).get("collectionId", ""): + changes += 1 + if interval == "UNSET" and schedule: + changes += 1 + if (interval == "WEEKLY" or (interval is None and week_days is not None)) and ( + not schedule.get("daysOfWeek") or (week_days is not None and wdays != curr_wdays) + ): + changes += 1 + if (interval == "MONTHLY" or (interval is None and month_days is not None)) and ( + not schedule.get("daysOfMonth") + or (month_days is not None and mdays != curr_mdays) + ): + changes += 1 + if hour is not None and hour != schedule.get("hour"): + changes += 1 + if minute is not None and minute != schedule.get("minute"): + changes += 1 + if email_notifiers is not None and req_notifiers != curr_notifiers: + changes += 1 + + if changes == 0: + module.exit_json(changed=False, id=config.get("id", "")) + + if changes < min_changes: + min_changes = changes + obj = config + + # Update the configuration + id = obj.get("id", "") + name = new_name if new_name else name + new_fields = copy.deepcopy(obj) + if new_name: + new_fields["name"] = new_name + if description is not None: + new_fields["description"] = description + if fixability: + new_fields["vulnReportFilters"]["fixability"] = fixability + if severities: + new_fields["vulnReportFilters"]["severities"] = list(req_severities) + if image_types: + new_fields["vulnReportFilters"]["imageTypes"] = list(req_image_types) + if since == "ALL_TIME": + new_fields["vulnReportFilters"].pop("sinceLastSentScheduledReport", None) + new_fields["vulnReportFilters"].pop("sinceStartDate", None) + new_fields["vulnReportFilters"]["allVuln"] = True + if since == "LAST_SENT": + new_fields["vulnReportFilters"].pop("allVuln", None) + new_fields["vulnReportFilters"].pop("sinceStartDate", None) + new_fields["vulnReportFilters"]["sinceLastSentScheduledReport"] = True + if since == "DATE" or (not since and date is not None): + new_fields["vulnReportFilters"].pop("allVuln", None) + new_fields["vulnReportFilters"].pop("sinceLastSentScheduledReport", None) + if "sinceStartDate" not in new_fields["vulnReportFilters"] or date is not None: + new_fields["vulnReportFilters"]["sinceStartDate"] = start_date + if collection is not None: + new_fields["resourceScope"] = { + "collectionScope": { + "collectionId": req_collection.get("id", ""), + "collectionName": req_collection.get("name", ""), + } + } + if interval == "UNSET": + new_fields["schedule"] = None + if interval == "WEEKLY" or (not interval and week_days is not None): + if not new_fields["schedule"]: + new_fields["schedule"] = { + "intervalType": "WEEKLY", + "hour": hour if hour is not None else 18, + "minute": minute if minute is not None else 0, + "daysOfWeek": {"days": list(wdays)}, + } + else: + new_fields["schedule"].pop("daysOfMonth", None) + new_fields["schedule"]["intervalType"] = "WEEKLY" + if "daysOfWeek" not in new_fields["schedule"] or week_days is not None: + new_fields["schedule"]["daysOfWeek"] = {"days": list(wdays)} + if interval == "MONTHLY" or (not interval and month_days is not None): + if not new_fields["schedule"]: + new_fields["schedule"] = { + "intervalType": "MONTHLY", + "hour": hour if hour is not None else 18, + "minute": minute if minute is not None else 0, + "daysOfMonth": {"days": list(mdays)}, + } + else: + new_fields["schedule"].pop("daysOfWeek", None) + new_fields["schedule"]["intervalType"] = "MONTHLY" + if "daysOfMonth" not in new_fields["schedule"] or month_days is not None: + new_fields["schedule"]["daysOfMonth"] = {"days": list(mdays)} + if hour is not None and new_fields["schedule"]: + new_fields["schedule"]["hour"] = hour + if minute is not None and new_fields["schedule"]: + new_fields["schedule"]["minute"] = minute + if email_notifiers is not None: + new_fields["notifiers"] = notifiers + + module.unconditional_update( + "vulnerability reporting schedule", + new_fields["name"], + "/v2/reports/configurations/{id}".format(id=id), + new_fields, + ) + module.exit_json(changed=True, id=id) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/rhacs_compliance_schedule/files/gcs_sa_key.json b/tests/integration/targets/rhacs_compliance_schedule/files/gcs_sa_key.json new file mode 100644 index 0000000..ce04f9d --- /dev/null +++ b/tests/integration/targets/rhacs_compliance_schedule/files/gcs_sa_key.json @@ -0,0 +1,9 @@ +{ + "type": "service_account", + "private_key_id": "abc", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDY3E8o1NEFcjMM\nHW/5ZfFJw29/8NEqpViNjQIx95Xx5KDtJ+nWn9+OW0uqsSqKlKGhAdAo+Q6bjx2c\nuXVsXTu7XrZUY5Kltvj94DvUa1wjNXs606r/RxWTJ58bfdC+gLLxBfGnB6CwK0YQ\nxnfpjNbkUfVVzO0MQD7UP0Hl5ZcY0Puvxd/yHuONQn/rIAieTHH1pqgW+zrH/y3c\n59IGThC9PPtugI9ea8RSnVj3PWz1bX2UkCDpy9IRh9LzJLaYYX9RUd7++dULUlat\nAaXBh1U6emUDzhrIsgApjDVtimOPbmQWmX1S60mqQikRpVYZ8u+NDD+LNw+/Eovn\nxCj2Y3z1AgMBAAECggEAWDBzoqO1IvVXjBA2lqId10T6hXmN3j1ifyH+aAqK+FVl\nGjyWjDj0xWQcJ9ync7bQ6fSeTeNGzP0M6kzDU1+w6FgyZqwdmXWI2VmEizRjwk+/\n/uLQUcL7I55Dxn7KUoZs/rZPmQDxmGLoue60Gg6z3yLzVcKiDc7cnhzhdBgDc8vd\nQorNAlqGPRnm3EqKQ6VQp6fyQmCAxrr45kspRXNLddat3AMsuqImDkqGKBmF3Q1y\nxWGe81LphUiRqvqbyUlh6cdSZ8pLBpc9m0c3qWPKs9paqBIvgUPlvOZMqec6x4S6\nChbdkkTRLnbsRr0Yg/nDeEPlkhRBhasXpxpMUBgPywKBgQDs2axNkFjbU94uXvd5\nznUhDVxPFBuxyUHtsJNqW4p/ujLNimGet5E/YthCnQeC2P3Ym7c3fiz68amM6hiA\nOnW7HYPZ+jKFnefpAtjyOOs46AkftEg07T9XjwWNPt8+8l0DYawPoJgbM5iE0L2O\nx8TU1Vs4mXc+ql9F90GzI0x3VwKBgQDqZOOqWw3hTnNT07Ixqnmd3dugV9S7eW6o\nU9OoUgJB4rYTpG+yFqNqbRT8bkx37iKBMEReppqonOqGm4wtuRR6LSLlgcIU9Iwx\nyfH12UWqVmFSHsgZFqM/cK3wGev38h1WBIOx3/djKn7BdlKVh8kWyx6uC8bmV+E6\nOoK0vJD6kwKBgHAySOnROBZlqzkiKW8c+uU2VATtzJSydrWm0J4wUPJifNBa/hVW\ndcqmAzXC9xznt5AVa3wxHBOfyKaE+ig8CSsjNyNZ3vbmr0X04FoV1m91k2TeXNod\njMTobkPThaNm4eLJMN2SQJuaHGTGERWC0l3T18t+/zrDMDCPiSLX1NAvAoGBAN1T\nVLJYdjvIMxf1bm59VYcepbK7HLHFkRq6xMJMZbtG0ryraZjUzYvB4q4VjHk2UDiC\nlhx13tXWDZH7MJtABzjyg+AI7XWSEQs2cBXACos0M4Myc6lU+eL+iA+OuoUOhmrh\nqmT8YYGu76/IBWUSqWuvcpHPpwl7871i4Ga/I3qnAoGBANNkKAcMoeAbJQK7a/Rn\nwPEJB+dPgNDIaboAsh1nZhVhN5cvdvCWuEYgOGCPQLYQF0zmTLcM+sVxOYgfy8mV\nfbNgPgsP5xmu6dw2COBKdtozw0HrWSRjACd1N4yGu75+wPCcX/gQarcjRcXXZeEa\nNtBLSfcqPULqD+h7br9lEJio\n-----END PRIVATE KEY-----\n", + "client_email": "123-abc@developer.gserviceaccount.com", + "client_id": "123-abc.apps.googleusercontent.com", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "http://localhost:8080/token" +} diff --git a/tests/integration/targets/rhacs_compliance_schedule/meta/main.yml b/tests/integration/targets/rhacs_compliance_schedule/meta/main.yml new file mode 100644 index 0000000..e978127 --- /dev/null +++ b/tests/integration/targets/rhacs_compliance_schedule/meta/main.yml @@ -0,0 +1,4 @@ +--- +dependencies: + - setup_token +... diff --git a/tests/integration/targets/rhacs_compliance_schedule/tasks/main.yml b/tests/integration/targets/rhacs_compliance_schedule/tasks/main.yml new file mode 100644 index 0000000..032601c --- /dev/null +++ b/tests/integration/targets/rhacs_compliance_schedule/tasks/main.yml @@ -0,0 +1,547 @@ +--- +- name: ERROR EXPECTED Ensure the scan is scheduled (wrong hour) + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + - ocp4-cis-node + interval: WEEKLY + week_days: + - Monday + hour: 42 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS compliance scan report + clusters: + - production + - infra + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong hour) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong hour) + +- name: ERROR EXPECTED Ensure the scan is scheduled (wrong min) + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + - ocp4-cis-node + interval: WEEKLY + week_days: + - Monday + hour: 12 + minute: 62 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS compliance scan report + clusters: + - production + - infra + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong minute) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong minute) + +- name: ERROR EXPECTED Ensure the scan is scheduled (wrong month day) + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + - ocp4-cis-node + interval: MONTHLY + month_days: + - 1 + - 15 + - 32 + hour: 12 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS compliance scan report + clusters: + - production + - infra + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong month day) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong month day) + +- name: ERROR EXPECTED Ensure the scan is scheduled (unknown cluster) + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + - ocp4-cis-node + interval: WEEKLY + week_days: + - Monday + hour: 12 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS compliance scan report + clusters: + - not such cluster + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (unknown cluster) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (unknown cluster) + +- name: ERROR EXPECTED Ensure the scan is scheduled (unknown notifier) + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + - ocp4-cis-node + interval: WEEKLY + week_days: + - Monday + hour: 12 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS compliance scan report + clusters: + - production + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (unknown notifier) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (unknown notifier) + +- name: Ensure the Google Cloud SCC notification method exists + herve4m.rhacs_configuration.rhacs_notifier_integration: + name: ansible google conf 100 + type: google + google: + source_id: organizations/4242/sources/4242 + use_workload_id: false + service_account_key: "{{ lookup('ansible.builtin.file', + 'gcs_sa_key.json') }}" + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: ERROR EXPECTED Ensure the scan is scheduled (wrong notifier type) + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + - ocp4-cis-node + interval: WEEKLY + week_days: + - Monday + hour: 12 + minute: 42 + email_notifiers: + - notifier: "{{ result['id'] }}" + to: + - security@example.com + - secteam@example.com + subject: RHACS compliance scan report + clusters: + - production + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong notifier type) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong notifier type) + +- name: Ensure the Google Cloud SCC notification method is removed + herve4m.rhacs_configuration.rhacs_notifier_integration: + name: ansible google conf 100 + type: google + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + +- name: Ensure the email notification method exists + herve4m.rhacs_configuration.rhacs_notifier_integration: + name: ansible email conf 100 + type: email + email: + username: rhacsint + password: vs9mrD55NP + server: smtp.example.com:465 + from_header: Security Alerts + sender: rhacs@example.com + recipient: security@example.com + validate_certs: false + starttls: LOGIN + unauthenticated: false + annotation_key: email + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + +- name: ERROR EXPECTED Ensure the scan is scheduled (unknown profile) + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + - foobar + interval: WEEKLY + week_days: + - Monday + hour: 12 + minute: 42 + email_notifiers: + - notifier: ansible email conf 100 + to: + - security@example.com + - secteam@example.com + subject: RHACS compliance scan report + clusters: + - production + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (unknown profile) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (unknown profile) + +- name: ERROR EXPECTED Ensure the scan is scheduled (missing parameters) + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + interval: WEEKLY + week_days: + - Monday + hour: 12 + minute: 42 + email_notifiers: + - notifier: ansible email conf 100 + to: + - security@example.com + - secteam@example.com + subject: RHACS compliance scan report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (missing profiles and clusters) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (missing parameters) + +# Cannot test further in the integration environment because there is no +# cluster, the compliance operator is not installed, and no compliance +# profiles are available. +- name: Test compliance schedule configuration + when: false + block: + - name: Ensure the weekly scan is scheduled + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + interval: WEEKLY + week_days: + - Monday + hour: 4 + minute: 42 + email_notifiers: + - notifier: ansible email conf 100 + subject: Questo è l'oggetto della mail + body: "Hi,\nQuesto è un corpo di email di esempio.\n" + to: + - security@example.com + - secteam@example.com + clusters: + - production + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + + - name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + + - name: Ensure the weekly scan is scheduled (no change) 1 + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + new_name: weekly-scan + description: Weekly compliance scan + profiles: + - ocp4-cis + append_profiles: false + interval: WEEKLY + week_days: + - Monday + hour: 4 + minute: 42 + email_notifiers: + - notifier: ansible email conf 100 + to: + - security@example.com + - secteam@example.com + subject: Questo è l'oggetto della mail + body: "Hi,\nQuesto è un corpo di email di esempio.\n" + clusters: + - production + append_clusters: true + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + + - name: Ensure that the task did not change anything + ansible.builtin.assert: + that: not result['changed'] + fail_msg: The preceding task should not have changed anything + + - name: Ensure the weekly scan is scheduled (no change) 2 + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: does-not-exist + new_name: weekly-scan + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + + - name: Ensure that the task did not change anything + ansible.builtin.assert: + that: not result['changed'] + fail_msg: The preceding task should not have changed anything + + - name: Ensure the monthly scan is scheduled + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: monthly-scan + profiles: + - ocp4-cis-node + interval: MONTHLY + month_days: + - 1 + - 15 + - 25 + email_notifiers: + - notifier: ansible email conf 100 + clusters: + - production + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + + - name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + + - name: ERROR EXPECTED Ensure the monthly scan is renamed (dest exists) + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: monthly-scan + new_name: weekly-scan + profiles: + - ocp4-cis-node + month_days: + - 1 + - 15 + email_notifiers: + - notifier: ansible email conf 100 + clusters: + - production + state: present + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + + - name: Ensure that the task failed (destination exists) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (dest exists) + + - name: Ensure the monthly scan is removed + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: monthly-scan + profiles: + - ocp4-cis-node + interval: MONTHLY + month_days: + - 26 + email_notifiers: + - notifier: ansible email conf 100 + clusters: + - production + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + + - name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + + - name: Ensure the weekly scan is updated 1 + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + description: Weekly compliance scan 2 + profiles: + - ocp4-cis-node + append_profiles: true + week_days: + - Monday + - Tuesday + hour: 5 + minute: 55 + email_notifiers: + - notifier: ansible email conf 100 + subject: Tárgysor + body: "" + to: [] + clusters: [] + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + + - name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + + - name: Ensure the weekly scan is updated 2 + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + new_name: weekly-scan + profiles: + - ocp4-cis-node + append_profiles: false + interval: MONTHLY + hour: 4 + email_notifiers: [] + clusters: + - production + append_clusters: false + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + + - name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + + - name: Ensure the weekly scan is updated 3 + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + new_name: weekly-scan + interval: DAILY + minute: 33 + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + + - name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + + - name: Ensure the weekly scan is removed + herve4m.rhacs_configuration.rhacs_compliance_schedule: + name: weekly-scan + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + + - name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + +- name: Ensure the email notification method is removed + herve4m.rhacs_configuration.rhacs_notifier_integration: + name: ansible email conf 100 + type: email + email: + username: rhacsint + password: vs9mrD55NP + server: smtp.example.com:465 + from_header: Security Alerts + sender: rhacs@example.com + recipient: security@example.com + validate_certs: false + starttls: LOGIN + unauthenticated: false + annotation_key: email + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" +... diff --git a/tests/integration/targets/rhacs_notifier_integration/tasks/main.yml b/tests/integration/targets/rhacs_notifier_integration/tasks/main.yml index 6c2219e..fe1c0fe 100644 --- a/tests/integration/targets/rhacs_notifier_integration/tasks/main.yml +++ b/tests/integration/targets/rhacs_notifier_integration/tasks/main.yml @@ -542,7 +542,7 @@ type: splunk splunk: url: https://splunk.example.com:8088/services/collector/event - token: B5A79AAD-D822-46CC-80D1-819F80D7BFB0 + token: B5A79AAD-D822-46CC-80D1-819F80D7BFB0 # notsecret validate_certs: false truncate: 5000 audit_logging: true @@ -1260,7 +1260,7 @@ type: splunk splunk: url: https://splunk.example.com:8088/services/collector/event - token: B5A79AAD-D822-46CC-80D1-819F80D7BFB0 + token: B5A79AAD-D822-46CC-80D1-819F80D7BFB0 # notsecret validate_certs: false truncate: 5000 audit_logging: true diff --git a/tests/integration/targets/rhacs_report_schedule/files/gcs_sa_key.json b/tests/integration/targets/rhacs_report_schedule/files/gcs_sa_key.json new file mode 100644 index 0000000..ce04f9d --- /dev/null +++ b/tests/integration/targets/rhacs_report_schedule/files/gcs_sa_key.json @@ -0,0 +1,9 @@ +{ + "type": "service_account", + "private_key_id": "abc", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDY3E8o1NEFcjMM\nHW/5ZfFJw29/8NEqpViNjQIx95Xx5KDtJ+nWn9+OW0uqsSqKlKGhAdAo+Q6bjx2c\nuXVsXTu7XrZUY5Kltvj94DvUa1wjNXs606r/RxWTJ58bfdC+gLLxBfGnB6CwK0YQ\nxnfpjNbkUfVVzO0MQD7UP0Hl5ZcY0Puvxd/yHuONQn/rIAieTHH1pqgW+zrH/y3c\n59IGThC9PPtugI9ea8RSnVj3PWz1bX2UkCDpy9IRh9LzJLaYYX9RUd7++dULUlat\nAaXBh1U6emUDzhrIsgApjDVtimOPbmQWmX1S60mqQikRpVYZ8u+NDD+LNw+/Eovn\nxCj2Y3z1AgMBAAECggEAWDBzoqO1IvVXjBA2lqId10T6hXmN3j1ifyH+aAqK+FVl\nGjyWjDj0xWQcJ9ync7bQ6fSeTeNGzP0M6kzDU1+w6FgyZqwdmXWI2VmEizRjwk+/\n/uLQUcL7I55Dxn7KUoZs/rZPmQDxmGLoue60Gg6z3yLzVcKiDc7cnhzhdBgDc8vd\nQorNAlqGPRnm3EqKQ6VQp6fyQmCAxrr45kspRXNLddat3AMsuqImDkqGKBmF3Q1y\nxWGe81LphUiRqvqbyUlh6cdSZ8pLBpc9m0c3qWPKs9paqBIvgUPlvOZMqec6x4S6\nChbdkkTRLnbsRr0Yg/nDeEPlkhRBhasXpxpMUBgPywKBgQDs2axNkFjbU94uXvd5\nznUhDVxPFBuxyUHtsJNqW4p/ujLNimGet5E/YthCnQeC2P3Ym7c3fiz68amM6hiA\nOnW7HYPZ+jKFnefpAtjyOOs46AkftEg07T9XjwWNPt8+8l0DYawPoJgbM5iE0L2O\nx8TU1Vs4mXc+ql9F90GzI0x3VwKBgQDqZOOqWw3hTnNT07Ixqnmd3dugV9S7eW6o\nU9OoUgJB4rYTpG+yFqNqbRT8bkx37iKBMEReppqonOqGm4wtuRR6LSLlgcIU9Iwx\nyfH12UWqVmFSHsgZFqM/cK3wGev38h1WBIOx3/djKn7BdlKVh8kWyx6uC8bmV+E6\nOoK0vJD6kwKBgHAySOnROBZlqzkiKW8c+uU2VATtzJSydrWm0J4wUPJifNBa/hVW\ndcqmAzXC9xznt5AVa3wxHBOfyKaE+ig8CSsjNyNZ3vbmr0X04FoV1m91k2TeXNod\njMTobkPThaNm4eLJMN2SQJuaHGTGERWC0l3T18t+/zrDMDCPiSLX1NAvAoGBAN1T\nVLJYdjvIMxf1bm59VYcepbK7HLHFkRq6xMJMZbtG0ryraZjUzYvB4q4VjHk2UDiC\nlhx13tXWDZH7MJtABzjyg+AI7XWSEQs2cBXACos0M4Myc6lU+eL+iA+OuoUOhmrh\nqmT8YYGu76/IBWUSqWuvcpHPpwl7871i4Ga/I3qnAoGBANNkKAcMoeAbJQK7a/Rn\nwPEJB+dPgNDIaboAsh1nZhVhN5cvdvCWuEYgOGCPQLYQF0zmTLcM+sVxOYgfy8mV\nfbNgPgsP5xmu6dw2COBKdtozw0HrWSRjACd1N4yGu75+wPCcX/gQarcjRcXXZeEa\nNtBLSfcqPULqD+h7br9lEJio\n-----END PRIVATE KEY-----\n", + "client_email": "123-abc@developer.gserviceaccount.com", + "client_id": "123-abc.apps.googleusercontent.com", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "http://localhost:8080/token" +} diff --git a/tests/integration/targets/rhacs_report_schedule/meta/main.yml b/tests/integration/targets/rhacs_report_schedule/meta/main.yml new file mode 100644 index 0000000..e978127 --- /dev/null +++ b/tests/integration/targets/rhacs_report_schedule/meta/main.yml @@ -0,0 +1,4 @@ +--- +dependencies: + - setup_token +... diff --git a/tests/integration/targets/rhacs_report_schedule/tasks/main.yml b/tests/integration/targets/rhacs_report_schedule/tasks/main.yml new file mode 100644 index 0000000..e475c4d --- /dev/null +++ b/tests/integration/targets/rhacs_report_schedule/tasks/main.yml @@ -0,0 +1,813 @@ +--- +- name: Ensure the supporting collection exists + herve4m.rhacs_configuration.rhacs_collection: + name: Collection 100 + description: Deployments that handle financial information + rules: + deployments: + name_matchings: + - match_type: REGEX + value: ".*-cc$" + - match_type: EXACT + value: credit-cards + namespaces: + label_matchings: + - or_values: + - "app=cc" + - "app=financial" + - or_values: + - "env=production" + clusters: + name_matchings: + - match_type: EXACT + value: production + state: present + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: collection + +- name: ERROR EXPECTED Ensure the report is scheduled (wrong hour) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + since: DATE + date: "2024-05-25" + collection: "{{ collection['id'] }}" + interval: WEEKLY + week_days: + - Saturday + - Sunday + hour: 42 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong hour) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong hour) + +- name: ERROR EXPECTED Ensure the report is scheduled (wrong minute) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + since: DATE + date: "2024-05-25" + collection: "{{ collection['id'] }}" + interval: WEEKLY + week_days: + - Saturday + - Sunday + hour: 2 + minute: 4242 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong minute) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong minute) + +- name: ERROR EXPECTED Ensure the report is scheduled (wrong date) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + since: DATE + date: "2024-25-05" + collection: "{{ collection['id'] }}" + interval: WEEKLY + week_days: + - Saturday + - Sunday + hour: 2 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong date) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong date) + +- name: ERROR EXPECTED Ensure the report is scheduled (wrong month day) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + since: DATE + date: "2024-05-25" + collection: "{{ collection['id'] }}" + interval: MONTHLY + month_days: + - 1 + - 15 + - 32 + hour: 2 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong month day) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong month day) + +- name: ERROR EXPECTED Ensure the report is scheduled (unknown collection) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + since: DATE + date: "2024-05-25" + collection: Does not exist + interval: WEEKLY + week_days: + - Saturday + - Sunday + hour: 2 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (unknown collection) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (unknown collection) + +- name: ERROR EXPECTED Ensure the report is scheduled (unknown notifier) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + since: DATE + date: "2024-05-25" + collection: "{{ collection['id'] }}" + interval: WEEKLY + week_days: + - Saturday + - Sunday + hour: 2 + minute: 42 + email_notifiers: + - notifier: email notifications + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (unknown notifier) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (unknown notifier) + +- name: Ensure the Google Cloud SCC notification method exists + herve4m.rhacs_configuration.rhacs_notifier_integration: + name: ansible google conf 200 + type: google + google: + source_id: organizations/4242/sources/4242 + use_workload_id: false + service_account_key: "{{ lookup('ansible.builtin.file', + 'gcs_sa_key.json') }}" + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: ERROR EXPECTED Ensure the report is scheduled (wrong notifier type) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + since: DATE + date: "2024-05-25" + collection: "{{ collection['id'] }}" + interval: WEEKLY + week_days: + - Saturday + - Sunday + hour: 2 + minute: 42 + email_notifiers: + - notifier: "{{ result['id'] }}" + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (wrong notifier type) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (wrong notifier type) + +- name: Ensure the Google Cloud SCC notification method is removed + herve4m.rhacs_configuration.rhacs_notifier_integration: + name: ansible google conf 200 + type: google + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + +- name: Ensure the email notification method exists + herve4m.rhacs_configuration.rhacs_notifier_integration: + name: ansible email conf 200 + type: email + email: + username: rhacsint + password: vs9mrD55NP + server: smtp.example.com:465 + from_header: Security Alerts + sender: rhacs@example.com + recipient: security@example.com + validate_certs: false + starttls: LOGIN + unauthenticated: false + annotation_key: email + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + +- name: ERROR EXPECTED Ensure the report is scheduled (missing parameter) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + since: DATE + date: "2024-05-25" + interval: WEEKLY + week_days: + - Saturday + - Sunday + hour: 2 + minute: 42 + email_notifiers: + - notifier: ansible email conf 200 + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (missing collection parameter) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (missing parameter) + +- name: ERROR EXPECTED Ensure the report is scheduled 1 (missing notifier) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + collection: "{{ collection['id'] }}" + since: LAST_SENT + interval: WEEKLY + week_days: + - Saturday + - Sunday + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (missing notifier) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (missing notifier) + +- name: ERROR EXPECTED Ensure the report is scheduled 1 (missing schedule) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + collection: "{{ collection['id'] }}" + since: LAST_SENT + interval: UNSET + email_notifiers: + - notifier: ansible email conf 200 + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (missing schedule) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (missing schedule) + +- name: Ensure the report is scheduled 1 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + collection: "{{ collection['id'] }}" + since: DATE + date: "2024-05-25" + interval: WEEKLY + week_days: + - Saturday + - Sunday + hour: 2 + minute: 42 + email_notifiers: + - notifier: ansible email conf 200 + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + +- name: Ensure the report is scheduled 1 (no change) 1 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + new_name: Weekly report 1 + description: Weekly report + fixability: FIXABLE + severities: + - CRITICAL + - CRITICAL + - IMPORTANT + image_types: + - DEPLOYED + collection: "{{ collection['id'] }}" + since: DATE + date: "2024-05-25" + interval: WEEKLY + week_days: + - Sunday + - Saturday + - Sunday + hour: 2 + minute: 42 + email_notifiers: + - notifier: ansible email conf 200 + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did not change anything + ansible.builtin.assert: + that: not result['changed'] + fail_msg: The preceding task should not have changed anything + +- name: Ensure the report is scheduled 1 (no change) 2 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + state: present + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did not change anything + ansible.builtin.assert: + that: not result['changed'] + fail_msg: The preceding task should not have changed anything + +- name: Ensure the report is scheduled 1 (no change) 3 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + date: "2024-05-25" + week_days: + - Saturday + - Sunday + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did not change anything + ansible.builtin.assert: + that: not result['changed'] + fail_msg: The preceding task should not have changed anything + +- name: Ensure the report is scheduled 1 (no change) 4 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + since: DATE + interval: WEEKLY + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did not change anything + ansible.builtin.assert: + that: not result['changed'] + fail_msg: The preceding task should not have changed anything + +- name: Ensure the report schedule 1 is updated 1 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: does not exist + new_name: Weekly report 1 + description: Weekly report update 1 + fixability: NOT_FIXABLE + severities: + - IMPORTANT + - CRITICAL + - MODERATE + image_types: + - WATCHED + collection: Collection 100 + since: ALL_TIME + date: "2024-06-01" + interval: MONTHLY + month_days: + - 1 + - 15 + hour: 14 + minute: 30 + email_notifiers: + - notifier: ansible email conf 200 + to: [] + subject: RHACS report update 1 + state: present + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + +- name: Ensure the report schedule 1 is updated 2 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report update 1 + fixability: NOT_FIXABLE + severities: + - IMPORTANT + - CRITICAL + - MODERATE + image_types: + - WATCHED + collection: Collection 100 + since: LAST_SENT + date: "2024-06-01" + interval: WEEKLY + email_notifiers: + - notifier: ansible email conf 200 + to: [] + subject: RHACS report update 1 + state: present + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + +- name: Ensure the report schedule 1 is updated 3 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + description: Weekly report update 1 + fixability: NOT_FIXABLE + severities: + - IMPORTANT + - CRITICAL + - MODERATE + image_types: + - WATCHED + collection: Collection 100 + since: DATE + date: "2024-06-01" + interval: WEEKLY + email_notifiers: + - notifier: ansible email conf 200 + to: [] + subject: RHACS report update 1 + state: present + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + +- name: Ensure the report is scheduled 2 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 2 + collection: "{{ collection['id'] }}" + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + +# Rename and update +- name: ERROR EXPECTED Ensure the report 2 is renamed to 1 (existing new_name) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 2 + new_name: Weekly report 1 + interval: MONTHLY + month_days: + - 1 + - 15 + hour: 14 + minute: 30 + email_notifiers: + - notifier: ansible email conf 200 + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + ignore_errors: true + register: result + +- name: Ensure that the task failed (existing new_name) + ansible.builtin.assert: + that: result['failed'] + fail_msg: The preceding task should have failed (existing new_name) + +- name: Ensure the report is renamed 3 + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 2 + new_name: Weekly report 3 + interval: MONTHLY + month_days: + - 1 + - 15 + hour: 14 + minute: 30 + email_notifiers: + - notifier: ansible email conf 200 + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + +- name: Ensure the report 2 is removed (no change) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 2 + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did not change anything + ansible.builtin.assert: + that: not result['changed'] + fail_msg: The preceding task should not have changed anything + +# Delete +- name: Ensure the report 1 is removed + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + +- name: Ensure the report 1 is removed (no change) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 1 + new_name: foo bar + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did not change anything + ansible.builtin.assert: + that: not result['changed'] + fail_msg: The preceding task should not have changed anything + +- name: Ensure the report 3 is removed + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 3 + description: Weekly report + fixability: FIXABLE + severities: + - IMPORTANT + - CRITICAL + image_types: + - DEPLOYED + collection: "{{ collection['id'] }}" + since: DATE + date: "2024-05-25" + interval: WEEKLY + week_days: + - Saturday + - Sunday + hour: 2 + minute: 42 + email_notifiers: + - notifier: ansible email conf 200 + to: + - security@example.com + - secteam@example.com + subject: RHACS report report + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did change something + ansible.builtin.assert: + that: result['changed'] + fail_msg: The preceding task should have changed something + +- name: Ensure the report 3 is removed (no change) + herve4m.rhacs_configuration.rhacs_report_schedule: + name: Weekly report 3 + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + register: result + +- name: Ensure that the task did not change anything + ansible.builtin.assert: + that: not result['changed'] + fail_msg: The preceding task should not have changed anything + +- name: Ensure the email notification method is removed + herve4m.rhacs_configuration.rhacs_notifier_integration: + name: ansible email conf 200 + type: email + email: + username: rhacsint + password: vs9mrD55NP + server: smtp.example.com:465 + from_header: Security Alerts + sender: rhacs@example.com + recipient: security@example.com + validate_certs: false + starttls: LOGIN + unauthenticated: false + annotation_key: email + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" + +- name: Ensure the supporting collection is removed + herve4m.rhacs_configuration.rhacs_collection: + name: Collection 100 + state: absent + skip_validate_certs: true + rhacs_host: "{{ rhacs_host }}" + rhacs_token: "{{ rhacs_token }}" +...