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:
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:
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This code parse the audit logs exported from azure devops and saved as file auditlog.json | |
function Parse-AzureDevOpsAuditLogs { | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory=$true)] | |
[ValidateNotNullOrEmpty()] | |
[string] $Path | |
) | |
Begin { | |
Write-Verbose "In Begin Block: Parse-AzureDevOpsAuditLogs" | |
} | |
Process{ | |
Write-Verbose "In Process Block: Parse-AzureDevOpsAuditLogs" | |
if (!(Test-Path $Path)){ | |
Write-Host "The mentioned file: $Path not found." | |
return | |
} | |
$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" | |
} | |
} | |
Parse–AzureDevOpsAuditLogs –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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Gets the Azure DevOps logs using the REST API | |
Function Get-AzureDevOpsAuditLogs { | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory=$true)] | |
[ValidateNotNullOrEmpty()] | |
[String] $OrganizationName, | |
[Parameter(Mandatory=$false)] | |
[ValidateNotNullOrEmpty()] | |
[String] $OutFilePath = "auditlogs.json", | |
[Parameter(Mandatory=$false,ParameterSetName='TimeRange')] | |
[ValidateNotNullOrEmpty()] | |
[String] $StartTime, | |
[Parameter(Mandatory=$false,ParameterSetName='TimeRange')] | |
[ValidateNotNullOrEmpty()] | |
[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))) | |
} | |
Process{ | |
Write-Verbose "In Process Block: Get-AzureDevOpsAuditLogs()" | |
$url = "" | |
if ($StartTime -or $EndTime){ | |
$url = "https://auditservice.dev.azure.com/$($OrganizationName)/_apis/audit/downloadlog?format=$($LogType)&startTime=$($StartTime)&endTime=$($EndTime)&api-version=5.1-preview.1" | |
} | |
else{ | |
$url = "https://auditservice.dev.azure.com/$($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 {organization–name} |
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.
Nice summary Mohit!
LikeLike
Thanks Jaap for your kind words.
LikeLike
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.
LikeLike
Helpful article!
We added a link to this article here: https://automate.guru/azure-devops-audit-logs-forwarding/
There is also information about forwarding ADO Audit Logs to Log Analytics and then querying them with KQL.
LikeLike