Major rewrite

This commit is contained in:
anosov1960
2021-01-12 12:01:56 -08:00
parent 7e9d62677d
commit cb2e9459ee
3 changed files with 1 additions and 395 deletions
@@ -8,7 +8,7 @@ ms.date: 1/11/2021
# Overview
This script provided a simple solution to analyze adn track the consolidated utilization of SQL Server licenses by all of the SQL resources in a specific subscription or the entire the account. By default, the script scans all subscriptions the user account has access. Alternatively, you can specify a single subscription or a .CSV file with a list of subscription. The usage report includes the following information for each scanned subscription.
This script provides a simple solution to analyze adn track the consolidated utilization of SQL Server licenses by all of the SQL resources in a specific subscription or the entire the account. By default, the script scans all subscriptions the user account has access. Alternatively, you can specify a single subscription or a .CSV file with a list of subscription. The usage report includes the following information for each scanned subscription.
| **Category** | **Description** |
|:--|:--|
@@ -1,392 +0,0 @@
# ----------------------------------------------------------------------------------
#
# Copyright Microsoft Corporation
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ---------------------------------------------------------------------------------
#
# Sample script to calculate the consolidated SQL Server license usage by all of the SQL resources in a specific subscription or the entire the account. PowerShell 7$PSverionTable.1 or higher is required.
#
# This script accepts a -SubId parameter to specify a single subscription or a .CSV file with a list of subscription. If omitted, all subsriptions in the account will be scanned.
# To create such a .CSV file, use the following command and then edit to remove the subscriptions you don't want to scan:
# > Get-AzSubscription | Export-Csv .\mysubscriptions.csv -NoTypeInformation
#
# The results are saved to a .\sql-license-usage.csv or the database table sql-license-usage if specidied. To save the results in the table, you must provide the following parameters to the script:
# -S [protocol:]server[instance_name][,port]
# -D database_name
# -U user_name
# -P password
#
# NOTE: The script does not calculate usage for Azure SQL resources that use the DTU-based purchasing model
#
param ([string] $SubId, [string] $S, [string] $U, [string] $P, [string] $D, [Boolean] $UseInRunbook = $False, [Boolean] $UseEC = $False)
#The following block is required for runbooks only
if ($UseInRunbook){
# Ensures you do not inherit an AzContext in your runbook
Disable-AzContextAutosave Scope Process
$connection = Get-AutomationConnection -Name AzureRunAsConnection
# Wrap authentication in retry logic for transient network failures
$logonAttempt = 0
while(!($connectionResult) -and ($logonAttempt -le 10))
{
$LogonAttempt++
# Logging in to Azure...
$connectionResult = Connect-AzAccount `
-ServicePrincipal `
-Tenant $connection.TenantID `
-ApplicationId $connection.ApplicationID `
-CertificateThumbprint $connection.CertificateThumbprint
Start-Sleep -Seconds 5
}
}
# Subscriptions to scan
if ($SubId -like "*.csv") {
$subscriptions = Import-Csv $args[0]
}elseif($SubId -ne $null){
$subscriptions = [PSCustomObject]@{SubscriptionId = $SubId} | Get-AzSubscription
}else{
$subscriptions = Get-AzSubscription
}
$SubId
$subscriptions
[Boolean] $useDatabase = $PSBoundParameters.ContainsKey("S") -and $PSBoundParameters.ContainsKey("U") -and $PSBoundParameters.ContainsKey("P") -and $PSBoundParameters.ContainsKey("D")
#Initialize tables and arrays
if ($useDatabase){
#Database setup
[String] $tableName = "Usage-per-subscription"
[String] $testSQL = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = '$tableName'"
[String] $createSQL = "CREATE TABLE [dbo].[$tableName](
[Date] [date] NOT NULL,
[Time] [time](7) NOT NULL,
[SubscriptionName] [nvarchar](50) NOT NULL,
[SubscriptionID] [nvarchar](50) NOT NULL,
[AHB_EC] [int] NULL,
[PAYG_EC] [int] NULL,
[AHB_STD_vCores] [int] NULL,
[AHB_ENT_vCores] [int] NULL,
[PAYG_STD_vCores] [int] NULL,
[PAYG_ENT_vCores] [int] NULL,
[HADR_STD_vCores] [int] NULL,
[HADR_ENT_vCores] [int] NULL,
[Developer_vCores] [int] NULL,
[Express_vCores] [int] NULL)"
[String] $insertSQL = "INSERT INTO [dbo].[$tableName](
[Date],
[Time],
[SubscriptionName],
[SubscriptionID],
[AHB_EC],
[PAYG_EC],
[AHB_STD_vCores],
[AHB_ENT_vCores],
[PAYG_STD_vCores],
[PAYG_ENT_vCores],
[HADR_STD_vCores],
[HADR_ENT_vCores],
[Developer_vCores],
[Express_vCores])
VALUES
('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}','{13}')"
$propertiesToSplat = @{
Database = $D
ServerInstance = $S
User = $U
Password = $P
Query = $testSQL
}
# Create table if does not exist
if ((Invoke-SQLCmd @propertiesToSplat).Column1 -eq 0) {
$propertiesToSplat.Query = $createSQL
Invoke-SQLCmd @propertiesToSplat
}
}else{
#File setup
[String] $fileName = '.\sql-license-usage.csv'
[System.Collections.ArrayList]$usageTable = @()
$usageTable += ,(@("Date", "Time", "Subscription Name", "Subscription ID", "AHB ECs", "PAYG ECs", "AHB Std vCores", "AHB Ent vCores", "PAYG Std vCores", "PAYG Ent vCores", "HADR Std vCores", "HADR Ent vCores", "Developer vCores", "Express vCores"))
}
$subtotal = [pscustomobject]@{ahb_std=0; ahb_ent=0; payg_std=0; payg_ent=0; hadr_std=0; hadr_ent=0; developer=0; express=0}
$total = [pscustomobject]@{}
$subtotal.psobject.properties.name | %{$total | Add-Member -MemberType NoteProperty -Name $_ -Value 0}
#Save the VM SKU table for future use
$VM_SKUs = Get-AzComputeResourceSku
Write-Host ([Environment]::NewLine + "-- Scanning subscriptions --")
# Calculate usage for each subscription
foreach ($sub in $subscriptions){
if ($sub.State -ne "Enabled") {continue}
try {
Set-AzContext -SubscriptionId $sub.Id
}catch {
write-host "Invalid subscription: " $sub.Id
{continue}
}
# Reset the subtotals
$subtotal.psobject.properties.name | %{$subtotal.$_ = 0}
#Get all logical servers
$servers = Get-AzSqlServer
#Get all SQL database resources in the subscription
$databases = $servers | Get-AzSqlDatabase
# Process the vCore-based databases
foreach ($db in $databases ){
if ($db.SkuName -eq "ElasticPool") {continue}
if ($db.LicenseType -eq "LicenseIncluded") {
if ($db.Edition -eq "BusinessCritical") {
$subtotal.ahb_ent += $db.Capacity
} elseif ($db.Edition -eq "GeneralPurpose") {
$subtotal.ahb_std += $db.Capacity
}
}else{
if ($db.Edition -eq "BusinessCritical") {
$subtotal.payg_ent += $db.Capacity
} elseif ($db.Edition -eq "GeneralPurpose") {
$subtotal.payg_std += $db.Capacity
}
}
}
#Get all SQL elastic pool resources in the subscription
$pools = $servers | Get-AzSqlElasticPool
# Process the vCore-based elastic pools
foreach ($pool in $pools){
if ($pool.LicenseType -eq "LicenseIncluded") {
if ($pool.Edition -eq "BusinessCritical") {
$subtotal.ahb_ent += $pool.Capacity
} elseif ($pool.Edition -eq "GeneralPurpose") {
$subtotal.ahb_std += $pool.Capacity
}
}else{
if ($pool.Edition -eq "BusinessCritical") {
$subtotal.payg_ent += $pool.Capacity
} elseif ($pool.Edition -eq "GeneralPurpose") {
$subtotal.payg_std += $pool.Capacity
}
}
}
#Get all SQL managed instance resources in the subscription
$instances = Get-AzSqlInstance
# Process the SQL managed instances with License Included and add to VCore count
foreach ($ins in $instances){
if ($ins.InstancePoolName -eq $null){
if ($ins.LicenseType -eq "LicenseIncluded") {
if ($ins.Sku.Tier -eq "BusinessCritical") {
$subtotal.ahb_ent += $ins.VCores
} elseif ($ins.Sku.Tier -eq "GeneralPurpose") {
$subtotal.ahb_std += $ins.VCores
}
}else{
if ($ins.Edition -eq "BusinessCritical") {
$subtotal.payg_ent += $pool.Capacity
} elseif ($ins.Edition -eq "GeneralPurpose") {
$subtotal.payg_std += $ins.Capacity
}
}
}
}
#Get all instance pool resources in the subscription
$ipools = Get-AzSqlInstancePool
# Process the instance pools
foreach ($ip in $ipools){
if ($ip.LicenseType -eq "LicenseIncluded") {
if ($ip.Edition -eq "BusinessCritical") {
$subtotal.ahb_ent += $ip.VCores
} elseif ($ip.Edition -eq "GeneralPurpose") {
$subtotal.ahb_std += $ip.VCores
}
}else{
if ($ip.Edition -eq "BusinessCritical") {
$subtotal.payg_ent += $ip.Capacity
} elseif ($ip.Edition -eq "GeneralPurpose") {
$subtotal.payg_std += $ip.Capacity
}
}
}
#Get all SSIS imtegration runtime resources in the subscription
$ssis_irs = Get-AzResourceGroup | Get-AzDataFactoryV2 | Get-AzDataFactoryV2IntegrationRuntime
# Get the VM size, match it with the corresponding VCPU count and add to VCore count
foreach ($ssis_ir in $ssis_irs){
# Select first size and get the VCPus available
$size_info = $VM_SKUs | where { $_.Name -like $ssis_ir.NodeSize} | Select-Object -First 1
# Save the VCPU count
$vcpu= $size_info.Capabilities | Where-Object {$_.name -eq "vCPUsAvailable"}
if ($ssis_ir.State -eq "Started"){
if ($ssis_ir.LicenseType -like "LicenseIncluded"){
if ($ssis_ir.Edition -like "Enterprise"){
$subtotal.ahb_ent += $vcpu.value
}elseif ($ssis_ir.Edition -like "Standard"){
$subtotal.ahb_std += $vcpu.value
}
}elseif ($data.license -like "BasePrice"){
if ($ssis_ir.Edition -like "Enterprise"){
$subtotal.payg_ent += $vcpu.value
}elseif ($ssis_ir.Edition -like "Standard"){
$subtotal.payg_std += $vcpu.value
}elseif ($ssis_ir.Edition -like "Developer"){
$subtotal.developer += $vcpu.value
}elseif ($ssis_ir.Edition -like "Express"){
$subtotal.express += $vcpu.value
}
}
}
}
#Get All SQL VMs resources in the subscription
$sql_vms = Get-AzSqlVM
# Get the VM size, match it with the corresponding VCPU count and add to VCore count
foreach ($sql_vm in $sql_vms){
$vm = Get-AzVm -Name $sql_vm.Name -ResourceGroupName $sql_vm.ResourceGroupName
$vm_size = $vm.HardwareProfile.VmSize
# Select first size and get the VCPus available
$size_info = $VM_SKUs | where {$_.ResourceType.Contains('virtualMachines') -and $_.Name -like $vm_size} | Select-Object -First 1
# Save the VCPU count
$vcpu= $size_info.Capabilities | Where-Object {$_.name -eq "vCPUsAvailable"}
if ($vcpu){
$data = [pscustomobject]@{vm_resource_uri=$vm.Id;sku=$sql_vm.Sku;license=$sql_vm.LicenseType;size=$vm_size;vcpus=$vcpu.value}
if ($data.license -like "DR"){
if ($data.sku -like "Enterprise"){
$subtotal.hadr_ent += $data.vcpus
}elseif ($data.sku -like "Standard"){
$subtotal.hadr_std += $data.vcpus
}
}elseif ($data.license -like "AHUB"){
if ($data.sku -like "Enterprise"){
$subtotal.ahb_ent += $data.vcpus
}elseif ($data.sku -like "Standard"){
$subtotal.ahb_std += $data.vcpus
}
}elseif ($data.license -like "PAYG"){
if ($data.sku -like "Enterprise"){
$subtotal.payg_ent += $data.vcpus
}elseif ($data.sku -like "Standard"){
$subtotal.payg_std += $data.vcpus
}elseif ($data.sku -like "Developer"){
$subtotal.developer += $data.vcpus
}elseif ($data.sku -like "Express"){
$subtotal.express += $data.vcpus
}
}
}
}
# Get All VMs hosts in the subscription
$host_groups = Get-AzHostGroup
# Get the dedicated host size, match it with the corresponding VCPU count and add to VCore count
foreach ($host_group in $host_groups){
$vm_hosts = $host_group | Select-Object -Property @{Name = 'HostGroupName'; Expression = {$_.Name}},@{Name = 'ResourceGroupName'; Expression = {$_.ResourceGroupName}} | Get-AzHost
foreach ($vm_host in $vm_hosts){
$token = (Get-AzAccessToken).Token
$params = @{
Uri = "https://management.azure.com/subscriptions/" + $sub +
"/resourceGroups/" + $vm_host.ResourceGroupName.ToLower() +
"/providers/Microsoft.Compute/hostGroups/" + $host_group.Name +
"/hosts/" + $vm_host.Name +
"/providers/Microsoft.SoftwarePlan/hybridUseBenefits/SQL_" + $host_group.Name + "_" + $vm_host.Name + "?api-version=2019-06-01-preview"
Headers = @{ 'Authorization' = "Bearer $token" }
Method = 'GET'
ContentType = 'application/json'
}
$softwarePlan = Invoke-RestMethod @params
if ($softwarePlan.Sku.Name -like "SQL*"){
$size_info = $VM_SKUs | where {$_.ResourceType.Contains('hostGroups/hosts') -and $_.Name.Contains($vm_host.Sku.Name)} | Select-Object -First 1
$cores= $size_info.Capabilities | Where-Object {$_.name -eq "Cores"}
$subtotal.ahb_ent += $cores.Value
}
}
}
# Increment the totals and add subtotals to the usage array
$subtotal.psobject.properties.name | %{$total.$_ += $subtotal.$_}
$Date = Get-Date -Format "yyy-MM-dd"
$Time = Get-Date -Format "HH:mm:ss"
if ($includeEC -eq $null){
$ahb_ec = 0
$payg_ec = 0
}else{
$ahb_ec = ($subtotal.ahb_std + $subtotal.ahb_ent*4)
$payg_ec = ($subtotal.payg_std + $subtotal.payg_ent*4)
}
if ($useDatabase){
$propertiesToSplat.Query = $insertSQL -f $Date, $Time, $sub.Name, $sub.Id, $ahb_ec, $payg_ec, $subtotal.ahb_std, $subtotal.ahb_ent, $subtotal.payg_std, $subtotal.payg_ent, $subtotal.hadr_std, $subtotal.hadr_ent, $subtotal.developer, $subtotal.express
Invoke-SQLCmd @propertiesToSplat
}else{
$usageTable += ,(@($Date, $Time, $sub.Name, $sub.Id, $ahb_ec, $payg_ec, $subtotal.ahb_std, $subtotal.ahb_ent, $subtotal.payg_std, $subtotal.payg_ent, $subtotal.hadr_std, $subtotal.hadr_ent, $subtotal.developer, $subtotal.express))
($usageTable | %{ $_ -join ','} ) | Write-Host
}
}
# Add the total numbers to the usage array
#if ($includeEC -eq $null){
# $usageTable += ,(@($Date, $Time, "Total", $null, $total.ahb_std, $total.ahb_ent, $total.payg_std, $total.payg_ent, $total.hadr_std, $total.hadr_ent, $total.developer, $total.express))
#}else{
# $usageTable += ,(@(($Date, $Time$total, "Total", $null, ($total.ahb_std + $total.ahb_ent*4), ($total.payg_std + $total.payg_ent*4), $total.ahb_std, $total.ahb_ent, $total.payg_std, $total.payg_ent, $total.hadr_std, $total.hadr_ent, $total.developer, $total.express))
#}
if ($useDatabase){
Write-Host ([Environment]::NewLine + "-- Added the usage data to $tableName table --")
}else{
# Write usage data to the .csv file
($usageTable | %{ $_ -join ','} ) | Out-file -FilePath $fileName -Append
Write-Host ([Environment]::NewLine + "-- Added the usage data to $fileName --")
}
@@ -1,2 +0,0 @@
Date,Time,Subscription Name,Subscription ID,AHB ECs,PAYG ECs,AHB Std vCores,AHB Ent vCores,PAYG Std vCores,PAYG Ent vCores,HADR Std vCores,HADR Ent vCores,Developer vCores,Express vCores
2020-12-31,13:52:07,geodr_m2_365640,10a238d6-a139-46d0-818d-d091394072b6,0,0,48,17,2,1,0,0,0,0
1 Date Time Subscription Name Subscription ID AHB ECs PAYG ECs AHB Std vCores AHB Ent vCores PAYG Std vCores PAYG Ent vCores HADR Std vCores HADR Ent vCores Developer vCores Express vCores
2 2020-12-31 13:52:07 geodr_m2_365640 10a238d6-a139-46d0-818d-d091394072b6 0 0 48 17 2 1 0 0 0 0