1- #
2- # This script scan each VM in scope to detect if SQL server insstalled and if it is regsitered with IaaS extension.
3- # The report includes the following information for each VM.
4- #
5- # Subscription Name
6- # Subscription ID
7- # ResourceGroupName
8- # Server Name
9- # Location
10- # Server SKU
11- # Size in vCores
12- # OS Type
13- # OS Version
14- # SQL Version
15- # SQL Edition
16- # IaaS registration status
17- #
18- # The script accepts the following command line parameters:
19- #
20- # -SubId [subscription_id] (Optional. If not specified all subscription the user has access to Accepts a .csv file with the list of subscriptions)
21- # -FilePath [csv_file_name] (Optional. Sprcifies a .csv file to save the data. if not specified, saves it in sql-vcm-inventory.csv)
22- #
23-
24- param (
1+ param (
252 [Parameter (Mandatory = $false )]
263 [string ] $SubId ,
274 [Parameter (Mandatory = $false )]
5+ [PSCredential ] $Cred ,
6+ [Parameter (Mandatory = $false )]
287 [string ] $FilePath
29-
308)
319
3210function CheckModule ($m ) {
@@ -56,76 +34,10 @@ function CheckModule ($m) {
5634 }
5735}
5836
59- function GetVCores {
60- # This function translates each VM or Host sku type and name into vCores
61-
62- [CmdletBinding ()]
63- param (
64- [Parameter (Mandatory )]
65- [string ]$type ,
66- [Parameter (Mandatory )]
67- [string ]$name
68- )
69-
70- if ($global :VM_SKUs.Count -eq 0 ){
71- $global :VM_SKUs = Get-AzComputeResourceSku " westus" | where-object {$_.ResourceType -in ' virtualMachines' , ' hostGroups/hosts' }
72- }
73- # Select first size and get the VCPus available
74- $size_info = $global :VM_SKUs | Where-Object {$_.ResourceType.Contains ($type ) -and ($_.Name -eq $name )} | Select-Object - First 1
75-
76- # Save the VCPU count
77- switch ($type ) {
78- " hosts" {$vcpu = $size_info.Capabilities | Where-Object {$_.name -eq " Cores" } }
79- " virtualMachines" {$vcpu = $size_info.Capabilities | Where-Object {$_.name -eq " vCPUsAvailable" } }
80- }
81-
82- if ($vcpu ){
83- return $vcpu.Value
84- }
85- else {
86- return 0
87- }
88- }
89-
90- function DiscoveryOnWindows {
91-
92- # This script checks if SQL Server is installed on Windows
93-
94- [string ] $SqlInstalled = " "
95- $regPath = ' HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server'
96- if (Test-Path $regPath ) {
97- $inst = (get-itemproperty $regPath ).InstalledInstances
98- # $SqlInstalled = ($inst.Count -gt 0)
99- foreach ($i in $inst ) {
100- # Read registry data
101- #
102- $p = (Get-ItemProperty ' HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' ).$i
103- $setupValues = (Get-ItemProperty " HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$p \Setup" )
104- $edition = ($setupValues.Edition -split ' ' )[0 ]
105- $version = ($setupValues.Version )
106- $SqlInstalled = $version , $edition -join ' :'
107- }
108- }
109- Write-Output $SqlInstalled
110- }
111-
112- #
113- # This script checks if SQL Server is installed on Linux
11437#
38+ # Suppress warnings
11539#
116- $DiscoveryOnLinux =
117- ' if ! systemctl is-active --quiet mssql-server.service; then dir
118-
119- echo "False"
120- exit
121- else
122- echo "True"
123- fi'
124-
125-
126-
127- # Ensure that the required modules are imported
128- # In Runbooks these modules must be added to the automation account manually
40+ Update-AzConfig - DisplayBreakingChangeWarning $false
12941
13042$requiredModules = @ (
13143 " Az.Accounts" ,
@@ -137,37 +49,27 @@ $requiredModules = @(
13749)
13850$requiredModules | Foreach-Object {CheckModule $_ }
13951
140- # Save the function definitions to run in parallel loops
141- $GetVCoresDef = $function: GetVCores.ToString ()
142-
143- # Create a script file with the SQL server discovery logic
144- New-Item - ItemType file - path DiscoverSql.ps1 - value $function: DiscoveryOnWindows.ToString () - Force | Out-Null
145- New-Item - ItemType file - path DiscoverSql.sh - value $DiscoveryOnLinux - Force | Out-Null
146-
14752# Subscriptions to scan
14853
149-
15054if ($SubId -like " *.csv" ) {
15155 $subscriptions = Import-Csv $SubId
152- }elseif ($SubId.length -gt 0 ){
56+ }elseif ($SubId -ne $null ){
15357 $subscriptions = [PSCustomObject ]@ {SubscriptionId = $SubId } | Get-AzSubscription
15458}else {
15559 $subscriptions = Get-AzSubscription
15660}
15761
158-
159- # File setup
62+ # Log file setup
16063if (! $PSBoundParameters.ContainsKey (" FilePath" )) {
161- $FilePath = ' .\sql-vm-inventory .csv'
64+ $FilePath = ' .\sql-change-log .csv'
16265}
16366
164- [System.Collections.ArrayList ]$inventoryTable = @ ()
165- $inventoryTable += , (@ (" Subscription Name" , " Subscription ID" , " ResourceGroupName" , " Name" , " Location" , " SKU" , " vCores" , " OS Type" , " OS Version" , " SQL Version" , " SQL Edition" , " IaaS registration" ))
166-
167- $global :VM_SKUs = @ {} # To hold the VM SKU table for future use
168-
16967Write-Host ([Environment ]::NewLine + " -- Scanning subscriptions --" )
17068
69+ # Record the start time
70+ $startTime = Get-Date
71+ Write-Host (" Script execution started at: $startTime " )
72+
17173# Calculate usage for each subscription
17274
17375foreach ($sub in $subscriptions ){
@@ -178,78 +80,90 @@ foreach ($sub in $subscriptions){
17880 Set-AzContext - SubscriptionId $sub.Id
17981 }catch {
18082 write-host " Invalid subscription: " $sub.Id
181- { continue }
83+ continue
18284 }
18385
184- # Reset the subtotals
185- # $subtotal.psobject.properties.name | Foreach-object {$subtotal.$_ = 0}
186-
18786 # Get all resource groups in the subscription
188- # $rgs = Get-AzResourceGroup
189-
190- # Scan all VMs with SQL server installed using a parallel loop (up to 10 at a time).
191- # NOTE: ForEach-Object -Parallel requires PS v7.1 or higher
192- if ($PSVersionTable.PSVersion.Major -ge 7 ){
193- # Get-AzVM -Status | Where-Object { $_.powerstate -eq 'VM running' } | ForEach-Object -ThrottleLimit 10 -Parallel {
194- Get-AzVM - Status | Where-Object { $_.powerstate -eq ' VM running' } | ForEach-Object {
195- # $function:GetVCores = $using:GetVCoresDef
196- $SqlEdition = ' '
197- $SqlVersion = ' '
198- $vCores = GetVCores - type ' virtualMachines' - name $_.HardwareProfile.VmSize
199- $sql_vm = Get-AzSqlVm - ResourceGroupName $_.ResourceGroupName - Name $_.Name - ErrorAction Ignore
200-
201-
202- if ($sql_vm ) {
203- $RegStatus = ' SQL Server registered'
204- $SqlEdition = $Sql_vm.Sku
205- $SqlVersion = $Sql_vm.Offer
206- }
207- else {
208- if ($_.StorageProfile.OSDisk.OSType -eq " Windows" ){
209- $params = @ {
210- ResourceGroupName = $_.ResourceGroupName
211- Name = $_.Name
212- CommandId = ' RunPowerShellScript'
213- ScriptPath = ' DiscoverSql.ps1'
214- ErrorAction = ' Stop'
215- }
216- }
217- else {
218- $params = @ {
219- ResourceGroupName = $_.ResourceGroupName
220- Name = $_.Name
221- CommandId = ' RunShellScript'
222- ScriptPath = ' DiscoverSql.sh'
223- ErrorAction = ' Stop'
224- }
225- }
226- try {
227- $out = Invoke-AzVMRunCommand @params
228- if (! $out.Value [0 ].Message){
229- $RegStatus = ' SQL Server not installed'
230- }
231- else {
232- $SqlVersion , $SqlEdition = $out.Value [0 ].Message -split ' :' , 2
233- $RegStatus = ' SQL Server not registered'
234- }
235- }
236- catch {
237- $RegStatus = ' No VM access'
238- }
87+ $rgs = Get-AzResourceGroup
88+
89+ # Get all logical servers
90+ $servers = Get-AzSqlServer
91+
92+ # Scan all vCore-based SQL database resources in the subscription
93+ $servers | Get-AzSqlDatabase | Where-Object { $_.SkuName -ne " ElasticPool" -and $_.Edition -in @ (" GeneralPurpose" , " BusinessCritical" , " Hyperscale" ) } | ForEach-Object {
94+ if ($_.LicenseType -ne " LicenseIncluded" ) {
95+ Set-AzSqlDatabase - ResourceGroupName $_.ResourceGroupName - ServerName $_.ServerName - DatabaseName $_.DatabaseName - LicenseType " LicenseIncluded"
96+ Write-Host ([Environment ]::NewLine + " -- Database $_ .DatabaseName is set to \" LicenseIncluded\" " )
97+ }
98+ }
99+ [system.gc ]::Collect()
100+
101+ # Scan all vCore-based SQL elastic pool resources in the subscription
102+ $servers | Get-AzSqlElasticPool | Where-Object { $_.Edition -in @ (" GeneralPurpose" , " BusinessCritical" , " Hyperscale" ) } | ForEach-Object {
103+ if ($_.LicenseType -ne " LicenseIncluded" ) {
104+ Set-AzSqlElasticPool - ResourceGroupName $_.ResourceGroupName - ServerName $_.ServerName - ElasticPoolName $_.ElasticPoolName - LicenseType " LicenseIncluded"
105+ Write-Host ([Environment ]::NewLine + " -- ElasticPool $_ .ElasticPoolName is set to \" LicenseIncluded\" " )
106+ }
107+ }
108+ [system.gc ]::Collect()
109+
110+ # Scan all SQL managed instance resources in the subscription
111+ Get-AzSqlInstance | Where-Object { $_.InstancePoolName -eq $null } | ForEach-Object {
112+ if ($_.LicenseType -ne " LicenseIncluded" ) {
113+ Set-AzSqlInstance - ResourceGroupName $_.ResourceGroupName - ServerName $_.ServerName - InstanceName $_.InstanceName - LicenseType " LicenseIncluded"
114+ Write-Host ([Environment ]::NewLine + " -- Instance $_ .InstanceName is set to \" LicenseIncluded\" " )
115+ }
116+ }
117+ [system.gc ]::Collect()
118+
119+ # Scan all instance pool resources in the subscription
120+ Get-AzSqlInstancePool | Foreach-Object {
121+ if ($_.LicenseType -ne " LicenseIncluded" ) {
122+ Set-AzSqlInstancePool - ResourceGroupName $_.ResourceGroupName - ServerName $_.ServerName - InstanceName $_.InstanceName - LicenseType " LicenseIncluded"
123+ Write-Host ([Environment ]::NewLine + " -- InstancePool $_ .InstanceName is set to \" LicenseIncluded\" " )
124+ }
125+ }
126+ [system.gc ]::Collect()
127+
128+ # Scan all SSIS integration runtime resources in the subscription
129+ $rgs | Get-AzDataFactoryV2 | Get-AzDataFactoryV2IntegrationRuntime | Where-Object { $_.State -eq " Started" -and $_.NodeSize -ne $null } | ForEach-Object {
130+ if ($_.LicenseType -ne " LicenseIncluded" ) {
131+ # Set the license type to "LicenseIncluded"
132+ Set-AzDataFactoryV2IntegrationRuntime - ResourceGroupName $_.ResourceGroupName - DataFactoryName $_.DataFactoryName - Name $_.Name - LicenseType " LicenseIncluded"
133+ Write-Host ([Environment ]::NewLine + " -- DataFactory $_ .DataFactoryName is set to \" LicenseIncluded\" " )
134+ }
135+ }
136+ [system.gc ]::Collect()
137+
138+ # Scan all SQL VMs in the subscription
139+ $rgs | Get-AzVM | Where-Object { $_.StorageProfile.ImageReference.Offer -like " *sql*" -and $_.ProvisioningState -eq " Succeeded" } | ForEach-Object {
140+ $vmName = $_.Name
141+ $resourceGroupName = $_.ResourceGroupName
142+
143+ # Get the SQL configuration for the VM
144+ $sqlConfig = Get-AzVMExtension - ResourceGroupName $resourceGroupName - VMName $vmName - Name " SqlIaaSAgent"
145+
146+ if ($sqlConfig -ne $null ) {
147+ $licenseType = $sqlConfig.Settings.LicenseType
148+
149+ if ($licenseType -ne " LicenseIncluded" ) {
150+ # Set the license type to "LicenseIncluded"
151+ Set-AzVMExtension - ResourceGroupName $resourceGroupName - VMName $vmName - Name " SqlIaaSAgent" - Publisher " Microsoft.SqlServer.Management" - ExtensionType " SqlIaaSAgent" - TypeHandlerVersion " 1.5" - Settings @ { " LicenseType" = " LicenseIncluded" }
152+ Write-Host ([Environment ]::NewLine + " -- SQL VM $vmName is set to \" LicenseIncluded\" " )
239153 }
240- $inventoryTable += , (@ ( $sub.Name , $sub.Id , $_.ResourceGroupName , $_.Name , $_.Location , $_.HardwareProfile.VmSize , $vCores , $_.StorageProfile.ImageReference.Offer , $_.StorageProfile.ImageReference.Sku , $SqlVersion , $SqlEdition , $RegStatus ))
241- # write-host $_.ResourceGroupName $_.Name $_.Location $_.HardwareProfile.VmSize $vCores $_.StorageProfile.ImageReference.Offer $_.StorageProfile.ImageReference.Sku $SqlVersion $SqlEdition $RegStatus
154+
242155 }
243156 }
244157 [system.gc ]::Collect()
158+
245159}
246160
161+ # Record the end time
162+ $endTime = Get-Date
163+ Write-Host (" Script execution ended at: $endTime " )
247164
248- # Write usage data to the .csv file
249- if ($FilePath ){
250- (ConvertFrom-Csv ($inventoryTable | % {$_ -join ' ,' })) | Export-Csv $FilePath - NoType # -Append
251- Write-Host ([Environment ]::NewLine + " -- Added the usage data to $FilePath --" )
252- } else {
253- Write-Host $inventoryTable
254- }
165+ # Calculate the duration of script execution
166+ $executionDuration = $endTime - $startTime
167+ Write-Host (" Script execution duration: $executionDuration " )
255168
169+ Write-Host ([Environment ]::NewLine + " -- Script execution completed --" )
0 commit comments