Get-AzVMSku.psm1
<#
.SYNOPSIS Retrieves Azure Virtual Machine SKUs information based on specified criteria. .DESCRIPTION The Get-AzVMSKU function retrieves Azure Virtual Machine SKUs information based on specified criteria such as location, operating system, and other parameters. It can filter VM SKUs based on different settings and provide detailed information about available SKUs. .PARAMETER Location Specifies the Azure region where you want to retrieve VM SKUs. Mandatory parameter when using ManualSettings parameter set. .PARAMETER ContinueOnError Specifies whether the function should continue processing in case of errors. By default, it stops on errors. .PARAMETER OperatingSystem Specifies the target operating system for which you want to retrieve VM SKUs. Mandatory parameter when using ManualSettings parameter set. .PARAMETER OSPattern Specifies a pattern to filter operating system SKUs. It can be a partial match pattern. .PARAMETER VMPattern Specifies a pattern to filter VM SKUs. It can be a partial match pattern. .PARAMETER RawFormat Specifies whether to return the VM sizes in raw format without formatting. .PARAMETER NoInteractive Specifies whether to suppress interactive prompts. .PARAMETER NewestSKUs Specifies whether to retrieve only the newest available SKUs. .PARAMETER NewestSKUsVersions Specifies whether to retrieve only the newest available versions of SKUs. .PARAMETER CheckAgreement Specifies whether to check for legal agreements for SKUs. .PARAMETER ShowLocations Specifies whether to retrieve and display available Azure locations. .PARAMETER ShowVMCategories Specifies whether to retrieve and display available VM categories. .PARAMETER ShowVMOperatingSystems Specifies whether to retrieve and display available VM operating systems. .NOTES File Name : Get-AzSku.psm1 Author : Christoffer Windahl Madsen Prerequisite : This function requires the Az PowerShell modules: . .LINK https://github.com/ChristofferWin/codeterraform #> function Get-AzVMSKU { [cmdletBinding(DefaultParameterSetName = 'ManualSettings')] param( [Parameter(ParameterSetName = "ManualSettings", Mandatory = $true)][string]$Location, [Parameter(ParameterSetName = "ManualSettings")][switch]$ContinueOnError, [Parameter(ParameterSetName = "ManualSettings", Mandatory = $true)][string]$OperatingSystem, [Parameter(ParameterSetName = "ManualSettings")][String]$VMPattern, [Parameter(ParameterSetName = "ManualSettings")][string]$OSPattern, [Parameter(ParameterSetName = "ManualSettings")][switch]$RawFormat, [Parameter(ParameterSetName = "ManualSettings")][switch]$NoInteractive, [Parameter(ParameterSetName = "ManualSettings")][switch]$NewestSKUs, [Parameter(ParameterSetName = "ManualSettings")][switch]$NewestSKUsVersions, [Parameter(ParameterSetName = "ManualSettings")][switch]$CheckAgreement, [Parameter(ParameterSetName = "ShowCommandLocations")][switch]$ShowLocations, [Parameter(ParameterSetName = "ShowCommandVMs")][switch]$ShowVMCategories, [Parameter(ParameterSetName = "ShowCommandVMsOS")][switch]$ShowVMOperatingSystems ) $MSVMURL = "https://azure.microsoft.com/en-us/pricing/details/virtual-machines/series/" $CategoryObjects = @() $LocationObjects = @() $HelperObjects = @( [pscustomobject]@{ Publisher = "MicrosoftWindowsServer" Offer = "WindowsServer" SKUs = "" #All WindowsServer SKUs's share the same offer and publisher Alias = "Server2008, Server2012, Server2012R2, Server2016, Server2019, Server2022" }, [PSCustomObject]@{ Publisher = "MicrosoftWindowsDesktop" Offer = "Windows-7" SKUs = "win7*$OSPattern*" Alias = "Windows7" }, [pscustomobject]@{ Publisher = "MicrosoftWindowsDesktop" Offer = "Windows-10" SKUs = "win10*$OSPattern*" Alias = "Windows10" }, [pscustomobject]@{ Publisher = "MicrosoftWindowsDesktop" Offer = "Windows-11" SKUs = "win11*$OSPattern*" Alias = "Windows11" }, [pscustomobject]@{ Publisher = "OpenLogic" Offer = "CentOS" SKUs = "*$OSPattern*" Alias = "CentOS" }, [pscustomobject]@{ Publisher = "Canonical" Offer = "UbuntuServer" SKUs = "*$OSPattern*" Alias = "Ubuntu" }, [pscustomobject]@{ Publisher = "Debian" Offer = "Debian-10" SKUs = "*$OSPattern*" Alias = "Debian10" }, [pscustomobject]@{ Publisher = "Debian" Offer = "Debian-11" SKUs = "*$OSPattern*" Alias = "Debian11" }, [pscustomobject]@{ Publisher = "Redhat" Offer = "rhel" SKUs = "*$OSPattern*" Alias = "Redhat" } ) $FinalOutput = [PSCustomObject]@{ SubscriptionID = "" SubscriptionName = "" TenantID = "" TenantName = "" Publisher = "" Offer = "" SKUs = "*$OSPattern*" Alias = "" Versions = [System.Collections.ArrayList]@() VMSizes = [System.Collections.ArrayList]@() CoresAvailable = 0 CoresLimit = 0 } $AliasArray = @() $AliasArray += $HelperObjects[0].Alias.Split(",") $AliasArray += $HelperObjects[1..8].Alias $AliasArray = $AliasArray | % {$_.Trim().ToLower()} $Context = Get-AzContext $FinalOutput.SubscriptionID = $Context.Subscription.Id if(!$FinalOutput.SubscriptionID -and (!$ShowVMCategories -and !$ShowVMOperatingSystems)){ Write-Error "No Azure context found. Please use either Login-AzAccount or Set-AzAdvancedContext to get one..." return } $FinalOutput.SubscriptionName = $Context.Subscription.Name $FinalOutput.TenantID = $Context.Tenant.id try { $FinalOutput.TenantName = (Get-AzTenant -ErrorAction Stop | ? {$_.Id -eq $FinalOutput.TenantID}).Name } catch { Write-Verbose "Was not possible to retrieve the Tenant name, continuing..." } if(!$ShowVMCategories -and !$ShowVMOperatingSystems){ try{ $Locations = Get-AzLocation -ErrorAction Stop } catch{ Write-Error "An error occured while trying to retrieve all available locations from Azure...`n$_" return } } if($ShowVMOperatingSystems){ return $AliasArray } if($ShowLocations){ try{ Get-AzVMUsage -Location "abc" -ErrorAction Stop #Made to fail to retrieve exception } catch{ $AcceptableLocations = $_.Exception.Message.Split("`n") $AcceptableLocations = $AcceptableLocations[2].Split(" ") $AcceptableLocations = (($AcceptableLocations[($AcceptableLocations.IndexOf("locations") + 2)..$AcceptableLocations.Length]) -Replace "[^\w\s]", "").Trim() } foreach($Location in $AcceptableLocations){ $LocationObjects += [PSCustomObject]@{ ShortName = $Location LongName = ($Locations | ? {$_.Location -eq $Location}).DisplayName } } return $LocationObjects } if($ShowVMCategories){ try{ $WebsiteContent = (Invoke-WebRequest -UseBasicParsing -Uri $MSVMURL -ErrorAction Stop).Content.Split("`n") } catch{ Write-Warning "Was not able to retrieve required information from Microsoft for the flag 'ShowVMCategories'..." } $Categories = $WebsiteContent | ? {$_ -like "*h2*series*"} foreach($Category in $Categories){ $CategoryObjects += [pscustomobject]@{ Title = $Category.Trim().Replace("<h2>", "").Replace("</h2>", "") Description = $WebsiteContent[$WebsiteContent.IndexOf($Category) + 2].Trim() -Replace "<[^>]+>|&#\d+;", "" } } return $CategoryObjects } do{ $OperatingSystemOK = $false if($AliasArray.Contains($OperatingSystem.ToLower())){ $OperatingSystemOK = $true Continue } if($NoInteractive -or !$ContinueOnError){ Write-Error "The provided operating system did not match any possible value.`nPlease use the command again with switch 'ShowVMOperatingSystems'" return } $OperatingSystem = Read-Host "Please provide a valid operating system, if in any doubt, run the function with switch 'ShowVMOperatingSystems'" } while(!$OperatingSystemOK) try{ Get-AzVMUsage -Location "abc" -ErrorAction Stop #Made to fail to retrieve exception } catch{ $AcceptableLocations = $_.Exception.Message.Split("`n") $AcceptableLocations = $AcceptableLocations[2].Split(" ") $AcceptableLocations = ($AcceptableLocations[($AcceptableLocations.IndexOf("locations") + 2)..$AcceptableLocations.Length]) -Replace "[^\w\s]", "" } do{ $LocationOK = $false if($AcceptableLocations.contains(($Location.Replace(" ", "").ToLower()))){ $LocationOK = $true Break } else{ if($NoInteractive -or !$ContinueOnError){ Write-Error "The location: $Location was not found in the Azure database..." return } else{ Write-Warning "The location: $Location was not found in the Azure database..." $Location = Read-Host "Please provide a new location to use... If in any doubt, run this function with the -ShowLocations switch" } } } while(!$LocationOK) do{ try{ $Usage = (Get-AzVMUsage -Location $Location -ErrorAction Stop | ? {$_.Name.LocalizedValue -eq "Total Regional vCPUs"}) $FinalOutput.CoresAvailable = $Usage.Limit - $Usage.CurrentValue $FinalOutput.CoresLimit = $Usage.Limit } catch{ if($_.Exception.Message -like "*Microsoft.Azure.Management.Compute.Models.VirtualMachine', on 'T MaxInteger*"){ $Usage = $true Write-Verbose "Powershell_5 detected, cannot retrieve the quota for VM cores on subscription: $SubscriptionID" continue } if(!$ContinueOnError){ Write-Error "An error occured while trying to retrieve the current quotas from the subscription: $SubscriptionID`n$_" return } } } while(!$Usage) do{ try{ if($VMPattern){ $VMsizes = Get-AzVMSize -Location $Location -ErrorAction Stop | ? {$_.Name -like "Standard_$VMPattern*" -or $_.Name -like "Basic_$VMPattern*"} if($VMsizes.Count -eq 0){ if($ContinueOnError -and !$NoInteractive){ Write-Warning "0 Virtual machine sizes found using pattern '$VMPattern'..." $VMPattern = Read-Host "Please provide a new pattern or simply press return to retrieve all vm sizes instead..." Continue } Write-Error "No Virtual machine sizes found using pattern '$VMPattern'..." return } Write-Verbose "Found $($VMsizes.Count) VM sizes that matches the pattern '$VMPattern'..." } else{ $VMSizes = Get-AzVmSize -Location $Location -ErrorAction Stop } Write-Verbose "VM sizes successfully retrieved..." } catch{ Write-Error "The following error occured while trying to retrieve vm sizes...:`n$_" return } if($RawFormat){ $FinalOutput.VMSizes = $VMsizes } else{ foreach($VM in $VMsizes){ $FinalOutput.VMSizes.Add([PSCustomObject]@{ Name = $VM.Name CoresAvailable = $VM.NumberOfCores MemoryInGB = if($VM.MemoryInMB -gt 0){$VM.MemoryInMB / 1024} else{0} MaxDataDiskCount = $VM.MaxDataDiskCount OSDiskSizeInGB = if($VM.OSDiskSizeInMB -gt 0){$VM.OSDiskSizeInMB / 1024} else{0} TempDriveSizeInGB = if($VM.ResourceDiskSizeInMB -gt 0){$VM.ResourceDiskSizeInMB / 1024} else{0} }) | Out-Null } } } while(!$VMsizes) switch($OperatingSystem){ "Server2008" {$FinalOutput.SKUs = "2008-*$OSPattern*"; $FinalOutput.Offer = $HelperObjects[0].Offer; $FinalOutput.Publisher = $HelperObjects[0].Publisher} "Server2012" {$FinalOutput.SKUs = "2012-data*$OSPattern*";$FinalOutput.Offer = $HelperObjects[0].Offer; $FinalOutput.Publisher = $HelperObjects[0].Publisher} "Server2012R2" {$FinalOutput.SKUs = "2012-r2*$OSPattern*"; $FinalOutput.Offer = $HelperObjects[0].Offer; $FinalOutput.Publisher = $HelperObjects[0].Publisher} "Server2016" {$FinalOutput.SKUs = "2016*$OSPattern*"; $FinalOutput.Offer = $HelperObjects[0].Offer; $FinalOutput.Publisher = $HelperObjects[0].Publisher} "Server2019" {$FinalOutput.SKUs = "2019*$OSPattern*"; $FinalOutput.Offer = $HelperObjects[0].Offer; $FinalOutput.Publisher = $HelperObjects[0].Publisher} "Server2022" {$FinalOutput.SKUs = "2022*$OSPattern*"; $FinalOutput.Offer = $HelperObjects[0].Offer; $FinalOutput.Publisher = $HelperObjects[0].Publisher} "Windows7" {$FinalOutput.Offer = $HelperObjects[1].Offer; $FinalOutput.Publisher = $HelperObjects[1].Publisher} "Windows10" {$FinalOutput.Offer = $HelperObjects[2].Offer; $FinalOutput.Publisher = $HelperObjects[2].Publisher} "Windows11" {$FinalOutput.Offer = $HelperObjects[3].Offer; $FinalOutput.Publisher = $HelperObjects[3].Publisher} "CentOS" {$FinalOutput.Offer = $HelperObjects[4].Offer; $FinalOutput.Publisher = $HelperObjects[4].Publisher} "Ubuntu" {$FinalOutput.Offer = $HelperObjects[5].Offer; $FinalOutput.Publisher = $HelperObjects[5].Publisher} "Debian10" {$FinalOutput.Offer = $HelperObjects[6].Offer; $FinalOutput.Publisher = $HelperObjects[6].Publisher} "Debian11" {$FinalOutput.Offer = $HelperObjects[7].Offer; $FinalOutput.Publisher = $HelperObjects[7].Publisher} "Redhat" {$FinalOutput.Offer = $HelperObjects[8].Offer; $FinalOutput.Publisher = $HelperObjects[8].Publisher} } try{ $FinalOutput.SKUs = (Get-AzVMImageSku -Location $Location -PublisherName $FinalOutput.Publisher -Offer $FinalOutput.Offer -ErrorAction Stop | ? {$_.SKUs -like $FinalOutput.SKUs}).Skus if($NewestSKUs){ $FinalOutput.SKUs = $FinalOutput.SKUs[-1] } if($FinalOutput.SKUs.count -eq 0) { Write-Warning "No SKUs found using the operating system: '$OperatingSystem' and OS pattern: '$OSPattern'" return } } catch{ Write-Error "The following error occured while trying to retrieve the current SKUs for OS: '$OperatingSystem'`n$_" return } $i = 0 do { try{ $SKUPlaceholder = if($FinalOutput.SKUs.count -eq 1){$FinalOutput.Skus}else{$FinalOutput.Skus[$i]} $Versions = (Get-AzVMImage -Location $Location -PublisherName $FinalOutput.Publisher -Offer $FinalOutput.Offer -Skus $SKUPlaceholder -ErrorAction Stop).Version try{ if($CheckAgreement) { $Agreement = Get-AzMarketplaceterms -Publisher $FinalOutput.Publisher -Product $FinalOutput.Offer -Name $SKUPlaceholder -ErrorAction Stop $FinalOutput.Versions.Add([PSCustomObject]@{ SKU = $SKUPlaceholder Versions = $Versions Agreement = $Agreement }) | Out-Null } } catch{ if($_.Exception.Message -like "*Unable to find legal terms for this offer*" -or $_.Exception.Message -like "*The offer with Offer ID*"){ Write-Verbose "No legal terms to sign for sku: $SKUPlaceholder" } } if($Agreement.count -gt 0){ Write-Warning "Legal agreement found for SKU: $SKUPlaceholder" } if($NewestSKUsVersions -and $Versions.Count -gt 0){ $FinalOutput.Versions.Add([PSCustomObject]@{ SKU = $SKUPlaceholder Versions = if($Versions.count -gt 1){$Versions[-1]}else{$Versions} }) | Out-Null } else { $FinalOutput.Versions.Add([PSCustomObject]@{ SKU = $SKUPlaceholder Versions = $Versions }) | Out-Null } } catch{ if($_.Exception.Message -like "*VMImage was not found*"){ Write-Warning "The SKU: $SKUPlaceholder was not found in Azure" if($SKUPlaceholder.Count -gt 1) { $FinalOutput.Versions.Remove($SKUPlaceholder) } } else{ if(!$ContinueOnError){ Write-Error "The following error occured while trying to information about the SKU: $SKUPlaceholder`n$_" return } } } $i++ #Need to use a do-while instead of for, as we want to run at least 1 time. } while($i -le $FinalOutput.SKUs.Count -1) #Please check whether a pattern of null results in 0 skus being found or simply that every single possible SKUs for a given OS is found if($FinalOutput.Versions.SKU.Count -eq 0){ Write-Error "No SKUs were found for Operating system: $OperatingSystem $(if($OSPattern){"and using OSPattern: $($OSPattern)"})`nTry to change the pattern or simply ommit it..." return } return $FinalOutput } Export-ModuleMember Get-AzVMSKU |