Get Azure cost estimates in Azure DevOps PR comments with Bicep

Shifting FinOps left

Not too long ago, I discovered an open source project on GitHub called InfraCost. It aims to help cloud teams shift left FinOps by helping provide Terraform Azure cost estimations, into Pull Request comments within GitHub/Azure DevOps, including a fancy VS Code extension. And whilst I was excited to try it out, it became quickly clear that it did not support ARM/Bicep yet.

There has been suggestions in the past to get cost estimations added to the official Bicep VS Code extension. Whilst there is some curiosity as a concept from the Bicep team on that, I suspect it’s unlikely to materialise, given other higher-priority demands—understandably so.

However, this got me thinking about how fantastic of an idea it is to have cost estimations whilst writing Infrastructure as Code. FinOps shouldn’t be someone’s job; it’s a shared responsibility. As FinOps is both a framework and a culture, we all need to incorporate it into our skillsets and stay mindful of cloud costs, no matter our role.

For me, the idea of seamlessly obtaining cost estimations from Azure Bicep code feels like a natural evolution!

Before we continue, I want to point out that this is a concept and is not a ‘one size fits all’ approach. The scripts and script logic is generic for a basic setup and assumes certain parameters in this blog. Feel free to take my provided examples and expand or change them however you see fit for your use cases!

Azure Cost Estimator

I thought to myself: surely there must already be a project out there that does something similar for Bicep, even if it’s not InfraCost, right? And I found one—(Azure Cost Estimator)!

Now, ACE isn’t as mature as InfraCost, largely because it’s founded and predominantly maintained by Kamil as a solo effort, whereas InfraCost benefits from a large team of contributors. But it’s still an incredible tool! I strongly encourage you to star the repository and support Kamil’s work. (If you’d like to advocate for Bicep support in InfraCost as well, check out this GitHub issue to help raise awareness.)

Having found a Bicep-supported cost estimation tool, I wanted to take things a step further: could I get it to report cost estimations in Azure DevOps pull request comments?

Spoiler alert: yes!

Setup & Prereqs

Next, for the setup, I will assume you already have a few things created, but if not there is plenty of guides that already exist for these:

Firstly, you will need to grant the ‘build service’ account the ability to contribute to pull requests. This is easily done by navigating to the repositories area, under your project settings, selecting the repository you want to implement this in, and allowing the service to contribute to pull requests:

Build Service permissions - azure cost estimator

Next, upload these files to your repository. In this example, they are located in the root of the repository. However, if you’re familiar with Azure DevOps (ADO), you can easily organise them elsewhere to suit your preferences.

Pipeline – YAML

This is what will run when a new pull request is created, it will download the Azure Cost Estimator tool and then run the additional scripts I have created for the solution to work.

trigger: none
pool:
vmImage: ubuntu-latest
jobs:
– job: RunNoCache
displayName: Bicep Estimator
steps:
– checkout: self
fetchDepth: 0
– task: Bash@3
inputs:
targetType: ‘inline’
script: |
wget https://github.com/TheCloudTheory/arm-estimator/releases/download/1.5.1/linux-x64.zip
unzip linux-x64.zip
chmod +x ./azure-cost-estimator
workingDirectory: $(System.DefaultWorkingDirectory)
– task: AzureCLI@2
inputs:
azureSubscription: ‘change_me’
scriptType: ‘pscore’
scriptLocation: ‘scriptPath’
scriptPath: ‘$(System.DefaultWorkingDirectory)/bicep-diff.ps1’
addSpnToEnvironment: true
workingDirectory: ‘$(System.DefaultWorkingDirectory)’
– task: PowerShell@2
condition: eq(variables[‘Build.Reason’], ‘PullRequest’)
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
inputs:
filePath: ‘post-markdown.ps1’
workingDirectory: ‘$(System.DefaultWorkingDirectory)’
– task: PublishBuildArtifacts@1
inputs:
PathtoPublish: ‘$(System.DefaultWorkingDirectory)/md-reports’
ArtifactName: ‘cost-reports’

view rawbicep-estimatior.yaml hosted with ❤ by GitHub

PowerShell – Bicep Git Diff

This PowerShell script is invoked by the build validation pipeline task to scan the merging branch for changes to any Bicep files within the bicep/infra folder. You’ll need to update this folder path (line 2) to match your repository structure.

The script also creates the required Markdown report directory. The subscription and resource group (set to bicep-estimator in my case) on line 10 are used by the ACE tool to perform a what-if analysis.

