diff --git a/.gitignore b/.gitignore index 5159e91abbbc..f1fff314484b 100644 --- a/.gitignore +++ b/.gitignore @@ -207,4 +207,5 @@ FakesAssemblies/ /tools/*.dll *.GhostDoc.xml pingme.txt -groupMapping*.json \ No newline at end of file +groupMapping*.json +.vscode/ \ No newline at end of file diff --git a/build.proj b/build.proj index 1595b1c2c865..f8b18dac7835 100644 --- a/build.proj +++ b/build.proj @@ -61,6 +61,7 @@ Condition=" '$(Scope)' == 'ServiceManagement'"/> + @@ -140,8 +141,8 @@ $(MSBuildProjectDirectory)\restore.config -ConfigFile "$(NuGetRestoreConfigFile)" -PackagesDirectory $(LibraryNugetPackageFolder) + -PackagesDirectory ".\experiments\Websites.Experiments\packages" -PackagesDirectory $(LibraryToolsFolder)\BuildPackagesTask\packages - C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe @@ -171,6 +172,8 @@ + @@ -196,7 +199,17 @@ Targets="Build" Properties="Configuration=$(Configuration);Platform=Any CPU" BuildInParallel="$(BuildInParallel)" - ContinueOnError="false" /> + ContinueOnError="false" /> + + + + + DestinationFolder="$(LibrarySourceFolder)\Package\$(Configuration)" Condition= " '$(NetCore)' == 'false' "/> + @@ -460,6 +474,9 @@ + + diff --git a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.Tests.ps1 b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.Tests.ps1 new file mode 100644 index 000000000000..2bc2c51f4506 --- /dev/null +++ b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.Tests.ps1 @@ -0,0 +1,27 @@ +Import-Module .\..\..\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Profile\AzureRM.Profile.psd1 +Import-Module .\..\..\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Resources\AzureRM.Resources.psd1 +Import-Module .\..\..\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Network\AzureRM.Network.psd1 +Import-Module .\..\..\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\AzureRM.Compute.psd1 +Import-Module .\..\..\experiments\Compute.Experiments\AzureRM.Compute.Experiments.psd1 + +# Login +$credentials = Get-Content -Path "C:\Users\sergey\Desktop\php-test.json" | ConvertFrom-Json +$clientSecret = ConvertTo-SecureString $credentials.clientSecret -AsPlainText -Force +$pscredentials = New-Object System.Management.Automation.PSCredential($credentials.applicationId, $clientSecret) +Login-AzureRmAccount -ServicePrincipal -Credential $pscredentials -TenantId $credentials.tenantId | Out-Null + +$vmComputerPassword = $credentials.vmPassword; +$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 + +# $vm = New-AzVm +# $vm = New-AzVm -Credential $vmCredential +$vm = New-AzVm -Name MyVM -Credential $vmCredential + +$vm + +# clean-up +Remove-AzureRmResourceGroup -ResourceId $vm.ResourceGroupId \ No newline at end of file diff --git a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psd1 b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psd1 new file mode 100644 index 000000000000..a68c03dea379 --- /dev/null +++ b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psd1 @@ -0,0 +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.7' + +# 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.1"; }, + @{ ModuleName = "AzureRM.Network"; ModuleVersion = "4.3.1"; }, + @{ ModuleName = "AzureRM.Compute"; ModuleVersion = "3.3.1"; } +) + +# 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 new file mode 100644 index 000000000000..d8520069081f --- /dev/null +++ b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.psm1 @@ -0,0 +1,448 @@ +<# +.ExternalHelp AzureRM.Compute.Experiments-help.xml +#> +function New-AzVm { + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + [Parameter(Mandatory=$true, Position=0)][string] $Name = "VM", + + [Parameter()][string] $ResourceGroupName, + [Parameter()][string] $Location, + + [Parameter()][string] $VirtualNetworkName, + [Parameter()][string] $PublicIpAddressName, + [Parameter()][string] $SecurityGroupName, + + [Parameter()][PSCredential] $Credential, + [Parameter()][string] $ImageName = "Win2012R2Datacenter", + [Parameter()][string] $Size = "Standard_DS1_v2" + ) + + PROCESS { + $rgi = [ResourceGroup]::new($ResourceGroupName); + + $vni = [VirtualNetwork]::new($VirtualNetworkName); + $piai = [PublicIpAddress]::new($PublicIpAddressName); + $sgi = [SecurityGroup]::new($SecurityGroupName); + + # we don't allow to reuse NetworkInterface so $name is $null. + $nii = [NetworkInterface]::new( + $null, + $vni, + $piai, + $sgi); + + # the purpouse of the New-AzVm cmdlet is to create (not get) a VM so $name is $null. + $vmi = [VirtualMachine]::new( + $null, + $nii, + $rgi, + $Credential, + $ImageName, + $images, + $Size); + + # infer a location + $locationi = [Location]::new(); + if (-not $Location) { + $vmi.UpdateLocation($locationi); + if (-not $locationi.Value) { + $locationi.Value = "eastus"; + } + } else { + $locationi.Value = $Location; + } + + $createParams = [CreateParams]::new($Name, $locationi.Value, $Name); + + if ($PSCmdlet.ShouldProcess($Name, "Creating a virtual machine")) { + $resourceGroup = $rgi.GetOrCreate($createParams); + $vmResponse = $vmi.Create($createParams); + + return [PSAzureVm]::new( + $resourceGroup.ResourceId, + $Name + ); + } + } +} + +class PSAzureVm { + [string] $ResourceGroupId; + [string] $Name; + + PSAzureVm([string] $resourceGroupId, [string] $name) { + $this.ResourceGroupId = $resourceGroupId; + $this.Name = $name; + } +} + +class Location { + [int] $Priority; + [string] $Value; + + Location() { + $this.Priority = 0; + $this.Value = $null; + } +} + +class CreateParams { + [string] $Name; + [string] $Location; + [string] $ResourceGroupName; + + CreateParams([string] $name, [string] $location, [string] $resourceGroupName) { + $this.Name = $name; + $this.Location = $location; + $this.ResourceGroupName = $resourceGroupName; + } +} + +class AzureObject { + [string] $Name; + [AzureObject[]] $Children; + [int] $Priority; + + AzureObject([string] $name, [AzureObject[]] $children) { + $this.Name = $name; + $this.Children = $children; + $this.Priority = 0; + foreach ($child in $this.Children) { + if ($this.Priority -lt $child.Priority) { + $this.Priority = $child.Priority; + } + } + $this.Priority++; + } + + # This function should be called only when $this.Name is not $null. + [object] GetInfo() { + return $null; + } + + [object] Create([CreateParams] $p) { + return $null; + } + + [void] UpdateLocation([Location] $location) { + if ($this.Priority -gt $location.Priority) { + if ($this.Name) { + $location.Value = $this.GetInfo().Location; + $location.Priority = $this.Priority; + } else { + foreach ($child in $this.Children) { + $child.UpdateLocation($location); + } + } + } + } + + [object] GetOrCreate([CreateParams] $p) { + if ($this.Name) { + return $this.GetInfo(); + } else { + $result = $this.Create($p); + $this.Name = $p.Name; + return $result; + } + } +} + +class ResourceGroup: AzureObject { + ResourceGroup([string] $name): base($name, @()) { + } + + [object] GetInfo() { + return Get-AzureRmResourceGroup -Name $this.Name; + } + + [object] Create([CreateParams] $p) { + return New-AzureRmResourceGroup ` + -Name $p.Name ` + -Location $p.Location ` + -WarningAction SilentlyContinue; + } +} + +class Resource1: AzureObject { + Resource1([string] $name): base($name, @([ResourceGroup]::new($null))) { + } +} + +class VirtualNetwork: Resource1 { + VirtualNetwork([string] $name): base($name) { + } + + [object] GetInfo() { + return Get-AzureRmVirtualNetwork -Name $this.Name; + } + + [object] Create([CreateParams] $p) { + $subnetConfig = New-AzureRmVirtualNetworkSubnetConfig ` + -Name "Subnet" ` + -AddressPrefix "192.168.1.0/24" + return New-AzureRmVirtualNetwork ` + -ResourceGroupName $p.ResourceGroupName ` + -Location $p.Location ` + -Name $p.Name ` + -AddressPrefix "192.168.0.0/16" ` + -Subnet $subnetConfig ` + -WarningAction SilentlyContinue + } +} + +class PublicIpAddress: Resource1 { + PublicIpAddress([string] $name): base($name) { + } + + [object] GetInfo() { + return Get-AzureRMPublicIpAddress -Name $this.Name; + } + + [object] Create([CreateParams] $p) { + return New-AzureRmPublicIpAddress ` + -ResourceGroupName $p.ResourceGroupName ` + -Location $p.Location ` + -Name $p.Name ` + -AllocationMethod Static ` + -WarningAction SilentlyContinue + } +} + +class SecurityGroup: Resource1 { + SecurityGroup([string] $name): base($name) { + } + + [object] GetInfo() { + return Get-AzureRMSecurityGroup -Name $this.Name; + } + + [object] Create([CreateParams] $p) { + $securityRuleConfig = New-AzureRmNetworkSecurityRuleConfig ` + -Name $p.Name ` + -Protocol "Tcp" ` + -Priority 1000 ` + -Access "Allow" ` + -Direction "Inbound" ` + -SourcePortRange "*" ` + -SourceAddressPrefix "*" ` + -DestinationPortRange 3389 ` + -DestinationAddressPrefix "*" + + return New-AzureRmNetworkSecurityGroup ` + -ResourceGroupName $p.ResourceGroupName ` + -Location $p.Location ` + -Name $p.Name ` + -SecurityRules $securityRuleConfig ` + -WarningAction SilentlyContinue + } +} + +class NetworkInterface: AzureObject { + [VirtualNetwork] $VirtualNetwork; + [PublicIpAddress] $PublicIpAddress; + [SecurityGroup] $SecurityGroup; + + NetworkInterface( + [string] $name, + [VirtualNetwork] $virtualNetwork, + [PublicIpAddress] $publicIpAddress, + [SecurityGroup] $securityGroup + ): base($name, @($virtualNetwork, $publicIpAddress, $securityGroup)) { + $this.VirtualNetwork = $virtualNetwork; + $this.PublicIpAddress = $publicIpAddress; + $this.SecurityGroup = $securityGroup; + } + + [object] GetInfo() { + return Get-AzureRMNetworkInterface -Name $this.Name; + } + + [object] Create([CreateParams] $p) { + $xpublicIpAddress = $this.PublicIpAddress.GetOrCreate($p); + $xvirtualNetwork = $this.VirtualNetwork.GetOrCreate($p); + $xsecurityGroup = $this.SecurityGroup.GetOrCreate($p); + return New-AzureRmNetworkInterface ` + -ResourceGroupName $p.ResourceGroupName ` + -Location $p.Location ` + -Name $p.Name ` + -PublicIpAddressId $xpublicIpAddress.Id ` + -SubnetId $xvirtualNetwork.Subnets[0].Id ` + -NetworkSecurityGroupId $xsecurityGroup.Id ` + -WarningAction SilentlyContinue + } +} + +class VirtualMachine: AzureObject { + [NetworkInterface] $NetworkInterface; + [pscredential] $Credential; + [string] $ImageName; + [object] $Images; + [string] $Size; + + VirtualMachine( + [string] $name, + [NetworkInterface] $networkInterface, + [ResourceGroup] $resourceGroup, + [PSCredential] $credential, + [string] $imageName, + [object] $images, + [string] $size): + base($name, @($networkInterface, $resourceGroup)) { + + $this.Credential = $credential; + $this.ImageName = $imageName; + $this.NetworkInterface = $networkInterface; + $this.Images = $images; + $this.Size = $size; + } + + [object] GetInfo() { + return Get-AzureRMVirtualMachine -Name $this.Name; + } + + [object] Create([CreateParams] $p) { + $networkInterfaceInstance = $this.NetworkInterface.GetOrCreate($p); + + if (-not $this.Credential) { + $this.Credential = Get-Credential; + } + + $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; + $vmComputerName = $p.Name; + switch ($vmImage.Type) { + "Windows" { + $vmConfig = $vmConfig | Set-AzureRmVMOperatingSystem ` + -Windows ` + -ComputerName $vmComputerName ` + -Credential $this.Credential; + } + "Linux" { + $vmConfig = $vmConfig | Set-AzureRmVMOperatingSystem ` + -Linux ` + -ComputerName $vmComputerName ` + -Credential $this.Credential; + } + } + + $vmImageImage = $vmImage.Image + $vmConfig = $vmConfig ` + | Set-AzureRmVMSourceImage ` + -PublisherName $vmImageImage.publisher ` + -Offer $vmImageImage.offer ` + -Skus $vmImageImage.sku ` + -Version $vmImageImage.version ` + | Add-AzureRmVMNetworkInterface -Id $networkInterfaceInstance.Id + + return New-AzureRmVm ` + -ResourceGroupName $p.ResourceGroupName ` + -Location $p.Location ` + -VM $vmConfig ` + -WarningAction SilentlyContinue + } +} + +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 \ No newline at end of file diff --git a/experiments/Compute.Experiments/AzureRM.Compute.Experiments.test.ps1 b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.test.ps1 new file mode 100644 index 000000000000..8763d3de5c11 --- /dev/null +++ b/experiments/Compute.Experiments/AzureRM.Compute.Experiments.test.ps1 @@ -0,0 +1,9 @@ + +$clientSecret = ConvertTo-SecureString ([Guid]::NewGuid().ToString().Substring(1,10)) -AsPlainText -Force +$pscredentials = New-Object System.Management.Automation.PSCredential("vmadmin", $clientSecret) +New-AzVm -Name myVm1234 -Credential $pscredentials -WhatIf +$vm = New-AzVm -Name myVm1234 -Credential $pscredentials +$vm + +#Cleanup +Remove-AzureRmResourceGroup -ResourceId $vm.ResourceGroupId -Force \ No newline at end of file diff --git a/experiments/Compute.Experiments/copy-dev.ps1 b/experiments/Compute.Experiments/copy-dev.ps1 new file mode 100644 index 000000000000..05011d312d45 --- /dev/null +++ b/experiments/Compute.Experiments/copy-dev.ps1 @@ -0,0 +1,7 @@ +param([string] $Config="Release") + +$outPath = Join-Path $PSScriptRoot -ChildPath "..\..\src\Package\$Config\ResourceManager\AzureResourceManager\AzureRM.Compute.Experiments\" +Write-Host $outPath +Copy-Item -Path $PSScriptRoot -Destination $outPath -Recurse -Exclude "AzureRM.Compute.Experiments.Tests.ps1", "publish-dev.ps1", "copy-dev.ps1" -Force + + diff --git a/experiments/Compute.Experiments/help/New-AzVm.md b/experiments/Compute.Experiments/help/New-AzVm.md new file mode 100644 index 000000000000..8f75bd817378 --- /dev/null +++ b/experiments/Compute.Experiments/help/New-AzVm.md @@ -0,0 +1,167 @@ +--- +external help file: AzureRM.Compute.Experiments-help.xml +Module Name: AzureRM.Compute.Experiments +online version: +schema: 2.0.0 +--- + +# New-AzVm + +## SYNOPSIS +Creates a virtual machine and all required resources. + +## SYNTAX + +``` +New-AzVm [-Name] [[-Credential] ] [[-ImageName] ] + [[-ResourceGroupName] ] [[-Location] ] [[-VirtualNetworkName] ] + [[-PublicIpAddressName] ] [[-SecurityGroupName] ] +``` + +## DESCRIPTION +The cmdlet creates a virtual machine and all required resources in Azure. + +## EXAMPLES + +### Example 1 +``` +PS C:\> New-AzVm -Name MyCoolVM +``` + +Creates a virtual machine with name `MyCoolVM`. + +## PARAMETERS + +### -Credential +Specifies the user name and password for the virtual machine as a PSCredential object. + +```yaml +Type: PSCredential +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ImageName +A name of virtual machine image. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Location +Specifies a location for the virtual machine. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 4 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Name +A name of a virtual machine. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PublicIpAddressName +Specifies a name of PublicIPAddress object to assign to a network interface. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 6 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ResourceGroupName +Specifies the name of a resource group. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SecurityGroupName +Specifies a Network Security Group name. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 7 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -VirtualNetworkName +Specifies a Virtual Network name. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 5 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## INPUTS + +### None + + +## OUTPUTS + +### System.Object + +## NOTES + +## RELATED LINKS + diff --git a/experiments/Compute.Experiments/publish-dev.ps1 b/experiments/Compute.Experiments/publish-dev.ps1 new file mode 100644 index 000000000000..ac1402c1fe90 --- /dev/null +++ b/experiments/Compute.Experiments/publish-dev.ps1 @@ -0,0 +1,15 @@ +$out = "..\build\AzureRM.Compute.Experiments\" +$repository = "sergey" +$dep = @("AzureRM.Resources", "AzureRM.Network", "AzureRM.Compute") +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 +foreach ($d in $dep) { + Install-Module $d -Repository $repository +} +Publish-Module -Path $out -Repository $repository -NuGetApiKey somekey +foreach ($d in $dep) { + Uninstall-Module $d +} diff --git a/experiments/Websites.Experiments/AzureRM.Websites.Experiments.Tests.ps1 b/experiments/Websites.Experiments/AzureRM.Websites.Experiments.Tests.ps1 new file mode 100644 index 000000000000..008011ebc2e1 --- /dev/null +++ b/experiments/Websites.Experiments/AzureRM.Websites.Experiments.Tests.ps1 @@ -0,0 +1,1176 @@ +# ---------------------------------------------------------------------------------- +# +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ---------------------------------------------------------------------------------- + + +InModuleScope AzureRM.Websites.Experiments { + + function Get-AzureRmAppServicePlanMock { + param( + [string]$PlanName, + [string]$GroupName, + [object[]]$Plans + ) + if ($PlanName -and $GroupName){ + return @{Id=$PlanName;Name=$PlanName;ResourceGroup=$GroupName} + } else { + return $Plans + } + } + + function Get-AzureRmResourceGroupMock { + param( + [string]$Name, + [object[]]$Groups + ) + + return $Groups | Where-Object {$_.ResourceGroupName -eq $Name} + } + + Describe "Get-WebAppName "{ + $randomNumber = 1 + $defaultName = "WebApp$randomNumber" + Context "[mock] When name is not provided: " { + Mock Get-Random {return $randomNumber} + Mock Test-NameAvailability {return $true} + + $result = Get-WebAppName -ProvidedParameters @{} + + It "Returns string with defaultName" { + $result | Should BeOfType System.String + $result | Should Be $defaultName + } + } + + Context "[mock] When name is provided and + name is available: " { + $customName = "customName" + Mock Test-NameAvailability {return $true} + + $result = Get-WebAppName -ProvidedParameters @{WebAppName=$customName} + + It "Returns a string with provided name" { + $result | Should BeOfType System.String + $result | Should Be $customName + } + } + + Context "[mock] When name is provided and + name is NOT available: " { + $customName = "customName" + Mock Test-NameAvailability {return $false} + + It "Should throw." { + {$result = Get-WebAppName -ProvidedParameters @{WebAppName=$customName}} | Should Throw + } + } + } + + Describe "Get-ResourceGroupInfo "{ + $webAppName = "MyWebAppName1" + Context "[mock] When a ResourceGroupName is not provided and + WebAppName is not equal to an existing resource group: " { + Mock Test-ResourceGroupExistence {return $false} + + $result = Get-ResourceGroupInfo ` + -ProvidedParameters @{} ` + -WebAppName $webAppName + + It "Returns a hashtable." { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same value as webAppName." { + $result.Name| Should Be $webAppName + } + + It "Returns a hashtable with key 'Exists' set to 'False'." { + $result.Exists | Should Be $false + } + } + + Context "[mock] When a ResourceGroupName is not provided and + WebAppName is equal to an existing resource group: " { + Mock Test-ResourceGroupExistence {return $true} + + $result = Get-ResourceGroupInfo ` + -ProvidedParameters @{} ` + -WebAppName $webAppName + + It "Returns a hashtable." { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same value as webAppName." { + $result.Name| Should Be $webAppName + } + + It "Returns a hashtable with key 'Exists' set to 'False'." { + $result.Exists | Should Be $true + } + } + + Context "[mock] When a ResourceGroupName is provided and + that name does not already exist: " { + $customName = "customName" + Mock Test-ResourceGroupExistence {return $false} + + $result = Get-ResourceGroupInfo ` + -ProvidedParameters @{ResourceGroupName=$customName} ` + -WebAppName $webAppName + + It "Returns a hashtable." { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same value as provided name." { + $result.Name| Should Be $customName + } + + It "Returns a hashtable with key 'Exists' set to 'False'." { + $result.Exists | Should Be $false + } + } + + Context "[mock] When a ResourceGroupName is provided and + that name already exists: " { + $customName = "customName" + Mock Test-ResourceGroupExistence {return $true} + + $result = Get-ResourceGroupInfo ` + -ProvidedParameters @{ResourceGroupName=$customName} ` + -WebAppName $webAppName + + It "Returns a hashtable." { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same value as provided name." { + $result.Name| Should Be $customName + } + + It "Returns a hashtable with key 'Exists' set to 'True'." { + $result.Exists | Should Be $true + } + } + } + + + Describe "Get-AppServicePlanInfo" { + $webAppName = "MyWebAppName1" + Context "When no parameters are provided and + there is not a default app service plan: " { + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $groupName = "group1" + Mock Get-AzureRmAppServicePlan {return $plans} + + $result = Get-AppServicePlanInfo ` + -ProvidedParameters @{} ` + -WebAppName $webAppName ` + -ResourceGroupName $groupName + + It "Returns a hashtable. " { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same value as the WebAppName. " { + $result.Name | Should Be $webAppName + } + + It "Returns a hashtable with key 'Exists' set to 'False'. " { + $result.Exists | Should Be $false + } + + It "Returns a hashtable with key 'ResourceGroup' set to ResourceGroupName passed. " { + $result.ResourceGroup | Should Be $groupName + } + + It "Returns a hashtable with key 'IsDefaultPlan' set to 'False'. " { + $result.IsDefaultPlan | Should Be $false + } + } + + Context "When no parameters are provided and + there is a default app service plan: " { + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2Id";Name="plan2";Sku=@{Tier="Free"};ResourceGroup="group2"} + $plan3 = @{Id="plan3Id";Name="plan3";Sku=@{Tier="Free"};ResourceGroup="group3"} + $plans = @($plan1,$plan2,$plan3) + $groupName = "group1" + Mock Get-AzureRmAppServicePlan {return $plans} + + $result = Get-AppServicePlanInfo ` + -ProvidedParameters @{} ` + -WebAppName $webAppName ` + -ResourceGroupName $groupName + + It "Returns a hashtable. " { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same vaue as the first plan with default characteristics. " { + $result.Name | Should Be $plan2.Name + } + + It "Returns a hashtable with key 'Exists' set 'True'. " { + $result.Exists | Should Be $true + } + + It "Returns a hashtable with key 'ResourceGroup' set to same value as the first default plan's resource group. " { + $result.ResourceGroup | Should Be $plan2.ResourceGroup + } + + It "Returns a hashtable with key 'IsDefaultPlan' set to 'False'. " { + $result.IsDefaultPlan | Should Be $true + } + } + + Context "When an Id is provided and + plan name exists and + group provided matches the provided in the id: " { + $planName = "plan1" + $groupName = "group1" + $planId = "/subscriptions/f30a7701-df2c-4bc7-ba8d-ab11861ca13c/resourceGroups/$groupName/providers/Microsoft.Web/serverfarms/$planName" + $plan1 = @{Id=$planId;Name=$planName;Sku=@{Tier="Basic"};ResourceGroup=$groupName} + $plan2 = @{Id="plan2Id";Name="plan2";Sku=@{Tier="Free"};ResourceGroup="group2"} + $plan3 = @{Id="plan3Id";Name="plan3";Sku=@{Tier="Free"};ResourceGroup="group3"} + $plans = @($plan1,$plan2,$plan3) + Mock Get-AzureRmAppServicePlan {return $plans} + + $result = Get-AppServicePlanInfo ` + -ProvidedParameters @{AppServicePlan=$planId} ` + -WebAppName $webAppName ` + -ResourceGroupName $groupName + + It "Returns a hashtable. " { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same vaue as the plan wiht matching ID. " { + $result.Name | Should Be $planName + } + + It "Returns a hashtable with key 'Exists' set 'True'. " { + $result.Exists | Should Be $true + } + + It "Returns a hashtable with key 'ResourceGroup' set to same resource group found in the ID. " { + $result.ResourceGroup | Should Be $groupName + } + + It "Returns a hashtable with key 'IsDefaultPlan' set to 'False'. " { + $result.IsDefaultPlan | Should Be $false + } + } + + Context "When an Id is provided and + the Id does not belong to any existent plan: " { + $planName = "plan1" + $groupName = "group1" + $planId = "/subscriptions/f30a7701-df2c-4bc7-ba8d-ab11861ca13c/resourceGroups/$groupName/providers/Microsoft.Web/serverfarms/$planName" + $plan2 = @{Id="plan2Id";Name="plan2";Sku=@{Tier="Free"};ResourceGroup="group2"} + $plan3 = @{Id="plan3Id";Name="plan3";Sku=@{Tier="Free"};ResourceGroup="group3"} + $plans = @($plan2,$plan3) + Mock Get-AzureRmAppServicePlan {return $plans} + + It "Should throw. " { + { + $result = Get-AppServicePlanInfo ` + -ProvidedParameters @{AppServicePlan=$planId} ` + -WebAppName $webAppName ` + -ResourceGroupName $nonMatchingGroupName + } | Should Throw + } + } + + Context "When a plan name is provided and + the plan name does not exist: " { + $planName = "customName" + $groupName = "customGroup" + $plan1 = @{Id="plan1Id";Name="plan1";Sku=@{Tier="Free"};ResourceGroup="group1"} + $plan2 = @{Id="plan2Id";Name="plan2";Sku=@{Tier="Free"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + Mock Get-AzureRmAppServicePlan {return $plans} + + $result = Get-AppServicePlanInfo ` + -ProvidedParameters @{AppServicePlan=$planName} ` + -WebAppName $webAppName ` + -ResourceGroupName $groupName + + It "Returns a hashtable. " { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same name provided. " { + $result.Name | Should Be $planName + } + + It "Returns a hashtable with key 'Exists' set 'False'. " { + $result.Exists | Should Be $false + } + + It "Returns a hashtable with key 'ResourceGroup' set to ResourceGroupName passed. " { + $result.ResourceGroup | Should Be $groupName + } + + It "Returns a hashtable with key 'IsDefaultPlan' set to 'False'. " { + $result.IsDefaultPlan | Should Be $false + } + } + + Context "When a plan name is provided and + plan name exists and + there are various plans with the same name and + a Resource Group matching ResourceGroupName passed contains that plan name : " { + $existingPlanName = "existingPlan" + $groupName = "existingGroup" + $plan1 = @{Id="plan1Id";Name=$existingPlanName;Sku=@{Tier="Free"};ResourceGroup="group1"} + $plan2 = @{Id="plan2Id";Name=$existingPlanName;Sku=@{Tier="Free"};ResourceGroup=$groupName} + $plan3 = @{Id="plan3Id";Name="plan3";Sku=@{Tier="Free"};ResourceGroup="group3"} + $plans = @($plan1,$plan2,$plan3) + Mock Get-AzureRmAppServicePlan {return $plans} + + $result = Get-AppServicePlanInfo ` + -ProvidedParameters @{AppServicePlan=$existingPlanName} ` + -WebAppName $webAppName ` + -ResourceGroupName $groupName + + It "Returns a hashtable. " { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same name provided. " { + $result.Name | Should Be $existingPlanName + } + + It "Returns a hashtable with key 'Exists' set to 'True'. " { + $result.Exists | Should Be $true + } + + It "Returns a hashtable with key 'ResourceGroup' set to passed ResourceGroupName. " { + $result.ResourceGroup | Should Be $groupName + } + + It "Returns a hashtable with key 'IsDefaultPlan' set to 'False'. " { + $result.IsDefaultPlan | Should Be $false + } + } + + Context "When a plan name is provided and + plan name exists and + there are various plans with the same name and + a there is not Resource Group matching ResourceGroupName passed which contains that plan name : " { + $existingPlanName = "customPlan" + $groupName = "customGroup" + $plan1 = @{Id="plan1Id";Name=$existingPlanName;Sku=@{Tier="Free"};ResourceGroup="group1"} + $plan2 = @{Id="plan2Id";Name=$existingPlanName;Sku=@{Tier="Free"};ResourceGroup="group2"} + $plan3 = @{Id="plan3Id";Name="plan3";Sku=@{Tier="Free"};ResourceGroup="group3"} + $plans = @($plan1,$plan2,$plan3) + Mock Get-AzureRmAppServicePlan {return $plans} + + It "Should Throw. " { + { + $result = Get-AppServicePlanInfo ` + -ProvidedParameters @{AppServicePlan=$existingPlanName} ` + -WebAppName $webAppName ` + -ResourceGroupName $groupName + } | Should Throw + } + } + + Context "When a plan name is provided and + plan name exists and + there is one with that same name and + a there is not Resource Group matching ResourceGroupName passed which contains that plan name : " { + $existingPlanName = "customPlan" + $existingPlanGroup = "customGroup" + $groupName = "passedGroup" + $plan1 = @{Id="plan1Id";Name=$existingPlanName;Sku=@{Tier="Free"};ResourceGroup=$existingPlanGroup} + $plan2 = @{Id="plan2Id";Name="plan2";Sku=@{Tier="Free"};ResourceGroup="group2"} + $plan3 = @{Id="plan3Id";Name="plan3";Sku=@{Tier="Free"};ResourceGroup="group3"} + $plans = @($plan1,$plan2,$plan3) + Mock Get-AzureRmAppServicePlan {return $plans} + + $result = Get-AppServicePlanInfo ` + -ProvidedParameters @{AppServicePlan=$existingPlanName} ` + -WebAppName $webAppName ` + -ResourceGroupName $groupName + + It "Returns a hashtable. " { + $result | Should BeOfType System.Collections.Hashtable + } + + It "Returns a hashtable with key 'Name' set to same name provided. " { + $result.Name | Should Be $existingPlanName + } + + It "Returns a hashtable with key 'Exists' set to 'True'. " { + $result.Exists | Should Be $true + } + + It "Returns a hashtable with key 'ResourceGroup' set to passed ResourceGroupName. " { + $result.ResourceGroup | Should Be $existingPlanGroup + } + + It "Returns a hashtable with key 'IsDefaultPlan' set to 'False'. " { + $result.IsDefaultPlan | Should Be $false + } + + } + } + + Describe "Get-AppLocation" { + $defaultLocation = "West Europe" + Mock Get-DefaultLocation {return $defaultLocation} + Context "When a location is not provided and + ResourceGroupName passed does not exist."{ + $groupName = "customGroup" + $resourceGroupExists = $false + + $result = Get-AppLocation -ProvidedParameters @{} -ResourceGroupName $groupName -ResourceGroupExists $resourceGroupExists + + It "Returns a string with default location. " { + $result | Should BeOfType System.String + $result | Should Be $defaultLocation + } + } + + Context "When a location is not provided and + ResourceGroupName passed exists."{ + $groupName = "customGroup" + $groupLocation = "customGroupLocation" + $group1 = @{ResourceGroupName=$groupName;Location=$groupLocation} + $group2 = @{ResourceGroupName="group2";Location="loc2"} + $groups = @($group1,$group2) + $resourceGroupExists = $true + Mock Get-AzureRmResourceGroup {return $groups | Where-Object {$_.ResourceGroupName -eq $Name}} + + $result = Get-AppLocation -ProvidedParameters @{} -ResourceGroupName $groupName -ResourceGroupExists $resourceGroupExists + + It "Returns a string with default location. " { + $result | Should BeOfType System.String + $result | Should Be $groupLocation + } + } + + Context "When a location is provided."{ + $providedLocation = "customLocation" + + $result = Get-AppLocation -ProvidedParameters @{Location=$providedLocation} + + It "Returns a string with the provided location. " { + $result | Should BeOfType System.String + $result | Should Be $providedLocation + } + } + } + + Describe "New-AzWebAppJustDoIt"{ + $randomNumber = 1 + $defaultName = "WebApp$randomNumber" + $defaultLocation = "West Europe" + Mock Get-Random {return $randomNumber} + Mock Get-DefaultLocation {return $defaultLocation} + $validLocations = @("loc1","loc2","loc3") + Mock Get-AzureRmResourceProvider{return @(@{ProviderNamespace="Microsoft.Web";Locations=$validLocations})} + Mock Get-Context {} + Mock Get-WebSitesClient {} + Mock Get-ResourceManagementClient {} + Context "[mock] When no parameters are provided and + there is not default plan: " { + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2Id";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmAppServicePlan {return @{Id=$Name;Name=$Name;Location=$Location;Sku=@{Tier=$Tier};ResourcerGroupName=$ResourceGroupName}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt + + It "Returns Site with default name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with a NEW ResourceGroup with same name as the webapp." { + $result.ResourceGroupName | Should be $defaultName + $result.ResourceGroupName | Should be $result.Name + } + + It "Returns Site with default location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with a NEW AppServicePlan with same name as the webapp." { + $result.AppServicePlan | Should be $defaultName + $result.AppServicePlan | Should be $result.Name + } + } + + Context "[mock] When name is provided and + there is not default plan: " { + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2Id";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $customName = "customName" + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $appPlans} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmAppServicePlan {return @{Id=$Name;Name=$Name;Location=$Location;Sku=@{Tier=$Tier};ResourcerGroupName=$ResourceGroupName}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -WebAppName $customName + + It "Returns Site with provided name." { + $result.Name | Should be $customName + } + + It "Returns Site with a NEW ResourceGroup with same name as the webapp." { + $result.ResourceGroupName | Should be $customName + } + + It "Returns Site with default location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with a NEW AppServicePlan with same name as the webapp." { + $result.AppServicePlan | Should be $customName + } + } + + Context "[mock] When no parameters are provided and + there is one default plan: " { + $defaultPlanName = "defaultPlan" + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="$defaultPlanName";Name=$defaultPlanName;Sku=@{Tier="Free"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt + + It "Returns Site with default name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with a NEW ResourceGroup with same name as the webapp." { + $result.ResourceGroupName | Should be $defaultName + $result.ResourceGroupName | Should be $result.Name + } + + It "Returns Site with default Location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with the only EXISTING default plan." { + $result.AppServicePlan | Should be $defaultPlanName + } + } + + Context "[mock] When no parameters are provided and + there is multiple default plans: " { + $defaultPlanName1 = "defaultPlan1" + $defaultPlanName2 = "defaultPlan2" + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="$defaultPlanName1";Name=$defaultPlanName1;Sku=@{Tier="Free"};ResourceGroup="group2"} + $plan3 = @{Id="$defaultPlanName2";Name=$defaultPlanName2;Sku=@{Tier="Free"};ResourceGroup="group3"} + $plans = @($plan1,$plan2,$plan3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt + + It "Returns Site with default name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with a NEW ResourceGroup with same name as the webapp." { + $result.ResourceGroupName | Should be $defaultName + $result.ResourceGroupName | Should be $result.Name + } + + It "Returns Site with default Location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with the first EXISTING default plan." { + $result.AppServicePlan | Should be $defaultPlanName1 + } + } + + Context "[mock] When group name is provided and + group with provided name exists and + default plan does not exist: " { + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $existingGroupName = "existingGroupName" + $existingGroupLocation = "existingGroupLocation" + $group1 = @{ResourceGroupName=$existingGroupName;Location=$existingGroupLocation} + $group2 = @{ResourceGroupName="rg2";Location="loc2"} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $true} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmAppServicePlan {return @{Id=$Name;Name=$Name;Location=$Location;Sku=@{Tier=$Tier};ResourcerGroupName=$ResourceGroupName}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -ResourceGroupName $existingGroupName + + It "Returns Site with default name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with EXISTING group with provided ResourceGroupName." { + $result.ResourceGroupName | Should be $existingGroupName + } + + It "Returns Site with the SAME Location as the existing resource group + that matches the provided ResourceGroupName." { + $result.Location | Should be $existingGroupLocation + } + + It "Returns Site with NEW AppServicePlan with the same name as the webapp." { + $result.AppServicePlan | Should be $defaultName + $result.AppServicePlan | Should be $result.Name + } + } + + Context "[mock] When group name is provided and + group with provided name does not exist and + default plan does not exist: " { + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $providedGroupName = "providedGroupName" + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName="rg2";Location="loc2"} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmAppServicePlan {return @{Id=$Name;Name=$Name;Location=$Location;Sku=@{Tier=$Tier};ResourcerGroupName=$ResourceGroupName}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -ResourceGroupName $providedGroupName + + It "Returns Site with default name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with NEW group with provided ResourceGroupName." { + $result.ResourceGroupName | Should be $providedGroupName + } + + It "Returns Site with the default Location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with NEW AppServicePlan with the same name as the webapp." { + $result.AppServicePlan | Should be $defaultName + $result.AppServicePlan | Should be $result.Name + } + } + + Context "[mock] When plan name is provided and + plan with provided name does not exist: " { + $providedPlanName = "ProvidedPlanName" + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName="rg2";Location="loc2"} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmAppServicePlan {return @{Id=$Name;Name=$Name;Location=$Location;Sku=@{Tier=$Tier};ResourcerGroupName=$ResourceGroupName}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $providedPlanName + + It "Returns Site with default name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with NEW group with same name as the webapp." { + $result.ResourceGroupName | Should Be $defaultName + $result.ResourceGroupName | Should Be $result.Name + } + + It "Returns Site with the default Location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with NEW AppServicePlan with the same name as the webapp." { + $result.AppServicePlan | Should be $providedPlanName + } + } + + + Context "[mock] When plan name is provided and + one plan with provided name exist: " { + $existingPlanName = "ExistingPlanName" + $existingPlanGroup = "ExistingPlanGroup" + $plan1 = @{Id=$existingPlanName;Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup=$existingPlanGroup} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName="rg2";Location="loc2"} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $existingPlanName + + It "Returns Site with default name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with NEW group with same name as the webapp." { + $result.ResourceGroupName | Should Be $defaultName + $result.ResourceGroupName | Should Be $result.Name + } + + It "Returns Site with the default Location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with EXISTING AppServicePlan name." { + $result.AppServicePlan | Should be $existingPlanName + } + } + + Context "[mock] When plan name is provided and + multiple plans with provided name exist: " { + $existingPlanName = "ExistingPlanName" + $existingPlanGroup = "ExistingPlanGroup" + $plan1 = @{Id="plan1";Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2";Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName="rg2";Location="loc2"} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmAppServicePlan {return @{Id=$Name;Name=$Name;Location=$Location;Sku=@{Tier=$Tier};ResourcerGroupName=$ResourceGroupName}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + + + It "Should throw." { + {$result = New-AzWebAppJustDoIt -AppServicePlan $existingPlanName} | Should Throw + } + } + + + Context "[mock] When plan name and group name are provided and + no plan nor group with those names exist: " { + $providedPlanName = "PlanName" + $providedGroupName = "GroupNAme" + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName="rg2";Location="loc2"} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmAppServicePlan {return @{Id=$Name;Name=$Name;Location=$Location;Sku=@{Tier=$Tier};ResourcerGroupName=$ResourceGroupName}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $providedPlanName -ResourceGroupName $providedGroupName + + It "Returns Site with DEFAULT name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with NEW group with same name provided." { + $result.ResourceGroupName | Should Be $providedGroupName + } + + It "Returns Site with the DEFAULT Location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with EXISTING AppServicePlan name." { + $result.AppServicePlan | Should Be $providedPlanName + } + } + + Context "[mock] When plan name and group name are provided and + no group with that name exist and + plan with that name exist: " { + $existingPlanName = "ExistingPlanName" + $providedGroupName = "GroupNAme" + $plan1 = @{Id=$existingPlanName;Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName="rg2";Location="loc2"} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $existingPlanName -ResourceGroupName $providedGroupName + + It "Returns Site with DEFAULT name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with NEW group with same name provided." { + $result.ResourceGroupName | Should Be $providedGroupName + } + + It "Returns Site with the DEFAULT Location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with EXISTING AppServicePlan name." { + $result.AppServicePlan | Should Be $existingPlanName + } + } + + Context "[mock] When plan name and group name are provided and + no group with that name exist and + multiple plans with that name exist: " { + $existingPlanName = "ExistingPlanName" + $providedGroupName = "GroupNAme" + $plan1 = @{Id="plan1";Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup="group1"} + $plan2 = @{Id="plan2";Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName="rg2";Location="loc2"} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $false} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + + It "Should throw." { + {$result = New-AzWebAppJustDoIt -AppServicePlan $existingPlanName -ResourceGroupName $providedGroupName} | Should Throw "There are various App Service Plans with that name" + } + } + + Context "[mock] When plan name and group name are provided and + group with that name exists + plan with that name exist and belongs to that group: " { + $existingPlanName = "ExistingPlanName" + $existingGroupName = "GroupNAme" + $existingGroupLocation = "locationgroup" + $plan1 = @{Id=$existingPlanName;Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup=$existingGroupName} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName=$existingGroupName;Location=$existingGroupLocation} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $true} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $existingPlanName -ResourceGroupName $existingGroupName + + It "Returns Site with DEFAULT name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with EXISTING group with same name provided." { + $result.ResourceGroupName | Should Be $existingGroupName + } + + It "Returns Site with the location of EXISTING Location." { + $result.Location | Should be $existingGroupLocation + } + + It "Returns Site with EXISTING AppServicePlan name." { + $result.AppServicePlan | Should Be $existingPlanName + } + } + + Context "[mock] When plan name and group name are provided and + group with that name exists + plan with that name exist and belongs to another group: " { + $existingPlanName = "ExistingPlanName" + $existingPlanGroup = "ExistingPlanGroup" + $existingGroupName = "GroupNAme" + $existingGroupLocation = "locationgroup" + $plan1 = @{Id=$existingPlanName;Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup=$existingPlanGroup} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup=$existingGroupName} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName=$existingGroupName;Location=$existingGroupLocation} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $true} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $existingPlanName -ResourceGroupName $existingGroupName + + It "Returns Site with DEFAULT name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with EXISTING group with same name provided." { + $result.ResourceGroupName | Should Be $existingGroupName + } + + It "Returns Site with the location of EXISTING Location." { + $result.Location | Should be $existingGroupLocation + } + + It "Returns Site with EXISTING AppServicePlan name." { + $result.AppServicePlan | Should Be $existingPlanName + } + } + + Context "[mock] When plan name and group name are provided and + group with that name exists + multiple plans with that name and + one belongs to the existing group: " { + $existingPlanName = "ExistingPlanName" + $existingPlanGroup = "ExistingPlanGroup" + $existingPlanGroupLocation = "locationgroup" + $plan1 = @{Id=$existingPlanName;Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup=$existingPlanGroup} + $plan2 = @{Id="plan2";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName=$existingPlanGroup ;Location=$existingPlanGroupLocation} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $true} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $existingPlanName -ResourceGroupName $existingPlanGroup + + It "Returns Site with DEFAULT name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with EXISTING group with same name provided." { + $result.ResourceGroupName | Should Be $existingPlanGroup + } + + It "Returns Site with the location of EXISTING Location." { + $result.Location | Should be $existingPlanGroupLocation + } + + It "Returns Site with EXISTING AppServicePlan name which belongs to the existing group." { + $result.AppServicePlan | Should Be $existingPlanName + } + } + + Context "[mock] When plan name and group name are provided and + group with that name exists and + plan with that name does not exist, and there is + a default plan: " { + $defaultPlanName = "DefaultPlanName" + $customPlan = "customPlan" + $existingGroup = "exisitingGroup" + $existingGroupLocation = "locationgroup" + $plan1 = @{Id="plan1";Name="plan2";Sku=@{Tier="Free"};ResourceGroup="group1"} + $plan2 = @{Id=$defaultPlanName;Name=$defaultPlanName;Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName=$existingGroup ;Location=$existingGroupLocation} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $true} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmAppServicePlan {return @{Id=$Name;Name=$Name;Location=$Location;Sku=@{Tier=$Tier};ResourcerGroupName=$ResourceGroupName}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $customPlan -ResourceGroupName $existingGroup + + It "Returns Site with DEFAULT name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with EXISTING group with same name provided." { + $result.ResourceGroupName | Should Be $existingGroup + } + + It "Returns Site with the location of EXISTING Location." { + $result.Location | Should be $existingGroupLocation + } + + It "Returns Site with EXISTING AppServicePlan name which belongs to the existing group." { + $result.AppServicePlan | Should Be $customPlan + } + } + + Context "[mock] When plan ID and group name are provided and + group with that name does not exist and + plan with that ID exist: " { + $existingPlanName = "existingPlan" + $existingPlanGroup = "existingPlanGroup" + $groupExistence = $false + $existingPlanId = "/subscriptions/f30a7701-df2c-1bc2-ba9d-pb11861cr13c/resourceGroups/$existingPlanGroup/providers/Microsoft.Web/serverfarms/$existingPlanName" + $providedGroupName = "ProvidedGroupName" + $plan1 = @{Id="plan1";Name="plan2";Sku=@{Tier="Free"};ResourceGroup="group1"} + $plan2 = @{Id=$existingPlanId;Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup=$existingPlanGroup} + $plans = @($plan1,$plan2) + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName=$existingPlanGroup ;Location="loc2"} + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $groupExistence} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmResourceGroup {return @{ResourceGroupName=$Name;Location=$Location}} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $existingPlanId -ResourceGroupName $providedGroupName + + It "Returns Site with DEFAULT name." { + $result.Name | Should be $defaultName + } + + It "Returns Site with NEW group with same name as the app." { + $result.ResourceGroupName | Should be $providedGroupName + } + + It "Returns Site with the location of DEFAULT Location." { + $result.Location | Should be $defaultLocation + } + + It "Returns Site with EXISTING AppServicePlan name." { + $result.AppServicePlan | Should Be $existingPlanName + } + } + + Context "[mock] When plan ID and group name are provided and + group with that name exists and + plan with that ID exist: " { + $existingPlanName = "existingPlan" + $existingPlanGroup = "existingPlanGroup" + $existingPlanId = "/subscriptions/f30a7701-df2c-1bc2-ba9d-pb11861cr13c/resourceGroups/$existingPlanGroup/providers/Microsoft.Web/serverfarms/$existingPlanName" + $groupExistence = $true + $plan1 = @{Id="plan1";Name="plan2";Sku=@{Tier="Free"};ResourceGroup="group1"} + $plan2 = @{Id=$existingPlanId;Name=$existingPlanName;Sku=@{Tier="Basic"};ResourceGroup=$existingPlanGroup} + $plans = @($plan1,$plan2) + $groupLocation = "groupLocation" + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName=$existingPlanGroup ;Location=$groupLocation } + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $groupExistence} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + $result = New-AzWebAppJustDoIt -AppServicePlan $existingPlanId -ResourceGroupName $existingPlanGroup + + It "Returns Site with DEFAULT name." { + $result.Name | Should Be $defaultName + } + + It "Returns Site with NEW group with same name as the app." { + $result.ResourceGroupName | Should Be $existingPlanGroup + } + + It "Returns Site with the location of DEFAULT Location." { + $result.Location | Should Be $groupLocation + } + + It "Returns Site with EXISTING AppServicePlan name." { + $result.AppServicePlan | Should Be $existingPlanName + } + } + + Context "[mock] When plan ID and group name are provided and + group with that name exists and + plan with that ID DOES NOT exist: " { + $providedPlanName = "ProvidedPlanName" + $providedPlanGroup = "ProvidedPlanGroup" + $providedPlanId = "/subscriptions/f30a7701-df2c-1bc2-ba9d-pb11861cr13c/resourceGroups/$providedPlanName/providers/Microsoft.Web/serverfarms/$providedPlanGroup" + $groupExistence = $true + $plan1 = @{Id="plan1";Name="plan1";Sku=@{Tier="Free"};ResourceGroup="group1"} + $plan2 = @{Id="plan1";Name="plan2";Sku=@{Tier="Basic"};ResourceGroup="group2"} + $plans = @($plan1,$plan2) + $existingGroup = "Existing Group" + $groupLocation = "groupLocation" + $group1 = @{ResourceGroupName="rg1";Location="loc1"} + $group2 = @{ResourceGroupName=$existingGroup ;Location=$groupLocation } + $group3 = @{ResourceGroupName="rg3";Location="loc3"} + $groups = @($group1, $group2, $group3) + Mock Test-NameAvailability {return $true} + Mock Test-ResourceGroupExistence {return $groupExistence} + Mock Get-AzureRmAppServicePlan {return Get-AzureRmAppServicePlanMock $Name $ResourceGroupName $plans} + Mock Get-AzureRmResourceGroup {return Get-AzureRmResourceGroupMock $Name $groups} + Mock New-AzureRmWebApp {return @{Name=$Name;ResourceGroupName=$ResourceGroupName;AppServicePlan=$AppServicePlan;Location=$Location}} + + It "Should Throw." { + {$result = New-AzWebAppJustDoIt -AppServicePlan $providedPlanId -ResourceGroupName $existingGroup} | Should Throw "The app service plan with the id provided does not exist" + } + } + + Context "[mock] When NON-VALID LOCATION is provided: " { + $nonValidLocation = "customLocation" + + It "Should throw." { + {$result = New-AzWebAppJustDoIt -ResourceGroupName $existingGroup.ResourceGroupName -Location $nonValidLocation} | Should Throw + } + } + } +} diff --git a/experiments/Websites.Experiments/AzureRM.Websites.Experiments.format.ps1xml b/experiments/Websites.Experiments/AzureRM.Websites.Experiments.format.ps1xml new file mode 100644 index 000000000000..4fae04944e6a --- /dev/null +++ b/experiments/Websites.Experiments/AzureRM.Websites.Experiments.format.ps1xml @@ -0,0 +1,44 @@ + + + + + Microsoft.Azure.Management.WebSites.Models.Site + + Microsoft.Azure.Management.WebSites.Models.Site + + + + + + + SiteName + + + ResourceGroup + + + + ($_.ServerFarmId.split('/'))[($_.ServerFarmId.split('/')).Count - 1] + + + Location + + + + DefaultHostName + + + + ($_.EnabledHostNames[1]) + + + + Learn how to deploy the files for your web app to Azure App Service following this link https://docs.microsoft.com/en-us/azure/app-service-web/web-sites-deploy + + + + + + + + diff --git a/experiments/Websites.Experiments/AzureRM.Websites.Experiments.psd1 b/experiments/Websites.Experiments/AzureRM.Websites.Experiments.psd1 new file mode 100644 index 000000000000..a3d850ea9e26 Binary files /dev/null and b/experiments/Websites.Experiments/AzureRM.Websites.Experiments.psd1 differ diff --git a/experiments/Websites.Experiments/AzureRM.Websites.Experiments.psm1 b/experiments/Websites.Experiments/AzureRM.Websites.Experiments.psm1 new file mode 100644 index 000000000000..cf31c26585d6 --- /dev/null +++ b/experiments/Websites.Experiments/AzureRM.Websites.Experiments.psm1 @@ -0,0 +1,1055 @@ +# ---------------------------------------------------------------------------------- +# +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ---------------------------------------------------------------------------------- + +function New-AzWebAppJustDoIt +{ + [CmdletBinding()] + [OutputType([Microsoft.Azure.Management.WebSites.Models.Site])] + param( + [string][Parameter(Mandatory=$false)][alias("Name")]$WebAppName, + [string][Parameter(Mandatory=$false)][alias("Group")]$ResourceGroupName, + [string][Parameter(Mandatory=$false)][alias("Plan")]$AppServicePlan + ) + DynamicParam{ + #Set the dynamic parameters' name + $ParamName_location = 'Location' + # Create the collection of attributes + $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + # Create and set the parameters' attributes + $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute + $ParameterAttribute.Mandatory = $false + # Add the attributes to the attributes collection + $AttributeCollection.Add($ParameterAttribute) + # Create the dictionary + $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary + #Generate and set the ValidateSet + $providerNamespace = "Microsoft.Web" + try + { + $availableLocations = $(Get-AzureRmResourceProvider | Where-Object {$_.ProviderNamespace -eq $providerNamespace}).Locations + } + catch + { + throw $_ + } + $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($availableLocations) + # Add the ValidateSet to the attributes collection + $AttributeCollection.Add($ValidateSetAttribute) + # Create and return the dynamic parameter + $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_location, [string], $AttributeCollection) + $RuntimeParameterDictionary.Add($ParamName_location, $RuntimeParameter) + + return $RuntimeParameterDictionary + } + + BEGIN { + $context = Get-Context + $webSitesClient = Get-WebSitesClient $context + $resourceManagementClient = Get-ResourceManagementClient $context + } + + PROCESS { + $mainActivity = "Create Azure Web App" + [string]$Location = $PSBoundParameters[$ParamName_location] + Write-Progress ` + -Activity "Some Activity." ` + -CurrentOperation "Getting App Name information." + [string]$appName = Get-WebAppName $PSBoundParameters $webSitesClient + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting Resource Group information." + $operationNumber = 2 + [hashtable]$groupInfo = Get-ResourceGroupInfo $PSBoundParameters $appName $resourceManagementClient + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting App Service Plan information." + $operationNumber = 3 + [hashtable]$appPlanInfo = Get-AppServicePlanInfo $PSBoundParameters $appName $groupInfo.Name + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting App Location information." + $operationNumber++ + [string]$appLocation = Get-AppLocation $PSBoundParameters $groupInfo.Name $groupInfo.Exists $availableLocations + if ($groupInfo.Exists) { + $appGroup = Get-AzureRmResourceGroup ` + -Name $groupInfo.Name + $message = "Using resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'." + Write-Information -MessageData $message -InformationAction Continue + } else { + $appGroup = New-AzureRmResourceGroup ` + -Name $groupInfo.Name ` + -Location $appLocation + $message = "Created resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'." + Write-Information -MessageData $message -InformationAction Continue + } + + $operationNumber = 4 + if ($appPlanInfo.Exists) { + $appPlan = Get-AzureRmAppServicePlan ` + -Name $appPlanInfo.Name ` + -ResourceGroupName $appPlanInfo.ResourceGroup + $message = "Using app service plan '$($appPlan.Name)' in location '$($appPlan.Location)'." + Write-Information -MessageData $message -InformationAction Continue + } else { + $defaultTier = "Free" + $appPlan = New-AzureRmAppServicePlan ` + -Name $appPlanInfo.Name ` + -Location $appLocation ` + -Tier $defaultTier ` + -ResourceGroupName $appPlanInfo.ResourceGroup + $message = "Created app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." + Write-Information -MessageData $message -InformationAction Continue + } + + $webapp = New-AzureRmWebApp ` + -Name $appName ` + -AppServicePlan $appPlan.Id ` + -ResourceGroupName $appGroup.ResourceGroupName ` + -Location $appLocation + + Write-Output $webapp + } + + END {} +} + +<# +.ExternalHelp AzureRM.Websites.Experiments-help.xml +#> +function New-AzWebApp +{ + [CmdletBinding(SupportsShouldProcess=$true)] + [OutputType([Microsoft.Azure.Management.WebSites.Models.Site])] + param( + [string][Parameter(Mandatory=$false)][alias("Name")]$WebAppName, + [string][Parameter(Mandatory=$false)][alias("Group")]$ResourceGroupName, + [string][Parameter(Mandatory=$false)][alias("Plan")]$AppServicePlan, + [switch][Parameter(Mandatory=$false)]$Auto, + [switch][Parameter(Mandatory=$false)]$AddRemote, + [string][Parameter(Mandatory=$false)]$GitRepositoryPath + ) + DynamicParam{ + #Set the dynamic parameters' name + $ParamName_location = 'Location' + # Create the collection of attributes + $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + # Create and set the parameters' attributes + $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute + $ParameterAttribute.Mandatory = $false + # Add the attributes to the attributes collection + $AttributeCollection.Add($ParameterAttribute) + # Create the dictionary + $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary + #Generate and set the ValidateSet + $providerNamespace = "Microsoft.Web" + try + { + $availableLocations = $(Get-AzureRmResourceProvider | Where-Object {$_.ProviderNamespace -eq $providerNamespace}).Locations + } + catch + { + throw $_ + } + $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($availableLocations) + # Add the ValidateSet to the attributes collection + $AttributeCollection.Add($ValidateSetAttribute) + # Create and return the dynamic parameter + $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_location, [string], $AttributeCollection) + $RuntimeParameterDictionary.Add($ParamName_location, $RuntimeParameter) + + return $RuntimeParameterDictionary + } + + BEGIN { + $context = Get-Context + $webSitesClient = Get-WebSitesClient $context + $resourceManagementClient = Get-ResourceManagementClient $context + } + + PROCESS { + #Validate Parameters + if (-not $PSBoundParameters.ContainsKey('Auto')) { + $parametersNotProvided = @() + if(-not $PSBoundParameters.ContainsKey('WebAppName')){ + $parametersNotProvided += 'WebAppName' + } + + if(-not $PSBoundParameters.ContainsKey('ResourceGroupName')){ + $parametersNotProvided += 'ResourceGroupName' + } + + if(-not $PSBoundParameters.ContainsKey('AppServicePlan')){ + $parametersNotProvided += 'AppServicePlan' + } + + if(-not $PSBoundParameters.ContainsKey('Location')){ + $parametersNotProvided += 'Location' + } + + $message = "The following parameters were not provided: " + $message += $($parametersNotProvided -join ',') + " ." + $message += "You can provide -Auto switch to use Smart Defaults." + $exception = New-Object -TypeName System.Exception -ArgumentList $message + throw $exception + } + + $mainActivity = "Create Azure Web App" + [string]$Location = $PSBoundParameters[$ParamName_location] + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting App Name information." + [string]$appName = Get-WebAppName ` + -ProvidedParameters $PSBoundParameters ` + -WebSitesClient $webSitesClient + if ($PSCmdlet.ShouldProcess($appName, "Create an Azure Web App")) { + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting Resource Group information." + [hashtable]$groupInfo = Get-ResourceGroupInfo ` + -ProvidedParameters $PSBoundParameters ` + -WebAppName $appName ` + -ResourceManagementClient $resourceManagementClient + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting App Service Plan information." + [hashtable]$appPlanInfo = Get-AppServicePlanInfo ` + -ProvidedParameters $PSBoundParameters ` + -WebAppName $appName ` + -ResourceGroupName $groupInfo.Name + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting App Location information." + [string]$appLocation = Get-AppLocation ` + -ProvidedParameters $PSBoundParameters ` + -ResourceGroupName $groupInfo.Name` + -ResourceGroupExists $groupInfo.Exists ` + -AvailableLocations $availableLocations + if ($groupInfo.Exists) { + $appGroup = Get-AzureRmResourceGroup ` + -Name $groupInfo.Name + $message = "Using resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'." + Write-Information -MessageData $message -InformationAction Continue + } else { + $appGroup = New-AzureRmResourceGroup ` + -Name $groupInfo.Name ` + -Location $appLocation + $message = "Created resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'." + Write-Information -MessageData $message -InformationAction Continue + } + + if ($appPlanInfo.Exists) { + $appPlan = Get-AzureRmAppServicePlan ` + -Name $appPlanInfo.Name ` + -ResourceGroupName $appPlanInfo.ResourceGroup + $message = "Using app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." + Write-Information -MessageData $message -InformationAction Continue + } else { + $defaultTier = "Free" + $appPlan = New-AzureRmAppServicePlan ` + -Name $appPlanInfo.Name ` + -Location $appLocation ` + -Tier $defaultTier ` + -ResourceGroupName $appPlanInfo.ResourceGroup + $message = "Created app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." + Write-Information -MessageData $message -InformationAction Continue + } + + $webapp = New-AzureRmWebApp ` + -Name $appName ` + -AppServicePlan $appPlan.Id ` + -ResourceGroupName $appGroup.ResourceGroupName ` + -Location $appLocation + + Write-Output $webapp + + if (($PSBoundParameters.ContainsKey('AddRemote') -or $PSBoundParameters.ContainsKey('Auto')) -and $webapp) { + Add-Remote -ProvidedParameters $PSBoundParameters -WebApp $webapp -GitRepositoryPath $GitRepositoryPath + } + } + } + + END {} +} + +function Add-Remote +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [Microsoft.Azure.Management.WebSites.Models.Site][Parameter()]$WebApp, + [string][Parameter()] $GitRepositoryPath + ) + [bool]$repoDetected = $true + [bool]$repoAdded = $true + $OriginalErrorActionPreference = $ErrorActionPreference + + if(-Not $ProvidedParameters.ContainsKey('GitRepositoryPath')){ + $GitRepositoryPath = (Get-Location).Path + } + + try + { + $ErrorActionPreference = 'Stop' + git -C $GitRepositoryPath status | Out-Null + } + catch + { + $repoDetected = $false + } + finally + { + $ErrorActionPreference = $OriginalErrorActionPreference + } + + if ($repoDetected) { + $message = "A git repository has been detected. " + try + { + $ErrorActionPreference = 'Stop' + # Get app-level deployment credentials + $xml = [xml](Get-AzureRmWebAppPublishingProfile -Name $WebApp.Name -ResourceGroupName $WebApp.ResourceGroup -OutputFile null) + $username = [System.Uri]::EscapeDataString($xml.SelectNodes("//publishProfile[@publishMethod=`"MSDeploy`"]/@userName").value) + $password = [System.Uri]::EscapeDataString($xml.SelectNodes("//publishProfile[@publishMethod=`"MSDeploy`"]/@userPWD").value) + $remoteName = "azure" + $url = ("https://$username" + ':' + "$password@$($WebApp.EnabledHostNames[1])") + # Add the Azure remote to a local Git respository + $command = "git -C $GitRepositoryPath remote add $remoteName $url" + Invoke-Expression -Command $command | Out-Null + if ($gitOutPut) { + $repoAdded = $false + } + } + catch + { + $repoAdded = $false + } + finally + { + $ErrorActionPreference = $OriginalErrorActionPreference + } + + if ($repoAdded) { + $message += "Added remote '$($remoteName)'. Push your code by running the command 'git push $($remoteName) master.' " + } else { + $message += "However, remote '$($remoteName)' could not be added. " + } + + Write-Information $message -InformationAction Continue + } +} + +function Get-WebAppName +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [Microsoft.Azure.Commands.Websites.Experiments.CustomWebSiteManagementClient][Parameter()]$WebSitesClient + ) + [string]$name = "" + [bool]$nameIsAvailable = $false + + if ($ProvidedParameters.ContainsKey('WebAppName')) { + $name = $ProvidedParameters.WebAppName + $nameIsAvailable = Test-NameAvailability $name $WebSitesClient + if (-not $NameIsAvailable) { + $message = "Website with given name '$name' already exists." + $exception = New-Object -TypeName System.Exception -ArgumentList $message + throw $exception + } + } else { + for ($i = 0; $i -le 2; $i++) { + $name ="WebApp$(Get-Random -max 1000000)" + $nameIsAvailable = Test-NameAvailability $name $WebSitesClient + if ($NameIsAvailable) { + break + } + } + } + return $name +} + +function Test-NameAvailability +{ + param( + [string][Parameter()]$WebAppName, + [Microsoft.Azure.Commands.Websites.Experiments.CustomWebSiteManagementClient][Parameter()]$WebSitesClient + ) + [string]$resourceType = "Site" + return $WebSitesClient.CheckNameAvailabilityWithHttpMessagesAsync($WebAppName, $resourceType).Result.Body.NameAvailable +} + +function Get-ResourceGroupInfo +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [string][Parameter(Mandatory=$false)]$WebAppName, + [Microsoft.Azure.Management.ResourceManager.ResourceManagementClient][Parameter()]$ResourceManagementClient + ) + [hashtable]$resourceGroupInfo = @{Name="";Exists=$false} + $defaultName = $WebAppName + + if ($ProvidedParameters.ContainsKey('ResourceGroupName')) { + $resourceGroupInfo.Name = $ProvidedParameters.ResourceGroupName + } else { + $resourceGroupInfo.Name = $defaultName + } + + $resourceGroupInfo.Exists = Test-ResourceGroupExistence $resourceGroupInfo.Name $ResourceManagementClient + + return $resourceGroupInfo +} + +function Test-ResourceGroupExistence +{ + param( + [string][Parameter()]$ResourceGroupName, + [Microsoft.Azure.Management.ResourceManager.ResourceManagementClient][Parameter()]$ResourceManagementClient + ) + return $ResourceManagementClient.ResourceGroups.CheckExistenceWithHttpMessagesAsync($ResourceGroupName).Result.Body +} + +function Get-AppServicePlanInfo +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [string][Parameter()]$WebAppName, + [string][Parameter()]$ResourceGroupName + ) + [hashtable]$appServicePlanInfo = @{Name="";ResourceGroup="";IsDefaultPlan=$false;Exists=$false} + [object[]]$appServicePlans = Get-AzureRmAppServicePlan + $defaultName = $WebAppName + + if ($ProvidedParameters.ContainsKey('AppServicePlan')) { + $regexp = '/subscriptions/[-A-Za-z0-9]+/resourceGroups/[-\w\._\(\)]+/providers/Microsoft.Web/serverfarms/[-\w\._\(\)]+$' + $idWasProvided = $ProvidedParameters.AppServicePlan -imatch $regexp + if ($idWasProvided) { + $parsedId = $ProvidedParameters.AppServicePlan.split('/') + $appServicePlanInfo.Name = $parsedId[$parsedId.Length - 1] + $appServicePlanInfo.ResourceGroup = $parsedId[4] + $existingPlan = $appServicePlans | Where-Object {$_.Id -eq $ProvidedParameters.AppServicePlan} + if (-not $existingPlan) { + $message = "The app service plan with the id provided does not exist." + $exception = New-Object -TypeName System.Exception -ArgumentList $message + throw $exception + } + } else { + $appServicePlanInfo.Name = $ProvidedParameters.AppServicePlan + } + $appServicePlanInfo.IsDefaultPlan = $false + } else { + $existentDefaultPlan = Get-DefaultAppServicePlan $AppServicePlans + if ($existentDefaultPlan) { + $appServicePlanInfo.Name = $existentDefaultPlan.Name + $appServicePlanInfo.ResourceGroup = $existentDefaultPlan.ResourceGroup + $appServicePlanInfo.IsDefaultPlan = $true + $appServicePlanInfo.Exists = $true + } else { + $appServicePlanInfo.Name = $defaultName + $appServicePlanInfo.IsDefaultPlan = $false + } + } + + if (-not $appServicePlanInfo.IsDefaultPlan) { + [object[]]$appServicePlansWithProvidedName = $appServicePlans | Where-Object {$_.Name -eq $appServicePlanInfo.Name} + if ($appServicePlansWithProvidedName) { + $appServicePlanInfo.Exists = $true + $appServicePlanWithProvidedNameAndGroup = $appServicePlansWithProvidedName | Where-Object {$_.ResourceGroup -eq $ResourceGroupName} + if ($appServicePlanWithProvidedNameAndGroup) { + $appServicePlanInfo.ResourceGroup = $appServicePlanWithProvidedNameAndGroup.ResourceGroup + } else { + if ($appServicePlansWithProvidedName.Count -gt 1) { + $message = "There are various App Service Plans with that name. An existing Resource Group name should be provided." + $exception = New-Object -TypeName System.Exception -ArgumentList $message + throw $exception + } else { + $appServicePlanInfo.ResourceGroup = $appServicePlansWithProvidedName.ResourceGroup + } + } + } else { + $appServicePlanInfo.Exists = $false + $appServicePlanInfo.ResourceGroup = $ResourceGroupName + } + } + + return $appServicePlanInfo +} + +function Get-DefaultAppServicePlan +{ + param( + [object[]][Parameter()]$AppServicePlans + ) + [object[]]$appServicePlanMatches = $AppServicePlans | Where-Object {$_.Sku.Tier -eq "Free"} + if($appServicePlanMatches){ + return $appServicePlanMatches[0] + } else { + return $null + } +} + +function Get-AppLocation +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [string][Parameter()]$ResourceGroupName, + [bool][Parameter()]$ResourceGroupExists, + [string[]][Parameter()]$AvailableLocations + ) + [string]$location = "" + + if ($ProvidedParameters.ContainsKey('Location')) { + $location = $ProvidedParameters.Location + } else { + if ($ResourceGroupExists) { + $location = $(Get-AzureRmResourceGroup -Name $ResourceGroupName).Location + } else { + $location = Get-DefaultLocation $AvailableLocations + } + } + return $location +} + +function Get-DefaultLocation +{ + param( + [string[]][Parameter()]$AvailableLocations + ) + # TODO: figure out a way to get a 'Smart Default Location' + return $AvailableLocations[0] +} + +function Get-Context +{ + return [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext +} + +function Get-ResourceManagementClient +{ + param( + [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext] $Context + ) + $factory = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.ClientFactory + [System.Type[]]$types = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext], [string] + $resourceManagementClient = [Microsoft.Azure.Management.ResourceManager.ResourceManagementClient] + $method = [Microsoft.Azure.Commands.Common.Authentication.IClientFactory].GetMethod("CreateArmClient", $types) + + $closedMethod = $method.MakeGenericMethod($resourceManagementClient) + $arguments = $Context, [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureEnvironment+Endpoint]::ResourceManager + $client = $closedMethod.Invoke($factory, $arguments) + + return $client +} + +function Get-WebSitesClient +{ + param( + [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext] $Context + ) + $factory = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.ClientFactory + [System.Type[]]$types = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext], [string] + $webSitesClient = [Microsoft.Azure.Commands.Websites.Experiments.CustomWebSiteManagementClient] + $method = [Microsoft.Azure.Commands.Common.Authentication.IClientFactory].GetMethod("CreateArmClient", $types) + + $closedMethod = $method.MakeGenericMethod($webSitesClient) + $arguments = $context, [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureEnvironment+Endpoint]::ResourceManager + $client = $closedMethod.Invoke($factory, $arguments) + + return $client +} + +<# +.ExternalHelp AzureRM.Websites.Experiments-help.xml +#> +function New-AzWebAppGrayParam +{ + [CmdletBinding(SupportsShouldProcess=$true)] + [OutputType([Microsoft.Azure.Management.WebSites.Models.Site])] + param( + [string][Parameter(Mandatory=$false)][alias("Name")]$WebAppName, + [string][Parameter(Mandatory=$false)][alias("Group")]$ResourceGroupName, + [string][Parameter(Mandatory=$false)][alias("Plan")]$AppServicePlan, + [switch][Parameter(Mandatory=$false)]$Auto, + [switch][Parameter(Mandatory=$false)]$AddRemote, + [string][Parameter(Mandatory=$false)]$GitRepositoryPath + ) + DynamicParam{ + #Set the dynamic parameters' name + $ParamName_location = 'Location' + # Create the collection of attributes + $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + # Create and set the parameters' attributes + $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute + if ($PSBoundParameters.ContainsKey('Auto')) { + $ParameterAttribute.Mandatory = $false + } else { + $ParameterAttribute.Mandatory = $true + } + # Add the attributes to the attributes collection + $AttributeCollection.Add($ParameterAttribute) + # Create the dictionary + $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary + #Generate and set the ValidateSet + $providerNamespace = "Microsoft.Web" + try + { + $availableLocations = $(Get-AzureRmResourceProvider | Where-Object {$_.ProviderNamespace -eq $providerNamespace}).Locations + } + catch + { + throw $_ + } + $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($availableLocations) + # Add the ValidateSet to the attributes collection + $AttributeCollection.Add($ValidateSetAttribute) + # Create and return the dynamic parameter + $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_location, [string], $AttributeCollection) + $RuntimeParameterDictionary.Add($ParamName_location, $RuntimeParameter) + + return $RuntimeParameterDictionary + } + + BEGIN { + $context = Get-Context + $webSitesClient = Get-WebSitesClient $context + $resourceManagementClient = Get-ResourceManagementClient $context + } + + PROCESS { + $mainActivity = "Create Azure Web App" + [string]$Location = $PSBoundParameters[$ParamName_location] + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting App Name information." + [string]$appName = Get-WebAppNameGrayParam $PSBoundParameters $webSitesClient + if ($PSCmdlet.ShouldProcess($appName, "Create an Azure Web App")) { + + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting Resource Group information." + [hashtable]$groupInfo = Get-ResourceGroupInfoGrayParam $PSBoundParameters $appName $resourceManagementClient + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting App Service Plan information." + [hashtable]$appPlanInfo = Get-AppServicePlanInfoGrayParam $PSBoundParameters $appName $groupInfo.Name + Write-Progress ` + -Activity $mainActivity ` + -CurrentOperation "Getting App Location information." + [string]$appLocation = Get-LocationGrayParam $PSBoundParameters $groupInfo.Name $groupInfo.Exists $availableLocations + if ($groupInfo.Exists) { + $appGroup = Get-AzureRmResourceGroup ` + -Name $groupInfo.Name + $message = "Using resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'." + Write-Information -MessageData $message -InformationAction Continue + } else { + $appGroup = New-AzureRmResourceGroup ` + -Name $groupInfo.Name ` + -Location $appLocation + $message = "Created resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'." + Write-Information -MessageData $message -InformationAction Continue + } + + if ($appPlanInfo.Exists) { + $appPlan = Get-AzureRmAppServicePlan ` + -Name $appPlanInfo.Name ` + -ResourceGroupName $appPlanInfo.ResourceGroup + $message = "Using app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." + Write-Information -MessageData $message -InformationAction Continue + } else { + $defaultTier = "Free" + $appPlan = New-AzureRmAppServicePlan ` + -Name $appPlanInfo.Name ` + -Location $appLocation ` + -Tier $defaultTier ` + -ResourceGroupName $appPlanInfo.ResourceGroup + $message = "Created app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." + Write-Information -MessageData $message -InformationAction Continue + } + + $webapp = New-AzureRmWebApp ` + -Name $appName ` + -AppServicePlan $appPlan.Id ` + -ResourceGroupName $appGroup.ResourceGroupName ` + -Location $appLocation + + Write-Output $webapp + + Add-RemoteGrayParam -ProvidedParameters $PSBoundParameters -WebApp $webapp -GitRepositoryPath $GitRepositoryPath + } + } + + END {} +} + +function Add-RemoteGrayParam +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [Microsoft.Azure.Management.WebSites.Models.Site][Parameter()]$WebApp, + [string][Parameter()] $GitRepositoryPath + ) + [bool]$repoDetected = $true + [bool]$repoAdded = $true + $OriginalErrorActionPreference = $ErrorActionPreference + + if(-Not $ProvidedParameters.ContainsKey('GitRepositoryPath')){ + $GitRepositoryPath = (Get-Location).Path + } + + try + { + $ErrorActionPreference = 'Stop' + git -C $GitRepositoryPath status | Out-Null + } + catch + { + $repoDetected = $false + } + finally + { + $ErrorActionPreference = $OriginalErrorActionPreference + } + + if ($repoDetected) { + $message = "A git repository has been detected. " + try + { + $ErrorActionPreference = 'Stop' + + # Get app-level deployment credentials + $xml = [xml](Get-AzureRmWebAppPublishingProfile -Name $WebApp.Name -ResourceGroupName $WebApp.ResourceGroup -OutputFile null) + $username = $xml.SelectNodes("//publishProfile[@publishMethod=`"MSDeploy`"]/@userName").value + $password = $xml.SelectNodes("//publishProfile[@publishMethod=`"MSDeploy`"]/@userPWD").value + + # Add remote azure + $remoteName = "azure" + if ($ProvidedParameters.ContainsKey('Auto') -or $ProvidedParameters.ContainsKey('AddRemote')){ + Write-Information $message -InformationAction Continue + } else { + $title = $message + $message = "Would you like to add this webapp as a remote named '$remoteName'?" + $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", ` + "Adds this webapp as a remote named '$remoteName'" + $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", ` + "No action is taken" + $options = [System.Management.Automation.Host.ChoiceDescription[]]($no, $yes) + $result = $host.ui.PromptForChoice($title, $message, $options, 0) + } + + if ($result -eq 1 -or $ProvidedParameters.ContainsKey('AddRemote') -or $ProvidedParameters.ContainsKey('Auto')) { + # Add the Azure remote to a local Git respository + $command = "git -C $GitRepositoryPath remote add $remoteName 'https://${username}:$password@$($WebApp.EnabledHostNames[0])'" + " 2> $gitOutput" + Invoke-Expression -Command $command | Out-Null + + } + + if ($gitOutPut) { + $repoAdded = $false + } + } + catch + { + $repoAdded = $false + } + finally + { + $ErrorActionPreference = $OriginalErrorActionPreference + } + + if ($repoAdded) { + $message = "Added remote '$($remoteName)'. Push your code by running the command 'git push $($remoteName) master.' " + } else { + $message = "Remote '$($remoteName)' could not be added. " + } + + Write-Information $message -InformationAction Continue + } +} + +function Get-WebAppNameGrayParam +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [Microsoft.Azure.Commands.Websites.Experiments.CustomWebSiteManagementClient][Parameter()]$WebSitesClient + ) + [string]$name = "" + [bool]$nameIsAvailable = $false + + if ($ProvidedParameters.ContainsKey('WebAppName')) { + $name = $ProvidedParameters.WebAppName + $nameIsAvailable = Test-NameAvailability $name $WebSitesClient + if (-not $NameIsAvailable) { + $message = "Website with given name '$name' already exists." + $exception = New-Object -TypeName System.Exception -ArgumentList $message + throw $exception + } + } else { + for ($i = 0; $i -le 2; $i++) { + $defaultName ="WebApp$(Get-Random -max 1000000)" + $nameIsAvailable = Test-NameAvailability $defaultName $WebSitesClient + if ($NameIsAvailable) { + break + } + } + + if ($ProvidedParameters.ContainsKey('Auto')) { + $name = $defaultName + } else { + $name = Get-WebAppNameFromUser $defaultName + } + } + + return $name +} + +function Get-WebAppNameFromUser +{ + param( + [string][Parameter()]$DefaultName + ) + + $selection = Read-Host "Enter a name for you WebApp or leave blank for default($defaultName)" + if ($selection) { + $nameIsAvailable = Test-NameAvailability $selection $WebSitesClient + if (-not $NameIsAvailable) { + $message = "Website with given name '$selection' already exists." + $exception = New-Object -TypeName System.Exception -ArgumentList $message + throw $exception + } else { + $name = $selection + } + } else { + $name = $DefaultName + } + + return $name +} + + +function Get-ResourceGroupInfoGrayParam +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [string][Parameter(Mandatory=$false)]$WebAppName, + [Microsoft.Azure.Management.ResourceManager.ResourceManagementClient][Parameter()]$ResourceManagementClient + ) + [hashtable]$resourceGroupInfo = @{Name="";Exists=$false} + $defaultName = $WebAppName + + if ($ProvidedParameters.ContainsKey('ResourceGroupName')) { + $resourceGroupInfo.Name = $ProvidedParameters.ResourceGroupName + } else { + if ($ProvidedParameters.ContainsKey('Auto')) { + $resourceGroupInfo.Name = $defaultName + } else { + $resourceGroupInfo.Name = Get-ResourceGroupNameFromUser $defaultName + } + + } + + $resourceGroupInfo.Exists = Test-ResourceGroupExistence $resourceGroupInfo.Name $ResourceManagementClient + + return $resourceGroupInfo +} + +function Get-ResourceGroupNameFromUser +{ + param( + [string][Parameter()]$DefaultName + ) + [object[]]$resourceGroups = Get-AzureRmResourceGroup + Write-Host "Resource Group options: " + Write-Host "[Default] $DefaultName" + for ($i = 1; $i -le $resourceGroups.Count; $i++) { + Write-Host "[$i] $($resourceGroups[$i-1].ResourceGroupName)" + } + + $selection = Read-Host "Enter your selection or a new resource group name (leave blank for default)" + if ($selection) { + if ($selection -match '^\d+$' -and $selection -le $resourceGroups.Count -and $selection -gt 0) { + $name = $resourceGroups[$selection - 1].ResourceGroupName + } else { + $name = $selection + } + } else { + $name = $DefaultName + } + + return $name +} + +function Get-AppServicePlanInfoGrayParam +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [string][Parameter()]$WebAppName, + [string][Parameter()]$ResourceGroupName + ) + [hashtable]$appServicePlanInfo = @{Name="";ResourceGroup="";IsDefaultPlan=$false;Exists=$false} + [object[]]$appServicePlans = Get-AzureRmAppServicePlan + $defaultName = $WebAppName + + if ($ProvidedParameters.ContainsKey('AppServicePlan')) { + $regexp = '/subscriptions/[-A-Za-z0-9]+/resourceGroups/[-\w\._\(\)]+/providers/Microsoft.Web/serverfarms/[-\w\._\(\)]+$' + $idWasProvided = $ProvidedParameters.AppServicePlan -imatch $regexp + if ($idWasProvided) { + $parsedId = $ProvidedParameters.AppServicePlan.split('/') + $appServicePlanInfo.Name = $parsedId[$parsedId.Length - 1] + $appServicePlanInfo.ResourceGroup = $parsedId[4] + $existingPlan = $appServicePlans | Where-Object {$_.Id -eq $ProvidedParameters.AppServicePlan} + if (-not $existingPlan) { + $message = "The app service plan with the id provided does not exist." + $exception = New-Object -TypeName System.Exception -ArgumentList $message + throw $exception + } + } else { + $appServicePlanInfo.Name = $ProvidedParameters.AppServicePlan + } + $appServicePlanInfo.IsDefaultPlan = $false + } else { + if($ProvidedParameters.ContainsKey('Auto')){ + $existentDefaultPlan = Get-DefaultAppServicePlan $appServicePlans + if ($existentDefaultPlan) { + $appServicePlanInfo.Name = $existentDefaultPlan.Name + $appServicePlanInfo.ResourceGroup = $existentDefaultPlan.ResourceGroup + $appServicePlanInfo.IsDefaultPlan = $true + $appServicePlanInfo.Exists = $true + } else { + $appServicePlanInfo.Name = $defaultName + $appServicePlanInfo.IsDefaultPlan = $false + } + } else { + $appServicePlanInfo = Get-AppServicePlanInfoFromUser $appServicePlans $defaultName + } + } + + if (-not $appServicePlanInfo.IsDefaultPlan) { + [object[]]$appServicePlansWithProvidedName = $appServicePlans | Where-Object {$_.Name -eq $appServicePlanInfo.Name} + if ($appServicePlansWithProvidedName) { + $appServicePlanInfo.Exists = $true + $appServicePlanWithProvidedNameAndGroup = $appServicePlansWithProvidedName | Where-Object {$_.ResourceGroup -eq $ResourceGroupName} + if ($appServicePlanWithProvidedNameAndGroup) { + $appServicePlanInfo.ResourceGroup = $appServicePlanWithProvidedNameAndGroup.ResourceGroup + } else { + if ($appServicePlansWithProvidedName.Count -gt 1) { + $message = "There are various App Service Plans with that name. An existing Resource Group name should be provided." + $exception = New-Object -TypeName System.Exception -ArgumentList $message + throw $exception + } else { + $appServicePlanInfo.ResourceGroup = $appServicePlansWithProvidedName.ResourceGroup + } + } + } else { + $appServicePlanInfo.Exists = $false + $appServicePlanInfo.ResourceGroup = $ResourceGroupName + } + } + + return $appServicePlanInfo +} + +function Get-AppServicePlanInfoFromUser +{ + param( + [object[]]$Plans, + [string]$DefaultName + ) + [hashtable]$appServicePlanInfo = @{Name="";ResourceGroup="";IsDefaultPlan=$false;Exists=$false} + $existentDefaultPlan = Get-DefaultAppServicePlan $Plans + + Write-Host "Plan options:" + if ($existentDefaultPlan) { + Write-Host "[Default] $($existentDefaultPlan.Name) {Tier=$($existentDefaultPlan.Sku.Tier);Location=$($existentDefaultPlan.Location)}" + } else { + Write-Host "[Default] $($defaultName) {Tier=Free}" + } + + for ($i = 1; $i -le $Plans.Count; $i++) { + Write-Host "[$i] $($Plans[$i-1].Name) {Tier=$($Plans[$i-1].Sku.Tier);Location=$($Plans[$i-1].Location)}" + } + + $selection = Read-Host "Enter your selection (leave blank for default)" + if ($selection) { + if ($selection -match '^\d+$' -and $selection -le $Plans.Count -and $selection -gt 0) { + $appServicePlanInfo.Name = $Plans[$selection - 1].Name + $appServicePlanInfo.IsDefaultPlan = $false + } else { + $appServicePlanInfo.Name = $selection + $appServicePlanInfo.IsDefaultPlan = $false + } + } else { + if ($existentDefaultPlan) { + $appServicePlanInfo.Name = $existentDefaultPlan.Name + $appServicePlanInfo.ResourceGroup = $existentDefaultPlan.ResourceGroup + $appServicePlanInfo.IsDefaultPlan = $true + $appServicePlanInfo.Exists = $true + } else { + $appServicePlanInfo.Name = $defaultName + $appServicePlanInfo.IsDefaultPlan = $false + } + } + + return $appServicePlanInfo + } + +function Get-LocationGrayParam +{ + param( + [hashtable][Parameter()]$ProvidedParameters, + [string][Parameter()]$ResourceGroupName, + [bool][Parameter()]$ResourceGroupExists, + [string[]][Parameter()]$AvailableLocations + ) + [string]$location = "" + + if ($ProvidedParameters.ContainsKey('Location')) { + $location = $ProvidedParameters.Location + } else { + if ($ResourceGroupExists) { + $location = $(Get-AzureRmResourceGroup -Name $ResourceGroupName).Location + } else { + $location = Get-DefaultLocation $AvailableLocations + } + } + + return $location +} + +function Get-LocationFromUser +{ + param( + [string][Parameter()]$DefaultLocation, + [string[]][Parameter()]$AvailableLocations + ) + Write-Host "WebApp Location options: " + Write-Host "[Default] $DefaultLocation" + for ($i = 1; $i -le $AvailableLocations.Count; $i++) { + Write-Host "[$i] $($AvailableLocations[$i-1])" + } + + $selection = Read-Host "Enter your selection (leave blank for default)" + if ($selection) { + if ($selection -match '^\d+$' -and $selection -le $AvailableLocations.Count -and $selection -gt 0) { + $location = $AvailableLocations[$selection - 1] + } else { + $location = $selection + } + } else { + $location = $DefaultLocation + } + + return $location +} + + +Export-ModuleMember -Cmdlet New-AzWebApp +Export-ModuleMember -Cmdlet New-AzWebAppGrayParam + diff --git a/experiments/Websites.Experiments/CustomWebSiteManagementClient.cs b/experiments/Websites.Experiments/CustomWebSiteManagementClient.cs new file mode 100644 index 000000000000..1554de5aa4ba --- /dev/null +++ b/experiments/Websites.Experiments/CustomWebSiteManagementClient.cs @@ -0,0 +1,510 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator 1.0.0.0 +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. + +using System.Net.Http.Headers; +using Microsoft.Rest; +using Microsoft.Rest.Azure; +using Microsoft.Rest.Serialization; +using Microsoft.Azure.Commands.Websites.Models; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Microsoft.Azure.Commands.Websites.Experiments +{ + public partial class CustomWebSiteManagementClient : ServiceClient, IAzureClient + { + /// + /// The base URI of the service. + /// + public System.Uri BaseUri { get; set; } + + /// + /// Gets or sets json serialization settings. + /// + public JsonSerializerSettings SerializationSettings { get; private set; } + + /// + /// Gets or sets json deserialization settings. + /// + public JsonSerializerSettings DeserializationSettings { get; private set; } + + /// + /// Credentials needed for the client to connect to Azure. + /// + public ServiceClientCredentials Credentials { get; private set; } + + /// + /// Your Azure subscription ID. This is a GUID-formatted string (e.g. + /// 00000000-0000-0000-0000-000000000000). + /// + public string SubscriptionId { get; set; } + + /// + /// Gets or sets the preferred language for the response. + /// + public string AcceptLanguage { get; set; } + + /// + /// Gets or sets the retry timeout in seconds for Long Running Operations. + /// Default value is 30. + /// + public int? LongRunningOperationRetryTimeout { get; set; } + + /// + /// When set to true a unique x-ms-client-request-id value is generated and + /// included in each request. Default is true. + /// + public bool? GenerateClientRequestId { get; set; } + + /// + /// Initializes a new instance of the CustomWebSiteManagementClient class. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + protected CustomWebSiteManagementClient(params DelegatingHandler[] handlers) : base(handlers) + { + Initialize(); + } + + /// + /// Initializes a new instance of the CustomWebSiteManagementClient class. + /// + /// + /// Optional. The http client handler used to handle http transport. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + protected CustomWebSiteManagementClient(HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : base(rootHandler, handlers) + { + Initialize(); + } + + /// + /// Initializes a new instance of the CustomWebSiteManagementClient class. + /// + /// + /// Optional. The base URI of the service. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + protected CustomWebSiteManagementClient(System.Uri baseUri, params DelegatingHandler[] handlers) : this(handlers) + { + if (baseUri == null) + { + throw new System.ArgumentNullException("baseUri"); + } + BaseUri = baseUri; + } + + /// + /// Initializes a new instance of the CustomWebSiteManagementClient class. + /// + /// + /// Optional. The base URI of the service. + /// + /// + /// Optional. The http client handler used to handle http transport. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + protected CustomWebSiteManagementClient(System.Uri baseUri, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers) + { + if (baseUri == null) + { + throw new System.ArgumentNullException("baseUri"); + } + BaseUri = baseUri; + } + + /// + /// Initializes a new instance of the CustomWebSiteManagementClient class. + /// + /// + /// Required. Credentials needed for the client to connect to Azure. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + public CustomWebSiteManagementClient(ServiceClientCredentials credentials, params DelegatingHandler[] handlers) : this(handlers) + { + if (credentials == null) + { + throw new System.ArgumentNullException("credentials"); + } + Credentials = credentials; + if (Credentials != null) + { + Credentials.InitializeServiceClient(this); + } + } + + /// + /// Initializes a new instance of the CustomWebSiteManagementClient class. + /// + /// + /// Required. Credentials needed for the client to connect to Azure. + /// + /// + /// Optional. The http client handler used to handle http transport. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + public CustomWebSiteManagementClient(ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers) + { + if (credentials == null) + { + throw new System.ArgumentNullException("credentials"); + } + Credentials = credentials; + if (Credentials != null) + { + Credentials.InitializeServiceClient(this); + } + } + + /// + /// Initializes a new instance of the CustomWebSiteManagementClient class. + /// + /// + /// Optional. The base URI of the service. + /// + /// + /// Required. Credentials needed for the client to connect to Azure. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + public CustomWebSiteManagementClient(System.Uri baseUri, ServiceClientCredentials credentials, params DelegatingHandler[] handlers) : this(handlers) + { + if (baseUri == null) + { + throw new System.ArgumentNullException("baseUri"); + } + if (credentials == null) + { + throw new System.ArgumentNullException("credentials"); + } + BaseUri = baseUri; + Credentials = credentials; + if (Credentials != null) + { + Credentials.InitializeServiceClient(this); + } + } + + /// + /// Initializes a new instance of the CustomWebSiteManagementClient class. + /// + /// + /// Optional. The base URI of the service. + /// + /// + /// Required. Credentials needed for the client to connect to Azure. + /// + /// + /// Optional. The http client handler used to handle http transport. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + public CustomWebSiteManagementClient(System.Uri baseUri, ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers) + { + if (baseUri == null) + { + throw new System.ArgumentNullException("baseUri"); + } + if (credentials == null) + { + throw new System.ArgumentNullException("credentials"); + } + BaseUri = baseUri; + Credentials = credentials; + if (Credentials != null) + { + Credentials.InitializeServiceClient(this); + } + } + + /// + /// Initializes client properties. + /// + private void Initialize() + { + this.HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + BaseUri = new System.Uri("https://management.azure.com"); + AcceptLanguage = "en-US"; + LongRunningOperationRetryTimeout = 30; + GenerateClientRequestId = true; + SerializationSettings = new JsonSerializerSettings + { + Formatting = Newtonsoft.Json.Formatting.Indented, + DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat, + DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc, + NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, + ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize, + ContractResolver = new ReadOnlyJsonContractResolver(), + Converters = new System.Collections.Generic.List + { + new Iso8601TimeSpanConverter() + } + }; + SerializationSettings.Converters.Add(new TransformationJsonConverter()); + DeserializationSettings = new JsonSerializerSettings + { + DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat, + DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc, + NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, + ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize, + ContractResolver = new ReadOnlyJsonContractResolver(), + Converters = new System.Collections.Generic.List + { + new Iso8601TimeSpanConverter() + } + }; + DeserializationSettings.Converters.Add(new TransformationJsonConverter()); + DeserializationSettings.Converters.Add(new CloudErrorJsonConverter()); + } + + /// + /// Check if a resource name is available. + /// + /// + /// Check if a resource name is available. + /// + /// + /// Resource name to verify. + /// + /// + /// Resource type used for verification. Possible values include: 'Site', + /// 'Slot', 'HostingEnvironment' + /// + /// + /// Is fully qualified domain name. + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> CheckNameAvailabilityWithHttpMessagesAsync(string name, string type, bool? isFqdn = default(bool?), Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (SubscriptionId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.SubscriptionId"); + } + if (name == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "name"); + } + if (type == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "type"); + } + string apiVersion = "2016-03-01"; + ResourceNameAvailabilityRequest request = new ResourceNameAvailabilityRequest(); + if (name != null || type != null || isFqdn != null) + { + request.Name = name; + request.Type = type; + request.IsFqdn = isFqdn; + } + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("apiVersion", apiVersion); + tracingParameters.Add("request", request); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "CheckNameAvailability", tracingParameters); + } + // Construct URL + var _baseUrl = BaseUri.AbsoluteUri; + var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "subscriptions/{subscriptionId}/providers/Microsoft.Web/checknameavailability").ToString(); + _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(SubscriptionId)); + List _queryParameters = new List(); + if (apiVersion != null) + { + _queryParameters.Add(string.Format("api-version={0}", System.Uri.EscapeDataString(apiVersion))); + } + if (_queryParameters.Count > 0) + { + _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); + } + // Create HTTP transport objects + var _httpRequest = new System.Net.Http.HttpRequestMessage(); + System.Net.Http.HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new System.Net.Http.HttpMethod("POST"); + _httpRequest.RequestUri = new System.Uri(_url); + // Set Headers + if (GenerateClientRequestId != null && GenerateClientRequestId.Value) + { + _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); + } + if (AcceptLanguage != null) + { + if (_httpRequest.Headers.Contains("accept-language")) + { + _httpRequest.Headers.Remove("accept-language"); + } + _httpRequest.Headers.TryAddWithoutValidation("accept-language", AcceptLanguage); + } + if (customHeaders != null) + { + foreach (var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + if (request != null) + { + _requestContent = SafeJsonConvert.SerializeObject(request, SerializationSettings); + _httpRequest.Content = new System.Net.Http.StringContent(_requestContent, System.Text.Encoding.UTF8); + _httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + } + // Set Credentials + if (Credentials != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new CloudException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + CloudError _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, DeserializationSettings); + if (_errorBody != null) + { + ex = new CloudException(_errorBody.Message); + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + ex.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new AzureOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + } +} diff --git a/experiments/Websites.Experiments/MSSharedLibKey.snk b/experiments/Websites.Experiments/MSSharedLibKey.snk new file mode 100644 index 000000000000..695f1b38774e Binary files /dev/null and b/experiments/Websites.Experiments/MSSharedLibKey.snk differ diff --git a/experiments/Websites.Experiments/Microsoft.Azure.Commands.Websites.Experiments.csproj b/experiments/Websites.Experiments/Microsoft.Azure.Commands.Websites.Experiments.csproj new file mode 100644 index 000000000000..6950cad000d6 --- /dev/null +++ b/experiments/Websites.Experiments/Microsoft.Azure.Commands.Websites.Experiments.csproj @@ -0,0 +1,94 @@ + + + + + Debug + AnyCPU + {870244A0-0703-4D43-AF20-58E9F2C6C63E} + Library + Properties + Microsoft.Azure.Commands.Websites.Experiments + Microsoft.Azure.Commands.Websites.Experiments + v4.5.2 + 512 + + + true + full + false + ..\..\src\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Websites.Experiments\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\src\Package\Release\ResourceManager\AzureResourceManager\AzureRM.Websites.Experiments\ + TRACE; SIGN + prompt + 4 + true + MSSharedLibKey.snk + true + + + + packages\Microsoft.Rest.ClientRuntime.2.3.9\lib\net452\Microsoft.Rest.ClientRuntime.dll + + + packages\Microsoft.Rest.ClientRuntime.Azure.3.3.9\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll + + + packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + PreserveNewest + + + Always + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + PreserveNewest + + + + + + + + Always + + + + \ No newline at end of file diff --git a/experiments/Websites.Experiments/Microsoft.Azure.Commands.Websites.Experiments.sln b/experiments/Websites.Experiments/Microsoft.Azure.Commands.Websites.Experiments.sln new file mode 100644 index 000000000000..85269d81eff6 --- /dev/null +++ b/experiments/Websites.Experiments/Microsoft.Azure.Commands.Websites.Experiments.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Commands.Websites.Experiments", "Microsoft.Azure.Commands.Websites.Experiments.csproj", "{870244A0-0703-4D43-AF20-58E9F2C6C63E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {870244A0-0703-4D43-AF20-58E9F2C6C63E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {870244A0-0703-4D43-AF20-58E9F2C6C63E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {870244A0-0703-4D43-AF20-58E9F2C6C63E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {870244A0-0703-4D43-AF20-58E9F2C6C63E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/experiments/Websites.Experiments/Models/ResourceNameAvailability.cs b/experiments/Websites.Experiments/Models/ResourceNameAvailability.cs new file mode 100644 index 000000000000..d32860c63a0f --- /dev/null +++ b/experiments/Websites.Experiments/Models/ResourceNameAvailability.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator 1.0.0.0 +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. + +using Newtonsoft.Json; + +namespace Microsoft.Azure.Commands.Websites.Models +{ + /// + /// Information regarding availbility of a resource name. + /// + public partial class ResourceNameAvailability + { + /// + /// Initializes a new instance of the ResourceNameAvailability class. + /// + public ResourceNameAvailability() { } + + /// + /// Initializes a new instance of the ResourceNameAvailability class. + /// + /// <code>true</code> indicates + /// name is valid and available. <code>false</code> + /// indicates the name is invalid, unavailable, or both. + /// <code>Invalid</code> indicates the + /// name provided does not match Azure App Service naming requirements. + /// <code>AlreadyExists</code> indicates that the name is + /// already in use and is therefore unavailable. Possible values + /// include: 'Invalid', 'AlreadyExists' + /// If reason == invalid, provide the user with + /// the reason why the given name is invalid, and provide the resource + /// naming requirements so that the user can select a valid name. If + /// reason == AlreadyExists, explain that resource name is already in + /// use, and direct them to select a different name. + public ResourceNameAvailability(bool? nameAvailable = default(bool?), string reason = default(string), string message = default(string)) + { + NameAvailable = nameAvailable; + Reason = reason; + Message = message; + } + + /// + /// Gets or sets &lt;code&gt;true&lt;/code&gt; + /// indicates name is valid and available. + /// &lt;code&gt;false&lt;/code&gt; indicates the name + /// is invalid, unavailable, or both. + /// + [JsonProperty(PropertyName = "nameAvailable")] + public bool? NameAvailable { get; set; } + + /// + /// Gets or sets &lt;code&gt;Invalid&lt;/code&gt; + /// indicates the name provided does not match Azure App Service naming + /// requirements. + /// &lt;code&gt;AlreadyExists&lt;/code&gt; indicates + /// that the name is already in use and is therefore unavailable. + /// Possible values include: 'Invalid', 'AlreadyExists' + /// + [JsonProperty(PropertyName = "reason")] + public string Reason { get; set; } + + /// + /// Gets or sets if reason == invalid, provide the user with the reason + /// why the given name is invalid, and provide the resource naming + /// requirements so that the user can select a valid name. If reason == + /// AlreadyExists, explain that resource name is already in use, and + /// direct them to select a different name. + /// + [JsonProperty(PropertyName = "message")] + public string Message { get; set; } + + } +} + diff --git a/experiments/Websites.Experiments/Models/ResourceNameAvailabilityRequest.cs b/experiments/Websites.Experiments/Models/ResourceNameAvailabilityRequest.cs new file mode 100644 index 000000000000..e8815bcd0ffb --- /dev/null +++ b/experiments/Websites.Experiments/Models/ResourceNameAvailabilityRequest.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator 1.0.0.0 +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. + +using Microsoft.Rest; +using Newtonsoft.Json; + +namespace Microsoft.Azure.Commands.Websites.Models +{ + /// + /// Resource name availability request content. + /// + public partial class ResourceNameAvailabilityRequest + { + /// + /// Initializes a new instance of the ResourceNameAvailabilityRequest + /// class. + /// + public ResourceNameAvailabilityRequest() { } + + /// + /// Initializes a new instance of the ResourceNameAvailabilityRequest + /// class. + /// + /// Resource name to verify. + /// Resource type used for verification. Possible + /// values include: 'Site', 'Slot', 'HostingEnvironment' + /// Is fully qualified domain name. + public ResourceNameAvailabilityRequest(string name, string type, bool? isFqdn = default(bool?)) + { + Name = name; + Type = type; + IsFqdn = isFqdn; + } + + /// + /// Gets or sets resource name to verify. + /// + [JsonProperty(PropertyName = "name")] + public string Name { get; set; } + + /// + /// Gets or sets resource type used for verification. Possible values + /// include: 'Site', 'Slot', 'HostingEnvironment' + /// + [JsonProperty(PropertyName = "type")] + public string Type { get; set; } + + /// + /// Gets or sets is fully qualified domain name. + /// + [JsonProperty(PropertyName = "isFqdn")] + public bool? IsFqdn { get; set; } + + /// + /// Validate the object. + /// + /// + /// Thrown if validation fails + /// + public virtual void Validate() + { + if (Name == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "Name"); + } + if (Type == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "Type"); + } + } + } +} + diff --git a/experiments/Websites.Experiments/Properties/AssemblyInfo.cs b/experiments/Websites.Experiments/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..fc401feae3db --- /dev/null +++ b/experiments/Websites.Experiments/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.Azure.Commands.Websites.Experiments")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Microsoft.Azure.Commands.Websites.Experiments")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("870244a0-0703-4d43-af20-58e9f2c6c63e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/experiments/Websites.Experiments/Run-UnitTests.ps1 b/experiments/Websites.Experiments/Run-UnitTests.ps1 new file mode 100644 index 000000000000..68debcdb8413 --- /dev/null +++ b/experiments/Websites.Experiments/Run-UnitTests.ps1 @@ -0,0 +1,5 @@ +#Requires -Modules AzureRM.Websites.Experiments, Pester + +$defaults = [System.IO.Path]::GetDirectoryName($PSCommandPath) +Set-Location $defaults +Invoke-Pester -EnableExit \ No newline at end of file diff --git a/experiments/Websites.Experiments/help/AzureRM.Websites.Experiments-help.xml b/experiments/Websites.Experiments/help/AzureRM.Websites.Experiments-help.xml new file mode 100644 index 000000000000..9f3cad6cbbbe --- /dev/null +++ b/experiments/Websites.Experiments/help/AzureRM.Websites.Experiments-help.xml @@ -0,0 +1,410 @@ + + + + +New-AzWebApp +New +AzWebApp +Create a new Azure AppService website and attach it to a git repository. + + + +Create a new Azure AppService website and attach it to a git repository. + + +New-AzWebApp +WebAppName +The name of the Website. The website will automatically use this value as the subdomain for the created website. + + +String +String + +None + +ResourceGroupName +The name of the resource group for the Website. + + +String +String + +None + +AppServicePlan +The name or id of the AppService Plan to use with this WebApp. + + +String +String + +None + +GitRepositoryPath +The path to a github repository where remotes should be added. + + +String +String + +None + +AddRemote +Add a remote to local github repo. + + +SwitchParameter + +False + +Auto +Accept default values for all settings not provided, withotu prompting. + + +SwitchParameter + +False + +Location +{{Fill Location Description}} + + +String +String + +None + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter + +False + + + +AddRemote +Add a remote to local github repo. + + +SwitchParameter +SwitchParameter + +False + +AppServicePlan +The name or id of the AppService Plan to use with this WebApp. + + +String +String + +None + +Auto +Accept default values for all settings not provided, withotu prompting. + + +SwitchParameter +SwitchParameter + +False + +GitRepositoryPath +The path to a github repository where remotes should be added. + + +String +String + +None + +Location +{{Fill Location Description}} + + +String +String + +None + +ResourceGroupName +The name of the resource group for the Website. + + +String +String + +None + +WebAppName +The name of the Website. The website will automatically use this value as the subdomain for the created website. + + +String +String + +None + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter +SwitchParameter + +False + + +None + + + + + + +Microsoft.Azure.Management.WebSites.Models.Site + +Details about the created website, including URL and github repo url. + + + + + + + + +Example 1: Create a website with prompts for settings +PS C:\> New-AzWebApp contosoWebApp +Creates a web application. The user will be prompted for the appservice to use and other settings. + + + +Example 2: Create a website with default settings +PS C:\> New-AzWebApp contosoWebApp -Auto +Creates a web application using default settings. + + + + + + + +New-AzWebAppGrayParam +New +AzWebAppGrayParam +Create an Azure Website using Azure App Service. + + + +Create an Azure Website using Azure App Service. This cmdlet uses the 'Gray Parameter' experience, which will prompt the user with default values for parameters that are not provided. Using -Auto indicates that further prompting for defaults should not occur. + + +New-AzWebAppGrayParam +WebAppName +The name of the website. + + +String +String + +None + +ResourceGroupName +The name of the resource group to create the website in. + + +String +String + +None + +AppServicePlan +The AppService Plan to use for thsi website. If not provided, the website will create an app service plan, or join an existing free AppService plan. + + +String +String + +None + +GitRepositoryPath +The path to a github repository containingg the application for the website. A remote for pushign to the website will be added to this repository. + + +String +String + +None + +AddRemote +Add a remote github repository to the given github repo. + + +SwitchParameter + +False + +Auto +Skip parameter prompting for remaining parameters. + + +SwitchParameter + +False + +Location +{{Fill Location Description}} + + +String +String + +None + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter + +False + + + +AddRemote +Add a remote github repository to the given github repo. + + +SwitchParameter +SwitchParameter + +False + +AppServicePlan +The AppService Plan to use for thsi website. If not provided, the website will create an app service plan, or join an existing free AppService plan. + + +String +String + +None + +Auto +Skip parameter prompting for remaining parameters. + + +SwitchParameter +SwitchParameter + +False + +GitRepositoryPath +The path to a github repository containingg the application for the website. A remote for pushign to the website will be added to this repository. + + +String +String + +None + +Location +{{Fill Location Description}} + + +String +String + +None + +ResourceGroupName +The name of the resource group to create the website in. + + +String +String + +None + +WebAppName +The name of the website. + + +String +String + +None + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter +SwitchParameter + +False + + +None + + + + + + +Microsoft.Azure.Management.WebSites.Models.Site + +Details about the created website, including its URL. + + + + + + + + +Example 1 +PS C:\> New-AzWebAppGrayParam -Location "East US" +Create a web application with detailed prompts for parameter values that are not provided, including simple defaults. + + + + + + + diff --git a/experiments/Websites.Experiments/help/New-AzWebApp.md b/experiments/Websites.Experiments/help/New-AzWebApp.md new file mode 100644 index 000000000000..445098a589f4 --- /dev/null +++ b/experiments/Websites.Experiments/help/New-AzWebApp.md @@ -0,0 +1,190 @@ +--- +external help file: AzureRM.Websites.Experiments-help.xml +online version: +schema: 2.0.0 +--- + +# New-AzWebApp + +## SYNOPSIS +Create a new Azure AppService website and attach it to a git repository. + +## SYNTAX + +``` +New-AzWebApp [[-WebAppName] ] [[-ResourceGroupName] ] [[-AppServicePlan] ] [-Auto] + [-AddRemote] [[-GitRepositoryPath] ] [-WhatIf] [-Confirm] [-Location ] [] +``` + +## DESCRIPTION +Create a new Azure AppService website and attach it to a git repository. + +## EXAMPLES + +### Example 1: Create a website with prompts for settings +``` +PS C:\> New-AzWebApp contosoWebApp +``` + +Creates a web application. The user will be prompted for the appservice to use and other settings. + +### Example 2: Create a website with default settings +``` +PS C:\> New-AzWebApp contosoWebApp -Auto +``` + +Creates a web application using default settings. + +## PARAMETERS + +### -AddRemote +Add a remote to local github repo. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AppServicePlan +The name or id of the AppService Plan to use with this WebApp. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: Plan + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Auto +Accept default values for all settings not provided, withotu prompting. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -GitRepositoryPath +The path to a github repository where remotes should be added. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Location +{{Fill Location Description}} + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ResourceGroupName +The name of the resource group for the Website. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: Group + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WebAppName +The name of the Website. The website will automatically use this value as the subdomain for the created website. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: Name + +Required: False +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### Microsoft.Azure.Management.WebSites.Models.Site +Details about the created website, including URL and github repo url. + +## NOTES + +## RELATED LINKS + diff --git a/experiments/Websites.Experiments/help/New-AzWebAppGrayParam.md b/experiments/Websites.Experiments/help/New-AzWebAppGrayParam.md new file mode 100644 index 000000000000..b10ab175b04e --- /dev/null +++ b/experiments/Websites.Experiments/help/New-AzWebAppGrayParam.md @@ -0,0 +1,185 @@ +--- +external help file: AzureRM.Websites.Experiments-help.xml +online version: +schema: 2.0.0 +--- + +# New-AzWebAppGrayParam + +## SYNOPSIS +Create an Azure Website using Azure App Service. + +## SYNTAX + +``` +New-AzWebAppGrayParam [[-WebAppName] ] [[-ResourceGroupName] ] [[-AppServicePlan] ] + [-Auto] [-AddRemote] [[-GitRepositoryPath] ] [-WhatIf] [-Confirm] -Location + [] +``` + +## DESCRIPTION +Create an Azure Website using Azure App Service. This cmdlet uses the 'Gray Parameter' experience, which will prompt the user with default +values for parameters that are not provided. Using -Auto indicates that further prompting for defaults should not occur. + +## EXAMPLES + +### Example 1 +``` +PS C:\> New-AzWebAppGrayParam -Location "East US" +``` + +Create a web application with detailed prompts for parameter values that are not provided, including simple defaults. + +## PARAMETERS + +### -AddRemote +Add a remote github repository to the given github repo. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AppServicePlan +The AppService Plan to use for thsi website. If not provided, the website will create an app service plan, or join an existing free AppService plan. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: Plan + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Auto +Skip parameter prompting for remaining parameters. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -GitRepositoryPath +The path to a github repository containingg the application for the website. A remote for pushign to the website will be added to this repository. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Location +{{Fill Location Description}} + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ResourceGroupName +The name of the resource group to create the website in. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: Group + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WebAppName +The name of the website. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: Name + +Required: False +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### Microsoft.Azure.Management.WebSites.Models.Site +Details about the created website, including its URL. + +## NOTES + +## RELATED LINKS + diff --git a/experiments/Websites.Experiments/packages.config b/experiments/Websites.Experiments/packages.config new file mode 100644 index 000000000000..d0a5c6543553 --- /dev/null +++ b/experiments/Websites.Experiments/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tools/PublishModules.ps1 b/tools/PublishModules.ps1 index a67f6a0445b2..4d91a283eb1b 100644 --- a/tools/PublishModules.ps1 +++ b/tools/PublishModules.ps1 @@ -223,10 +223,12 @@ function Change-RMModule ren $nupkgPath $zipPath Write-Output "Expanding $zipPath" Expand-Archive $zipPath -DestinationPath $dirPath - Write-Output "Adding PSM1 dependency to $unzippedManifest" - Add-PSM1Dependency -Path $unzippedManifest - Write-Output "Removing module manifest dependencies for $unzippedManifest" - Remove-ModuleDependencies -Path $unzippedManifest + if (!($moduleName.Contains("Experiment"))) { + Write-Output "Adding PSM1 dependency to $unzippedManifest" + Add-PSM1Dependency -Path $unzippedManifest + Write-Output "Removing module manifest dependencies for $unzippedManifest" + Remove-ModuleDependencies -Path $unzippedManifest + } Remove-Item -Path $zipPath -Force Write-Output "Repackaging $dirPath" diff --git a/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs b/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs index 04e8c729450a..7acc189d1137 100644 --- a/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs +++ b/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs @@ -85,6 +85,10 @@ public void Analyze( foreach (var directory in probingDirectories) { var service = Path.GetFileName(directory); + if (service.ToLower().EndsWith("experiments")) + { + return; + } var manifestFiles = Directory.EnumerateFiles(directory, "*.psd1").ToList(); diff --git a/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs b/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs index 75f3b2e9960b..9663418de6e3 100644 --- a/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs +++ b/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs @@ -168,6 +168,8 @@ private void AnalyzeMarkdownHelp( HelpFile = service + "/folder", ProblemId = MissingHelpFile }); + + return; } var helpFiles = Directory.EnumerateFiles(helpFolder, "*.md").Select(f => Path.GetFileNameWithoutExtension(f)).ToList(); @@ -194,7 +196,14 @@ private void AnalyzeMarkdownHelp( " -FileName " + psd1FileName + " -BindingVariable ModuleMetadata; $ModuleMetadata.NestedModules"); var cmdletResult = powershell.Invoke(); - var cmdletFiles = cmdletResult.Select(c => c.ToString().Substring(2)); + + // used for webapp experimental cmdlets. Reason: no nested modules. + if (cmdletResult == null) + { + return; + } + + var cmdletFiles = cmdletResult.Where(i => i != null && !string.IsNullOrWhiteSpace(i.ToString()) && i.ToString().Length > 2).Select(c => c.ToString().Substring(2)); if (cmdletFiles.Any()) { List allCmdlets = new List(); diff --git a/tools/UpdateModules.ps1 b/tools/UpdateModules.ps1 index 2aef5ba93465..2b2808c91e71 100644 --- a/tools/UpdateModules.ps1 +++ b/tools/UpdateModules.ps1 @@ -98,7 +98,7 @@ if ($scope -eq 'All') { foreach ($module in $resourceManagerModules) { # filter out AzureRM.Profile which always gets published first # And "Azure.Storage" which is built out as test dependencies - if (($module.Name -ne "AzureRM.Profile") -and ($module.Name -ne "Azure.Storage")) { + if (($module.Name -ne "AzureRM.Profile") -and ($module.Name -ne "Azure.Storage") -and (!$module.Name.Contains("Experiment"))) { $modulePath = $module.FullName Write-Host "Updating $module module from $modulePath" Create-ModulePsm1 -ModulePath $modulePath -TemplatePath $templateLocation