OCI Events and Notifications: Building Event-Driven Operations with Terraform

Every action in OCI generates an event: a compute instance starts, a bucket is created, a security list is modified. By default those events go nowhere. With OCI Events and Notifications, you route them to Functions for automated remediation, to email for human alerting, and to Streams for audit archival. This post covers the full pattern with Terraform.

Step 1: IAM Policy and Notification Topics

resource "oci_identity_policy" "events_policy" {
  compartment_id = var.compartment_id
  name           = "oci-events-policy"
  statements = [
    "Allow service events to use stream-push in compartment id COMPARTMENT_OCID",
    "Allow service events to use ons-topics in compartment id COMPARTMENT_OCID",
    "Allow service events to use functions-family in compartment id COMPARTMENT_OCID"
  ]
}

resource "oci_ons_notification_topic" "security_alerts" {
  compartment_id = var.compartment_id
  name           = "security-alerts"
  description    = "Security and compliance event notifications"
}

resource "oci_ons_subscription" "security_email" {
  compartment_id = var.compartment_id
  topic_id       = oci_ons_notification_topic.security_alerts.id
  protocol       = "EMAIL"
  endpoint       = var.security_team_email
}

resource "oci_ons_subscription" "pagerduty" {
  compartment_id = var.compartment_id
  topic_id       = oci_ons_notification_topic.security_alerts.id
  protocol       = "HTTPS"
  endpoint       = var.pagerduty_webhook_url
}

Step 2: IAM and Network Change Detection

resource "oci_events_rule" "iam_change_alert" {
  compartment_id = var.compartment_id
  display_name   = "iam-change-security-alert"
  is_enabled     = true

  # Match any IAM policy or group modification
  condition = jsonencode({
    "eventType" = [
      "com.oraclecloud.identitycontrolplane.createpolicy",
      "com.oraclecloud.identitycontrolplane.updatepolicy",
      "com.oraclecloud.identitycontrolplane.deletepolicy",
      "com.oraclecloud.identitycontrolplane.creategroup",
      "com.oraclecloud.identitycontrolplane.addusertogroup"
    ]
  })

  actions {
    actions {
      action_type = "ONS"
      is_enabled  = true
      topic_id    = oci_ons_notification_topic.security_alerts.id
      description = "Alert security team on IAM changes"
    }
  }
}

resource "oci_events_rule" "network_change_alert" {
  compartment_id = var.compartment_id
  display_name   = "network-security-change-alert"
  is_enabled     = true

  condition = jsonencode({
    "eventType" = [
      "com.oraclecloud.virtualnetwork.createsecuritylist",
      "com.oraclecloud.virtualnetwork.updatesecuritylist",
      "com.oraclecloud.virtualnetwork.updatenetworksecuritygroup"
    ]
  })

  actions {
    actions {
      action_type = "ONS"
      is_enabled  = true
      topic_id    = oci_ons_notification_topic.security_alerts.id
    }
  }
}

Step 3: Public Bucket Auto-Remediation

resource "oci_events_rule" "public_bucket_remediation" {
  compartment_id = var.compartment_id
  display_name   = "public-bucket-auto-remediate"
  is_enabled     = true

  condition = jsonencode({
    "eventType" = ["com.oraclecloud.objectstorage.createbucket"]
    "data"      = { "publicAccessType" = ["ObjectRead", "ObjectReadWithoutList"] }
  })

  actions {
    actions {
      action_type = "FAAS"
      is_enabled  = true
      function_id = var.remediate_bucket_function_id
      description = "Disable public access automatically"
    }
    actions {
      action_type = "ONS"
      is_enabled  = true
      topic_id    = oci_ons_notification_topic.security_alerts.id
    }
  }
}

The Function receives the full event payload as its input. It reads the bucket name and namespace from the event data, calls the Object Storage API to set public_access_type = NoPublicAccess, and returns. The entire remediation takes a few seconds from event generation to bucket access being reverted, with no human involvement required.

Step 4: Tag Validation on Compute Launch

resource "oci_events_rule" "instance_launch_validate" {
  compartment_id = var.compartment_id
  display_name   = "instance-tag-validation"
  is_enabled     = true

  condition = jsonencode({
    "eventType" = ["com.oraclecloud.computeapi.launchinstance.end"]
  })

  actions {
    actions {
      action_type = "FAAS"
      is_enabled  = true
      function_id = var.tag_validator_function_id
      description = "Validate required defined tags on new instances"
    }
  }
}

Step 5: Database Lifecycle Events to Audit Stream

resource "oci_events_rule" "database_lifecycle_audit" {
  compartment_id = var.compartment_id
  display_name   = "database-lifecycle-audit"
  is_enabled     = true

  condition = jsonencode({
    "eventType" = [
      "com.oraclecloud.database.autonomous.start",
      "com.oraclecloud.database.autonomous.stop",
      "com.oraclecloud.database.autonomous.backup.end"
    ]
  })

  actions {
    actions {
      action_type = "OSS"
      is_enabled  = true
      stream_id   = var.audit_stream_id
      description = "Stream all database lifecycle events for compliance audit"
    }
  }
}

resource "oci_events_rule" "resource_deletion_alert" {
  compartment_id = var.compartment_id
  display_name   = "critical-resource-deletion"
  is_enabled     = true

  condition = jsonencode({
    "eventType" = [
      "com.oraclecloud.database.autonomous.delete",
      "com.oraclecloud.objectstorage.deletebucket",
      "com.oraclecloud.virtualnetwork.deletevcn"
    ]
  })

  actions {
    actions {
      action_type = "ONS"
      is_enabled  = true
      topic_id    = oci_ons_notification_topic.security_alerts.id
      description = "Alert on deletion of critical infrastructure resources"
    }
  }
}

Operational Notes

Events carry a processing delay of seconds to a few minutes. Do not use OCI Events for real-time control plane decisions. Use them for operational awareness, compliance audit trails, and remediation workflows where a small latency is acceptable.

Build condition expressions against real event payloads from a test environment. The OCI Console shows recent events matching a rule and the full JSON payload. Test your condition in the console before applying in Terraform, since event payloads sometimes contain undocumented fields that are more reliable to match against than the top-level event type alone.

Regards,
Osama

#OCI #OracleCloud #Events #Notifications #Terraform #IaC #EventDriven #CloudAutomation #TechBlog #Oracle #PlatformEngineering #DevOps #CloudSecurity #Remediation #OracleCloudInfrastructure #Functions #Streams #Observability #CloudGovernance #CSPM

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.