Skip to content

Instantly share code, notes, and snippets.

@rupeshtech
Created February 20, 2021 14:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rupeshtech/2b7c4c4a8ab4103605409426547ac797 to your computer and use it in GitHub Desktop.
Save rupeshtech/2b7c4c4a8ab4103605409426547ac797 to your computer and use it in GitHub Desktop.
Create a Consumption Budget for ResourceGroups in Azure
param (
[string]$ResourceGroupsToExclude
)
function Login-AzureAutomation([bool] $AzModuleOnly) {
try {
$RunAsConnection = Get-AutomationConnection -Name "AzureRunAsConnection"
Write-Output "Logging in to Azure ($AzureEnvironment)..."
if (!$RunAsConnection.ApplicationId) {
$ErrorMessage = "Connection 'AzureRunAsConnection' is incompatible type."
throw $ErrorMessage
}
if ($AzModuleOnly) {
Connect-AzAccount `
-ServicePrincipal `
-TenantId $RunAsConnection.TenantId `
-ApplicationId $RunAsConnection.ApplicationId `
-CertificateThumbprint $RunAsConnection.CertificateThumbprint `
-Environment $AzureEnvironment
Select-AzSubscription -SubscriptionId $RunAsConnection.SubscriptionID | Write-Verbose
} else {
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $RunAsConnection.TenantId `
-ApplicationId $RunAsConnection.ApplicationId `
-CertificateThumbprint $RunAsConnection.CertificateThumbprint `
-Environment $AzureEnvironment
Select-AzureRmSubscription -SubscriptionId $RunAsConnection.SubscriptionID | Write-Verbose
}
} catch {
if (!$RunAsConnection) {
$RunAsConnection | fl | Write-Output
Write-Output $_.Exception
$ErrorMessage = "Connection 'AzureRunAsConnection' not found."
throw $ErrorMessage
}
throw $_.Exception
}
}
function Get-AzCachedAccessToken()
{
$ErrorActionPreference = 'Stop'
if(-not (Get-Module Az.Accounts)) {
Import-Module Az.Accounts
}
$azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
if(-not $azProfile.Accounts.Count) {
Write-Error "Ensure you have logged in before calling this function."
}
$currentAzureContext = Get-AzContext
$profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azProfile)
Write-Debug ("Getting access token for tenant" + $currentAzureContext.Tenant.TenantId)
$token = $profileClient.AcquireAccessToken($currentAzureContext.Tenant.TenantId)
$token.AccessToken
}
$AzureEnvironment='AzureCloud'
$UseAzModule = $true
Login-AzureAutomation $UseAzModule
$subscriptionId= (Get-AzContext).Subscription.id
$token= Get-AzCachedAccessToken
$bearerToken="Bearer $token"
$contactEmails= @('xxxxxxxxx')
$bodyObject=@{
"properties" = @{
"category"= "Cost"
"amount"="100"
"timeGrain" = "Monthly"
"timePeriod"= @{
"startDate"= "2021-01-01"
"endDate"= "2025-01-01"
}
"notifications"=@{
"NotificationForExceededBudget"= @{
"enabled"= "true"
"operator"= "GreaterThan"
"threshold"= "90"
"contactEmails"=$contactEmails
}
}
}
}
$headers = @{
'Authorization' = $bearerToken
}
$body = $bodyObject | convertto-json -Depth 2
$rgs= Get-AzResourceGroup | where { $ResourceGroupsToExclude -notlike "*$($_.ResourceGroupName)*" }
foreach ($rg in $rgs) {
$resourceGroupName= $rg.ResourceGroupName
$budgetName= "$resourceGroupName-cb"
$URI="https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Consumption/budgets/$budgetName"+"?api-version=2019-10-01"
Invoke-RestMethod -Method Put -ContentType "application/json" -Uri $URI -Headers $headers -Body $body
}
@erwinkramer
Copy link

@rupeshtech thanks for the example, but | convertto-json -Depth 2 will fail as it's 3 deep. NotificationForExceededBudget will not be converted to JSON. Maybe the behavior is different in another version, but in PowerShell Core it won't work with the value 2.

Also, to get the token, I find that the following is a much cleaner approach:

$context = Get-AzContext
$token = (Get-AzAccessToken -TenantId $context.Tenant.Id.ToString() -ResourceUrl 'https://management.azure.com').Token
$bearerToken="Bearer $token"

@rmccarrick
Copy link

Thanks for this ... I get the following error:

Invoke-RestMethod : {"error":{"code":"400","message":"Notification cannot have all of Contact Emails, Contact Roles
and Contact Groups empty.\r\n (Request ID: 290e6349-3219-46d4-9c82-9861a3d25132)"}}
At line:1 char:17

  • ... ateBudget = Invoke-RestMethod -Uri $restUri -Method Put -Headers $aut ...
  •             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExc
      eption
    • FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

I have no Contact Roles or Contact Groups and already have budgets with those fields empty. Any ideas?
thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment