-
Notifications
You must be signed in to change notification settings - Fork 0
/
Execute-AzureVMRemoting.ps1
300 lines (251 loc) · 13.5 KB
/
Execute-AzureVMRemoting.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
<#
.SYNOPSIS
Sets up connection to all Azure ARM VMs in an Azure Subscription, by enabling Win RM on all of them through Connect-AzureVM, and remote into
them through Remote-AzureVM to execute commands/script.
.DESCRIPTION
This runbook is the entry point for setting up and remotely executing Powershell scripts on one/more/all Azure ARM virtual machines in your Azure Subscription.
It enables you to traverse through all resource groups and corresponding VMs in your Azure Subscription, check the current state of VMs (and skip the deallocated ones),
check OS type (Windows or Linux, and skip Linux ones). Thereafter, this script triggers child runbooks to enable and configure Windows Remote Management service on each VM,
setup a connection to the Azure subscription, get the public IP Address of the VM, and remote into it to for execution of whatever commands/script needs to be executed there.
You also need to pass the script to be executed on the VMs as an Inline string.
.PARAMETER KeyVaultName
Name of the Azure KeyVault, where username/password for each of the VMs are stored.
Assuming Username and Passwords for each VM are stored in Azure Keyvault in the format - Name = <VM Name>, Secret = <Domain:Username:Password> (Domain can be empty)
.PARAMETER AzureAutomationAccountName
Name of the Azure Automation Account, from where this runbook will be run
.PARAMETER AzureAutomationResourceGroupName
Name of the Resource Group for the Azure Automation Account, from where this runbook will be run
.PARAMETER RemoteScript
The string represetation of the Remote PS Script you want to execute on the target VMs
.PARAMETER ResourceGroupName
Name of the Resource Group containing the VMs you want to remote Into. Specifying just the Resource Group without the $VMName parameter, will consider all VMs in this specified Resource Group
.PARAMETER VMName
Name of the VM you want to remote Into. this parameter cannot be specified without it's Resource group in the $ResourceGroupName parameter, or else will throw error
.EXAMPLE
Execute-AzureVMRemoting -KeyVaultName "CoreKV1" -AzureAutomationAccountName "Automation-AC1" -AzureAutomationResourceGroupName "Automation-RG1" -ResourceGroupName "RG1" -VMName "VM01" -RemoteScript "Write-Output 'Hello World!'"
.Notes
Author: Arjun Bahree
E-mail: arjun.bahree@gmail.com
Creation Date: 6/Dec/2017
Last Revision Date: 17/Dec/2017
Version: 4.0
Development Environment: Azure Automation Runbook Editor and VS Code IDE
PS Version: 5.1
Platform: Windows
#>
param(
[Parameter(Mandatory=$true)]
[String]$KeyVaultName,
[Parameter(Mandatory=$true)]
[String]$AzureAutomationAccountName,
[Parameter(Mandatory=$true)]
[String]$AzureAutomationResourceGroupName,
[Parameter(Mandatory=$true)]
[String]$RemoteScript,
[Parameter()]
[String]$ResourceGroupName,
[Parameter()]
[String]$VMName
)
$connectionName = "AzureRunAsConnection"
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
"Logging in to Azure..."
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
# Check if both Resource Groups and VM Name params are not passed
If (!$PSBoundParameters.ContainsKey('ResourceGroupName') -And !$PSBoundParameters.ContainsKey('VMName'))
{
# Get a list of all the Resource Groups in the Azure Subscription
$RGs = Get-AzureRmResourceGroup -ErrorAction "SilentlyContinue"
# Check if there are Resource Groups in the current Azure Subscription
if ($RGs)
{
# Iterate through all the Resource Groups in the Azure Subscription
foreach ($rg in $RGs)
{
$RGBaseName = $rg.ResourceGroupName
# Get a list of all the VMs in the specific Resource Group for this Iteration
$VMs = Get-AzureRmVm -ResourceGroupName $RGBaseName -ErrorAction "SilentlyContinue"
if ($VMs)
{
# Iterate through all the VMs within the specific Resource Group for this Iteration
foreach ($vm in $VMs)
{
$VMBaseName = $vm.Name
$OSX = .\Get-AzureRMVMOSType.ps1 -ResourceGroupName $RGBaseName -VMName $VMBaseName
if ($OSX -And $OSX -eq "Linux")
{
Write-Output "The VM {$VMBaseName} in Resource Group {$RGBaseName} is on $OSX OS. Hence, cannot process further since only Windows OS supported. Skipping forward."
continue
}
$VMState = .\Get-AzureRMVMState.ps1 -ResourceGroupName $RGBaseName -VMName $VMBaseName -State "PowerState"
if ($VMState -And $VMState -eq "deallocated")
{
Write-Output "The VM {$VMBaseName} in Resource Group {$RGBaseName} is currently Deallocated. Hence, cannot get IP address, and skipping."
continue
}
# Form standardized name of the Azure Automation PS Credential for the VM in context
$RemoteVMCredName = $VMBaseName + "-AACredential"
# For the VM in context, extract the corresponding username/password from Azure KeyVault
$secret = Get-AzureKeyVaultSecret -VaultName $KeyVaultName -Name $VMBaseName
# Call the script to check if the Azure Automation Credential for the VM in context alredy exists, and create a new one if absent
$SetCredentials = .\Create-AzureAutomationCredentials.ps1 -AzureAutomationAccountName $AzureAutomationAccountName `
-AzureAutomationResourceGroupName $AzureAutomationResourceGroupName `
-CredentialName $VMBaseName `
-UserName $vm.oSProfile.AdminUsername `
-Password $secret.SecretValueText
if ($SetCredentials -eq 0)
{
# Call PS Script to Remote Into the VM in context
.\Remote-AzureARMVMPS.ps1 -RemoteVMCredName $RemoteVMCredName `
-ResourceGroupName $RGBaseName `
-VMName $VMBaseName `
-RemoteScript $RemoteScript
}
else
{
Write-Output "Unable to get or set Azure Automation Credentials for VM {$VMBaseName}. Skipping forward..."
continue
}
}
}
else
{
Write-Output "There are no VMs in the Resource Group {$RGBaseName}. Continuing with next Resource Group, if any."
continue
}
}
}
else
{
Write-Output "There are no Resource Groups in the Azure Subscription. Aborting..."
return
}
}
# Check if only Resource Group param is passed, but not the VM Name param
Elseif ($PSBoundParameters.ContainsKey('ResourceGroupName') -And !$PSBoundParameters.ContainsKey('VMName'))
{
# Get a list of all the VMs in the specific Resource Group
$VMs = Get-AzureRmVm -ResourceGroupName $ResourceGroupName -ErrorAction "SilentlyContinue"
if ($VMs)
{
# Iterate through all the VMs within the specific Resource Group for this Iteration
foreach ($vm in $VMs)
{
$VMBaseName = $vm.Name
$OSX = .\Get-AzureRMVMOSType.ps1 -ResourceGroupName $ResourceGroupName -VMName $VMBaseName
if ($OSX -And $OSX -eq "Linux")
{
Write-Output "The VM {$VMBaseName} in Resource Group {$ResourceGroupName} is on $OSX OS. Hence, cannot process further since only Windows OS supported. Skipping forward."
continue
}
$VMState = .\Get-AzureRMVMState.ps1 -ResourceGroupName $ResourceGroupName -VMName $VMBaseName -State "PowerState"
if ($VMState -And $VMState -eq "deallocated")
{
Write-Output "The VM {$VMBaseName} in Resource Group {$ResourceGroupName} is currently Deallocated. Hence, cannot get IP address, and skipping."
continue
}
# Form standardized name of the Azure Automation PS Credential for the VM in context
$RemoteVMCredName = $VMBaseName + "-AACredential"
# For the VM in context, extract the corresponding username/password from Azure KeyVault
$secret = Get-AzureKeyVaultSecret -VaultName $KeyVaultName -Name $VMBaseName
# Call the script to check if the Azure Automation Credential for the VM in context alredy exists, and create a new one if absent
$SetCredentials = .\Create-AzureAutomationCredentials.ps1 -AzureAutomationAccountName $AzureAutomationAccountName `
-AzureAutomationResourceGroupName $AzureAutomationResourceGroupName `
-CredentialName $VMBaseName `
-UserName $vm.oSProfile.AdminUsername `
-Password $secret.SecretValueText
if ($SetCredentials -eq 0)
{
# Call PS Script to Remote Into the VM in context
.\Remote-AzureARMVMPS.ps1 -RemoteVMCredName $RemoteVMCredName `
-ResourceGroupName $ResourceGroupName `
-VMName $VMBaseName `
-RemoteScript $RemoteScript
}
else
{
Write-Output "Unable to get or set Azure Automation Credentials for VM {$VMBaseName}. Skipping forward..."
continue
}
}
}
else
{
Write-Output "There are no Virtual Machines in Resource Group {$ResourceGroupName}. Aborting..."
return
}
}
# Check if both Resource Group and VM Name params are passed
Elseif ($PSBoundParameters.ContainsKey('ResourceGroupName') -And $PSBoundParameters.ContainsKey('VMName'))
{
# Get the specified VM in the specific Resource Group
$vm = Get-AzureRmVm -ResourceGroupName $ResourceGroupName -Name $VMName -ErrorAction "SilentlyContinue"
if ($vm)
{
$VMBaseName = $vm.Name
$OSX = .\Get-AzureRMVMOSType.ps1 -ResourceGroupName $ResourceGroupName -VMName $VMBaseName
if ($OSX -And $OSX -eq "Linux")
{
Write-Output "The VM {$VMBaseName} in Resource Group {$ResourceGroupName} is on $OSX OS. Hence, cannot process further since only Windows OS supported. Skipping forward."
continue
}
$VMState = .\Get-AzureRMVMState.ps1 -ResourceGroupName $ResourceGroupName -VMName $VMBaseName -State "PowerState"
if ($VMState -And $VMState -eq "deallocated")
{
Write-Output "The VM {$VMBaseName} in Resource Group {$ResourceGroupName} is currently Deallocated. Hence, cannot get IP address, and skipping."
continue
}
# Form standardized name of the Azure Automation PS Credential for the VM in context
$RemoteVMCredName = $VMBaseName + "-AACredential"
# For the VM in context, extract the corresponding username/password from Azure KeyVault
$secret = Get-AzureKeyVaultSecret -VaultName $KeyVaultName -Name $VMBaseName
# Call the script to check if the Azure Automation Credential for the VM in context alredy exists, and create a new one if absent
$SetCredentials = .\Create-AzureAutomationCredentials.ps1 -AzureAutomationAccountName $AzureAutomationAccountName `
-AzureAutomationResourceGroupName $AzureAutomationResourceGroupName `
-CredentialName $VMBaseName `
-UserName $vm.oSProfile.AdminUsername `
-Password $secret.SecretValueText
if ($SetCredentials -eq 0)
{
# Call PS Script to Remote Into the VM in context
.\Remote-AzureARMVMPS.ps1 -RemoteVMCredName $RemoteVMCredName `
-ResourceGroupName $ResourceGroupName `
-VMName $VMBaseName `
-RemoteScript $RemoteScript
}
else
{
Write-Output "Unable to get or set Azure Automation Credentials for VM {$VMBaseName}. Skipping forward..."
return
}
}
else
{
Write-Output "There is no Virtual Machine named {$VMName} in Resource Group {$ResourceGroupName}. Aborting..."
return
}
}
# Check if Resource Group param is not passed, but VM Name param is passed
Elseif (!$PSBoundParameters.ContainsKey('ResourceGroupName') -And $PSBoundParameters.ContainsKey('VMName'))
{
Write-Error "VM Name parameter cannot be specified alone, without specifying its Resource Group Name parameter also. Aborting..."
return
}