Collect Azure DevOps audit logs
Overview
This parser handles Azure DevOps audit logs in JSON format. It extracts fields from nested and top-level JSON structures, mapping them to the UDM. Conditional logic based on specific field values categorizes events and enriches the output with relevant security information. The parser also handles non-JSON formatted messages by attempting to extract a JSON payload using grok patterns.
Before you begin
Ensure you have the following prerequisites:
- Google SecOps instance
- An active Azure DevOps Organization
- Privileged access to Azure Devops Organization and Azure
Set up feeds
There are two different entry points to set up feeds in the Google SecOps platform:
- SIEM Settings > Feeds > Add New Feed
- Content Hub > Content Packs > Get Started
How to set up the Azure DevOps audit feed
- Click the Azure Platform pack.
- Locate the Azure DevOps audit log type and click Add new feed.
- Specify values for the following fields: - Source Type: Microsoft Azure Blob Storage V2.
- Azure URI: The blob endpoint URL.
- ENDPOINT_URL/BLOB_NAME- Replace the following:
- ENDPOINT_URL: The blob endpoint URL (- https://<storageaccountname>.blob.core.windows.net)
- BLOB_NAME: The name of the blob (such as,- insights-logs-<logname>)
 
 
- Replace the following:
 
- Source deletion options: Select the deletion option according to your ingestion preferences. 
- Maximum File Age: Includes files modified in the last number of days. Default is 180 days. 
- Shared key: The shared key (a 512-bit random string in base-64 encoding) used to access Azure resources. 
 - Advanced options - Feed Name: A prepopulated value that identifies the feed.
- Asset namespace: The asset namespace.
- Ingestion labels: The label applied to the events from this feed.
 
- Click Create feed. 
For more information about configuring multiple feeds for different log types within this product family, see Configure feeds by product.
Create an API key for the webhook feed
- Go to Google Cloud console > Credentials. 
- Click Create credentials, and then select API key. 
- Restrict the API key access to the Google Security Operations API. 
Specify the endpoint URL
- In your client application, specify the HTTPS endpoint URL provided in the webhook feed.
- Enable authentication by specifying the API key and secret key as part of the custom header in the following format: - X-goog-api-key = API_KEY X-Webhook-Access-Key = SECRET- Recommendation: Specify the API key as a header instead of specifying it in the URL. If your webhook client doesn't support custom headers, you can specify the API key and secret key using query parameters in the following format: - ENDPOINT_URL?key=API_KEY&secret=SECRET
Replace the following:
- ENDPOINT_URL: The feed endpoint URL.
- API_KEY: The API key to authenticate to Google Security Operations.
- SECRET: The secret key that you generated to authenticate the feed.
Configure Auditing feature in Azure Devops
- Sign in to your organization (https://dev.azure.com/{yourorganization}).
- Select the gear icon for Organization settings.
- Select Policies under Security.
- Toggle the Log Audit Events button to ON.
Configure an Event Grid Topic in Azure
- Sign in to the Azure Portal.
- Search for and access Event Grid.
- Locate Topics under Custom events.
- Click + Create.
- Select your Subscription and Resource Group. Provide a name (for example, DevopsAuditLog) and select the region. Click Review and create.
- Access the new Topic and copy the Topic Endpoint URL.
- Go to Settings > Access Keys and copy Key 1.
Configure Azure Devops Log Stream to Event Grid
- Sign in to your organization (https://dev.azure.com/{yourorganization}).
- Select the gear icon for Organization settings.
- Select Auditing.
- Go to the Streams tab and select New stream > Event Grid.
- Enter the topic endpoint and access key created in Configure an Event Grid Topic in Azure.
Configure a Webhook in Azure DevOps for Google SecOps
- In the Azure Portal, search for and access Event Grid.
- Select previously created Topic.
- Go to Entities > Event Subscription.
- Click + Event Subscription.
- Provide a descriptive name (for example, *Google SecOps Integration).
- Select Web Hook and click Configure an endpoint.
- Configure the endpoint:
- Subscriber endpoint: Enter the Google SecOps API endpoint URL.
- In the HTTP headers section, add the following headers:
- Header 1:
- Key: X-goog-api-key
- Value: The API key you created in the Create an API key for the webhook feed section.
 
- Key: 
- Header 2:
- Key: X-Webhook-Access-Key
- Value: The secret key you generated in the Configure a feed in Google SecOps to ingest the Azure Devops logs section.
 
- Key: 
 
- Header 1:
- Set the Content-Type header to application/json.
 
- Click Create.
UDM Mapping Table
| Log Field | UDM Mapping | Logic | 
|---|---|---|
| ActivityId | metadata.product_log_id | Directly mapped from the Idfield in the raw log when therecordsfield is not present, or from theActivityIdfield within thedataobject whenrecordsis present. | 
| ActionId | metadata.product_event_type | Directly mapped from the ActionIdfield within thedataobject. | 
| ActorCUID | additional.fields | Included as an additional field with key "Actor CUID". | 
| ActorDisplayName | principal.user.user_display_name | Directly mapped from the ActorDisplayNamefield if it's not "Azure DevOps Service". If it is "Azure DevOps Service", it's added as a label toprincipal.resource.attribute.labels. | 
| ActorUPN | principal.user.email_addresses | Directly mapped from the ActorUPNfield if it matches an email address pattern. | 
| ActorUserId | principal.user.userid | Directly mapped from the ActorUserIdfield. | 
| Area | target.application | Used to construct the target.applicationfield by prepending "DevOps " to theAreavalue. | 
| AuthenticationMechanism | extensions.auth.auth_details,security_result.rule_id | Parsed to extract authentication details and rule ID. The authentication details are mapped to extensions.auth.auth_details. The extracted rule ID is mapped tosecurity_result.rule_id. | 
| CategoryDisplayName | security_result.action_details | Directly mapped to security_result.action_details. | 
| City | principal.location.city | Directly mapped from the Cityfield. | 
| Conditions | additional.fields | Added as an additional field with key "Conditions". | 
| Country | principal.location.country_or_region | Directly mapped from the Countryfield. | 
| Data.* | Various | Fields within the Dataobject are mapped to different UDM fields based on their names and context. See below for specific examples. | 
| Data.AccessLevel | target.resource.attribute.labels | Added as a label with key "AccessLevel". | 
| Data.AgentId | target.resource.product_object_id | Mapped to target.resource.product_object_idifPipelineIdandAuthorizationIdare not present. | 
| Data.AgentName | target.resource.name | Mapped to target.resource.nameifPipelineName,NamespaceName, andDisplayNameare not present. | 
| Data.AuthorizationId | target.resource.product_object_id | Mapped to target.resource.product_object_idifPipelineIdis not present. | 
| Data.CallerProcedure | additional.fields | Added as an additional field with key "CallerProcedure". | 
| Data.CheckSuiteId | additional.fields | Added as an additional field with key "CheckSuiteId". | 
| Data.CheckSuiteStatus | additional.fields | Added as an additional field with key "CheckSuiteStatus". | 
| Data.ConnectionId | additional.fields | Added as an additional field with key "ConnectionId". | 
| Data.ConnectionName | additional.fields | Added as an additional field with key "ConnectionName". | 
| Data.ConnectionType | additional.fields | Added as an additional field with key "ConnectionType". | 
| Data.DefinitionId | additional.fields | Added as an additional field with key "DefinitionId". | 
| Data.DeploymentResult | additional.fields | Added as an additional field with key "DeploymentResult". | 
| Data.DisplayName | target.resource.name | Mapped to target.resource.nameifPipelineNameandNamespaceNameare not present. | 
| Data.EndpointIdList | additional.fields | Added as an additional field with key "EndpointIdList". | 
| Data.EnvironmentName | additional.fields | Added as an additional field with key "EnvironmentName". | 
| Data.Filter.continuationToken | target.resource.attribute.labels | Added as a label with key "continuation_token". | 
| Data.Filter.endTime | target.resource.attribute.labels | Added as a label with key "filter_end_time". | 
| Data.Filter.startTime | target.resource.attribute.labels | Added as a label with key "filter_start_time". | 
| Data.FinishTime | additional.fields | Added as an additional field with key "FinishTime". | 
| Data.GroupId | target.group.product_object_id | Directly mapped to target.group.product_object_idwhenData.Updates.0.GroupIdis not present. | 
| Data.GroupName | target.group.group_display_name | Directly mapped to target.group.group_display_name. | 
| Data.JobName | additional.fields | Added as an additional field with key "JobName". | 
| Data.MemberId | target.user.userid | Directly mapped to target.user.useridwhenData.Updates.0.MemberIdis not present. | 
| Data.MemberDisplayName | target.user.user_display_name | Directly mapped to target.user.user_display_name. | 
| Data.NamespaceId | target.resource.product_object_id | Mapped to target.resource.product_object_idifPipelineId,AuthorizationId, andAgentIdare not present. | 
| Data.NamespaceName | target.resource.name | Mapped to target.resource.nameifPipelineNameis not present. | 
| Data.ownerDetails | additional.fields | Added as an additional field with key "OwnerDetails". | 
| Data.OwnerId | additional.fields | Added as an additional field with key "OwnerId". | 
| Data.PipelineId | target.resource.product_object_id | Directly mapped to target.resource.product_object_id. | 
| Data.PipelineName | target.resource.name | Directly mapped to target.resource.name. | 
| Data.PipelineRevision | target.resource.attribute.labels | Added as a label with key "PipelineRevision". | 
| Data.PipelineScope | target.resource.attribute.labels | Added as a label with key "PipelineScope". | 
| Data.PlanType | additional.fields | Added as an additional field with key "PlanType". | 
| Data.PreviousAccessLevel | target.resource.attribute.labels | Added as a label with key "PreviousAccessLevel". | 
| Data.PublisherName | target.resource.attribute.labels | Added as a label with key "PublisherName". | 
| Data.Reason | additional.fields | Added as an additional field with key "Reason". | 
| Data.ReleaseId | additional.fields | Added as an additional field with key "ReleaseId". | 
| Data.ReleaseName | additional.fields | Added as an additional field with key "ReleaseName". | 
| Data.RequesterId | additional.fields | Added as an additional field with key "RequesterId". | 
| Data.RetentionLeaseId | additional.fields | Added as an additional field with key "RetentionLeaseId". | 
| Data.RetentionOwnerId | additional.fields | Added as an additional field with key "RetentionOwnerId". | 
| Data.RunName | additional.fields | Added as an additional field with key "RunName". | 
| Data.Scopes | target.resource.attribute.labels | Added as labels with key "Scope". | 
| Data.StageName | additional.fields | Added as an additional field with key "StageName". | 
| Data.StartTime | additional.fields | Added as an additional field with key "StartTime". | 
| Data.TargetUser | target.user.userid | Directly mapped to target.user.userid. | 
| Data.Timestamp | metadata.event_timestamp | Parsed and mapped to metadata.event_timestamp. | 
| Data.TokenType | target.resource.attribute.labels | Added as a label with key "TokenType". | 
| Data.Updates.0.GroupId | target.group.product_object_id | Directly mapped to target.group.product_object_id. | 
| Data.Updates.0.MemberId | target.user.userid | Directly mapped to target.user.userid. | 
| Data.ValidFrom | target.resource.attribute.labels | Added as a label with key "ValidFrom". | 
| Data.ValidTo | target.resource.attribute.labels | Added as a label with key "ValidTo". | 
| DewPoint | additional.fields | Added as an additional field with key "DewPoint". | 
| Details | metadata.description | Directly mapped to metadata.description. | 
| Humidity | additional.fields | Added as an additional field with key "Humidity". | 
| Icon | additional.fields | Added as an additional field with key "Icon". | 
| Id | metadata.product_log_id | Directly mapped to metadata.product_log_id. | 
| IpAddress | principal.ip | Directly mapped to principal.ip. | 
| MoonPhase | additional.fields | Added as an additional field with key "MoonPhase". | 
| Moonrise | additional.fields | Added as an additional field with key "Moonrise". | 
| Moonset | additional.fields | Added as an additional field with key "Moonset". | 
| OperationName | metadata.product_event_type | Directly mapped to metadata.product_event_type. | 
| Precipitation | additional.fields | Added as an additional field with key "Precipitation". | 
| Pressure | additional.fields | Added as an additional field with key "Pressure". | 
| ProjectId | target.resource_ancestors.product_object_id | Used to populate the product_object_idfield withintarget.resource_ancestorswhen the ancestor is of typeCLOUD_PROJECT. | 
| ProjectName | target.resource_ancestors.name,target.resource.attribute.labels | Used to populate the namefield withintarget.resource_ancestorswhen the ancestor is of typeCLOUD_PROJECT. Also added as a label totarget.resource.attribute.labelswith key "ProjectName". | 
| RoleLocation | target.location.name | Directly mapped to target.location.name. | 
| ScopeDisplayName | target.resource_ancestors.name | Used to populate the namefield withintarget.resource_ancestorswhen the ancestor is of typeCLOUD_ORGANIZATION. | 
| ScopeId | target.resource_ancestors.product_object_id | Used to populate the product_object_idfield withintarget.resource_ancestorswhen the ancestor is of typeCLOUD_ORGANIZATION. | 
| ScopeType | additional.fields | Added as an additional field with key "ScopeType". | 
| Sunrise | additional.fields | Added as an additional field with key "Sunrise". | 
| Sunset | additional.fields | Added as an additional field with key "Sunset". | 
| Temperature | additional.fields | Added as an additional field with key "Temperature". | 
| TenantId | metadata.product_deployment_id,additional.fields | Directly mapped to metadata.product_deployment_id. Also added as an additional field with key "TenantId". | 
| TimeGenerated | metadata.event_timestamp | Parsed and mapped to metadata.event_timestamp. | 
| UserAgent | network.http.user_agent,network.http.parsed_user_agent | Directly mapped to network.http.user_agent. Also parsed and mapped tonetwork.http.parsed_user_agent. | 
| UVIndex | additional.fields | Added as an additional field with key "UVIndex". | 
| Visibility | additional.fields | Added as an additional field with key "Visibility". | 
| WindDirection | additional.fields | Added as an additional field with key "WindDirection". | 
| WindSpeed | additional.fields | Added as an additional field with key "WindSpeed". | 
| _Internal_WorkspaceResourceId | additional.fields | Added as an additional field with key "workspace_resource_id". | 
| N/A | metadata.event_type | Determined by logic based on the OperationNameand other fields. Defaults to "GENERIC_EVENT" if no specific event type is matched. Possible values include "STATUS_SHUTDOWN", "RESOURCE_CREATION", "STATUS_UPDATE", "USER_RESOURCE_DELETION", "RESOURCE_READ", "RESOURCE_WRITTEN", "RESOURCE_DELETION", and "GROUP_MODIFICATION". | 
| N/A | metadata.vendor_name | Set to "Microsoft". | 
| N/A | metadata.product_name | Set to "Azure DevOps". | 
| N/A | metadata.log_type | Set to "AZURE_DEVOPS". | 
| N/A | principal.user.account_type | Set to "SERVICE_ACCOUNT_TYPE" if AuthenticationMechanismcontains "ServicePrincipal", otherwise set to "CLOUD_ACCOUNT_TYPE". | 
| N/A | target.asset.attribute.cloud.environment | Set to MICROSOFT_AZURE. | 
| N/A | security_result.action | Set to "ALLOW" for successful operations (Succeeded, Created, Modified, executed, updated, removed) and "BLOCK" for failed operations (Failed, TimedOut). | 
| N/A | extensions.auth.mechanism | Set to "USERNAME_PASSWORD" if summaryis "UserAuthToken". | 
| N/A | target.resource.resource_type | Set to "SETTING" if pipeline_idis present, "CREDENTIAL" ifauthorization_idis present, "DEVICE" ifagent_idis present, or "DATABASE" ifnamespace_idis present. Otherwise, it is set to "STORAGE_BUCKET" in some cases based onoperationName. | 
| N/A | target.resource.resource_subtype | Set to "Pipeline" if pipeline_idis present, "Token" ifauthorization_idis present, "Agent" ifagent_idis present, or "Namespace" ifnamespace_idis present. | 
Need more help? Get answers from Community members and Google SecOps professionals.