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