diff --git a/comments.md b/comments.md new file mode 100644 index 000000000000..be31b4b0f0d8 --- /dev/null +++ b/comments.md @@ -0,0 +1,171 @@ +# Comments + +- Local objects. +- Smart cmdlets for + - P2. Resource Group. + - P2. Virtual Network. + - P2. Security Group. +- P2. Resource Ids, a tree structure +- P1. Output for smart commands. In particular, information about (PS information stream ?) + - Resource Group!!! + - Virtual Network! + - Security Group! + - ...! +- Security Rules: + - DefaultSecurityRulesText - can/should we use it? +- P2. information stream +- P3. show progress +- P1. return PowerShell Job (async) + +## Tasks + +P0 is 09/05/2017 + +- [ ] P0 output +- [X] P0 create a package (PSM1, PSD1...) + - [X] P0 Manifest with dependencies +- image list + - [X] P0: json file or PSObject + - [ ] P1: json + - [ ] P2: source of image names. CDN, Azure... ??? +- names: + - P0: based on VM name specified by user (see also CLI, Portal). + - [X] P0: always fail if exist + - P1: can we reuse it? ??? + - P1: fallback: throw (suggests...) or reuse? + - P1: default shared name for Network (something else?!). CLI?? Portal?? +- P2 optional VM name. +- P1 Create Or Update scenario. +- P2 Rollback + +## Tasks + +- [X] P0: required modules +- [ ] replace Write-Host + - [X]: P0: remove Write-Host + - [ ]: P1: add progress +- [X] P0: parameter attributes +- [ ] P0: Linux image +- [ ] P0: more default parameters +- [X] P0: having a package. +- [ ] P1: Project build. + +## Graph + +1. `Location` +1. `ResourceGroup` + - depends on `Location` + - children + 1. `VirtualNetwork` + - depends on `Location` + - children + 1. `Subnet` + 1. `PublicIpAddress` + - depends on `Location` + 1. `SecurityGroup` + - depends on `Location` + - children + 1. `SecurityRule` + 1. `NetworkInterface` + - depends on `PublicIpAddress`, `Subnet`, `SecurityGroup`, `Location` + 1. `Vm` + - depends on `NetworkInterface`, `Location` + +## Levels + +1. Resources: + - `Location` (shared), + - `ResourceGroup` (shared) <- `Location` +1. Resources: + - `SecurityGroup` -> `SecurityRule` + - `Subnet` <- `VirtualNetwork` (shared?) + - `PublicIpAddress` +1. Resources: + - `NetworkInterface` (unique) ? +1. Resources: + - `VM` + +## Behavior + +a parameter value: + - undefined + - user's value + +if a resource exists + - then + - else + +resource name: +1. undefined + 1. resource exists - reuse|with validation (fail? or workaround or interactive) + 1. resource doesn't exist - create +1. user's value + 1. resource exist - reuse + 1. resource doesn't exist - fail (create?) + +smart location. Should it depends on a resource group location? + +default parameters - work backward + +## Resource Acquisition Matrix + +1. A resource name is + - undefined + - user's specified name + - user's specified id +1. A resource + - exists + - doesn't exist +1. A resource is + - shared + - unique + +## C# vs PowerShell + +### Proposal 1 + +- use C# to implement logic and type descriptions (no PowerShell dependencies, only .Net Standard and Azure SDK dependencies). +- use PowerShell to make a facade. + +### Proposal 2 + +- use C# to implement logic and type descriptions (with PowerShell dependencies) +- probably, no need for PSM1 files. + +### C# advantages + +- type/contract validations at compile time. +- much more feature to express logic, relations etc. +- testing is easier and, probably, faster. +- performance ?. +- customers may use the C# library for .Net programs (C#/VB/F#/...). +- C# type system is much more mature and well documented compare to PowerShell v5 classes. + +## Tasks + +- changelog +- remove warnings (-WarningAction=SilentContinue) +- help +- linux +- e2e manual test (Windows, Linux). +- feedback +- (-Auto) +- tab-completion +- VMSS +- rolling back +- output object + +## 9/15 + +- [X] [P-1] help (in doc, example) +- - signing (9/14) +- output format (Get-AzureRmVm) + - talk to Aaron + - how to use (connect to VM etc). +- output + - information stream (Write-Information) +- proper cmdlet + parameter attributes + struct + - [X] positional parameters (PR) (position 0 for name) +- [X] [P-1] shouldprocess (example) + +- [P0.5] validation (PSScript analyser) diff --git a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.Tests.ps1 b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.Tests.ps1 index 7129be2d677d..39b48f129c09 100644 --- a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.Tests.ps1 +++ b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.Tests.ps1 @@ -16,25 +16,35 @@ $vmComputerUser = $credentials.vmUser; $password = ConvertTo-SecureString $vmComputerPassword -AsPlainText -Force; $vmCredential = New-Object System.Management.Automation.PSCredential ($vmComputerUser, $password); -New-AzVm -Name MyVM -Credential $vmCredential -WhatIf - -# $job = New-AzVm -Name MyVMA -Credential $vmCredential -AsJob -# Receive-Job $job - -# exit - -# $vm = New-AzVm -# $vm = New-AzVm -Credential $vmCredential -$vm = New-AzVm -Name MyVMA1 -Credential $vmCredential -ResourceGroupName Crocodile -# $vm = New-AzVm -Name MyVMA - -$vm - -# Write-Host "" -# $job = New-AzVm -Name MyVMA3 -Credential $vmCredential -AsJob -# $vm = Receive-Job $job -Wait -# $vm -# Write-Host "" - -# clean-up -Remove-AzureRmResourceGroup -ResourceId $vm.ResourceGroupId \ No newline at end of file +Describe 'New-AzVm' { + It 'WhatIf' { + $result = New-AzVm -Name MyVM -Credential $vmCredential -WhatIf + } + It 'Create Windows VM' { + Remove-AzureRmResourceGroup -Name Something1 -Force + + $result = New-AzVm -Name MyVMA1 -Credential $vmCredential -ResourceGroupName Something1 -Verbose + + $result.Name | Should Be MyVMA1 + } + It 'Create Linux VM' { + $context = Get-AzureRmContext + $result = New-AzVm ` + -Name X2 ` + -Credential $vmCredential ` + -Location westus2 ` + -ResourceGroupName Something1 ` + -AzureRmContext $context ` + -ImageName UbuntuLTS ` + -Verbose + $result.Name | Should Be X2 + } + It 'Create Linux VM AsJob' { + Remove-AzureRmResourceGroup -Name MyVMA3 -Force + + $job = New-AzVm -Name MyVMA3 -Credential $vmCredential -AsJob -ImageName UbuntuLTS -Verbose + $result = Receive-Job $job -Wait -Verbose + + $result.Name | Should Be MyVMA3 + } +} diff --git a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psd1 b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psd1 index 568710be7d55..dbb14501494c 100644 --- a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psd1 +++ b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psd1 @@ -1,126 +1,126 @@ -# -# Module manifest for module 'AzureRM.Compute.Experiments' -# -# Generated by: Microsoft -# -# Generated on: 9/1/2017 -# - -@{ - -# Script module or binary module file associated with this manifest. -RootModule = ".\AzureRM.Compute.Experiments.psm1" - -# Version number of this module. -ModuleVersion = '1.0.20' - -# Supported PSEditions -# CompatiblePSEditions = @() - -# ID used to uniquely identify this module -GUID = 'b5a94030-df85-43fa-b581-54069f88428f' - -# Author of this module -Author = 'Microsoft' - -# Company or vendor of this module -CompanyName = 'Microsoft' - -# Copyright statement for this module -Copyright = 'Microsoft' - -# Description of the functionality provided by this module -Description = 'Azure Compute experiments for VM creation' - -# Minimum version of the Windows PowerShell engine required by this module -PowerShellVersion = '5.0' - -# Name of the Windows PowerShell host required by this module -# PowerShellHostName = '' - -# Minimum version of the Windows PowerShell host required by this module -# PowerShellHostVersion = '' - -# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. -# DotNetFrameworkVersion = '' - -# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. -# CLRVersion = '' - -# Processor architecture (None, X86, Amd64) required by this module -# ProcessorArchitecture = '' - -# Modules that must be imported into the global environment prior to importing this module -RequiredModules = @( - @{ ModuleName = "AzureRM.Resources"; ModuleVersion = "4.3.2"; }, - @{ ModuleName = "AzureRM.Network"; ModuleVersion = "4.3.2"; }, - @{ ModuleName = "AzureRM.Compute"; ModuleVersion = "3.3.2"; } -) - -# Assemblies that must be loaded prior to importing this module -# RequiredAssemblies = @() - -# Script files (.ps1) that are run in the caller's environment prior to importing this module. -# ScriptsToProcess = @() - -# Type files (.ps1xml) to be loaded when importing this module -# TypesToProcess = @() - -# Format files (.ps1xml) to be loaded when importing this module -# FormatsToProcess = @() - -# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -# NestedModules = @() - -# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. -FunctionsToExport = 'New-AzVm' - -# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. -# CmdletsToExport = - -# Variables to export from this module -VariablesToExport = '*' - -# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. -AliasesToExport = @() - -# DSC resources to export from this module -# DscResourcesToExport = @() - -# List of all modules packaged with this module -# ModuleList = @() - -# List of all files packaged with this module -# FileList = @() - -# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. -PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - # Tags = @() - - # A URL to the license for this module. - # LicenseUri = '' - - # A URL to the main website for this project. - # ProjectUri = '' - - # A URL to an icon representing this module. - # IconUri = '' - - # ReleaseNotes of this module - # ReleaseNotes = '' - - } # End of PSData hashtable - -} # End of PrivateData hashtable - -# HelpInfo URI of this module -# HelpInfoURI = '' - -# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. -# DefaultCommandPrefix = '' - -} +# +# Module manifest for module 'AzureRM.Compute.Experiments' +# +# Generated by: Microsoft +# +# Generated on: 9/1/2017 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = ".\AzureRM.Compute.Experiments.psm1" + +# Version number of this module. +ModuleVersion = '1.0.29' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = 'b5a94030-df85-43fa-b581-54069f88428f' + +# Author of this module +Author = 'Microsoft' + +# Company or vendor of this module +CompanyName = 'Microsoft' + +# Copyright statement for this module +Copyright = 'Microsoft' + +# Description of the functionality provided by this module +Description = 'Azure Compute experiments for VM creation' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '5.0' + +# Name of the Windows PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the Windows PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# CLRVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +RequiredModules = @( + @{ ModuleName = "AzureRM.Resources"; ModuleVersion = "4.4.0"; }, + @{ ModuleName = "AzureRM.Network"; ModuleVersion = "4.4.0"; }, + @{ ModuleName = "AzureRM.Compute"; ModuleVersion = "3.4.0"; } +) + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'New-AzVm' + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +# CmdletsToExport = + +# Variables to export from this module +VariablesToExport = '*' + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} diff --git a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psm1 b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psm1 index cc18a5b8b65c..ecbb7a1bc550 100644 --- a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psm1 +++ b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psm1 @@ -7,21 +7,21 @@ function New-AzVm { [Parameter(Mandatory=$true, Position=0)][string] $Name = "VM", [Parameter(Mandatory=$true)][PSCredential] $Credential, - [Parameter()][string] $ResourceGroupName, + [Parameter()][string] $ResourceGroupName = $Name, [Parameter()][string] $Location, - [Parameter()][string] $VirtualNetworkName, + [Parameter()][string] $VirtualNetworkName = $Name, [Parameter()][string] $AddressPrefix = "192.168.0.0/16", - [Parameter()][string] $SubnetName, + [Parameter()][string] $SubnetName = $Name, [Parameter()][string] $SubnetAddressPrefix = "192.168.1.0/24", - [Parameter()][string] $PublicIpAddressName, - [Parameter()][string] $DomainNameLabel = $Name, - [Parameter()][string] $AllocationMethod = "Static", + [Parameter()][string] $PublicIpAddressName = $Name, + [Parameter()][string] $DomainNameLabel = $Name + $ResourceGroupName, + [Parameter()][ValidateSet("Static", "Dynamic")][string] $AllocationMethod = "Static", - [Parameter()][string] $SecurityGroupName, - [Parameter()][int[]] $OpenPorts = @(3389, 5985), + [Parameter()][string] $SecurityGroupName = $Name, + [Parameter()][int[]] $OpenPorts, [Parameter()][string] $ImageName = "Win2016Datacenter", [Parameter()][string] $Size = "Standard_DS1_v2", @@ -38,6 +38,21 @@ function New-AzVm { Get-AzureRmContext } + # find image + $image = Get-Image($ImageName) + + # ports + if (!$OpenPorts) { + switch ($image.Type) { + "Windows" { + $OpenPorts = @(3389, 5985) + } + "Linux" { + $OpenPorts = @(22) + } + } + } + $rgi = [ResourceGroup]::new($ResourceGroupName) $vni = [VirtualNetwork]::new($VirtualNetworkName, $rgi, $AddressPrefix) @@ -45,9 +60,9 @@ function New-AzVm { $piai = [PublicIpAddress]::new($PublicIpAddressName, $rgi, $DomainNameLabel, $AllocationMethod) $sgi = [SecurityGroup]::new($SecurityGroupName, $rgi, $OpenPorts) - # we don't allow to reuse NetworkInterface so $name is $null. + # we don't allow to reuse NetworkInterface $nii = [NetworkInterface]::new( - $null, + $Name, $rgi, $subnet, $piai, @@ -55,12 +70,11 @@ function New-AzVm { # the purpouse of the New-AzVm cmdlet is to create (not get) a VM so $name is $null. $vmi = [VirtualMachine]::new( - $null, + $Name, $rgi, $nii, $Credential, - $ImageName, - $images, + $image, $Size) # infer a location @@ -74,7 +88,7 @@ function New-AzVm { $locationi.Value = $Location } - $createParams = [CreateParams]::new($Name, $locationi.Value, $context) + $createParams = [CreateParams]::new($locationi.Value, $context) if ($PSCmdlet.ShouldProcess($Name, "Creating a virtual machine")) { if ($AsJob) { @@ -93,25 +107,34 @@ function New-AzVm { $jobName = "Creating VM $Name" return Start-Job -Name $jobName -ScriptBlock $script -ArgumentList $arguments } else { - # Force to create Resource Group before anything else. - $rg = $rgi.GetOrCreate($createParams) - $vmResponse = $vmi.Create($createParams) - return [PSAzureVm]::new( - $rg.ResourceId, - $VirtualMachine.Name - ) + $vm = $vmi.GetOrCreate($createParams, [ProgressRange]::new(0.0, 1.0)) + Write-Progress "Done." -Completed + $fqdn = $piai.DomainNameLabel + "." + $locationi.Value + ".cloudapp.azure.com" + switch ($image.Type) { + "Windows" { + Write-Verbose ("Use 'mstsc /v:$fqdn' to connect to the VM." ) + } + "Linux" { + Write-Verbose ("Use 'ssh $($Credential.UserName)@$fqdn' to connect to the VM." ) + } + } + return [PSAzureVm]::new($vm, $fqdn) } } } } class PSAzureVm { - [string] $ResourceGroupId; + [Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine] $Vm; [string] $Name; + [string] $ResourceGroupName; + [string] $Fqdn; - PSAzureVm([string] $resourceGroupId, [string] $name) { - $this.ResourceGroupId = $resourceGroupId - $this.Name = $name + PSAzureVm([Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine] $vm, [string] $fqdn) { + $this.Vm = $vm + $this.Name = $vm.Name + $this.ResourceGroupName = $vm.ResourceGroupName + $this.Fqdn = $fqdn } } @@ -126,39 +149,55 @@ class Location { } class CreateParams { - [string] $Name; [string] $Location; [object] $Context; CreateParams( - [string] $name, [string] $location, [object] $context) { - $this.Name = $name $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 } @@ -167,11 +206,14 @@ class AzureObject { return $null } - # This function should be called only when $this.Name is not $null. [object] GetInfo([object] $context) { - if (!$this.Info) { + if (!$this.GetInfoCalled) { + $this.GetInfoCalled = $true try { $this.Info = $this.GetInfoOrThrow($context) + if ($this.Info) { + Write-Verbose ("Found '" + $this.Name + "' " + $this.GetResourceType() + ".") + } } catch { # ignore all errors } @@ -195,20 +237,24 @@ class AzureObject { } } - [object] GetOrCreate([CreateParams] $p) { + [object] GetOrCreate([CreateParams] $p, [ProgressRange] $progressRange) { $i = $this.GetInfo($p.Context) if ($i) { return $i } - if ($this.Name) { - $p = [CreateParams]::new( - $this.Name, - $p.Location, - $p.Context - ) + $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) - $this.Name = $p.Name return $this.Info } } @@ -217,6 +263,10 @@ class ResourceGroup: AzureObject { ResourceGroup([string] $name): base($name, @()) { } + [string] GetResourceType() { + return "Resource Group" + } + [object] GetInfoOrThrow([object] $context) { return Get-AzureRmResourceGroup ` -Name $this.Name ` @@ -226,7 +276,7 @@ class ResourceGroup: AzureObject { [object] Create([CreateParams] $p) { return New-AzureRmResourceGroup ` - -Name $p.Name ` + -Name $this.Name ` -Location $p.Location ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` @@ -241,7 +291,7 @@ class Resource1: AzureObject { [string] $name, [ResourceGroup] $resourceGroup, [AzureObject[]] $children - ): base($name, @($children)) { + ): base($name, @($resourceGroup) + $children) { $this.ResourceGroup = $resourceGroup } @@ -252,8 +302,8 @@ class Resource1: AzureObject { $this.ResourceGroup = $resourceGroup } - [string] GetResourceGroupName([CreateParams] $p) { - return $this.ResourceGroup.GetOrCreate($p).ResourceGroupName; + [string] GetResourceGroupName() { + return $this.ResourceGroup.Info.ResourceGroupName; } } @@ -268,8 +318,13 @@ class VirtualNetwork: Resource1 { $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 @@ -277,9 +332,9 @@ class VirtualNetwork: Resource1 { [object] Create([CreateParams] $p) { return New-AzureRmVirtualNetwork ` - -ResourceGroupName $this.GetResourceGroupName($p) ` + -ResourceGroupName $this.GetResourceGroupName() ` -Location $p.Location ` - -Name $p.Name ` + -Name $this.Name ` -AddressPrefix $this.AddressPrefix ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` @@ -297,12 +352,17 @@ class PublicIpAddress: Resource1 { [string] $domainNameLabel, [string] $allocationMethod ): base($name, $resourceGroup) { - $this.DomainNameLabel = $domainNameLabel + $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 @@ -310,10 +370,10 @@ class PublicIpAddress: Resource1 { [object] Create([CreateParams] $p) { return New-AzureRmPublicIpAddress ` - -ResourceGroupName $this.GetResourceGroupName($p) ` + -ResourceGroupName $this.GetResourceGroupName() ` -Location $p.Location ` - -Name $p.Name ` - -DomainNameLabel $this.DomainNameLabel.ToLower() ` + -Name $this.Name ` + -DomainNameLabel $this.DomainNameLabel ` -AllocationMethod $this.AllocationMethod ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` @@ -332,8 +392,13 @@ class SecurityGroup: Resource1 { $this.OpenPorts = $OpenPorts } + [string] GetResourceType() { + return "Security Group" + } + [object] GetInfoOrThrow([object] $context) { - return Get-AzureRMSecurityGroup ` + return Get-AzureRmNetworkSecurityGroup ` + -ResourceGroupName $this.ResourceGroup.Name ` -Name $this.Name ` -AzureRmContext $context ` -ErrorAction Stop @@ -344,7 +409,7 @@ class SecurityGroup: Resource1 { "System.Collections.Generic.List[Microsoft.Azure.Commands.Network.Models.PSSecurityRule]" $priority = 1000 foreach ($port in $this.OpenPorts) { - $name = $p.Name + $port + $name = $this.Name + $port $securityRuleConfig = New-AzureRmNetworkSecurityRuleConfig ` -Name $name ` -Protocol "Tcp" ` @@ -360,9 +425,9 @@ class SecurityGroup: Resource1 { ++$priority } return New-AzureRmNetworkSecurityGroup ` - -ResourceGroupName $this.GetResourceGroupName($p) ` + -ResourceGroupName $this.GetResourceGroupName() ` -Location $p.Location ` - -Name $p.Name ` + -Name $this.Name ` -SecurityRules $rules ` -AzureRmContext $p.Context ` -WarningAction SilentlyContinue ` @@ -380,28 +445,38 @@ class Subnet: AzureObject { $this.SubnetAddressPrefix = $subnetAddressPrefix } + [string] GetResourceType() { + return "Subnet" + } + + [object] GetInfoFromVirtualNetworkInfo([object] $virtualNetworkInfo) { + return $virtualNetworkInfo ` + | Get-AzureRmVirtualNetworkSubnetConfig -Name $this.Name -ErrorAction Stop + } + [object] GetInfoOrThrow([object] $context) { - $virutalNetworkInfo = $this.VirtualNetwork.GetInfo($context) - if (!$virutalNetworkInfo) { - return $null + $virtualNetworkInfo = $this.VirtualNetwork.GetInfo($context) + if ($virtualNetworkInfo) { + return $this.GetInfoFromVirtualNetworkInfo($virtualNetworkInfo) } - return $virutalNetworkInfo ` - | Get-AzureRmVirtualNetworkSubnetConfig -Name $this.Name -ErrorAction Stop + return $null } [object] Create([CreateParams] $p) { - $virtualNetworkInfo = $this.VirtualNetwork.GetOrCreate($p) - Add-AzureRmVirtualNetworkSubnetConfig ` + $virtualNetworkInfo = $this.VirtualNetwork.Info + try { + return $this.GetInfoFromVirtualNetworkInfo($virtualNetworkInfo) + } catch { + } + $virtualNetworkInfo = Add-AzureRmVirtualNetworkSubnetConfig ` -VirtualNetwork $virtualNetworkInfo ` - -Name $p.Name ` + -Name $this.Name ` -AddressPrefix $this.SubnetAddressPrefix $virtualNetworkInfo = Set-AzureRmVirtualNetwork ` -VirtualNetwork $virtualNetworkInfo ` -AzureRmContext $p.Context ` -ErrorAction Stop - return Get-AzureRmVirtualNetworkSubnetConfig ` - -VirtualNetwork $virtualNetworkInfo ` - -Name $p.Name + return $this.GetInfoFromVirtualNetworkInfo($virtualNetworkInfo) } } @@ -422,21 +497,26 @@ class NetworkInterface: Resource1 { $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.GetOrCreate($p) - $subnetInfo = $this.Subnet.GetOrCreate($p) - $securityGroupInfo = $this.SecurityGroup.GetOrCreate($p) + $publicIpAddressInfo = $this.PublicIpAddress.Info + $subnetInfo = $this.Subnet.Info + $securityGroupInfo = $this.SecurityGroup.Info return New-AzureRmNetworkInterface ` - -ResourceGroupName $this.GetResourceGroupName($p) ` + -ResourceGroupName $this.GetResourceGroupName() ` -Location $p.Location ` - -Name $p.Name ` + -Name $this.Name ` -PublicIpAddressId $publicIpAddressInfo.Id ` -SubnetId $subnetInfo.Id ` -NetworkSecurityGroupId $securityGroupInfo.Id ` @@ -449,8 +529,7 @@ class NetworkInterface: Resource1 { class VirtualMachine: Resource1 { [NetworkInterface] $NetworkInterface; [pscredential] $Credential; - [string] $ImageName; - [object] $Images; + [object] $Image; [string] $Size; VirtualMachine( @@ -458,36 +537,38 @@ class VirtualMachine: Resource1 { [ResourceGroup] $resourceGroup, [NetworkInterface] $networkInterface, [PSCredential] $credential, - [string] $imageName, - [object] $images, + [object] $image, [string] $size): base($name, $resourceGroup, @($networkInterface)) { $this.Credential = $credential - $this.ImageName = $imageName $this.NetworkInterface = $networkInterface - $this.Images = $images + $this.Image = $image $this.Size = $size } + [string] GetResourceType() { + return "Virtual Machine" + } + [object] GetInfoOrThrow([object] $context) { - return Get-AzureRMVirtualMachine ` + return Get-AzureRmVM ` + -ResourceGroupName $this.ResourceGroup.Name ` -Name $this.Name ` -AzureRmContext $context ` -ErrorAction Stop } [object] Create([CreateParams] $p) { - $networkInterfaceInstance = $this.NetworkInterface.GetOrCreate($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 $p.Name -VMSize $this.Size -ErrorAction Stop - $vmComputerName = $p.Name - switch ($vmImage.Type) { + $vmConfig = New-AzureRmVMConfig ` + -VMName $this.Name ` + -VMSize $this.Size ` + -ErrorAction Stop + $vmConfig = $vmConfig | Set-AzureRmVMBootDiagnostics -Disable -ErrorAction Stop + $vmComputerName = $this.Name + switch ($this.Image.Type) { "Windows" { $vmConfig = $vmConfig | Set-AzureRmVMOperatingSystem ` -Windows ` @@ -504,7 +585,7 @@ class VirtualMachine: Resource1 { } } - $vmImageImage = $vmImage.Image + $vmImageImage = $this.Image.Image $vmConfig = $vmConfig ` | Set-AzureRmVMSourceImage ` -PublisherName $vmImageImage.publisher ` @@ -516,13 +597,17 @@ class VirtualMachine: Resource1 { -Id $networkInterfaceInstance.Id ` -ErrorAction Stop - return New-AzureRmVm ` - -ResourceGroupName $this.GetResourceGroupName($p) ` - -Location $p.Location ` - -VM $vmConfig ` - -AzureRmContext $p.Context ` - -WarningAction SilentlyContinue ` - -ErrorAction Stop + $rgName = $this.GetResourceGroupName() + New-AzureRmVm ` + -ResourceGroupName $rgName ` + -Location $p.Location ` + -VM $vmConfig ` + -AzureRmContext $p.Context ` + -WarningAction SilentlyContinue ` + -ErrorAction Stop ` + | Out-Null + + return $this.GetInfoOrThrow($p.Context) } } @@ -623,4 +708,39 @@ $images = $staticImages.psobject.Properties | ForEach-Object { } } +function Get-Image([string] $imageName) { + $vmImage = $images | Where-Object { $_.Name -eq $imageName } | Select-Object -First 1 + if (-not $vmImage) { + throw "Unknown image: " + $this.ImageName + } + return $vmImage +} + Export-ModuleMember -Function New-AzVm + +$locations = $null + +function Get-WordToCompleteList { + param($List, $WordToComplete) + + return $List ` + | Where-Object { $_ -like "$WordToComplete*" } ` + | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_) } +} + +Register-ArgumentCompleter -CommandName New-AzVm -ParameterName Location -ScriptBlock { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) + + if (!$global:locations) { + $global:locations = Get-AzureRmLocation ` + | ForEach-Object { $_.Location } + } + return Get-WordToCompleteList -List $global:locations -WordToComplete $wordToComplete +} + +Register-ArgumentCompleter -CommandName New-AzVm -ParameterName ImageName -ScriptBlock { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) + + $list = $images | ForEach-Object { $_.Name } + return Get-WordToCompleteList -List $list -WordToComplete $wordToComplete +} diff --git a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.test.ps1 b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.test.ps1 index 8763d3de5c11..fc1ad746fcf7 100644 --- a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.test.ps1 +++ b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.test.ps1 @@ -6,4 +6,4 @@ $vm = New-AzVm -Name myVm1234 -Credential $pscredentials $vm #Cleanup -Remove-AzureRmResourceGroup -ResourceId $vm.ResourceGroupId -Force \ No newline at end of file +Remove-AzureRmResourceGroup -ResourceName $vm.Vm.ResourceGroupName -Force \ No newline at end of file diff --git a/experiments/Compute.Experiments/publish-dev.ps1 b/experiments/Compute.Experiments/publish-dev.ps1 index 06a866960e94..be188be87ef2 100644 --- a/experiments/Compute.Experiments/publish-dev.ps1 +++ b/experiments/Compute.Experiments/publish-dev.ps1 @@ -5,7 +5,7 @@ Remove-Item $out -Recurse mkdir $out Copy-Item .\AzureRM.Compute.Experiments.psd1 $out Copy-Item .\AzureRM.Compute.Experiments.psm1 $out -New-ExternalHelp -Path .\docs\ -OutputPath $out +New-ExternalHelp -Path .\help\ -OutputPath $out # foreach ($d in $dep) { # Install-Module $d -Repository $repository # }