Add audit event for Duo service account auto-provisioning
What does this MR do and why?
Adds a new DAP-specific audit event, duo_service_account_provisioned, that fires whenever a service account is auto-provisioned via Ai::Catalog::ItemConsumers::CreateService (for AI Catalog flows or third-party flows).
Currently, service account auto-provisioning only emits the generic member_created event, which is indistinguishable from a regular user being added to a project. SIEM rules cannot tell DAP-related service account creation apart from regular membership changes, and there is no clear audit trail of which flow/agent triggered the service account creation.
Changes
- New audit event type (
ee/config/audit_events/types/duo_service_account_provisioned.yml): Definesduo_service_account_provisionedscoped to[Group, Project, User],saved_to_database: true,streamed: true, feature categoryduo_agent_platform. - Audit call in
Ai::Catalog::ItemConsumers::CreateService(ee/app/services/ai/catalog/item_consumers/create_service.rb): After a successful service account creation inexecute, a newlog_service_account_provisioned_audit_eventprivate method emits the event. It includes:- service account username, id, composite-identity flag, provisioned-by group
- associated item (flow/agent) id, type, name
- foundational flow reference (when applicable)
- item consumer id
- the developer access level used when adding the SA to a project
- Spec (
ee/spec/services/ai/catalog/item_consumers/duo_service_account_provisioned_audit_spec.rb): Covers- successful flow provisioning at group scope (scope/target/details assertions)
- foundational flow context surfaces in the message +
additional_details - service account creation failure does NOT fire the new event
- agent items (no SA provisioned) do NOT fire the new event
- nil cases at unit level: nil service account, nil item consumer, both nil, and item without a foundational flow reference
Note on duplicate audit events
Both the generic member_created event and duo_service_account_provisioned will fire when a Duo SA is auto-provisioned. This is intentional: member_created is generic membership, while duo_service_account_provisioned carries DAP-specific scope and context for SIEM filtering. This mirrors the pattern from !229464 (service_account_created).
References
- Refs: https://gitlab.com/gitlab-org/gitlab/-/work_items/593021
- Related: !229464 (parent
service_account_createdevent) - Related:
composite_oauth_token_createdevent from work item 593023 - Audit event development guide
Screenshots
How to set up and validate locally
-
Ensure you have an Ultimate license (audit events require it).
-
Enable a DAP flow (e.g. via the AI Catalog Item Consumer GraphQL mutation
aiCatalogItemConsumerCreate) targeting a top-level group, with an item whose type isfloworthird_party_flow. -
Verify in the Rails console.
AuditEventhas noevent_namecolumn — the event name lives inside the YAML-serializeddetailshash, so filter in Ruby:# Most recent matching event AuditEvent.order(id: :desc).limit(200).find do |e| e.details[:event_name] == 'duo_service_account_provisioned' end # Or scope by the provisioned service account (faster, indexed) sa = User.find_by(username: '<service-account-username>') AuditEvent.where(target_id: sa.id, target_type: 'User') .order(id: :desc) .find { |e| e.details[:event_name] == 'duo_service_account_provisioned' } -
Confirm the event has the expected scope (group/project), target (the service account), and
additional_details(item id/type/name, foundational flow reference if any).
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

