1+ #
2+ # This script provides a scaleable solution to set or change the license type on all Azure-connected SQL Servers
3+ # in a specific subscription, a list of subscruiptions or the entire account. By default, it sets the new license
4+ # type value only on the servers where it is undefined.
5+ #
6+ # You can specfy a single subscription to scan, or provide subscriptions as a .CSV file with the list of IDs.
7+ # If not specified, all subscriptions your role has access to are scanned.
8+ #
9+ # The script accepts the following command line parameters:
10+ #
11+ # -SubId [subscription_id] | [csv_file_name] (Limit scope to specific subscriptions. Accepts a .csv file with the list of subscriptions)
12+ # -ResourceGroup [resource_goup] (Limit scope to a specific resoure group)
13+ # -MachineName [machine_name] (Limit scope to a specific machine)
14+ # -LicenseType [license_type_value] (Specific LT value)
15+ # -All (Optional. Set the new license type value only if undefined)
16+ #
17+
18+ param (
19+ [Parameter (Mandatory = $false )]
20+ [string ] $SubId ,
21+ [Parameter (Mandatory = $false )]
22+ [string ] $ResourceGroup ,
23+ [Parameter (Mandatory = $false )]
24+ [string ] $MachineName ,
25+ [Parameter (Mandatory = $false )]
26+ [string ] $LicenseType = " Paid" ,
27+ [Parameter (Mandatory = $false )]
28+ [boolean ] $All = $false
29+ )
30+
31+ function CheckModule ($m ) {
32+
33+ # This function ensures that the specified module is imported into the session
34+ # If module is already imported - do nothing
35+
36+ if (! (Get-Module | Where-Object {$_.Name -eq $m })) {
37+ # If module is not imported, but available on disk then import
38+ if (Get-Module - ListAvailable | Where-Object {$_.Name -eq $m }) {
39+ Import-Module $m
40+ }
41+ else {
42+
43+ # If module is not imported, not available on disk, but is in online gallery then install and import
44+ if (Find-Module - Name $m | Where-Object {$_.Name -eq $m }) {
45+ Install-Module - Name $m - Force - Verbose - Scope CurrentUser
46+ Import-Module $m
47+ }
48+ else {
49+
50+ # If module is not imported, not available and not in online gallery then abort
51+ write-host " Module $m not imported, not available and not in online gallery, exiting."
52+ EXIT 1
53+ }
54+ }
55+ }
56+ }
57+
58+ function ObjectToHashtable {
59+ [CmdletBinding ()]
60+ [OutputType (' hashtable' )]
61+ param (
62+ [Parameter (ValueFromPipeline )]
63+ $InputObject
64+ )
65+ process {
66+ # # Return null if the input is null. This can happen when calling the function
67+ # # recursively and a property is null
68+ if ($null -eq $InputObject ) {
69+ return $null
70+ }
71+ # # Check if the input is an array or collection. If so, we also need to convert
72+ # # those types into hash tables as well. This function will convert all child
73+ # # objects into hash tables (if applicable)
74+ if ($InputObject -is [System.Collections.IEnumerable ] -and $InputObject -isnot [string ]) {
75+ $collection = @ (
76+ foreach ($object in $InputObject ) {
77+ ObjectToHashtable - InputObject $object
78+ }
79+ )
80+ # # Return the array but don't enumerate it because the object may be pretty complex
81+ Write-Output - NoEnumerate $collection
82+ } elseif ($InputObject -is [psobject ]) {
83+ # # If the object has properties that need enumeration, cxonvert it to its own hash table and return it
84+ $hash = @ {}
85+ foreach ($property in $InputObject.PSObject.Properties ) {
86+ $hash [$property.Name ] = ObjectToHashtable - InputObject $property.Value
87+ }
88+ $hash
89+ } else {
90+ # # If the object isn't an array, collection, or other object, it's already a hash table
91+ # # So just return it.
92+ $InputObject
93+ }
94+ }
95+ }
96+
97+ #
98+ # Suppress warnings
99+ #
100+ Update-AzConfig - DisplayBreakingChangeWarning $false
101+
102+ # Load required modules
103+ $requiredModules = @ (
104+ " Az.Accounts" ,
105+ " Az.ConnectedMachine" ,
106+ " Az.ResourceGraph"
107+ )
108+ $requiredModules | Foreach-Object {CheckModule $_ }
109+
110+ # Subscriptions to scan
111+
112+ if ($SubId -like " *.csv" ) {
113+ $subscriptions = Import-Csv $SubId
114+ }elseif ($SubId -ne $null ){
115+ $subscriptions = [PSCustomObject ]@ {SubscriptionId = $SubId } | Get-AzSubscription
116+ }else {
117+ $subscriptions = Get-AzSubscription
118+ }
119+
120+
121+ Write-Host ([Environment ]::NewLine + " -- Scanning subscriptions --" )
122+
123+ # Scan arc-enabled servers in each subscription
124+
125+ foreach ($sub in $subscriptions ){
126+
127+ if ($sub.State -ne " Enabled" ) {continue }
128+
129+ try {
130+ Set-AzContext - SubscriptionId $sub.Id
131+ }catch {
132+ write-host " Invalid subscription: $ ( $sub.Id ) "
133+ {continue }
134+ }
135+
136+ $query = "
137+ resources
138+ | where type =~ 'microsoft.hybridcompute/machines/extensions'
139+ | extend extensionPublisher = tostring(properties.publisher), extensionType = tostring(properties.type), provisioningState = tostring(properties.provisioningState)
140+ | where extensionPublisher =~ 'Microsoft.AzureData'
141+ | where provisioningState =~ 'Succeeded'
142+ | parse id with * '/providers/Microsoft.HybridCompute/machines/' machineName '/extensions/' *
143+ | project machineName, extensionName = name, resourceGroup, location, subscriptionId, extensionPublisher, extensionType, properties
144+ "
145+
146+ if ($MachineName ) {$query += " | where machineName =~ '$ ( $MachineName ) '" }
147+ if ($ResourceGroup ) {$query += " | where resourceGroup =~ '$ ( $ResourceGroup ) '" }
148+
149+ $resources = Search-AzGraph - Query " $ ( $query ) | where subscriptionId =~ '$ ( $sub.Id ) '"
150+ foreach ($r in $resources ) {
151+
152+ $setID = @ {
153+ MachineName = $r.MachineName
154+ Name = $r.extensionName
155+ ResourceGroup = $r.resourceGroup
156+ Location = $r.location
157+ SubscriptionId = $r.subscriptionId
158+ Publisher = $r.extensionPublisher
159+ ExtensionType = $r.extensionType
160+ }
161+
162+ $settings = @ {}
163+ $settings = $r.properties.settings | ConvertTo-Json | ConvertFrom-Json | ObjectToHashtable
164+
165+ if ($settings.ContainsKey (" LicenseType" )) {
166+ if ($All ) {
167+ if ($settings [" LicenseType" ] -ne $LicenseType ) {
168+ $settings [" LicenseType" ] = $LicenseType
169+ Write-Host " Resource group: [$ ( $r.resourceGroup ) ] Connected machine: [$ ( $r.MachineName ) ] : License type: [$ ( $settings [" LicenseType" ]) ]"
170+ Set-AzConnectedMachineExtension @setId - Settings $settings - NoWait
171+ }
172+ }
173+ } else {
174+ $settings [" LicenseType" ] = $LicenseType
175+ Write-Host " Resource group: [$ ( $r.resourceGroup ) ] Connected machine: [$ ( $r.MachineName ) ] : License type: [$ ( $settings [" LicenseType" ]) ]"
176+ Set-AzConnectedMachineExtension @setId - Settings $settings
177+ }
178+ }
179+ }
180+
181+
0 commit comments