Skip to content

Commit 72b44be

Browse files
committed
Create VM rough draft.
1 parent d0ea9ae commit 72b44be

File tree

1 file changed

+269
-0
lines changed

1 file changed

+269
-0
lines changed
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
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

Comments
 (0)