Collect Azure DevOps audit logs

Supported in:

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 that you have a Google SecOps instance.
  • Ensure that you have an active Azure DevOps Organization.
  • Ensure that you have privileged access to Azure Devops Organization and Azure.

Configure a feed in Google SecOps to ingest the Azure Devops logs

  1. Go to SIEM Settings > Feeds.
  2. Click Add new.
  3. In the Feed name field, enter a name for the feed (for example, Azure Devops Logs).
  4. Select Webhook as the Source type.
  5. Select Azure Devops as the Log type.
  6. Click Next.
  7. Optional: specify values for the following input parameters:
    • Split delimiter: the delimiter that is used to separate log lines, such as \n.
    • Asset namespace: the asset namespace.
    • Ingestion labels: the label applied to the events from this feed.
  8. Click Next.
  9. Review the feed configuration in the Finalize screen, and then click Submit.
  10. Click Generate Secret Key to generate a secret key to authenticate this feed.
  11. Copy and store the secret key. You cannot view this secret key again. If needed, you can regenerate a new secret key, but this action makes the previous secret key obsolete.
  12. On the Details tab, copy the feed endpoint URL from the Endpoint Information field. You need to specify this endpoint URL in your client application.
  13. Click Done.

Create an API key for the webhook feed

  1. Go to Google Cloud console > Credentials.

    Go to Credentials

  2. Click Create credentials, and then select API key.

  3. Restrict the API key access to the Google Security Operations API.

