1
+ # Author: Aaron Roney.
2
+ # Modified: 20170827.
3
+ # Setup: Remove-Module Az.Compute; Import-Module .\Az.Compute.psm1; New-AzVm -Auto;
4
+
5
+ # Resources:
6
+ # * [EXTERNAL] Azure PowerShell Docs: https://aka.ms/azpsdocs.
7
+ # * [EXTERNAL] Strategy Doc: https://aka.ms/azpsstrategy.
8
+ # * [EXTERNAL] Inspiration Sample: https://aka.ms/azpscreatevm.
9
+ # * [EXTERNAL] Community Standups: https://aka.ms/azpsnetstandup.
10
+ # * [EXTERNAL] Gallery Package: https://aka.ms/psazurerm.
11
+ # * [EXTERNAL] Docker: https://aka.ms/azpsdocker.
12
+ # * [EXTERNAL] Core Docker: https://aka.ms/azpscoredocker.
13
+ # * [INTERNAL] Research Evidence: https://aka.ms/azpsimprovementresearch.
14
+
15
+ # TODO:
16
+ # * Implement `-Auto' parameter set (i.e., `Name` and `ResourceGroup` will become optional).
17
+ # * Implement new default formatter.
18
+ # * Lock down parameter list: should be good as is (if we add the option to set OS disk size).
19
+ # * Most of the parameters have "static" defaults: make these smart defaults, where applicable (e.g., location and open ports).
20
+ # * WinServer2016 is hardcoded: add parameters to allow people to use the full image provider, etc.
21
+ # * Import image providers, etc. from the "aliases.json".
22
+ # * Integrate into Nelson's build semantics.
23
+
24
+ # Requires -Modules AzureRM .Compute
25
+
26
+ function New-AzVm {
27
+ param (
28
+ [Parameter (Mandatory = $true )] [string ] $Name ,
29
+ [Parameter (Mandatory = $true )] [string ] $ResourceGroup ,
30
+
31
+ # Generate a random as a hash of the name so it will be idempotent (tack on resource group?).
32
+ [Parameter (DontShow)]
33
+ $Random = $ (Get-Hash $Name ),
34
+
35
+ [string ] $Location = " " ,
36
+
37
+ [string ] $Image = " WinServer2016" ,
38
+ [string ] $Size = " Standard_DS1_v2" ,
39
+
40
+ [string ] $VnetName = " $ ( $Name ) Vnet" ,
41
+ [string ] $SubnetAddressPrefix = " 192.168.1.0/24" ,
42
+ [string ] $VnetAddressPrefix = " 192.168.0.0/16" ,
43
+
44
+ [string ] $PublicIpName = " $ ( $Name ) PublicIp" ,
45
+ [string ] $PublicIpDnsLabel = " $Name -$Random " .ToLower(),
46
+ [string ] $PublicIpAllocationMethod = " Static" ,
47
+ [int ] $PublicIpIdleTimeoutInMinutes = 4 ,
48
+
49
+ [string ] $NsgName = " $ ( $Name ) Nsg" ,
50
+ [int []] $NsgOpenPorts = $null ,
51
+
52
+ [string ] $NicName = " $ ( $Name ) Nic"
53
+
54
+ # Storage - OS Disk Size.
55
+ # Compute: "this goes above and beyond the 80% scenario".
56
+ )
57
+
58
+ try {
59
+
60
+ # Build credentials.
61
+
62
+ $userName = $env: UserName ;
63
+ $ptPassword = -join ((33 .. 122 ) | Get-Random - Count 20 | ForEach-Object { [char ]$_ });
64
+ $password = ConvertTo-SecureString $ptPassword - AsPlainText - Force;
65
+ $creds = New-Object System.Management.Automation.PSCredential ($env: UserName , $password );
66
+
67
+ # Set the smart defaults.
68
+
69
+ if ($Location -eq " " ) {
70
+ # TODO: Infer the location somehow?
71
+ $Location = " westus2" ;
72
+ }
73
+
74
+ if ($NsgOpenPorts -eq $null ) {
75
+ # TODO: Infer the ports to open from the image types.
76
+ $NsgOpenPorts = @ (3389 , 5985 );
77
+ }
78
+
79
+ # Get image aliases.
80
+
81
+ # TODO: Properly set images from below. Put this in its own "ensure" method?
82
+ $images = Invoke-WebRequest " https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json" | ConvertFrom-Json ;
83
+
84
+ # Set variables.
85
+
86
+ $fqdn = " $PublicIpDnsLabel .$Location .cloudapp.azure.com" ;
87
+
88
+ # Create!
89
+
90
+ Write-Info " Ensuring resource group..." ;
91
+ $rg = Use-ResourceGroup - ResourceGroup $ResourceGroup - Location $Location ;
92
+
93
+ Write-Info " Ensuring Vnet..." ;
94
+ $vnet = Use-Vnet - Name $VnetName - ResourceGroup $ResourceGroup - Location $Location - SubnetAddressPrefix $SubnetAddressPrefix - VnetAddressPrefix $VnetAddressPrefix ;
95
+
96
+ Write-Info " Ensuring public IP..." ;
97
+ $pip = Use-Pip - Name $PublicIpName - ResourceGroup $ResourceGroup - Location $Location - DnsLabel $PublicIpDnsLabel - AllocationMethod $PublicIpAllocationMethod - IdleTimeoutInMinutes $PublicIpIdleTimeoutInMinutes ;
98
+
99
+ Write-Info " Ensuring NSG..." ;
100
+ $nsg = Use-Nsg - Name $PublicIpName - ResourceGroup $ResourceGroup - Location $Location - OpenPorts $NsgOpenPorts ;
101
+ $Global :nsg = $nsg ;
102
+
103
+ Write-Info " Ensuring NIC..." ;
104
+ $nic = Use-Nic - Name $NicName - ResourceGroup $ResourceGroup - Location $Location - SubnetId $vnet.Subnets [0 ].Id - PublicIpAddressId $pip.Id - NetworkSecurityGroupId $nsg.Id ;
105
+
106
+ # TODO: Add disk options (https://docs.microsoft.com/en-us/azure/virtual-machines/scripts/virtual-machines-windows-powershell-sample-create-vm-from-managed-os-disks?toc=%2fpowershell%2fmodule%2ftoc.json)?
107
+ # https://docs.microsoft.com/en-us/powershell/module/azurerm.compute/set-azurermvmosdisk?view=azurermps-4.2.0
108
+
109
+ # Create a virtual machine configuration
110
+ $vmConfig = New-AzureRmVMConfig - VMName $Name - VMSize $Size | `
111
+ Set-AzureRmVMOperatingSystem - Windows - ComputerName $Name - Credential $creds | `
112
+ Set-AzureRmVMSourceImage - PublisherName MicrosoftWindowsServer - Offer WindowsServer - Skus 2016 - Datacenter - Version latest | `
113
+ Add-AzureRmVMNetworkInterface - Id $nic.Id
114
+
115
+ # Create a virtual machine
116
+ $vm = New-AzureRmVM - ResourceGroupName $resourceGroup - Location $location - VM $vmConfig
117
+
118
+ # Write info about VM.
119
+
120
+ # TODO: Remove these and make this a formatter.
121
+ # Write-Info "$($tab)Resource group: $rgName.";
122
+ # Write-Info "$($tab)VM Name: MyVm$random.";
123
+ # Write-Info "$($tab)Location: $location.";
124
+ # Write-Info "$($tab)FQDN: $fqdn.";
125
+ Write-Info " $ ( $tab ) Username: $username ." ;
126
+ Write-Info " $ ( $tab ) Password: $ptPassword ." ;
127
+
128
+ return $vm ;
129
+ } catch {
130
+ Write-Error $_ ;
131
+ Write-Error " Something went wrong. Issue the command again: it is idempotent. :)" ;
132
+ }
133
+ }
134
+
135
+ Export-ModuleMember - Function New-AzVm
136
+
137
+ # Helpers.
138
+
139
+ function Use-ResourceGroup {
140
+ param (
141
+ [Parameter (Mandatory = $true )] [string ] $ResourceGroup ,
142
+ [Parameter (Mandatory = $true )] [string ] $Location
143
+ )
144
+
145
+ $rg = Get-AzureRmResourceGroup | Where-Object { $_.ResourceGroupName -eq $ResourceGroup } | Select-Object - First 1 - Wait;
146
+
147
+ if ($rg -eq $null ) {
148
+ return New-AzureRmResourceGroup - Name $ResourceGroup - Location $Location ;
149
+ } else {
150
+ return $rg ;
151
+ }
152
+ }
153
+
154
+ function Use-Vnet {
155
+ param (
156
+ [Parameter (Mandatory = $true )] [string ] $Name ,
157
+ [Parameter (Mandatory = $true )] [string ] $ResourceGroup ,
158
+ [Parameter (Mandatory = $true )] [string ] $Location ,
159
+ [Parameter (Mandatory = $true )] [string ] $SubnetAddressPrefix ,
160
+ [Parameter (Mandatory = $true )] [string ] $VnetAddressPrefix
161
+ )
162
+
163
+ $vnet = Get-AzureRmVirtualNetwork | Where-Object { $_.Name -eq $Name } | Select-Object - First 1 - Wait;
164
+
165
+ if ($vnet -eq $null ) {
166
+ # Create a subnet configuration.
167
+ $subnetConfig = New-AzureRmVirtualNetworkSubnetConfig - Name " $ ( $Name ) Subnet" - AddressPrefix $SubnetAddressPrefix ;
168
+
169
+ # Create a virtual network.
170
+ return New-AzureRmVirtualNetwork - ResourceGroupName $ResourceGroup - Location $Location - Name $Name - AddressPrefix $VnetAddressPrefix - Subnet $subnetConfig
171
+ } else {
172
+ return $vnet ;
173
+ }
174
+ }
175
+
176
+ function Use-Pip {
177
+ param (
178
+ [Parameter (Mandatory = $true )] [string ] $Name ,
179
+ [Parameter (Mandatory = $true )] [string ] $ResourceGroup ,
180
+ [Parameter (Mandatory = $true )] [string ] $Location ,
181
+ [Parameter (Mandatory = $true )] [string ] $DnsLabel ,
182
+ [Parameter (Mandatory = $true )] [string ] $AllocationMethod ,
183
+ [Parameter (Mandatory = $true )] [int ] $IdleTimeoutInMinutes
184
+ )
185
+
186
+ $pip = Get-AzureRmPublicIpAddress | Where-Object { $_.Name -eq $Name } | Select-Object - First 1 - Wait;
187
+
188
+ if ($pip -eq $null ) {
189
+ # Create a public IP address and specify a DNS name.
190
+ return New-AzureRmPublicIpAddress - ResourceGroupName $ResourceGroup - Location $Location - Name $Name - DomainNameLabel $DnsLabel - AllocationMethod $AllocationMethod - IdleTimeoutInMinutes $IdleTimeoutInMinutes ;
191
+ } else {
192
+ return $pip ;
193
+ }
194
+ }
195
+
196
+ function Use-Nsg {
197
+ param (
198
+ [Parameter (Mandatory = $true )] [string ] $Name ,
199
+ [Parameter (Mandatory = $true )] [string ] $ResourceGroup ,
200
+ [Parameter (Mandatory = $true )] [string ] $Location ,
201
+ [Parameter (Mandatory = $true )] [int []] $OpenPorts
202
+ )
203
+
204
+ $nsg = Get-AzureRmNetworkSecurityGroup | Where-Object { $_.Name -eq $Name } | Select-Object - First 1 - Wait;
205
+
206
+ if ($nsg -eq $null ) {
207
+ $rules = New-Object " System.Collections.Generic.List[Microsoft.Azure.Commands.Network.Models.PSSecurityRule]" ;
208
+ $priority = 1000 ;
209
+
210
+ foreach ($port in $OpenPorts )
211
+ {
212
+ $nsgRule = New-AzureRmNetworkSecurityRuleConfig - Name myNetworkSecurityGroupRuleRDP - Protocol Tcp - Direction Inbound - Priority $priority - SourceAddressPrefix * - SourcePortRange * - DestinationAddressPrefix * - DestinationPortRange $port - Access Allow;
213
+ $rules.Add ($nsgRule );
214
+
215
+ $priority -- ;
216
+ }
217
+
218
+ # Create an NSG.
219
+ return New-AzureRmNetworkSecurityGroup - ResourceGroupName $ResourceGroup - Location $Location - Name $Name - SecurityRules $rules ;
220
+ } else {
221
+ return $nsg ;
222
+ }
223
+ }
224
+
225
+ function Use-Nic {
226
+ param (
227
+ [Parameter (Mandatory = $true )] [string ] $Name ,
228
+ [Parameter (Mandatory = $true )] [string ] $ResourceGroup ,
229
+ [Parameter (Mandatory = $true )] [string ] $Location ,
230
+ [Parameter (Mandatory = $true )] [string ] $SubnetId ,
231
+ [Parameter (Mandatory = $true )] [string ] $PublicIpAddressId ,
232
+ [Parameter (Mandatory = $true )] [psobject ] $NetworkSecurityGroupId
233
+ )
234
+
235
+ $nic = Get-AzureRmNetworkInterface | Where-Object { $_.Name -eq $Name } | Select-Object - First 1 - Wait;
236
+
237
+ if ($nic -eq $null ) {
238
+ # Create a virtual network card and associate with public IP address and NSG
239
+ return New-AzureRmNetworkInterface - Name $Name - ResourceGroupName $resourceGroup - Location $location - SubnetId $SubnetId - PublicIpAddressId $PublicIpAddressId - NetworkSecurityGroupId $NetworkSecurityGroupId.ToString ();
240
+ } else {
241
+ return $nic ;
242
+ }
243
+ }
244
+
245
+ function Write-Info {
246
+ param (
247
+ [Parameter (Position = 0 , Mandatory = $true , ValueFromPipeline = $true )] [String ] $Text
248
+ )
249
+
250
+ Write-Host $Text - ForegroundColor Cyan;
251
+ }
252
+
253
+ function Get-Hash {
254
+ param (
255
+ [Parameter (Position = 0 , Mandatory = $true , ValueFromPipeline = $true )] [String ] $TextToHash ,
256
+ [int ] $Count = 10
257
+ )
258
+
259
+ $hasher = new-object System.Security.Cryptography.SHA256Managed;
260
+ $toHash = [System.Text.Encoding ]::UTF8.GetBytes($TextToHash );
261
+ $hashByteArray = $hasher.ComputeHash ($toHash );
262
+
263
+ foreach ($byte in $hashByteArray )
264
+ {
265
+ $res += $byte.ToString ();
266
+ }
267
+
268
+ return $res.substring (0 , $Count );
269
+ }
0 commit comments