> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# OCI Monitoring Notification Topics Should Have Active Subscriptions

### More Info:

Notification topics must have at least one active subscription (e.g., Email, Slack, PagerDuty). An un-subscribed topic creates a black hole where critical security alerts are dropped.

### Risk Level

Medium

### Address

Compliance, Security

### Compliance Standards

* APRA CPS 234 (Australia)
* AWS Well Architected Framework
* BSI C5 (Germany)
* Brazil LGPD
* CCPA / CPRA (California)
* CIS Critical Security Controls v8
* CMMC 2.0
* CSA Cloud Controls Matrix v4
* DPDPA
* Digital Operational Resilience Act (EU)
* ISO/IEC 27017
* ISO/IEC 27018
* ISO/IEC 27701
* KSA PDPL
* MAS Technology Risk Management (Singapore)
* MITRE ATT\&CK (Cloud)
* NIS2 Directive
* NIST SP 800-171
* NYDFS 23 NYCRR 500
* SWIFT Customer Security Controls Framework
* Sarbanes-Oxley IT General Controls
* UK NCSC Cyber Assessment Framework

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        To remediate “Monitoring Notification Topics Should Have Active Subscriptions” in OCI Alerting using the OCI Console, you need to add and confirm at least one active subscription to each Notification Topic used by alarms.

        ### 1. Identify the affected Notification Topics

        1. Sign in to the **OCI Console**.
        2. From the left menu, go to **Observability & Management → Alarms**.
        3. Check your alarms and note the **Notification Topic (OCID or name)** used in each alarm that is flagged in your scan/report.

        ### 2. Open the Notification Topic

        1. In the Console, go to **Developer Services → Application Integration → Notifications**\
           (in some UIs: **Application Integration → Notifications** directly).
        2. Ensure the correct **Compartment** is selected.
        3. Click **Topics**.
        4. Locate and click the **Topic** used by your alarm (by name or OCID).

        ### 3. Create a Subscription for the Topic

        1. Inside the Topic details page, go to the **Subscriptions** tab.
        2. Click **Create Subscription**.
        3. Choose a **Protocol**, for example:
           * **Email**
           * **PagerDuty**
           * **Slack**
           * **HTTPS** (custom webhook)
           * **Function** (OCI Functions)
        4. Enter the appropriate **endpoint**:
           * Email: the email address that should receive alerts.
           * HTTPS: the webhook URL.
           * Slack/PagerDuty: the integration endpoint.
        5. Click **Create**.

        ### 4. Confirm/Activate the Subscription

        The subscription must be confirmed for it to be considered active.

        **For Email:**

        1. The specified address receives a confirmation email from OCI.
        2. Open the email and click the **Confirm subscription** link.
        3. After confirmation, go back to the Topic → **Subscriptions** tab and verify the **Status** is **Active**.

        **For HTTPS/Slack/PagerDuty:**

        1. Ensure the endpoint correctly responds to OCI’s confirmation/handshake (if required).
        2. Check that the subscription status in OCI moves from **Pending** to **Active**.
           * If it remains **Pending**, verify networking, SSL certificates, and endpoint behavior.

        ### 5. (Optional) Test the Alert Path

        1. From the Topic page, click **Publish Message** (or **Publish to Topic**) if available.
        2. Send a test message and confirm it reaches the configured endpoint (email, webhook, etc.).

        ### 6. Re‑run Compliance/Scan

        Re-run your security/compliance tool or check after its next cycle to verify the finding is cleared: the topic now has at least one **Active** subscription.
      </Accordion>

      <Accordion title="Using CLI">
        Below are concise, CLI-focused steps to identify and remediate “OCI Monitoring Notification Topics Should Have Active Subscriptions” using the OCI CLI.

        ***

        ## 1. Prerequisites

        * OCI CLI installed and configured (`oci setup config` done).
        * OCID of the **compartment** where topics exist.
        * Appropriate IAM permissions for Notifications and Monitoring.

        Assume:

        ```bash theme={null}
        COMPARTMENT_OCID="ocid1.compartment.oc1..xxxxx"
        REGION="us-ashburn-1"
        ```

        ***

        ## 2. Find Notification Topics

        ```bash theme={null}
        oci ons topic list \
          --compartment-id "$COMPARTMENT_OCID" \
          --region "$REGION" \
          --all \
          --output table
        ```

        This shows all topics and their OCIDs.

        ***

        ## 3. Check Topics for Active Subscriptions

        For each topic, check subscriptions and their lifecycle state:

        ```bash theme={null}
        TOPIC_OCID="ocid1.onstopic.oc1..xxxxx"

        oci ons subscription list \
          --topic-id "$TOPIC_OCID" \
          --region "$REGION" \
          --all \
          --output table
        ```

        Look at `lifecycle-state`:

        * `PENDING` – not confirmed (email/SMS not activated).
        * `ACTIVE` – good.
        * No subscriptions or none `ACTIVE` – **non-compliant**.

        To find non-compliant topics programmatically:

        ```bash theme={null}
        oci ons topic list \
          --compartment-id "$COMPARTMENT_OCID" \
          --region "$REGION" \
          --all \
          --raw-output \
          --query "data[].{\"name\":name,\"id\":id}" > topics.json

        while read -r line; do
          TOPIC_ID=$(echo "$line" | jq -r '.id')
          NAME=$(echo "$line" | jq -r '.name')

          ACTIVE_COUNT=$(oci ons subscription list \
            --topic-id "$TOPIC_ID" \
            --region "$REGION" \
            --all \
            --raw-output \
            --query "length(data[?\"lifecycle-state\"=='ACTIVE'])")

          if [ "$ACTIVE_COUNT" -eq 0 ]; then
            echo "Topic with no ACTIVE subscriptions: $NAME ($TOPIC_ID)"
          fi
        done <<<"$(jq -c '.[]' topics.json)"
        ```

        ***

        ## 4. Create a Subscription (Remediation)

        ### 4.1 Email Subscription Example

        ```bash theme={null}
        TOPIC_OCID="ocid1.onstopic.oc1..xxxxx"
        EMAIL_ADDRESS="alerts@example.com"

        oci ons subscription create \
          --topic-id "$TOPIC_OCID" \
          --protocol "EMAIL" \
          --subscription-endpoint "$EMAIL_ADDRESS" \
          --region "$REGION" \
          --wait-for-state ACTIVE \
          --wait-interval-seconds 5 \
          --max-wait-seconds 300
        ```

        Notes:

        * The subscription will initially be `PENDING`.
        * The recipient must click the confirmation link in the email for it to become `ACTIVE`.
        * Using `--wait-for-state ACTIVE` only works once the confirmation is done; otherwise it will time out.

        If you prefer not to wait:

        ```bash theme={null}
        oci ons subscription create \
          --topic-id "$TOPIC_OCID" \
          --protocol "EMAIL" \
          --subscription-endpoint "$EMAIL_ADDRESS" \
          --region "$REGION" \
          --output table
        ```

        ### 4.2 Other Protocols (e.g., HTTPS)

        ```bash theme={null}
        oci ons subscription create \
          --topic-id "$TOPIC_OCID" \
          --protocol "HTTPS" \
          --subscription-endpoint "https://your-endpoint.example.com/notify" \
          --region "$REGION" \
          --output table
        ```

        ***

        ## 5. Confirm Subscription Is Active

        After confirming (via email or endpoint), check status:

        ```bash theme={null}
        oci ons subscription list \
          --topic-id "$TOPIC_OCID" \
          --region "$REGION" \
          --all \
          --query "data[].{\"endpoint\":\"endpoint\",\"protocol\":\"protocol\",\"state\":\"lifecycle-state\"}" \
          --output table
        ```

        Ensure at least one subscription shows `state: ACTIVE`.

        ***

        ## 6. (Optional) Bulk Remediation: Add a Standard Email to All Non-Compliant Topics

        ```bash theme={null}
        DEFAULT_EMAIL="alerts@example.com"

        oci ons topic list \
          --compartment-id "$COMPARTMENT_OCID" \
          --region "$REGION" \
          --all \
          --raw-output \
          --query "data[].id" > topic_ids.txt

        while read -r TOPIC_ID; do
          ACTIVE_COUNT=$(oci ons subscription list \
            --topic-id "$TOPIC_ID" \
            --region "$REGION" \
            --all \
            --raw-output \
            --query "length(data[?\"lifecycle-state\"=='ACTIVE'])")

          if [ "$ACTIVE_COUNT" -eq 0 ]; then
            echo "Creating subscription on topic $TOPIC_ID"
            oci ons subscription create \
              --topic-id "$TOPIC_ID" \
              --protocol "EMAIL" \
              --subscription-endpoint "$DEFAULT_EMAIL" \
              --region "$REGION" \
              --output table
          fi
        done < topic_ids.txt
        ```

        ***

        ## 7. Validate Monitoring Alarms Use These Topics

        List alarms and their destinations:

        ```bash theme={null}
        oci monitoring alarm list \
          --compartment-id "$COMPARTMENT_OCID" \
          --region "$REGION" \
          --all \
          --query "data[].{\"name\":display-name,\"topic\":destinations}" \
          --output table
        ```

        Ensure each `topic` OCID has at least one `ACTIVE` subscription as verified above.

        ***

        These steps will remediate the policy “Notification Topics Should Have Active Subscriptions” for OCI Monitoring Alerting using the OCI CLI.
      </Accordion>

      <Accordion title="Using Python">
        To remediate “OCI Monitoring Notification Topics Should Have Active Subscriptions” using Python, you essentially need to:

        1. Find notification topics used by Monitoring (Alarm) rules.
        2. Check each topic’s subscriptions for at least one `ACTIVE` subscription.
        3. For topics with no active subscriptions, create a new subscription (e.g., email or HTTPS).

        Below is a concise step‑by‑step guide and example Python script using the OCI Python SDK.

        ***

        ## 1. Prerequisites

        1. **Python and SDK**
           ```bash theme={null}
           pip install oci
           ```

        2. **OCI Config**
           * Have `~/.oci/config` configured with:
             * tenancy
             * user
             * fingerprint
             * key\_file
             * region
           * Or use instance principal / resource principal if running on OCI.

        3. **Permissions**
           The principal (user or instance) must have policies that allow:
           ```text theme={null}
           allow group <group-name> to read metrics-family in tenancy
           allow group <group-name> to manage ons-family in tenancy
           allow group <group-name> to read alarms in tenancy
           ```

        ***

        ## 2. High-Level Logic

        1. List all alarms (or alarms in a specific compartment).
        2. Extract the `destinations` (OCIDs of notification topics) from each alarm.
        3. For each topic:
           * List subscriptions.
           * Check if any subscription has `lifecycle_state == "ACTIVE"`.
        4. If no active subscription:
           * Create a new subscription with:
             * `protocol`: e.g., `"EMAIL"` or `"HTTPS"`.
             * `endpoint`: your email or webhook URL.

        > Note: Email subscriptions require manual confirmation via the email link. Programmatically you can only create them; activation happens when the user clicks the link.

        ***

        ## 3. Example Python Script

        Adjust the values in the `CONFIG` section to your environment.

        ```python theme={null}
        import oci
        from oci.monitoring import MonitoringClient
        from oci.ons import NotificationControlPlaneClient
        from oci.monitoring.models import ListAlarmsDetails

        # ---------------- CONFIG ----------------
        COMPARTMENT_OCID = "<your-compartment-ocid>"  # alarms compartment
        REGION = "<your-region>"                      # e.g., "us-ashburn-1"

        # For remediation: what kind of subscription to create for empty topics
        DEFAULT_SUB_PROTOCOL = "EMAIL"                # or "HTTPS"
        DEFAULT_SUB_ENDPOINT = "your-email@example.com"  # or webhook URL
        # ----------------------------------------


        def get_client(config, service_client):
            return service_client(config=config)


        def list_alarm_topics(monitoring_client, compartment_id):
            """
            Return a set of topic OCIDs used as destinations in all alarms in the compartment.
            """
            topic_ocids = set()

            list_alarms_details = oci.monitoring.models.ListAlarmsDetails(
                compartment_id=compartment_id
            )

            response = monitoring_client.list_alarms(
                compartment_id=compartment_id,
                lifecycle_state="ACTIVE",
                limit=1000,
            )

            for alarm in response.data:
                # alarm.destinations is a list of topic OCIDs
                if alarm.destinations:
                    for topic_ocid in alarm.destinations:
                        topic_ocids.add(topic_ocid)

            return topic_ocids


        def get_active_subscriptions(ons_client, topic_ocid):
            """
            Return a list of ACTIVE subscriptions for the given topic.
            """
            subs = []
            list_subs_response = oci.pagination.list_call_get_all_results(
                ons_client.list_subscriptions, topic_id=topic_ocid
            )

            for sub in list_subs_response.data:
                if sub.lifecycle_state == "ACTIVE":
                    subs.append(sub)

            return subs


        def create_subscription_if_needed(ons_client, topic_ocid, protocol, endpoint):
            """
            If topic has no ACTIVE subscriptions, create one using the given protocol and endpoint.
            """
            active_subs = get_active_subscriptions(ons_client, topic_ocid)

            if active_subs:
                print(f"Topic {topic_ocid} already has {len(active_subs)} ACTIVE subscription(s).")
                return

            print(f"Topic {topic_ocid} has no ACTIVE subscriptions. Creating one...")
            create_sub_details = oci.ons.models.CreateSubscriptionDetails(
                topic_id=topic_ocid,
                protocol=protocol,
                endpoint=endpoint,
            )

            response = ons_client.create_subscription(create_sub_details)
            sub = response.data
            print(f"Created subscription {sub.id} with protocol={protocol}, endpoint={endpoint}")
            print("NOTE: If protocol is EMAIL, the recipient must confirm via email for it to become ACTIVE.")


        def main():
            # Load config
            config = oci.config.from_file("~/.oci/config", "DEFAULT")
            config["region"] = REGION

            # Create clients
            monitoring_client = get_client(config, MonitoringClient)
            ons_client = get_client(config, NotificationControlPlaneClient)

            # 1. Get topics used by alarms
            topics_in_alarms = list_alarm_topics(monitoring_client, COMPARTMENT_OCID)
            print(f"Found {len(topics_in_alarms)} topic(s) referenced by alarms.")

            # 2. Check each topic and remediate if needed
            for topic_ocid in topics_in_alarms:
                create_subscription_if_needed(
                    ons_client,
                    topic_ocid,
                    DEFAULT_SUB_PROTOCOL,
                    DEFAULT_SUB_ENDPOINT,
                )


        if __name__ == "__main__":
            main()
        ```

        ***

        ## 4. How to Use

        1. Replace:
           * `COMPARTMENT_OCID`
           * `REGION`
           * `DEFAULT_SUB_PROTOCOL`
           * `DEFAULT_SUB_ENDPOINT`
        2. Ensure your OCI config/policies are correct.
        3. Run:
           ```bash theme={null}
           python remediate_oci_topics.py
           ```

        This will:

        * Inspect all active alarms in the specified compartment.
        * Identify their notification topics.
        * Ensure each of those topics has at least one ACTIVE subscription (or at least a newly created one waiting for confirmation if email).
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        # Create a notification topic (if not already managed in Terraform)
        resource "oci_ons_notification_topic" "security_alerts_topic" {
          compartment_id = var.COMPARTMENT_OCID   # replace with your compartment OCID
          name           = "security-alerts"      # replace with your topic display name
          description    = "Security alerts from OCI Monitoring"
        }

        # Add at least one active subscription to the topic (e.g., email)
        resource "oci_ons_subscription" "security_alerts_email" {
          compartment_id = var.COMPARTMENT_OCID          # replace with your compartment OCID
          topic_id       = oci_ons_notification_topic.security_alerts_topic.id

          protocol = "EMAIL"                             # or "HTTPS", "SLACK", "PAGERDUTY", etc.
          endpoint = "SECURITY_TEAM_EMAIL@example.com"   # replace with the destination address/URL

          # Optional: freeform or defined tags if your org requires them
          # freeform_tags = {
          #   Owner = "SECURITY_TEAM"
          # }
        }

        # Example: wire Monitoring Alarm to use this topic
        resource "oci_monitoring_alarm" "critical_security_alarm" {
          compartment_id = var.COMPARTMENT_OCID
          display_name   = "critical-security-alarm"
          namespace      = "oci_vcn"                     # replace as appropriate
          query          = "CpuUtilization[1m].mean() > 90"  # replace with your query
          severity       = "CRITICAL"
          is_enabled     = true

          destinations = [
            oci_ons_notification_topic.security_alerts_topic.id
          ]

          # other required arguments...
        }
        ```

        Substitute:

        * `var.COMPARTMENT_OCID` with your compartment OCID (or a literal OCID string).
        * `SECURITY_TEAM_EMAIL@example.com` with the real email (or HTTPS/Slack/PagerDuty endpoint).
        * Adjust the alarm’s `namespace` and `query` to match your actual Monitoring configuration.

        No existing topics are destroyed; adding `oci_ons_subscription` is non‑destructive and does not force replacement of the topic or alarms.

        For verification, `terraform plan` should show:

        * Creation of one or more `oci_ons_subscription` resources attached to each `oci_ons_notification_topic` that previously had no subscriptions, and
        * No planned destruction or recreation of the existing `oci_ons_notification_topic` resources.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