# get the latest changed bicep files – change bicep/infra folder for your needs
$files = (git diff origin/main –name-only — bicep/infra | Where-Object {$_.EndsWith(‘.bicep’)})
New-Item -Name “md-reports” -ItemType Directory
if ($files.count -ge 1) {
# foreach bicep file run cost estimator ACE
foreach ($file in $files) {
# amend bicepparam name as you see fit
$params = $file | Split-Path | Get-ChildItem -Recurse -Filter *prod.bicepparam
Write-Host “##[debug] $file found with parameter file $params…running cost estimator”
./azure-cost-estimator $file YOUR_SUB_GUID bicep-estimator –parameters $params –currency GBP –outputFormat Table –generate-markdown-output –generateJsonOutput true –generateHtmlOutput true
Copy-Item -Path “.\ace_*.md” -Destination md-reports
}
Write-Host “$($files.count) Bicep files found”
Write-Host “$params”
}
else {
Write-Host “No Bicep updates”
}

view rawbicep-diff.ps1 hosted with ❤ by GitHub

PowerShell – Post Markdown Reports to Pull Request as a comment

Finally, this PowerShell script is executed from the build YAML task. It retrieves all the Markdown reports generated by the Azure Cost Estimator, converts them into raw content, and posts them as comments on the pull request for review.

#Status codes https://learn.microsoft.com/en-us/dotnet/api/microsoft.teamfoundation.sourcecontrol.webapi.commentthreadstatus?view=azure-devops-dotnet
$statusCode = 1
# Finding all ACE Markdown reports in directory
$markdownFiles = Get-ChildItem -Path . -Recurse -Filter ace_*.md
foreach ($markdownFile in $markdownFiles) {
# MD Content & JSON Body
Write-Host “Attempting to post markdown $markdownFile to PR”
$content = Get-Content -Path $markdownFile -Raw
Show-Markdown -Path $markdownFile
$body = @”
{
“comments”: [
{
“parentCommentId”: 0,
“content”: “$content”,
“commentType”: 1
}
],
“status”: $StatusCode
}
“@
Write-Debug $Body
# Post content to PR
# https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20threads?view=azure-devops-rest-5.1
try {
$url = “$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/git/repositories/$($env:BUILD_REPOSITORY_NAME)/pullRequests/$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)/threads?api-version=7.0”
Write-Host “URL: $url”
$response = Invoke-RestMethod -Uri $url -Method POST -Headers @{Authorization = “Bearer $env:SYSTEM_ACCESSTOKEN”} -Body $Body -ContentType application/json
if ($response -ne $Null) {
Write-Host “*******************Bingo*********************************”
}
}
catch {
Write-Error $_
Write-Error $_.Exception.Message
}
}

view rawpost-markdown.ps1 hosted with ❤ by GitHub

Build Validation Policy

Once the above files are committed to the root directory of your main repository, you can add a build validation policy to the repository. This policy ensures that, during a pull request, the YAML jobs execute and post the Bicep cost estimations directly into the pull request’s comments section. Hopefully, the earlier explanation about granting access to the build service account now makes sense.

This is done within the project settings > repositories > your repo > main branch > Policies > Add new build policy > select the YAML pipeline.

ADO Build Policy - azure cost estimator

In action

To test if everything is working, create a new branch and make a change to a Bicep file (even a trivial change, like adding a space). Commit the change and create a pull request to the main branch. This will trigger the build validation pipeline. If everything is set up correctly, you should see a detailed report posted in the pull request comments, providing an Azure Cost Estimation for your Bicep templates.

Success! 🎉

Azure DevOps Pull Request Comment - azure cost estimator

In the example above, you can see the Azure cost estimation for an Application Gateway generated from a basic Bicep template. By running ACE and leveraging its Markdown reports, you can effectively gain cost insights before changes are merged into your main branch.

Additionally, by uploading the Markdown reports as pipeline artifacts, you can view the complete reports and download them directly from the pipeline:

Pipeline Artifact - azure cost estimator

Conclusion

This is an interesting concept to include in your pull request build validation tasks. Even if it only provides a ballpark cost estimation, I still find it useful. While the ACE tool doesn’t yet support all resource types, you can request additional resources or contribute to the project yourself if you’d like to help improve it.

In my view, this is a step in the right direction. Being able to create a Bicep template and receive even basic pricing calculator estimations is a valuable addition to FinOps practices.

About the Author:

Dan Rios

Microsoft Azure MVP | Azure Architect | Azure & Bicep advocate

Reference:

Rios, D (2024). Get Azure cost estimates in Azure DevOps PR comments with Bicep. Available at: Get Azure cost estimates in Azure DevOps PR comments with Bicep – Rios Engineer [Accessed: 19th December 2024].

Share this on...

Rate this Post:

Share:

Topics:

Azure