AzureRM.Compute.Experiments.psm1
<#
.ExternalHelp AzureRM.Compute.Experiments-help.xml #> function New-AzVm { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory=$true, Position=0)][string] $Name = "VM", [Parameter(Mandatory=$true)][PSCredential] $Credential, [Parameter()][string] $ResourceGroupName = $Name, [Parameter()][string] $Location, [Parameter()][string] $VirtualNetworkName = $Name, [Parameter()][string] $AddressPrefix = "192.168.0.0/16", [Parameter()][string] $SubnetName = $Name, [Parameter()][string] $SubnetAddressPrefix = "192.168.1.0/24", [Parameter()][string] $PublicIpAddressName = $Name, [Parameter()][string] $DomainNameLabel = $ResourceGroupName + $Name, [Parameter()][string] $AllocationMethod = "Static", [Parameter()][string] $SecurityGroupName = $Name, [Parameter()][int[]] $OpenPorts = @(3389, 5985), [Parameter()][string] $ImageName = "Win2016Datacenter", [Parameter()][string] $Size = "Standard_DS1_v2", [Parameter()][object] $AzureRmContext, [Parameter()][switch] $AsJob ) PROCESS { # TODO: make sure it's logged in. $context = if ($AzureRmContext) { Get-AzureRmContext -AzureRmContext $AzureRmContext } else { Get-AzureRmContext } $rgi = [ResourceGroup]::new($ResourceGroupName) $vni = [VirtualNetwork]::new($VirtualNetworkName, $rgi, $AddressPrefix) $subnet = [Subnet]::new($SubnetName, $vni, $SubnetAddressPrefix) $piai = [PublicIpAddress]::new($PublicIpAddressName, $rgi, $DomainNameLabel, $AllocationMethod) $sgi = [SecurityGroup]::new($SecurityGroupName, $rgi, $OpenPorts) # we don't allow to reuse NetworkInterface so $name is $null. $nii = [NetworkInterface]::new( $Name, $rgi, $subnet, $piai, $sgi) # the purpouse of the New-AzVm cmdlet is to create (not get) a VM so $name is $null. $vmi = [VirtualMachine]::new( $Name, $rgi, $nii, $Credential, $ImageName, $images, $Size) # infer a location $locationi = [Location]::new() if (-not $Location) { $vmi.UpdateLocation($locationi, $context) if (-not $locationi.Value) { $locationi.Value = "eastus" } } else { $locationi.Value = $Location } $createParams = [CreateParams]::new($locationi.Value, $context) if ($PSCmdlet.ShouldProcess($Name, "Creating a virtual machine")) { if ($AsJob) { $boundParams = $PSCmdlet.MyInvocation.BoundParameters $arguments = @{ 'AzureRmContext' = $context } foreach ($argName in $boundParams.Keys) { if ($argName -ne 'AsJob' -and $argName -ne 'AzureRmContext') { $arguments[$argName] = $boundParams[$argName] } } $script = { [hashtable] $params = $args[0] New-AzVm @params } $jobName = "Creating VM $Name" return Start-Job -Name $jobName -ScriptBlock $script -ArgumentList $arguments } else { $vm = $vmi.GetOrCreate($createParams, [ProgressRange]::new(0.0, 1.0)) Write-Progress "Done." -Completed return [PSAzureVm]::new( $vm, $piai.DomainNameLabel + "." + $locationi.Value + ".cloudapp.azure.com") } } } } class PSAzureVm { [Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine] $Vm; [string] $Name; [string] $ResourceGroupName; [string] $Fqdn; PSAzureVm([Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine] $vm, [string] $fqdn) { $this.Vm = $vm $this.Name = $vm.Name $this.ResourceGroupName = $vm.ResourceGroupName $this.Fqdn = $fqdn } } class Location { [int] $Priority; [string] $Value; Location() { $this.Priority = 0 $this.Value = $null } } class CreateParams { [string] $Location; [object] $Context; CreateParams( [string] $location, [object] $context) { $this.Location = $location $this.Context = $context } } class ProgressRange { [double] $Start; [double] $Size; ProgressRange([double] $start, [double] $size) { $this.Start = $start; $this.Size = $size; } } class AzureObject { [string] $Name; [AzureObject[]] $Children; [int] $Priority; [int] $ObjectSize; [bool] $GetInfoCalled = $false; [object] $info = $null; AzureObject([string] $name, [AzureObject[]] $children) { $this.Name = $name $this.Children = $children $this.Priority = 0 $this.ObjectSize = 1 foreach ($child in $this.Children) { if ($this.Priority -lt $child.Priority) { $this.Priority = $child.Priority } $this.ObjectSize += $child.ObjectSize } $this.Priority++ } [string] GetResourceType() { return $null } [object] GetInfoOrThrow([object] $context) { return $null } [object] Create([CreateParams] $p) { return $null } [object] GetInfo([object] $context) { if (!$this.GetInfoCalled) { $this.GetInfoCalled = $true try { $this.Info = $this.GetInfoOrThrow($context) } catch { # ignore all errors } } return $this.Info; } [void] UpdateLocation([Location] $location, [object] $context) { if ($this.Priority -gt $location.Priority) { if ($this.Name) { $i = $this.GetInfo($context) if ($i) { $location.Value = $i.Location $location.Priority = $this.Priority return; } } foreach ($child in $this.Children) { $child.UpdateLocation($location, $context) } } } [object] GetOrCreate([CreateParams] $p, [ProgressRange] $progressRange) { $i = $this.GetInfo($p.Context) if ($i) { return $i } $pSize = $progressRange.Size / $this.ObjectSize $offset = $progressRange.Start foreach ($child in $this.Children) { $pChildSize = $pSize * $child.ObjectSize $pc = [ProgressRange]::new($offset, $pChildSize) $child.GetOrCreate($p, $pc) | Out-Null $offset += $pChildSize } $message = "Creating '" + $this.Name + "' " + $this.GetResourceType() + "." $percent = [convert]::ToInt32($offset * 100) Write-Progress $message -PercentComplete $percent -Status "$percent% Complete:" Write-Verbose $message $this.Info = $this.Create($p) return $this.Info } } class ResourceGroup: AzureObject { ResourceGroup([string] $name): base($name, @()) { } [string] GetResourceType() { return "Resource Group" } [object] GetInfoOrThrow([object] $context) { return Get-AzureRmResourceGroup ` -Name $this.Name ` -AzureRmContext $context ` -ErrorAction Stop } [object] Create([CreateParams] $p) { return New-AzureRmResourceGroup ` -Name $this.Name ` -Location $p.Location ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` -ErrorAction Stop } } class Resource1: AzureObject { [ResourceGroup] $ResourceGroup; Resource1( [string] $name, [ResourceGroup] $resourceGroup, [AzureObject[]] $children ): base($name, $children) { $this.ResourceGroup = $resourceGroup } Resource1( [string] $name, [ResourceGroup] $resourceGroup ): base($name, @($resourceGroup)) { $this.ResourceGroup = $resourceGroup } [string] GetResourceGroupName() { return $this.ResourceGroup.Info.ResourceGroupName; } } class VirtualNetwork: Resource1 { [string] $AddressPrefix; VirtualNetwork( [string] $name, [ResourceGroup] $resourceGroup, [string] $addressPrefix ): base($name, $resourceGroup) { $this.AddressPrefix = $addressPrefix } [string] GetResourceType() { return "Virtual Network" } [object] GetInfoOrThrow([object] $context) { return Get-AzureRmVirtualNetwork ` -ResourceGroupName $this.ResourceGroup.Name ` -Name $this.Name ` -AzureRmContext $context ` -ErrorAction Stop } [object] Create([CreateParams] $p) { return New-AzureRmVirtualNetwork ` -ResourceGroupName $this.GetResourceGroupName() ` -Location $p.Location ` -Name $this.Name ` -AddressPrefix $this.AddressPrefix ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` -ErrorAction Stop } } class PublicIpAddress: Resource1 { [string] $DomainNameLabel; [string] $AllocationMethod; PublicIpAddress( [string] $name, [ResourceGroup] $resourceGroup, [string] $domainNameLabel, [string] $allocationMethod ): base($name, $resourceGroup) { $this.DomainNameLabel = $domainNameLabel.ToLower() $this.AllocationMethod = $allocationMethod } [string] GetResourceType() { return "Public IP Address" } [object] GetInfoOrThrow([object] $context) { return Get-AzureRMPublicIpAddress ` -ResourceGroupName $this.ResourceGroup.Name ` -Name $this.Name ` -AzureRmContext $context ` -ErrorAction Stop } [object] Create([CreateParams] $p) { return New-AzureRmPublicIpAddress ` -ResourceGroupName $this.GetResourceGroupName() ` -Location $p.Location ` -Name $this.Name ` -DomainNameLabel $this.DomainNameLabel ` -AllocationMethod $this.AllocationMethod ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` -ErrorAction Stop } } class SecurityGroup: Resource1 { [int[]] $OpenPorts; SecurityGroup( [string] $name, [ResourceGroup] $resourceGroup, [int[]] $OpenPorts ): base($name, $resourceGroup) { $this.OpenPorts = $OpenPorts } [string] GetResourceType() { return "Security Group" } [object] GetInfoOrThrow([object] $context) { return Get-AzureRmNetworkSecurityGroup ` -ResourceGroupName $this.ResourceGroup.Name ` -Name $this.Name ` -AzureRmContext $context ` -ErrorAction Stop } [object] Create([CreateParams] $p) { $rules = New-Object ` "System.Collections.Generic.List[Microsoft.Azure.Commands.Network.Models.PSSecurityRule]" $priority = 1000 foreach ($port in $this.OpenPorts) { $name = $this.Name + $port $securityRuleConfig = New-AzureRmNetworkSecurityRuleConfig ` -Name $name ` -Protocol "Tcp" ` -Priority $priority ` -Access "Allow" ` -Direction "Inbound" ` -SourcePortRange "*" ` -SourceAddressPrefix "*" ` -DestinationPortRange $port ` -DestinationAddressPrefix "*" ` -ErrorAction Stop $rules.Add($securityRuleConfig) ++$priority } return New-AzureRmNetworkSecurityGroup ` -ResourceGroupName $this.GetResourceGroupName() ` -Location $p.Location ` -Name $this.Name ` -SecurityRules $rules ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` -ErrorAction Stop } } class Subnet: AzureObject { [VirtualNetwork] $VirtualNetwork; [string] $SubnetAddressPrefix; Subnet([string] $name, [VirtualNetwork] $virtualNetwork, [string] $subnetAddressPrefix): base($name, @($virtualNetwork)) { $this.VirtualNetwork = $virtualNetwork $this.SubnetAddressPrefix = $subnetAddressPrefix } [string] GetResourceType() { return "Subnet" } [object] GetInfoFromVirtualNetworkInfo([object] $virtualNetworkInfo) { return $virtualNetworkInfo ` | Get-AzureRmVirtualNetworkSubnetConfig -Name $this.Name -ErrorAction Stop } [object] GetInfoOrThrow([object] $context) { $virtualNetworkInfo = $this.VirtualNetwork.GetInfo($context) if ($virtualNetworkInfo) { return $this.GetInfoFromVirtualNetworkInfo($virtualNetworkInfo) } return $null } [object] Create([CreateParams] $p) { $virtualNetworkInfo = $this.VirtualNetwork.Info try { return $this.GetInfoFromVirtualNetworkInfo($virtualNetworkInfo) } catch { } $virtualNetworkInfo = Add-AzureRmVirtualNetworkSubnetConfig ` -VirtualNetwork $virtualNetworkInfo ` -Name $this.Name ` -AddressPrefix $this.SubnetAddressPrefix $virtualNetworkInfo = Set-AzureRmVirtualNetwork ` -VirtualNetwork $virtualNetworkInfo ` -AzureRmContext $p.Context ` -ErrorAction Stop return $this.GetInfoFromVirtualNetworkInfo($virtualNetworkInfo) } } class NetworkInterface: Resource1 { [Subnet] $Subnet; [PublicIpAddress] $PublicIpAddress; [SecurityGroup] $SecurityGroup; NetworkInterface( [string] $name, [ResourceGroup] $resourceGroup, [Subnet] $subnet, [PublicIpAddress] $publicIpAddress, [SecurityGroup] $securityGroup ): base($name, $resourceGroup, @($subnet, $publicIpAddress, $securityGroup)) { $this.Subnet = $subnet $this.PublicIpAddress = $publicIpAddress $this.SecurityGroup = $securityGroup } [string] GetResourceType() { return "Network Interface" } [object] GetInfoOrThrow([object] $context) { return Get-AzureRMNetworkInterface ` -ResourceGroupName $this.ResourceGroup.Name ` -Name $this.Name ` -AzureRmContext $context ` -ErrorAction Stop } [object] Create([CreateParams] $p) { $publicIpAddressInfo = $this.PublicIpAddress.Info $subnetInfo = $this.Subnet.Info $securityGroupInfo = $this.SecurityGroup.Info return New-AzureRmNetworkInterface ` -ResourceGroupName $this.GetResourceGroupName() ` -Location $p.Location ` -Name $this.Name ` -PublicIpAddressId $publicIpAddressInfo.Id ` -SubnetId $subnetInfo.Id ` -NetworkSecurityGroupId $securityGroupInfo.Id ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` -ErrorAction Stop } } class VirtualMachine: Resource1 { [NetworkInterface] $NetworkInterface; [pscredential] $Credential; [string] $ImageName; [object] $Images; [string] $Size; VirtualMachine( [string] $name, [ResourceGroup] $resourceGroup, [NetworkInterface] $networkInterface, [PSCredential] $credential, [string] $imageName, [object] $images, [string] $size): base($name, $resourceGroup, @($networkInterface)) { $this.Credential = $credential $this.ImageName = $imageName $this.NetworkInterface = $networkInterface $this.Images = $images $this.Size = $size } [string] GetResourceType() { return "Virtual Machine" } [object] GetInfoOrThrow([object] $context) { return Get-AzureRmVM ` -ResourceGroupName $this.ResourceGroup.Name ` -Name $this.Name ` -AzureRmContext $context ` -ErrorAction Stop } [object] Create([CreateParams] $p) { $networkInterfaceInstance = $this.NetworkInterface.Info $vmImage = $this.Images | Where-Object { $_.Name -eq $this.ImageName } | Select-Object -First 1 if (-not $vmImage) { throw "Unknown image: " + $this.ImageName } $vmConfig = New-AzureRmVMConfig -VMName $this.Name -VMSize $this.Size -ErrorAction Stop $vmComputerName = $this.Name switch ($vmImage.Type) { "Windows" { $vmConfig = $vmConfig | Set-AzureRmVMOperatingSystem ` -Windows ` -ComputerName $vmComputerName ` -Credential $this.Credential ` -ErrorAction Stop } "Linux" { $vmConfig = $vmConfig | Set-AzureRmVMOperatingSystem ` -Linux ` -ComputerName $vmComputerName ` -Credential $this.Credential ` -ErrorAction Stop } } $vmImageImage = $vmImage.Image $vmConfig = $vmConfig ` | Set-AzureRmVMSourceImage ` -PublisherName $vmImageImage.publisher ` -Offer $vmImageImage.offer ` -Skus $vmImageImage.sku ` -Version $vmImageImage.version ` -ErrorAction Stop ` | Add-AzureRmVMNetworkInterface ` -Id $networkInterfaceInstance.Id ` -ErrorAction Stop $rgName = $this.GetResourceGroupName() New-AzureRmVm ` -ResourceGroupName $rgName ` -Location $p.Location ` -VM $vmConfig ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` -ErrorAction Stop return Get-AzureRmVM -ResourceGroupName $rgName -Name $this.Name -AzureRmContext $p.Context } } function New-PsObject { param([hashtable] $property) New-Object psobject -Property $property } $staticImages = New-PsObject @{ Linux = New-PsObject @{ CentOS = New-PsObject @{ publisher = "OpenLogic"; offer = "CentOS"; sku = "7.3"; version = "latest"; }; CoreOS = New-PsObject @{ publisher = "CoreOS"; offer = "CoreOS"; sku = "Stable"; version = "latest"; }; Debian = New-PsObject @{ publisher = "credativ"; offer = "Debian"; sku = "8"; version = "latest"; }; "openSUSE-Leap" = New-PsObject @{ publisher = "SUSE"; offer = "openSUSE-Leap"; sku = "42.2"; version = "latest"; }; RHEL = New-PsObject @{ publisher = "RedHat"; offer = "RHEL"; sku = "7.3"; version = "latest"; }; SLES = New-PsObject @{ publisher = "SUSE"; offer = "SLES"; sku = "12-SP2"; version = "latest"; }; UbuntuLTS = New-PsObject @{ publisher = "Canonical"; offer = "UbuntuServer"; sku = "16.04-LTS"; version = "latest"; }; }; Windows = New-PsObject @{ Win2016Datacenter = New-PsObject @{ publisher = "MicrosoftWindowsServer"; offer = "WindowsServer"; sku = "2016-Datacenter"; version = "latest"; }; Win2012R2Datacenter = New-PsObject @{ publisher = "MicrosoftWindowsServer"; offer = "WindowsServer"; sku = "2012-R2-Datacenter"; version = "latest"; }; Win2012Datacenter = New-PsObject @{ publisher = "MicrosoftWindowsServer"; offer = "WindowsServer"; sku = "2012-Datacenter"; version = "latest"; }; Win2008R2SP1 = New-PsObject @{ publisher = "MicrosoftWindowsServer"; offer = "WindowsServer"; sku = "2008-R2-SP1"; version = "latest"; }; }; } # Images # an array of @{ Type = ...; Name = ...; Image = ... } # $images = $jsonImages.outputs.aliases.value.psobject.Properties | ForEach-Object { $images = $staticImages.psobject.Properties | ForEach-Object { # e.g. "Linux" $type = $_.Name $_.Value.psobject.Properties | ForEach-Object { New-Object -TypeName psobject -Property @{ # e.g. "Linux" Type = $type; # e.g. "CentOs" Name = $_.Name; # e.g. @{ publisher = "OpenLogic"; offer = "CentOS"; sku = "7.3"; version = "latest" } Image = $_.Value } } } Export-ModuleMember -Function New-AzVm |