
OpenShift clusters generate continuous API activity – namespaces are created, workloads are deployed, and configuration changes propagate across the platform. Without a pipeline to capture and surface these changes, operations and automation teams lack real-time visibility into cluster resource lifecycle. Manual approaches such as oc get <resource> -w or custom controllers do not scale across clusters or teams.
This guide demonstrates how to build a push-based event pipeline that captures OpenShift API resource changes in real time. OpenShift Serverless (Knative Eventing) watches the Kubernetes API server through an APIServerSource, routes CloudEvents to a KafkaSink backed by Streams for Apache Kafka, and Event-Driven Ansible (EDA) consumes those messages to trigger automation in Ansible Automation Platform (AAP).
This guide builds the foundational event pipeline.
The walkthrough captures and logs namespace activity as it flows through each stage. For adding automated response actions (applying quotas, enforcing policies, triggering remediation), see the Maturity Path section.
Red Hat OpenShift is the platform where containerized workloads run and where cluster administrators manage shared infrastructure. Understanding what resources are created, modified, and deleted across the cluster is essential for governance, audit, and automated response. Event-Driven Ansible (EDA), a component of Ansible Automation Platform, is designed to react to enterprise events – but there is no native, supported integration that pulls OpenShift API changes directly into EDA.
Options for integrating these solutions are avilable in the Open Source community that utilize EDA source plugins to poll the OpenShift API, but pull-based monitoring has limitations at scale:
A supported alternative uses Knative Eventing (via OpenShift Serverless) to watch the Kubernetes API server and emit CloudEvents when resources change. Apache Kafka, provided by Streams for Apache Kafka, serves as the durable transport layer between Knative and EDA. Kafka adds message durability (events are retained even if EDA is temporarily unavailable), replay capability, and decoupling (multiple consumers can read from the same topic independently).
What is Red Hat OpenShift? – redhat.com
Event-Driven Ansible – redhat.com
Streams for Apache Kafka – redhat.com
OpenShift Serverless – redhat.com
Why Kafka instead of sending CloudEvents directly to EDA?
APIServerSource can deliver CloudEvents to an HTTP endpoint, but Kafka adds a buffering layer that guarantees message delivery even when EDA is not running. For production environments, this durability is critical – you do not want to miss a cluster change because EDA was temporarily unavailable.
Extensibility: The APIServerSource
resourceslist can watch any Kubernetes API resource (Deployments, ConfigMaps, Routes, and more). This guide uses Namespaces to serve as a jumping off point as they are easy to observe and validate. More complex implementations can then reuse the same pattern subsequently.
What makes up the solution?
| Persona | Challenge | What They Gain |
|---|---|---|
| No real-time visibility into resource lifecycle across clusters. | Automated capture of namespace events, streamed through Kafka for durability, surfaced in EDA for logging and future automation. | |
| Building custom Kubernetes controllers to react to API changes requires Go expertise and ongoing maintenance. | A reference architecture using supported products – no custom code required. | |
| Lack of audit trail for cluster resource changes. | Every resource change is captured, timestamped, and logged through a durable pipeline with an incremental adoption path. |
| Collection | Type | Purpose |
|---|---|---|
| ansible.eda | Certified | EDA event sources and filters (includes kafka source plugin) |
| ansible.controller | Certified | AAP configuration as code (job templates, workflows, surveys) |
| System | Required | Details |
|---|---|---|
| Git repository | Yes | Hosts EDA rulebooks and playbooks |
| Streams for Apache Kafka operator | Yes | Installed via OpenShift OperatorHub |
| OpenShift Serverless operator | Yes | Installed via OpenShift OperatorHub |
Operational Impact: Medium – deploying Kafka and OpenShift Serverless adds new operators and workloads to the cluster. The event pipeline itself is read-only.
To demonstrate this pattern, when a Namespace is created, updated, or deleted on the OpenShift cluster, the pipeline captures that change and surfaces it in Ansible Automation Platform:
openshift-resources Kafka topic.ansible.eda.kafka source plugin and evaluates rulebook conditions against each event.graph LR
subgraph openshift [OpenShift Cluster]
API([K8s API Server]) --> APISource[APIServerSource]
APISource --> KafkaSink[KafkaSink]
KafkaSink --> KafkaTopic[(Kafka Topic<br/>openshift-resources)]
end
KafkaTopic --> EDA[EDA Rulebook<br/>ansible.eda.kafka]
EDA --> AAP([AAP Job Template])
The APIServerSource is configured in Resource mode, which sends the full Kubernetes resource object in the CloudEvent data payload. This gives EDA access to all namespace metadata for condition matching and logging.
| Stage | Operational Impact | Why |
|---|---|---|
| 1. APIServerSource | None | Read-only watch against the K8s API. No changes to the cluster. |
| 2. KafkaSink / Kafka | None | Message transport only. No infrastructure changes after initial deploy. |
| 3. EDA | None | Evaluates events and triggers job templates. No direct changes. |
| 4. AAP Job Template | None | Logs event details. No modifications to any system. |
| CloudEvent Type | Trigger | What EDA Receives |
|---|---|---|
dev.knative.apiserver.resource.add |
oc new-project or API call |
Full Namespace resource JSON (name, labels, annotations, status) |
dev.knative.apiserver.resource.update |
Label or annotation change | Updated Namespace resource JSON with modified fields |
dev.knative.apiserver.resource.delete |
oc delete project or API call |
Namespace resource JSON with deletion timestamp |
Operational Impact: Medium – deploys a Kafka cluster on OpenShift. Does not affect existing workloads.
An external Kafka instance can be used instead of in-cluster deployment. This guide deploys Kafka on OpenShift for simplicity and reproducibility. Steps for connecting to a remote Kafka instance follow the same KafkaSink and EDA configuration patterns with different bootstrap addresses.
Log in to the OpenShift cluster with elevated access. Create a namespace for Kafka resources:
apiVersion: v1
kind: Namespace
metadata:
name: kafka
Install the Streams for Apache Kafka operator from OperatorHub (search for Streams for Apache Kafka, install to the kafka namespace, stable channel).
Kafka listener certificate
External access requires TLS. Create a TLS secret named kafka-external-cert in the kafka namespace. You can provide your own certificate or provision one with a tool such as cert-manager:
oc create secret tls -n kafka kafka-external-cert \
--cert=path/to/cert/file \
--key=path/to/key/file
Note: The Private key must be in PEM or PKCS #12 format
Create KafkaNodePool resources for the controller and broker components:
apiVersion: kafka.strimzi.io/v1
kind: KafkaNodePool
metadata:
name: controller
namespace: kafka
labels:
strimzi.io/cluster: cluster
spec:
replicas: 3
roles:
- controller
storage:
type: jbod
volumes:
- id: 0
type: persistent-claim
size: 50Gi
kraftMetadata: shared
apiVersion: kafka.strimzi.io/v1
kind: KafkaNodePool
metadata:
name: broker
namespace: kafka
labels:
strimzi.io/cluster: cluster
spec:
replicas: 3
roles:
- broker
storage:
type: jbod
volumes:
- id: 0
type: persistent-claim
size: 50Gi
kraftMetadata: shared
Create the Kafka cluster with internal and external (route) listeners. The external listener uses SCRAM-SHA-512 authentication so the KafkaSink and EDA can connect securely from outside the pod network:
apiVersion: kafka.strimzi.io/v1
kind: Kafka
metadata:
name: cluster
namespace: kafka
spec:
kafka:
version: 4.2.0
metadataVersion: "4.2"
authorization:
type: simple
listeners:
- name: plain
port: 9092
type: internal
tls: false
- name: tls
port: 9093
type: internal
tls: true
- name: external
port: 9094
type: route
tls: true
authentication:
type: scram-sha-512
configuration:
brokerCertChainAndKey:
secretName: kafka-external-cert
certificate: tls.crt
key: tls.key
config:
offsets.topic.replication.factor: 3
transaction.state.log.replication.factor: 3
transaction.state.log.min.isr: 2
default.replication.factor: 3
min.insync.replicas: 2
entityOperator:
topicOperator: {}
userOperator: {}
Note: Match the
versionandmetadataVersionfields to the Streams for Apache Kafka version installed on your cluster.
Create the Kafka topic for OpenShift API resources:
apiVersion: kafka.strimzi.io/v1
kind: KafkaTopic
metadata:
name: openshift-resources
namespace: kafka
labels:
strimzi.io/cluster: cluster
spec:
partitions: 1
replicas: 1
config:
retention.ms: 7200000
segment.bytes: 1073741824
Create two Kafka users with SCRAM-SHA-512 authentication:
openshift – Used by the KafkaSink to publish events (producer)eda – Used by Event-Driven Ansible to consume events (consumer)apiVersion: kafka.strimzi.io/v1
kind: KafkaUser
metadata:
name: openshift
namespace: kafka
labels:
strimzi.io/cluster: cluster
spec:
authentication:
type: scram-sha-512
authorization:
type: simple
acls:
- resource:
type: topic
name: openshift-resources
patternType: literal
operations:
- Describe
- Read
- Create
- Write
host: "*"
- resource:
type: group
name: openshift-group
patternType: literal
operations:
- Read
host: "*"
apiVersion: kafka.strimzi.io/v1
kind: KafkaUser
metadata:
name: eda
namespace: kafka
labels:
strimzi.io/cluster: cluster
spec:
authentication:
type: scram-sha-512
authorization:
type: simple
acls:
- resource:
type: topic
name: openshift-resources
patternType: literal
operations:
- Describe
- Read
host: "*"
- resource:
type: group
name: eda-group
patternType: literal
operations:
- Read
host: "*"
Streams for Apache Kafka (Strimzi) generates credentials in secrets named after each user in the kafka namespace. The password can be obtained using the following command:
oc extract -n kafka secret/<username> --keys=password --to=-
Operational Impact: Medium – installs the Serverless Operator and enables Knative Eventing.
Install the Red Hat OpenShift Serverless operator from OperatorHub (stable channel, default settings).
Deploy Knative Eventing:
apiVersion: operator.knative.dev/v1beta1
kind: KnativeEventing
metadata:
name: knative-eventing
namespace: knative-eventing
Verify Knative Eventing is ready:
oc get knativeeventing knative-eventing -n knative-eventing
Enable the KnativeKafka component with the sink capability (required for KafkaSink):
apiVersion: operator.serverless.openshift.io/v1alpha1
kind: KnativeKafka
metadata:
name: knative-kafka
namespace: knative-eventing
spec:
broker:
enabled: false
channel:
enabled: false
sink:
enabled: true
source:
enabled: false
Verify the KnativeKafka resource reports ready before proceeding.
oc get knativekafka knative-kafka -n knative-eventing
Operational Impact: None – read-only RBAC configuration will be created.
Create the namespace that holds the event pipeline resources:
apiVersion: v1
kind: Namespace
metadata:
name: eda-kafka
The APIServerSource needs a ServiceAccount with permission to watch Namespace resources:
apiVersion: v1
kind: ServiceAccount
metadata:
name: namespace-watcher-sa
namespace: eda-kafka
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-watcher
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: namespace-watcher-binding
subjects:
- kind: ServiceAccount
name: namespace-watcher-sa
namespace: eda-kafka
roleRef:
kind: ClusterRole
name: namespace-watcher
apiGroup: rbac.authorization.k8s.io
RBAC: Scope permissions tightly.
The ClusterRole above only grants
get,list, andwatchon Namespace resources – the minimum required for APIServerSource. Do not use the broadvieworeditClusterRoles. Use a dedicated ServiceAccount for each APIServerSource.
Operational Impact: None – creates a routing configuration.
The KafkaSink connects Knative Eventing to the Kafka topic. Create an authentication secret for the openshift Kafka user. Extract the username and password from the Strimzi-generated secret and create a secret in the eda-kafka namespace that the KafkaSink expects:
# Extract credentials from the Strimzi openshift user secret
KAFKA_USERNAME=$(oc get kafkauser openshift -n kafka -o jsonpath='{.status.username}')
KAFKA_PASSWORD=$(oc get secret $(oc get kafkauser openshift -n kafka -o jsonpath='{.status.secret}') -n kafka -o jsonpath='{.data.password}' | base64 -d)
# Create the KafkaSink auth secret (format required by Knative KafkaSink)
oc create secret generic openshift-resources-auth -n eda-kafka \
--from-literal=user="$KAFKA_USERNAME" \
--from-literal=password="$KAFKA_PASSWORD" \
--from-literal=sasl.mechanism="SCRAM-SHA-512" \
--from-literal=protocol="SASL_SSL"
Alternatively, the External Secret Operator (ESO) can be used to automatically synchronize the Secret between namespaces which simplifies how the credentials are managed.
Create the KafkaSink by referencing the Kafka bootstrap servers and the authentication credential created previously
oc apply -f - <<EOF
apiVersion: eventing.knative.dev/v1alpha1
kind: KafkaSink
metadata:
name: openshift-resources
namespace: eda-kafka
spec:
auth:
secret:
ref:
name: openshift-resources-auth
bootstrapServers:
- $(oc get kafka cluster -n kafka -o jsonpath='{.status.listeners[?(@.name=="external")].bootstrapServers}')
contentMode: structured
topic: openshift-resources
EOF
Verify the KafkaSink is ready:
oc get kafkasink openshift-resources -n eda-kafka
The READY column should show True.
Operational Impact: None – read-only watch against the Kubernetes API server.
Create the APIServerSource that specifies the OpenShift resources to monitor and the destination that the resources should be sent to (KafkaSink):
apiVersion: sources.knative.dev/v1
kind: ApiServerSource
metadata:
name: namespace-watcher
namespace: eda-kafka
spec:
serviceAccountName: namespace-watcher-sa
mode: Resource
resources:
- apiVersion: v1
kind: Namespace
sink:
ref:
apiVersion: eventing.knative.dev/v1alpha1
kind: KafkaSink
name: openshift-resources
Verify the APIServerSource is ready:
oc get apiserversource namespace-watcher -n eda-kafka
The READY column should show True and SINK should be populated. At this point, any Namespace change on the cluster produces a CloudEvent message in the openshift-resources Kafka topic.
Why direct KafkaSink?
Routing APIServerSource directly to a KafkaSink is the simplest topology for a single event source. Fewer custom resources means fewer components to troubleshoot.
Alternative: KafkaChannel + Subscription path
Instead of sinking directly to a KafkaSink, you can alternatively route events through a Knative KafkaChannel and Subscription. This adds an intermediate buffering layer inside Knative Eventing before events reach the external Kafka topic.
Path Strengths Limitations Direct KafkaSink (this guide) Fewer CRs, easier to reason about, ideal for single-source pipelines Less fan-out; harder to add multiple subscribers later KafkaChannel + Subscription Knative-native buffering, fan-out to multiple sinks, server-side filtering via Triggers Extra moving parts; requires KnativeKafka channel backend enabled; more CRs to troubleshoot To use the channel path, a separate Kafka cluster can be used to manage channel specific messages. This cluster is intended for only intra cluster use and may not need to have an external Route or authentication enabled.
Update the KnativeKafka resource to enable the
channelcomponent along with the location of the bootstrap server.apiVersion: operator.serverless.openshift.io/v1alpha1 kind: KnativeKafka metadata: name: knative-kafka namespace: knative-eventing spec: channel: bootstrapServers: '<bootstrap_server>' enabled: true ...If authentication is enabled, be sure to configure the authentication details for the broker within a
Secretusing theauthSecretNameandauthSecretNamespace.Create a KafkaChannel and Subscription, then point the APIServerSource sink at the channel instead of the KafkaSink:
apiVersion: messaging.knative.dev/v1beta1 kind: KafkaChannel metadata: name: openshift-api-events namespace: eda-kafka spec: numPartitions: 3 replicationFactor: 1apiVersion: messaging.knative.dev/v1 kind: Subscription metadata: name: openshift-resources namespace: eda-kafka spec: channel: apiVersion: messaging.knative.dev/v1beta1 kind: KafkaChannel name: openshift-api-events subscriber: ref: apiVersion: eventing.knative.dev/v1alpha1 kind: KafkaSink name: openshift-resourcesWith this path, change the APIServerSource
sink.refto point at the KafkaChannel instead of the KafkaSink.
Operational Impact: None – EDA is a read-only consumer of the Kafka topic.
Because EDA may run outside the OpenShift cluster, configure it to connect to Kafka via the external route with SCRAM-SHA-512 authentication. Store the credentials to connect with Kafka in AAP. As the credential requires parameters specific for the connection with Kafka, a custom credential type will be created instead of hardcoding the properties in the rulebook.
Create the AAP custom credential type
In Ansible Automation Platform, navigate to Automation Decisions -> Infrastructure -> Credential Types -> Create Credential Type.
Enter Kafka as the Name and use the following field and injector configurations:
fields:
- id: host
type: string
label: Host
- id: port
type: string
label: Port
default: '9092'
- id: encoding
type: string
label: Encoding
- id: cafile
type: string
label: CA File
multiline: true
- id: certfile
type: string
label: Certificate File
multiline: true
- id: keyfile
type: string
label: Key File
secret: true
multiline: true
- id: topic
type: string
label: Topic
- id: group_id
type: string
label: Group ID
- id: offset
type: string
label: Offset
- id: security_protocol
type: string
label: Security Protocol
- id: sasl_mechanism
type: string
label: SASL Mechanism
- id: sasl_username
type: string
label: SASL Username
- id: sasl_password
type: string
label: SASL Password
secret: true
- id: check_hostname
type: boolean
label: Check Hostname
- id: verify_mode
type: string
label: Verify Mode
- id: feedback
type: boolean
label: Feedback
required:
- host
- topic
- group_id
file:
template.cafile: '{{ cafile }}'
template.keyfile: '{{ keyfile }}'
template.certfile: '{{ certfile }}'
extra_vars:
host: '{{ host }}'
port: '{{ port }}'
topic: '{{ topic }}'
offset: '{{ offset }}'
encoding: '{{ encoding }}'
feedback: '{{ feedback }}'
group_id: '{{ group_id }}'
verify_mode: '{{ verify_mode }}'
sasl_password: '{{ sasl_password }}'
sasl_username: '{{ sasl_username }}'
check_hostname: '{{ check_hostname }}'
sasl_mechanism: '{{ sasl_mechanism }}'
security_protocol: '{{ security_protocol }}'
Create Kafka Credential
Create a new credential using the Kafka custom credential created previously that represents how EDA will connect to Kafka.
Navigate to Automation Decisions -> Infrastructure -> Credential -> Create Credential.
Enter the following in the Create Credential page:
| Field | Value |
|---|---|
| Name | Kafka Cluster |
| Organization | Default |
| Credential Type | Kafka |
| Host | <bootstrap_host> |
| Port | 443 |
| Topic | openshift-resources |
| Group ID | eda-group |
| SASL Protocol | SASL_SSL |
| SASL Mechanism | SCRAM-SHA-512 |
| SASL Username | <eda_username> |
| SASL Password | <eda_password> |
Replace the placeholders with the values associated with the Kafka cluster and associated EDA user.
Create the EDA rulebook
Create an Ansible Rulebook called openshift-namespace-events.yml in your Git repository to capture events from OpenShift stored in Kafka and trigger a Job Template in Ansible Automation Platform.
---
- name: Read OpenShift Resources from from Kafka topic
hosts: all
sources:
- name: kafka
ansible.eda.kafka:
host: "{{ host | default('localhost', true) }}"
port: "{{ port | default('9092', true) }}"
cafile: "{{ eda.filename.cafile | default(None, true) }}"
certfile: "{{ eda.filename.certfile | default(None, true) }}"
keyfile: "{{ eda.filename.keyfile | default(None, true) }}"
encoding: "{{ encoding | default('utf-8', true) }}"
topic: "{{ topic | default('openshift-resources', true) }}"
group_id: "{{ group_id | default('openshift-group', true) }}"
offset: "{{ offset | default('earliest', true) }}"
security_protocol: "{{ security_protocol | default('PLAINTEXT', true) }}"
sasl_mechanism: "{{ sasl_mechanism | default('PLAIN', true) }}"
sasl_plain_username: "{{ sasl_username | default(None, true) }}"
sasl_plain_password: "{{ sasl_password | default(None, true) }}"
check_hostname: "{{ check_hostname | default(None, true) }}"
verify_mode: "{{ verify_mode | default('CERT_NONE', true) }}"
rules:
- name: Namespace created
condition: event.body.type is defined and event.body.type == "dev.knative.apiserver.resource.add"
action:
run_job_template:
organization: "Default"
name: "Log Namespace Activity"
job_args:
extra_vars:
event_type: "created"
namespace_name: "{{ event.body.data.metadata.name }}"
namespace_labels: "{{ event.body.data.metadata.labels | default({}) }}"
- name: Namespace updated
condition: event.body.type is defined and event.body.type == "dev.knative.apiserver.resource.update"
action:
run_job_template:
organization: "Default"
name: "Log Namespace Activity"
job_args:
extra_vars:
event_type: "updated"
namespace_name: "{{ event.body.data.metadata.name }}"
namespace_labels: "{{ event.body.data.metadata.labels | default({}) }}"
- name: Namespace deleted
condition: event.body.type is defined and event.body.type == "dev.knative.apiserver.resource.delete"
action:
run_job_template:
organization: "Default"
name: "Log Namespace Activity"
job_args:
extra_vars:
event_type: "deleted"
namespace_name: "{{ event.body.data.metadata.name }}"
- name: Log all namespace events
condition: event.body is defined
action:
debug:
msg: "Namespace event received: {{ event.body.type }} - {{ event.body.data.metadata.name | default('unknown') }}"
Create EDA Project
Create an EDA project associated with the rulebook created previously and stored in the git repository.
Navigate to Automation Decisions -> Projects -> Create Project. Use the following in the creation form:
| Field | Value |
|---|---|
| Name | Ansible Solution Guides |
| Organization | Default |
| Source control type | Git |
| Source control URL | <git_repository_url> |
| Source control branch/tag/commit | <git_ref> |
Utilize any of the form fields as appropriate for your git repository.
Create API Token for EDA Authentication to Automation Controller
In order to invoke automation (Job Templates) defined in AAP, create a new Credential to enable EDA to authenticate with Automation controller.
Navigate to Access Management -> API Tokens -> Create API Token. Use the following in the creation form:
| Field | Value |
|---|---|
| Name | EDA |
| Scope | Write |
The generated token will be displayed. Take note of the value as it will be used in the next step.
Create EDA Credential for Automation Controller
With an API Token now created, create an EDA to authenticate to Automation Controller.
Navigate to Automation Decisions -> Infrastructure -> Credentials -> Create Credential:
| Field | Value |
|---|---|
| Name | Automation Controller |
| Organization | Default |
| Credential Type | Red Hat Ansible Automation Platform |
| Red Hat Ansible Automation Platform | https://<gateway_host>/api/controller/ |
| OAuth Token | Value of the API token created previously |
Create Decision Environment
Create a Decision Environment that utilizes the supported Ansible Automation Platform Decision Environment.
Navigate to Automation Decisions -> Decision Environments -> Create decision environment. Use the following in the creation form:
| Field | Value |
|---|---|
| Name | Supported DE |
| Image | registry.redhat.io/ansible-automation-platform-26/de-supported-rhel9:latest |
EDA Rulebook Activation configuration:
Create an EDA Rulebook Activation to define monitoring the Kafka topic.
Navigate to Automation Decisions -> Rulebook Activations -> Create rulebook activation. Use the following in the creation form:
| Field | Value |
|---|---|
| Name | OpenShift Namespace Events |
| Project | Ansible Solution Guides |
| Rulebook | openshift-namespace-events.yml |
| Decision Environment | Supported DE |
| Credentials | <ul><li>Kafka Cluster \| Kafka</li><li>Automation Controller \| Red Hat Ansible Automation Platform</li></ul> |
| Offset | earliest |
| Restart Policy | Always |
| Rulebook activation enabled? | Checked |
Once the Rulebook Activation has been created, it will be started. The state of the Rulebook Activation can be seen on the History tab. By selecting the running Rulebook Activation from the History tab, the logs can be viewed. The Rulebook Activation is functioning properly if the following message is displayed:
Job activation-job-1-1 is running
Operational Impact: None – the playbook only prints event details.
Create Automation Playbook
Create an Ansible playbook that will be triggered from a Job Template whenever an Event is consumed by EDA. Save the following as playbooks/log_namespace_activity.yml in your Git repository:
---
- name: Log OpenShift namespace activity
hosts: localhost
gather_facts: false
tasks:
- name: Display namespace event details
ansible.builtin.debug:
msg: |
Namespace Event Detected
========================
Event Type: {{ event_type }}
Namespace: {{ namespace_name }}
Labels: {{ namespace_labels | default('N/A') }}
Create Inventory
Since the playbook created in the previous section executes locally, create an inventory that targets localhost.
Navigate to Automation Execution -> Infrastructures -> Inventories . Click the Create Inventory dropdown -> Create Inventory.
Enter the following in the Create Inventory form:
| Field | Value |
|---|---|
| Name | localhost |
| Organization | Default |
Create a localhost host on the Hosts tab.
Create Automation Project
To make use of the newly created playbook, create a Project in Automation Controller.
Navigate to Automation Execution -> Projects -> Create project. Utilize the same parameters that were used when creating the Ansible Solution Guides EDA project as the content will originate from the same source.
Job Template configuration:
Create a Job Template to define the target for what automation should be executed when a Namespace event is received.
Navigate to Automation Execution -> Templates. Click the Create template dropdown -> Create job template.
Enter the following into the create job template form:
| Field | Value |
|---|---|
| Name | Display OpenShift Namespace Event |
| Job type | Run |
| Inventory | localhost |
| Project | Ansible Solution Guides |
| Playbook | playbooks/log_namespace_activity.yml |
| Extra Variables | Prompt on Launch: checked |
At this point, the steps for establishing an OpenShift API event pipeline is complete.
Validate each stage of the pipeline independently:
| Stage | What to Verify | How to Test | Success Indicator |
|---|---|---|---|
| Kafka | Kafka cluster is healthy | oc get kafka cluster -n kafka |
READY is True |
| KafkaTopic | Topic exists | oc get kafkatopic openshift-resources -n kafka |
READY is True |
| APIServerSource | Source is watching Namespaces | oc get apiserversource namespace-watcher -n eda-kafka |
READY is True, SINKURI is populated |
| KafkaSink | Sink is routing to Kafka | oc get kafkasink openshift-resources -n eda-kafka |
READY is True |
| EDA | Rulebook activation is running | Check EDA Controller UI | Activation shows Running |
| Automation Controller | Job template fires on event | Create a test namespace (see below) | Job appears in AAP with correct extra_vars |
Create a test namespace to trigger the full pipeline:
oc new-project test-eda-namespace
Verify the pipeline fires:
dev.knative.apiserver.resource.add.Log Namespace Activity job should appear with event_type: created and namespace_name: test-eda-namespace.Clean up and verify the deletion event:
oc delete project test-eda-namespace
A second job should appear in AAP with event_type: deleted.
EDA activation log (abbreviated):
INFO Namespace event received: dev.knative.apiserver.resource.add - test-eda-namespace
AAP job extra variables:
event_type: created
namespace_name: test-eda-namespace
Playbook output:
TASK [Display namespace event details] *****************************************
ok: [localhost] => {
"msg": "Namespace Event Detected\n========================\nEvent Type: created\nNamespace: test-eda-namespace\n"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0
| Symptom | Likely Cause | Fix |
|---|---|---|
APIServerSource shows READY=False |
ServiceAccount lacks RBAC permissions | Verify the ClusterRole grants get, list, watch on namespaces and the ClusterRoleBinding references the correct ServiceAccount and namespace. |
APIServerSource is READY but no messages in Kafka topic |
KafkaSink not ready or misconfigured | Run oc describe kafkasink openshift-resources -n eda-kafka and verify bootstrapServers and auth secret |
| KafkaSink auth failure | SCRAM credentials mismatch | Recreate openshift-resources-auth secret from the Strimzi openshift user secret |
| Messages in Kafka but EDA does not fire | host, topic, or group_id mismatch |
Verify credential extra vars match the external bootstrap address, topic openshift-resources, and group eda-group |
| EDA auth failure on startup | Wrong security protocol or CA (if defined) | Set security_protocol to SASL_SSL, sasl_mechanism to SCRAM-SHA-512, and provide the cluster CA in cafile |
| EDA receives events but no job launches | API token for Ansible Controller is not valid | Confirm the Ansible Automation Platform API token is valid and/or recreate the API token |
| KnativeKafka sink not ready | Sink capability not enabled | Verify spec.sink.enabled: true in the KnativeKafka CR |
| Maturity | Description | Automation Level |
|---|---|---|
| Crawl | Event Visibility – Capture namespace lifecycle events into Kafka and EDA. Log event details for audit and visibility. This is what this guide demonstrates. | Read-only: events are captured and logged |
| Walk | Automated Governance – Extend the pipeline to apply standard configuration to new namespaces (resource quotas, network policies, RBAC bindings, required labels). | Semi-automated: EDA triggers pre-approved configuration playbooks |
| Run | Autonomous Operations – Integrate with the AIOps pipeline for AI-driven policy evaluation, dynamic response selection, and fully autonomous namespace governance across multiple clusters. | Fully automated: AI-driven policy enforcement |
Ready for AI-driven operations?
The Run stage integrates this event pipeline with the AIOps reference architecture. The Kafka topic from this guide becomes the event source for the AIOps Enrichment Workflow. See AIOps automation with Ansible for the full self-healing pipeline.
This guide illustrated establishing a complete event pipeline from the OpenShift API to Event-Driven Ansible using Knative Eventing and Apache Kafka. Namespace lifecycle events – creates, updates, and deletes – are captured by an APIServerSource, published to a Kafka topic through a KafkaSink for durability and decoupling, and consumed by an EDA rulebook that triggers Automation Controller job templates on namespace creation and deletion. The pipeline logs every namespace event automatically, providing the foundation for adding governance actions, compliance validation, or integration with the AIOps self-healing architecture.