Had some fun the last day before Easter vacation. Created a script that downloads all invoices for each subscription in an Azure tenant.
We have found it cumbersome to control the Azure invoice number on the master card invoices sent from the economics department. The invoice number looks something like this “E0000XXXXX” and connecting this to a subscription is hard.
Our solution to the problem was a script downloading the invoices with a script, and using Adobe Acrobat Reader to find the invoice number with advanced search. In this post I go through the script and how you can use Adobe Acrobat to find the invoice number related to the correct subscription.
First I did was creating an advanced PowerShell function, this is specified by using the
attribute under the function name. The script takes two default parameters. The first one is CmdletBinding
, this is the path where all the invoices gets downloaded to.$FolderPath
Note: In the script I have not added checks to see if the path exists or creates the path if it doesn’t exist. To avoid error, make sure to use a path that exists or just use the default path.
The second parameter is
, this specifies the count of most recent invoices to download. If you want to download the 10 latest invoices as an example, you set the $MaxCount
to 10.$MaxCount
# Starting with creating an advanced function and the parameters function Get-AzureBillingInformation { [CmdletBinding()] param ( # Folder Path for storing invoices [Parameter(Mandatory = $false)] [String] $FolderPath = "C:\Temp\AzureInvoice", # Limit of invoices to download [Parameter(Mandatory = $false)] [int] $MaxCount = 1 ) }
Next step in the script is the begin
block. The begin
block is used for one time pre-processing, and here I’m using it to get the timestamp from when the script was initiated. The $StartTime
variable is used later to calculate how long it took to download all the invoices from the subscriptions.
# The begin block begin { $StartTime = Get-Date }
Next, is the
block. This is used to for multiple record processing and is the main part of the script. This is where the action happens.process
First I created three variables; the
variable is used to store all subscriptions i gets from the $Subscriptions
Get-AzSubscription
cmdlet, $InvoiceArray
is used to store the invoices per subscriptions, and the $TotalSubscriptions
counts the total number of subscriptions in the Azure tenant.
Then I’m looping through all the subscriptions stored in the
variable. Then using the $Subscription
cmdlet to set the context to the subscriptions specified in the variable. This is for getting the invoice(s) connected to each subscription, this is stored in the Set-AzContext
$InvoiceArray
.
Then I’m looping through the
to download the invoice(s) for each subscription. Here I added a simple check to see if a file with the invoice name exists in the specified folder, if it does the loop breaks. If not, the script continue and creates a file with the invoice name, this only creates empty files to avoid getting an error message from the $InvoiceArray
cmdlet, that says something like “Path not found” when asking it to output the file to a path that does not exists.Invoke-WebRequest
When the files are created, the
cmdlet downloads the file from the Azure Portal and stores it in the specified path. Each file created in previous step will now be an subscription invoice.Invoke-WebRequest
# The process block process { $Subscriptions = Get-AzSubscription $InvoiceArray = @() $TotalSubscriptions = 0 foreach ($Subscription in $Subscriptions) { $TotalSubscriptions++ $SubscriptionName = $Subscription.Name Write-Verbose "Setting Subscription Context to: $SubscriptionName.." Set-AzContext -Subscription "$SubscriptionName" Write-Verbose "Getting invoice information for: $SubscriptionName.." $InvoiceArray += (Get-AzBillingInvoice -MaxCount $MaxCount -GenerateDownloadUrl | Select-Object Name, DownloadUrl) Write-Output "" } foreach ($Invoice in $InvoiceArray) { $Name = $Invoice.Name $Url = $Invoice.DownloadUrl if (Test-Path "$FolderPath\$Name.pdf") { Write-Host -ForegroundColor Red "File $Name.pdf already exists.." exit } else { Write-Host -ForegroundColor Green "`nCreating invoice file: $Name.pdf.." New-Item -Path "C:\temp\AzureInvoice\" -Name "$Name.pdf" -ItemType File Write-Verbose "Downloading invoice: $Name.pdf.." Invoke-WebRequest -Uri $Url -Outfile "$FolderPath\$Name.pdf" } } }
At last we have the end
block, this is used for one time post-processing. In this case, to output the time it took downloading the specified amount of invoices per subscription and the total count of subscriptions. Using the
variable from the $StartTime
block divided by the time the script finished, and the begin
from the start of the process block.$TotalSubscriptions
# The end block end { Write-Output "" Write-Verbose "Outputting time taken to download all the invoices.." Write-Host -ForegroundColor Green "Time taken to download $MaxCount invoice(s) per subscription, from a total of $TotalSubscriptions subscriptions: $((Get-Date).Subtract($StartTime).Seconds) second(s)" }
Also I added a help comment for what the script does and how to use it.
# The powershell help manual for the command <# .SYNOPSIS Downloads subscriptions invoices from the Azure Portal. .EXAMPLE PS C:\> Get-AzureBillingInformation Uses default values and saves the three latest subscription invoices to C:\temp\AzureInvoice .EXAMPLE PS C:\> Get-AzureBillingInformation -MaxCount 1 Saves the latest subscription invoices to C:\temp\AzureInvoice .PARAMETER FolderPath Defines where to store the downloaded invoices. .PARAMETER MaxCount Specifies how many invoices to download back in time. .NOTES Use Connect-AzAccount to logon the portal. Also you need permission to Billing information. Use . .\Get-AzureBillingInformation.ps1 to load script into memory To find out which connect the encrypted invoice number on the credit card invoice to a Azure Subscription, use Adobe Acrobat > Edit > Advanced Search > Specify All PDF in selected folder > Enter invoice number > Then you should you find the correct invoice #>
Here is the complete script.
<# .SYNOPSIS Downloads subscriptions invoices from the Azure Portal. .EXAMPLE PS C:\> Get-AzureBillingInformation Uses default values and saves the three latest subscription invoices to C:\temp\AzureInvoice .EXAMPLE PS C:\> Get-AzureBillingInformation -MaxCount 1 Saves the latest subscription invoices to C:\temp\AzureInvoice .PARAMETER FolderPath Defines where to store the downloaded invoices. .PARAMETER MaxCount Specifies how many invoices to download back in time. .NOTES Use Connect-AzAccount to logon the portal. Also you need permission to Billing information. Use . .\Get-AzureBillingInformation.ps1 to load script into memory To find out which connect the encrypted invoice number on the credit card invoice to a Azure Subscription, use Adobe Acrobat > Edit > Advanced Search > Specify All PDF in selected folder > Enter invoice number > Then you should you find the correct invoice #> function Get-AzureBillingInformation { [CmdletBinding()] param ( # Folder Path for storing invoices [Parameter(Mandatory = $false)] [String] $FolderPath = "C:\Temp\AzureInvoice", # Limit of invoices to download [Parameter(Mandatory = $false)] [int] $MaxCount = 1 ) begin { $StartTime = Get-Date } process { $Subscriptions = Get-AzSubscription $InvoiceArray = @() $TotalSubscriptions = 0 foreach ($Subscription in $Subscriptions) { $TotalSubscriptions++ $SubscriptionName = $Subscription.Name Write-Verbose "Setting Subscription Context to: $SubscriptionName.." Set-AzContext -Subscription "$SubscriptionName" Write-Verbose "Getting invoice information for: $SubscriptionName.." $InvoiceArray += (Get-AzBillingInvoice -MaxCount $MaxCount -GenerateDownloadUrl | Select-Object Name, DownloadUrl) Write-Output "" } foreach ($Invoice in $InvoiceArray) { $Name = $Invoice.Name $Url = $Invoice.DownloadUrl if (Test-Path "$FolderPath\$Name.pdf") { Write-Host -ForegroundColor Red "File $Name.pdf already exists.." exit } else { Write-Host -ForegroundColor Green "`nCreating invoice file: $Name.pdf.." New-Item -Path "C:\temp\AzureInvoice\" -Name "$Name.pdf" -ItemType File Write-Verbose "Downloading invoice: $Name.pdf.." Invoke-WebRequest -Uri $Url -Outfile "$FolderPath\$Name.pdf" } } } end { Write-Output "" Write-Verbose "Outputting time taken to download all the invoices.." Write-Host -ForegroundColor Green "Time taken to download $MaxCount invoice(s) per subscription, from a total of $TotalSubscriptions subscriptions: $((Get-Date).Subtract($StartTime).Seconds) second(s)" } }
That was the script for downloading invoices from Azure. Now, over to how we find the subscription connected to the invoice number.
How to use Adobe Acrobat to find correct invoice number
To find the correct invoice number using Adobe Acrobat you can follow the steps below:
If you don’t have it installed you can get it from https://get.adobe.com/no/reader/, the free version works just fine.
First, open Adobe Acrobat Reader DC.
Then go to Edit > Advanced Search
Open the Dropdown list and select Browse for Location and select C:\temp\AzureInvoice
Then enter the Invoice number you want to find, can look something like E0000XXXXX.
Then hit the search button.
This should list out something like below. When opening the pdf in the search result, you should see the subscription correlated to the invoice number. In this case I won’t show the actual invoice. So the steps ends here.
This was a fun little project on a Friday afternoon. Hopefully will this save a lot of time, instead of searching through one by one invoice in the Azure Portal. Hope you enjoyed it and can find it useful.