Specify the endpoint URL

  1. In your client application, specify the HTTPS endpoint URL provided in the webhook feed.
  2. 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

  1. Sign in to your organization (https://dev.azure.com/{yourorganization}).
  2. Select the gear icon for Organization settings.
  3. Select Policies under Security.
  4. Toggle the Log Audit Events button to ON.

Configure an Event Grid Topic in Azure

  1. Sign in to the Azure Portal.
  2. Search for and access Event Grid.
  3. Locate Topics under Custom events.
  4. Click + Create.
  5. Select your Subscription and Resource Group. Provide a name (For example, DevopsAuditLog) and select the region. Click Review and create.
  6. Access the new Topic and copy the Topic Endpoint URL.
  7. Go to Settings > Access Keys and copy Key 1.

Configure Azure Devops Log Stream to Event Grid

  1. Sign in to your organization (https://dev.azure.com/{yourorganization}).
  2. Select the gear icon for Organization settings.
  3. Select Auditing.
  4. Go to Streams tab and select New stream > Event Grid.
  5. 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

  1. In the Azure Portal, search for and access Event Grid.
  2. Select previously created Topic.
  3. Go to Entities > Event Subscription.
  4. Click + Event Subscription.
  5. Provide a descriptive name (e.g Google SecOps Integration).
  6. Select Web Hook and click Configure an endpoint.
  7. Configure the endpoint:
    • Subscriber endpoint: Enter the Google SecOps API endpoint URL.
    • Append ?key=<API_KEY>&secret=<SECRET_KEY> to the Payload URL.
    • Set the Content-Type header to application/json.
  8. Click Create.

UDM Mapping Table

Log Field UDM Mapping Logic
ActivityId metadata.product_log_id Directly mapped from the Id field in the raw log when the records field is not present, or from the ActivityId field within the data object when records is present.
ActionId metadata.product_event_type Directly mapped from the ActionId field within the data object.
ActorCUID additional.fields Included as an additional field with key "Actor CUID".
ActorDisplayName principal.user.user_display_name Directly mapped from the ActorDisplayName field if it's not "Azure DevOps Service". If it is "Azure DevOps Service", it's added as a label to principal.resource.attribute.labels.
ActorUPN principal.user.email_addresses Directly mapped from the ActorUPN field if it matches an email address pattern.
ActorUserId principal.user.userid Directly mapped from the ActorUserId field.
Area target.application Used to construct the target.application field by prepending "DevOps " to the Area value.
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 to security_result.rule_id.
CategoryDisplayName security_result.action_details Directly mapped to security_result.action_details.
City principal.location.city Directly mapped from the City field.
Conditions additional.fields Added as an additional field with key "Conditions".
Country principal.location.country_or_region Directly mapped from the Country field.
Data.* Various Fields within the Data object 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_id if PipelineId and AuthorizationId are not present.
Data.AgentName target.resource.name Mapped to target.resource.name if PipelineName, NamespaceName, and DisplayName are not present.
Data.AuthorizationId target.resource.product_object_id Mapped to target.resource.product_object_id if PipelineId is 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.name if PipelineName and NamespaceName are 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_id when Data.Updates.0.GroupId is 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.userid when Data.Updates.0.MemberId is 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_id if PipelineId, AuthorizationId, and AgentId are not present.
Data.NamespaceName target.resource.name Mapped to target.resource.name if PipelineName is 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_id field within target.resource_ancestors when the ancestor is of type CLOUD_PROJECT.
ProjectName target.resource_ancestors.name, target.resource.attribute.labels Used to populate the name field within target.resource_ancestors when the ancestor is of type CLOUD_PROJECT. Also added as a label to target.resource.attribute.labels with key "ProjectName".
RoleLocation target.location.name Directly mapped to target.location.name.
ScopeDisplayName target.resource_ancestors.name Used to populate the name field within target.resource_ancestors when the ancestor is of type CLOUD_ORGANIZATION.
ScopeId target.resource_ancestors.product_object_id Used to populate the product_object_id field within target.resource_ancestors when the ancestor is of type CLOUD_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 to network.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 OperationName and 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 AuthenticationMechanism contains "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 summary is "UserAuthToken".
N/A target.resource.resource_type Set to "SETTING" if pipeline_id is present, "CREDENTIAL" if authorization_id is present, "DEVICE" if agent_id is present, or "DATABASE" if namespace_id is present. Otherwise, it is set to "STORAGE_BUCKET" in some cases based on operationName.
N/A target.resource.resource_subtype Set to "Pipeline" if pipeline_id is present, "Token" if authorization_id is present, "Agent" if agent_id is present, or "Namespace" if namespace_id is present.

Changes

2024-01-19

  • Changed "metadata.eventtype" value from "SERVICE*" to "USER_RESOURCE_UPDATE_CONTENT" if principal user data and target resource data are present.
  • Changed mapping for "IpAddress" from "target.ip" to "principal.ip".
  • Changed mapping for "ActorCUID" from "principal.user.product_object_id" to "additional.fields".
  • Changed mapping for "ScopeId" from "principal.asset_id" to "resource_ancestors.product_object_id".
  • Changed mapping for "_Internal_WorkspaceResourceId" from "target.resource.product_object_id" to "additional.fields".
  • Changed mapping for "ProjectId" from "target.resource.attribute.labels" to "target.resource_ancestors.product_object_id".
  • Changed mapping for "AuthenticationMechanism" from "security_result.summary" to "extensions.auth.auth_details".
  • Changed mapping for "CorrelationId" from "network.session_id" to "additional.fields".
  • Changed mapping for "ScopeDisplayName" from "additional.fields" to "target.resource_ancestors.name".
  • Changed mapping for "PipelineId" from "additional.fields" to "target.resource.product_object_id".
  • Changed mapping for "PipelineName" from "additional.fields" to "target.resource.name".
  • Changed mapping for "PipelineScope" from "additional.fields" to "target.resource.attribute.labels".
  • Changed mapping for "PipelineRevision" from "additional.fields" to "target.resource.attribute.labels".
  • Changed mapping for "ProjectId" from "target.resource.resource.attribute.labels" to "target.resource_ancestors.product_object_id".
  • Changed mapping for "Area" from "additional.fields" to "target.application".
  • Mapped "MICROSOFT_AZURE" value to "target.asset.attribute.cloud.environment".
  • When "AuthenticationMechanism" is having "ServicePrincipal" value, then set "SERVICE_ACCOUNT_TYPE" to "principal.user.account_type", else set "CLOUD_ACCOUNT_TYPE" to "principal.user.account_type".
  • Mapped "Category" to "security_result.action_details".
  • Mapped "ALLOW" or "BLOCK" to "security_result.action" based on "Details" field.
  • Mapped "ActivityId" to "additional.fields".

2024-01-09

  • Added Grok and gsub to parse the unparsed JSON logs.
  • Mapped "rec.correlationId", "properties.currentHealthStatus", "properties.previousHealthStatus", "properties.type", "properties.cause", "properties.title", "properties.details", "properties.recommendationType", "properties.recommendationCategory", "properties.recommendationImpact", "properties.recommendationName", "properties.recommendationResourceLink", "properties.recommendationSchemaVersion", "properties.eventCategory", "properties.hierarchy", "properties.message", "properties.entity", "identity.claims.xms.tcdt", "identity.claims.aio", "identity.claims.appid", "identity.claims.appidacr", "identity.claims.aud", "identity.claims.exp", "identity.claims.iat", "identity.claims.idtyp", "identity.claims.iss", "identity.claims.uti", "identity.claims.rh", "identity.claims.ver", "identity.claims.nbf", "identity.authorization.evidence.roleAssignmentId", "identity.authorization.evidence.principalType", "identity.authorization.evidence.principalId", "identity.authorization.evidence.roleAssignmentScope", "identity.authorization.evidence.roleDefinitionId" to "security_result.detection_fields".
  • Mapped "resultSignature.label", "rec.resultType", "Visibility", "Humidity", "Precipitation","MoonPhase", "Moonrise", "Moonset", "Pressure", "WindSpeed", "UVIndex", "DewPoint", WindDirection", "Sunrise", "Sunset", "Temperature", "Icon", "Conditions" to "additional.fields".
  • Mapped "level" to "security_result.severity".
  • Mapped "appname" to "target.application".
  • Mapped "category.details" to "security.result.category.details".
  • Mapped "rec.resourceId" to "target.resource.id".
  • Mapped "res.extensionResourceName" to "principal.hostname".

2023-11-23

  • Added support for a new pattern of JSON logs.
  • Mapped "data.TimeGenerated" to "metadata.event_timestamp".
  • When "_Internal_WorkspaceResourceId" is missing, then mapped "topic" to "target.resource.product_object_id".
  • Mapped "data.Data.ConnectionId" to "additional.fields".
  • Mapped "data.Data.ownerDetails" to "additional.fields".
  • Mapped "data.Data.DeploymentResult" to "additional.fields".
  • Mapped "data.Data.EnvironmentName" to "additional.fields".
  • Mapped "data.Data.JobName" to "additional.fields".
  • Mapped "data.Data.StageName" to "additional.fields".
  • Mapped "data.Data.RunName" to "additional.fields".
  • Mapped "data.Data.RetentionLeaseId" to "additional.fields".
  • Mapped "data.Data.CheckSuiteId" to "additional.fields".
  • Mapped "data.Data.CheckSuiteStatus" to "additional.fields".
  • Mapped "data.Data.ApprovalRequest" to "additional.fields".
  • Mapped "data.Data.ApprovalType" to "additional.fields".
  • Mapped "subject" to "additional.fields".
  • Mapped "data.ActorUserId" to "principal.user.userid".
  • Mapped "data.ActorDisplayName" to "principal.user.user_display_name".
  • Mapped "data.ActorCUID" to "principal.user.product_object_id".
  • Mapped "data.ActorUPN" to "principal.user.email_addresses".
  • Mapped "data.ScopeId" to "principal.asset_id".
  • Mapped "data.CorrelationId" to "network.session_id".
  • Mapped "data.UserAgent" to "network.http.user_agent".
  • Mapped "data.ProjectId" to "target.resource.attribute.labels".
  • Mapped "data.ScopeType" to "additional.fields".
  • Mapped "data.ProjectName" to "target.resource.attribute.labels".
  • Mapped "data.Details" to "metadata.description".
  • Mapped "data.CategoryDisplayName" to "security_result.rule_name".
  • Mapped "data.Area" to "additional.fields".
  • Mapped "data.Id" to "metadata.product_log_id".
  • Mapped "data.ActionId" to "metadata.product_event_type".
  • Mapped "data.Timestamp" to "metadata.event_timestamp".

2022-06-28

  • Newly created parser