Working with Audit logs in Azure DevOps

With the arrival of the Sprint 154 updates, auditing has been introduced in the Azure DevOps. This has been a long standing demand from various enterprises (including Ours !). We wanted to observe activities and monitor changes that have occurred in the Azure DevOps across the Organization. It is in the preview phase as of this writing of this blog post, but it is very useful in the preview phase as well. In this blog post, we are going to see what is recorded in the Azure DevOps as part of the auditing, how we can access it and what we can do with the same.

Enable Auditing for Azure DevOps

First things first, how do we enable auditing for Azure DevOps? In the cloud version of Azure DevOps, auditing is enabled by default at the organization level. So you do not need to do any actions on your end to enable the same. As a security measure, you are not allowed to disable auditing. Also, if someone has viewed the audit logs, that also gets recorded. This ensures that no one escapes auditing process.

Audit events are currently stored for 90 days (not an adjustable limit). After this, they are deleted. However, you can download audit logs explained later in the post), fed to log monitoring systems and then choose to keep the audit data for longer than 90-day period.

View and Export Audit Logs

Audit logs can be viewed by going to Organization Settings -> Auditing:

viewing auditing logs in azure devops

You can adjust the date and time range for which you want to view the audit logs. You can also export audit logs as CSV and JSON files using the Download button in the top-right-hand side of the auditing page. Events are downloaded based on the time range you’ve selected in the filter.

Once the logs are exported, we can analyze the same using tool of our choice or we can feed it to external log monitoring system by various means. We can also use REST APIs to export this information at defined frequency to automate the whole process from end to end.

Manage Access for the Audit Logs

By default, Project Collection Administrators are the only group that can access the auditing feature.

If you are not able to view audit logs for your organization, you need to ask for the same your from your admins. This can be done by going to Security -> Permissions -> find the group or users to provide auditing access to -> Set ‘View Audit log’ to allow:

allow audit log access to users or groups

What is captured as part of Auditing

Audit events may occur in any portion of Azure DevOps; some examples of auditable events include: Git repository creations, permission changes, resource deletions, code downloads, accessing the auditing feature, and much more.

The audit events include information such as who caused the event to be logged and their IP, what happened, and other useful details that can help you answer the who, what, when, and where questions.

Below details covers what is available on the auditing page:

Information Details
Actor Display name of the individual that triggered the audit event to be recorded
IP IP address of the individual that triggered the audit event to be recorded
Timestamp Time that the triggered event happened. Time is localized to your time zone
Area Location in Azure DevOps where the event took place
Category Description of the type of action that occurred For example, Modify, Rename, Create, Delete, Remove, Access
Details Brief description of what happened during the event

Besides what you can see on the auditing page, each audit event records additional pieces of information to what is viewable on the auditing page. This information includes the authentication mechanism, a correlation ID to link similar events together, user agent, and additional information that’s dependent on the type of audit event. This information can only be viewed by downloading auditing events.

Parse Audit logs using PowerShell

Since logs files are available in CSV and JSON, it can be easily parsed using native cmdlets such as Import-CSV and/or ConvertFrom-JSON and output can be tuned to one’s choice. Below is the quick and dirty example for same:

# This code parse the audit logs exported from azure devops and saved as file auditlog.json
function Parse-AzureDevOpsAuditLogs {
[string] $Path
Begin {
Write-Verbose "In Begin Block: Parse-AzureDevOpsAuditLogs"
Write-Verbose "In Process Block: Parse-AzureDevOpsAuditLogs"
if (!(Test-Path $Path)){
Write-Host "The mentioned file: $Path not found."
$auditLogs = Get-Content Path $Path | ConvertFrom-Json
Write-Host "Timestamp `t ScopeType `t ProjectName `t IpAddress `t Details `t Area `t CategoryDisplayName"
foreach($auditlog in $auditLogs){
Write-Host "$($auditLog.Timestamp) `t $($auditLog.ScopeType) `t $($auditLog.ProjectName) `t $($auditLog.IpAddress) `t $($auditLog.Details) `t $($auditLog.Area) `t $($auditLog.CategoryDisplayName)"
End {
Write-Verbose "In End Block: Parse-AzureDevOpsAuditLogs"
ParseAzureDevOpsAuditLogs Path .\auditLog.json

You can choose to use any programming language you want to.

Import Azure DevOps logs using REST API

Although you can go fed Azure DevOps logs to a variety of logs monitoring tools, the part that you need to go to Azure DevOps portal to download the logs is a manual process. Thankfully, there are REST APIs to automate this as well.

We can use below PowerShell code to download logs:

# Gets the Azure DevOps logs using the REST API
Function Get-AzureDevOpsAuditLogs {
[String] $OrganizationName,
[String] $OutFilePath = "auditlogs.json",
[String] $StartTime,
[String] $EndTime
Begin {
Write-Verbose "In Begin Block: Get-AzureDevOpsAuditLogs()"
$pat = Get-Content Path ".\.token"
$user = ""
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$pat)))
Write-Verbose "In Process Block: Get-AzureDevOpsAuditLogs()"
$url = ""
if ($StartTime -or $EndTime){
$url = "$($OrganizationName)/_apis/audit/downloadlog?format=$($LogType)&startTime=$($StartTime)&endTime=$($EndTime)&api-version=5.1-preview.1"
$url = "$($OrganizationName)/_apis/audit/downloadlog?format=$($LogType)&api-version=5.1-preview.1"
Invoke-RestMethod Uri $url Method GET ContentType "application/json" Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)} OutFile $OutFilePath
End {
Write-Verbose "In End Block: Get-AzureDevOpsAuditLogs()"
# call above function
Get-AzureDevOpsAuditLogs OrganizationName {organizationname}

You can tune it further to your needs and run this code at defined frequencies.

Summary and Notes

Audit logs functionality is a very useful feature and we can use the same to track all the activities that have occurred in Azure DevOps. We can also download the logs using REST APIs and then fed the logs to external log monitoring systems. We can also set alerts by parsing the logs downloaded and be notified of critical events.

The source code used in this blog post might not be shown due to feature restrictions set by AMP. If you are unable to view the same, open this blog post in a full browser.

The source code used in this blog post can be found here at GitHub and is available under blog/8480 and master branches.

4 thoughts on “Working with Audit logs in Azure DevOps

  1. So the issue currently is that Powershell currently doesn’t like JSON values that are ‘duplicates’ because of how powershell handles capitalization. Obviously, JSON doesn’t care…powershell barfs on it:

    .\Parse-AzureDevOpsAuditLogs.ps1 -path .\auditlogs.json
    ConvertFrom-Json : Cannot convert the JSON string because a dictionary that was converted from the string contains the
    duplicated keys ‘ReleaseId’ and ‘releaseId’.
    At C:\Users\username\Parse-AzureDevOpsAuditLogs.ps1:22 char:48
    + $auditLogs = Get-Content -Path $Path | ConvertFrom-Json
    + ~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [ConvertFrom-Json], InvalidOperationException
    + FullyQualifiedErrorId : DuplicateKeysInJsonString,Microsoft.PowerShell.Commands.ConvertFromJsonCommand

    I’ve searched for something to fix this for a few days now and I haven’t found anything yet.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s