Skip to content

Instantly share code, notes, and snippets.

@srMrktng
Created October 25, 2021 09:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save srMrktng/93e3c1f305557d902e5e6c4538021bfd to your computer and use it in GitHub Desktop.
Save srMrktng/93e3c1f305557d902e5e6c4538021bfd to your computer and use it in GitHub Desktop.
Create new Citrix PVS devices from a VMware vSphere template and optionally add to a Citrix machine catalogue and delivery group and optionally add to a published desktop.
#requires -version 3
<#
.SYNOPSIS
Create new Citrix PVS devices from a VMware vSphere template and optionally add to a Citrix machine catalogue and delivery group and optionally add to a published desktop.
.DESCRIPTION
.PARAMETER vmName
Name of the new VM. Use # where more than one is being created (via -count) where # will be replaced by digits.
Use the number of hashes for the number of digits, eg GLPVS### for a machine like GLPVS042
.PARAMETER templateName
Name/pattern of unique vSphere template VM which will be used to create the new PVS VMs
.PARAMETER diskName
Name of PVS disk to assign
.PARAMETER ExistingPVSDevice
Name of existing PVS device to use as template such as the site, collection, OU and disk
.PARAMETER datastore
The VMware datastore to store the new virtual machines and any disks in
.PARAMETER OU
The OU to place the new VMs in, in the a slash delimited format, eg "Sites/Wakefield/Computers/RDS/PVS" from "OU=PVS,OU=RDS,OU=Computers,OU=Wakefield,OU=Sites,DC=guyrleech,DC=local"
.PARAMETER count
Number of new VMs to create
.PARAMETER create
Create the named machine catalog, delivery group, site or collection if they do not exist otherwise an error will be thrown on non-existence
.PARAMETER retrySeconds
Retry period in seconds between attempts to check for the properties of a new VM in the hypervisor PS drive
.PARAMETER retries
Number of retries to check for the properties of a new VM in the hypervisor PS drive
.PARAMETER nopad
Do not pad the digits in the new VM name to match the number of # characters specified
.PARAMETER tagName
Name of optional Citrix tag to assign to new VMs. Will be created if non-existent and -create specified
.PARAMETER tagDescription
Description of optional Citrix tag to assign to new VMs, if the tag does not exist and -create is specified
.PARAMETER startNumber
The starting number for the new VM names. If not specified, the script will figure it out by checking AD, PVS, Studio and VMware
.PARAMETER pvsServer
The PVS server to use, otherwise the local machine will be used. If credentials are needed as opposed to using those for the user running the script, use -pvscredential
.PARAMETER ddc
The Delivery Controller to use
.PARAMETER viServers
The VMware vCenter server(s) to connect to
.PARAMETER hypervisorConnectionName
The name of the hypervisor connection in Studio. If not specified, the script will figure it out if -ExistingPVSDevice is specified
.PARAMETER description
Description text for the new VM and PVS device. Environment variables denoted with % will be expanded.
.PARAMETER collectionName
The name of the PVS collection to use, or create if -create specified if -ExistingPVSDevice is not specified
.PARAMETER folderName
The name of the VMware folder to place the VM in
.PARAMETER resourcePoolName
The name of the VMware resource pool to place the VM in
.PARAMETER siteName
The name of the PVS site to use, or create if -create specified if -ExistingPVSDevice is not specified
.PARAMETER catalog
The name of the Studio machine catalog to use, or create if -create specified if -ExistingPVSDevice is not specified
.PARAMETER deliveryGroup
The name of the Studio delivery group to use, or create if -create specified if -ExistingPVSDevice is not specified
.PARAMETER domain
The name of the AD domain to use if -ExistingPVSDevice is not specified
.PARAMETER networkName
The name of the VMware network to use the NIC attached to. Use if the VM template has more than one NIC, on different networks
.PARAMETER clone
Name/pattern of a single VM which will be cloned, including copies of its disks, to create the new PVS target devices
.PARAMETER powerOn
Power on the newly created VMs. Note that Citrix may already have powered them on.
.PARAMETER noNewVM
Do not create new VMs. They must already exist if this option is specified.
.PARAMETER maintenanceMode
Add the new machines to Studio with maintenance mode enabled
.PARAMETER desktopName
The name of a published desktop to add the new VMs to, or create if -create is specified
.PARAMETER desktopKind
The type of desktops served from a delivery group if -create is specified and it does not exist
.PARAMETER deliveryType
The type of desktops served from a delivery group if -create is specified and it does not exist
.PARAMETER slotNumber
The PCI slot number to set for the NIC in the new VMs. If this is different to the source machine for the vdisk and not changed, it can cause a BSOD in CHvdMp.sys
.PARAMETER sessionSupport
The session support to set for a delivery group and/or machine catalog if -create is specified and they do not exist
.PARAMETER allocationType
The allcoation type for machines in the machine catalog if -create is specified and it does not exist
.PARAMETER PersistUserChanges
Whether to persist user changes for the machine catalog specified if -create is specified and it does not exist
.PARAMETER vmwarecredential
Credentials to be used for the VMware vCenter connection otherwise the user running the script is used
.PARAMETER pvscredential
Credentials to be used for the PVS connection otherwise the user running the script is used
.EXAMPLE
& '.\New PVS Devices.ps1' -vmName GLXA19PVS## -ExistingPVSDevice GLXA19PVS39 -templateName 'PVS Server 2019' -pvsServer GRL-PVS02 -ddc GRL-XADDC02
Create 1 new VM from the 'PVS Server 2019' vSphere template, naming it with the first available non-existent name matching GLXA19PVS* using the same PVS properties as the existing device GLXA19PVS39 on PVS server GRL-PVS02 such as the collection and PVS disk assigned.
Once created, add it to the same machine catalog and delivery group as existing device GLXA19PVS39 via the delivery controller GRL-XADDC02
.EXAMPLE
& '.\New PVS Devices.ps1' -vmName GLXA19PVS## -templateName 'PVS Server 2019' -pvsServer GRL-PVS02 -startNumber 20 -count 5 -ddc GRL-XADDC02 -diskName xaserver2019 -OU "Sites/Wakefield/Computers/RDS/PVS" -viServers grl-vcenter04 -deliveryGroup Automated -siteName Primary -collectionName Automated -catalog Automated -description "Created by @guyrleech" -tagName "Machines created by script" -folderName "Citrix\PVS\NonProd Servers"
Create 5 new VMs from the 'PVS Server 2019' vSphere template on vCenter grl-vcenter04, naming them with the first available non-existent name from GLXA19PVS20 onwards. Create the corresponding PVS target devices in the PVS site "Primary" and collection "Automated" on PVS server GRL-PVS02 and assign the PVS disk "xaserver2019".
Once created, add it to the "Automated" machine catalog and "Automated" delivery group via the delivery controller GRL-XADDC02 and tag the machines with the tag ""Machines created by script"
.NOTES
Modification History:
08/02/2021 @guyrleech First version
01/03/2021 @guyrleech VMware folder error checking and finding folder if path contains \. Creates folders if -create specified. Fix for not getting hypervisor connection for non-FQDN
16/06/2021 @guyrleech Added -datastore parameter
10/07/2021 @guyrleech Added -slotnumber parameter
13/09/2021 @guyrleech Added -clone parameter
#>
<#
Copyright © 2021 Guy Leech
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#>
[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')]
Param
(
[Parameter(Mandatory=$true,HelpMessage="Name of new VM")]
[string]$vmName ,
[Parameter(Mandatory=$false,HelpMessage="Name/pattern of unique vSphere template VM")] ## TODO could add ability to create new VM either of specified CPU, RAM & a copy of a peristent disk or get this from a "template" but not using vSphere templating to create
[string]$templateName ,
[Parameter(Mandatory=$false,HelpMessage="Name unique vSphere VM to clone for new VMs")]
[string]$clone ,
[Parameter(Mandatory=$true,HelpMessage="Name of PVS disk to assign",ParameterSetName='Disk')]
[string]$diskName ,
[Parameter(Mandatory=$true,HelpMessage="Name of existing PVS device to use as template",ParameterSetName='ExistingPVSDevice')]
[string]$ExistingPVSDevice ,
[string]$OU ,
[int]$count = 1 ,
[switch]$create ,
[double]$retrySeconds = 10 ,
[int]$retries = 9 ,
[switch]$nopad ,
[string]$datastore ,
[string]$tagName ,
[string]$tagDescription ,
[int]$startNumber ,
[string]$pvsServer ,
[string]$ddc ,
[string[]]$viServers ,
[string]$hypervisorConnectionName ,
[string]$description = "Citrix PVS machine",
[string]$collectionName ,
[string]$folderName ,
[string]$resourcePoolName,
[string]$siteName ,
[string]$catalog ,
[string]$deliveryGroup ,
[string]$domain ,
[int]$slotNumber ,
[System.Management.Automation.PSCredential]$vmwarecredential ,
[System.Management.Automation.PSCredential]$pvscredential ,
[string]$networkName ,
[switch]$maintenanceMode ,
[string]$desktopName ,
[double]$powerOnDelaySeconds ,
[ValidateSet('Shared','Private')]
[string]$desktopKind = 'Shared' ,
[ValidateSet('AppsOnly','DesktopsOnly','DesktopsAndApps')]
[string]$deliveryType = 'DesktopsOnly' ,
[ValidateSet('SingleSession','MultiSession')]
[string]$sessionSupport = 'MultiSession' ,
[ValidateSet('Random','Permanent','Static')]
[string]$allocationType = 'Random' ,
[ValidateSet('Discard','OnLocal','OnPvd')]
[string]$PersistUserChanges = 'Discard' ,
[switch]$noNewVM
)
Function Copy-VMDisks
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)]
$chosenTemplate ,
[Parameter(Mandatory=$true)]
$cloneVM
)
[array]$sourceDisks = @( $chosenTemplate | Get-HardDisk )
[int]$diskCount = 0
[string]$destination = Split-Path -path $cloneVM.extensiondata.config.files.VmPathName -parent
Write-Verbose -Message "Copying $($sourceDisks.Count) disks from $($chosenTemplate.Name) to $destination"
if( ! $sourceDisks -or ! $sourceDisks.Count )
{
Write-Warning "Template $($chosenTemplate.Name) has no disks"
}
else
{
## Check chosen datastore has enough free space for the copies
[long]$totalDiskSize = $sourceDisks | Measure-Object -Property CapacityGB -Sum | Select -ExpandProperty Sum
[string]$destinationDatacentre = $cloneVM | Get-Datacenter
if( $destination -match '^\[(.*)\]\s*(.*)$' )
{
[string]$datastoreName = $Matches[1]
if( -Not ( $destinationDatastore = Get-Datastore -Name $datastoreName ) )
{
Write-Warning -Message "Unable to get datastore `"$datastoreName`""
}
else
{
if( $totalDiskSize -gt $destinationDatastore.FreeSpaceGB )
{
Write-Warning "Disks require $($totalDiskSize)GB but datastore $($destinationDatastore.Name) only has $($destinationDatastore.FreeSpaceGB)GB free space"
}
}
}
else
{
Write-Warning "Unexpected format of VM path name `"$destination`""
}
[int]$diskCount = 0
ForEach( $sourceDisk in $sourceDisks )
{
$diskCount++
Write-Verbose -Message "$diskcount / $($sourceDisks.Count) : cloning disk $($sourceDisk.FileName)"
if( $copiedDisk = Copy-HardDisk -HardDisk $sourceDisk -DestinationPath $destination )
{
New-HardDisk -DiskPath $copiedDisk.Filename -VM $cloneVM
}
else
{
Write-Warning -Message "Problem copying $sourceDisk to $destination"
}
}
}
}
Function Copy-VMHardwareToVM
{
Param
(
[Parameter(Mandatory=$true)]
$chosenTemplate ,
[Parameter(Mandatory=$true)]
$cloneVM
)
if( -Not ( $sourceView = $chosenTemplate | Get-View ) )
{
Write-Warning -Message "Failed to get view of source $($chosenTemplate.Name)"
return $false
}
if( -Not ( $destinationView = $cloneVM | Get-View ) )
{
Write-Warning -Message "Failed to get view of destination $($chosenTemplate.Name)"
return $false
}
[int]$busNumber = 0
[int]$key = 1
[hashtable]$scsiControllers = @{}
[bool]$returnCode = $true
[array]$deviceBefore = @()
$spec = New-Object -TypeName VMware.Vim.VirtualMachineConfigSpec
$sourceView.Config.Hardware.Device | ForEach-Object `
{
$existingDevice = $_
Write-Verbose "Examining device of type $($_.GetType().Name) base type $($_.GetType().BaseType)"
if( $_ -is [VMware.Vim.ParaVirtualSCSIController] -or $_ -is [VMware.Vim.VirtualBusLogicController] -or $_ -is [VMware.Vim.VirtualLsiLogicSASController] -or $_ -is [VMware.Vim.VirtualLsiLogicController] -or $_ -is [VMware.Vim.VirtualNVMEController] )
{
$NewSCSIDevice = New-Object -TypeName VMware.Vim.VirtualDeviceConfigSpec
$NewSCSIDevice.operation = 'add'
$NewSCSIDevice.Device = $existingDevice
$spec.deviceChange += $NewSCSIDevice
$deviceBefore = @( $destinationView.Config.Hardware.Device | Select -ExpandProperty Key )
#$destinationView.ReconfigVM($spec)
#$destinationView.UpdateViewData()
}
elseif( $_ -is [VMware.Vim.VirtualMachineVideoCard] )
{
$newDevice = New-Object -TypeName VMware.Vim.VirtualDeviceConfigSpec
$newDevice.operation = 'edit'
$newDevice.Device = $existingDevice
$spec.deviceChange += $newDevice
}
elseif( $_ -is [VMware.Vim.VirtualUSBXHCIController] -or $_ -is [VMware.Vim.VirtualE1000e] -or $_ -is [VMware.Vim.VirtualE1000] -or $_ -is [VMware.Vim.VirtualVmxnet3] -or $_ -is [VMware.Vim.VirtualVmxnet2] `
-or $_ -is [VMware.Vim.VirtualFloppy] -or $_ -is [VMware.Vim.VirtualUSBController] -or $_ -is [VMware.Vim.VirtualHdAudioCard]-or $_ -is [VMware.Vim.VirtualParallelPort]-or $_ -is [VMware.Vim.VirtualSerialPort] )
{
$newDevice = New-Object -TypeName VMware.Vim.VirtualDeviceConfigSpec
$newDevice.operation = 'add'
if( $existingDevice -and $existingDevice.PSObject.Properties[ 'AddressType' ] -and $existingDevice.PSObject.Properties[ 'MacAddress' ] )
{
## need to change MAC address
$existingDevice.AddressType = 'generated'
$existingDevice.MacAddress = $null
}
$newDevice.Device = $existingDevice
$spec.deviceChange += $newDevice
}
}
if( $spec )
{
$destinationView.ReconfigVM($spec)
if( ! $? )
{
## if fatal set returnCode to false
}
$destinationView.UpdateViewData()
[array]$devicesAfter = @( $destinationView.Config.Hardware.Device )
[int]$controllersBefore = $scsiControllers.Count
$devicesAfter | Where-Object { $_.Key -notin $deviceBefore } | ForEach-Object `
{
## So we can map disks to the correct controller
try
{
$scsiControllers.Add( $existingDevice.Key , $_.Key )
}
catch
{
}
}
if( $scsiControllers.Count -eq $controllersBefore )
{
Write-Warning "Failed to find newly added SCSI controller so disks may not be attached or attached incorrectly"
}
}
## can't add CDROM in above loop as may not have required controller
Get-CDDrive -VM $chosenTemplate | ForEach-Object `
{
[hashtable]$cdparams = @{
'StartConnected' = $_.ConnectionState.StartConnected
'VM' = $cloneVM
}
<#
if( $_.HostDevice )
{
$cdparams.Add( 'HostDevice' , $_.HostDevice )
}
if( $_.RemoteDevice )
{
$cdparams += @{
'IsoPath' = $_.IsoPath
'RemoteDevice' = $_.RemoteDevice
}
}
#>
if( -Not ( $newcd = New-CDDrive @cdparams ) )
{
Write-Warning -Message "Problem adding CD drive"
}
}
## Update VM now we have changed its hardware
##$cloneVM = VMware.VimAutomation.Core\Get-VM -Id $cloneVM.Id
$destinationView.UpdateViewData()
$returnCode
}
[string]$pvsInstallFolder = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Citrix\ProvisioningServices' -Name ConsoleTargetDir -ErrorAction SilentlyContinue)|Select-Object -ExpandProperty ConsoleTargetDir -ErrorAction SilentlyContinue
if( [string]::IsNullOrEmpty( $pvsInstallFolder ) )
{
Write-Warning -Message "Failed to find PVS console install folder in the registry"
$pvsInstallFolder = "$env:ProgramFiles\Citrix\Provisioning Services Console"
}
elseif( ! ( Test-Path -Path $pvsInstallFolder -PathType Container -ErrorAction SilentlyContinue ) )
{
Write-Warning -Message "PVS console install folder `"$pvsInstallFolder`" not found"
$pvsInstallFolder = "$env:ProgramFiles\Citrix\Provisioning Services Console"
}
[string]$pvsModule = Join-Path -Path $pvsInstallFolder -ChildPath 'Citrix.PVS.SnapIn.dll'
$PVSSession = $null
if( ( ! ( Test-Path -Path $pvsModule -PathType Leaf -ErrorAction SilentlyContinue ) -or ! ( Import-Module -Name $pvsModule -Verbose:$false -PassThru ) -or ! ( Get-Command -Name Set-PvsConnection -ErrorAction SilentlyContinue ) ) -and $PSBoundParameters[ 'pvsServer' ] -and $pvsServer -ne $env:COMPUTERNAME )
{
## if we don't have the PVS cmdlets we need, see if we can import from PVS server
if( $PVSSession = New-PSSession -ComputerName $pvsServer )
{
Invoke-Command -Session $PVSSession -ScriptBlock { Import-Module $using:pvsModule }
if( ! ( Import-PSSession -Session $PVSSession -Module 'Citrix.PVS.SnapIn' ) )
{
Throw "Failed to import PVS module from $pvsServer"
}
}
else
{
Throw "Unable to create remote session to $pvsServer to import PVS cmdlets"
}
}
[string[]]$citrixModules = @( 'Citrix.Broker.*' , 'Citrix.Host.*' )
ForEach( $citrixModule in $citrixModules )
{
if( ! ( Import-Module -Name $citrixModule -ErrorAction SilentlyContinue -PassThru -Verbose:$false) `
-and ! ( Add-PSSnapin -Name $citrixModule -ErrorAction SilentlyContinue -PassThru -Verbose:$false) )
{
## No point doing implicit remoting since wouldn't get the XDHyp: PSDrive which we need
Throw "Failed to load Citrix PowerShell cmdlets from $citrixModule - is this a Delivery Controller or have Studio or the PowerShell SDK installed ?"
}
}
Import-Module -Name VMware.VimAutomation.Core -Verbose:$false
if( ! ( Get-Command -Name Get-Template ) )
{
Throw 'Unable to find VMware PowerCLI cmdlets'
}
# may need this later if we have to discover the vCenter from the hypervisor connection
[hashtable]$vmwareParameters = @{ Server = $null ; Verbose = $false }
if( $PSBoundParameters[ 'vmwarecredential' ] )
{
$vmwareParameters += @{ User = ($vmwarecredential.UserName -split '\\')[-1] ; Domain = ($vmwarecredential.UserName -split '\\')[0] ; Password = $vmwarecredential.GetNetworkCredential().Password }
}
if( $PSBoundParameters[ 'viServers' ] )
{
$vmwareParameters.Server = $viServers
if( ! ( $vmwareConnection = Connect-VIServer @vmwareParameters ) )
{
Throw "Failed to connect to VMware $viServers"
}
}
if( $PSBoundParameters[ 'pvsserver' ] )
{
[hashtable]$pvsParameters = @{ Server = $pvsServer ; Verbose = $false }
if( $PSBoundParameters[ 'pvscredential' ] )
{
$pvsParameters += @{ User = ($pvscredential.UserName -split '\\')[-1] ; Domain = ($pvscredential.UserName -split '\\')[0] ; Password = $pvscredential.GetNetworkCredential().Password }
}
Set-PvsConnection @pvsParameters
if( ! $? )
{
Exit 1
}
}
[hashtable]$pvsdeviceParameters = @{ }
[hashtable]$ddcParams = @{ }
if( $PSBoundParameters[ 'ddc' ] )
{
$ddcParams.Add( 'Adminaddress' , $ddc )
}
$existingBrokerMachine = $null
$existingHostedMachine = $null
$existingVM = $null
[GUID]$HypervisorConnectionUid = [GUID]::Empty
## if we have a template device then get its properties so we can use them for the new VM
if( $PsCmdlet.ParameterSetName -eq 'ExistingPVSDevice' )
{
$searcher = [adsisearcher]"(&(objectCategory=Computer)(name=$ExistingPVSDevice))"
if( ! ( $machine = $searcher.FindAll() ) )
{
Throw "Unable to find machine $ExistingPVSDevice in AD"
}
elseif( $machine -is [array] )
{
Throw "Found $($machine.Count) AD computers matching $ExistingPVSDevice"
}
if( ! ( $pvsDevice = Get-PvsDevice -DeviceName $ExistingPVSDevice ) )
{
Throw "Unable to find machine $ExistingPVSDevice in PVS"
}
elseif( $pvsdevice -is [array] )
{
Throw "Found $($pvsdeviceParameters.Count) PVS devices matching $ExistingPVSDevice"
}
if( ! ( $pvsDisk = Get-PvsDiskInfo -DeviceName $ExistingPVSDevice ) )
{
Throw "Unable to find PVS disk for target device $ExistingPVSDevice"
}
$pvsdeviceParameters.Add( 'SiteName' , $pvsDisk.siteName )
$pvsdeviceParameters.Add( 'CollectionName' , $pvsDevice.CollectionName )
$storeName = $pvsDisk.StoreName
$diskName = $pvsDisk.DiskLocatorName
$siteName = $pvsDisk.SiteName
if( ! $PSBoundParameters[ 'OU' ] )
{
## LDAP://CN=GLXA19PVS01,OU=PVS,OU=RDS,OU=Computers,OU=Wakefield,OU=Sites,DC=guyrleech,DC=local
## need to turn into Sites/Wakefield/Computers/RDS/PVS for Add-PvsDeviceToDomain
[string[]]$fields = $machine[0].Path -split ','
$OU = $( For( [int]$index = $fields.Count - 1 ; $index -ge 0 ; $index-- )
{
if( $fields[ $index ] -match '^OU=(.*)$' )
{
$Matches[1]
}
}) -join '/'
}
if( ! ( $existingBrokerMachine = Get-BrokerMachine @ddcParams -HostedMachineName $ExistingPVSDevice -ProvisioningType PVS -ErrorAction SilentlyContinue ) )
{
Throw "Failed to find existing broker machine $ExistingPVSDevice"
}
if( ! $PSBoundParameters[ 'viServer' ] )
{
## need to get the vcenters for the hosting connection
if( ! ( $hypervisorConnection = Get-BrokerHypervisorConnection -Uid $existingBrokerMachine.HypervisorConnectionUid ) `
-or ! ( $connectionProperties = Get-ChildItem -Path 'XDHyp:\Connections' -Verbose:$false @ddcParams | Where-Object PSChildName -eq $existingBrokerMachine.HypervisorConnectionName ) )
{
Throw "Failed to connection details for hypervisor connection `"$($existingBrokerMachine.HypervisorConnectionName)`" via PS drive"
}
$vmwareParameters.Server = $connectionProperties.hypervisorAddress -replace '^https?://([^/]+)/(.*)$' , '$1'
if( ! ( $vmwareConnection = Connect-VIServer @vmwareParameters ) )
{
Throw "Failed to connect to VMware $viServers via hypervisor connection `"$($existingBrokerMachine.HypervisorConnectionName)`""
}
}
$HypervisorConnectionUid = $existingBrokerMachine.HypHypervisorConnectionUid.Guid
if( ! ( $existingVM = VMware.VimAutomation.Core\Get-VM -Name $ExistingPVSDevice ) )
{
Throw "Unable to find VM $ExistingPVSDevice"
}
}
else
{
if( $PSBoundParameters[ 'Domain' ] )
{
$pvsdeviceParameters.Add( 'Domain' , $domain )
}
[array]$allPVSSites = @( Get-PvsSite )
if( ! $allPVSSites -or ! $allPVSSites.Count )
{
Throw "No PVS sites found"
}
if( $PSBoundParameters[ 'siteName' ] )
{
[array]$matchingSites = @( $allPVSSites.Where( { $_.Name -eq $siteName } ) )
if( ! $matchingSites -or ! $matchingSites.Count )
{
Throw "No PVS site `"$siteName`" found"
}
elseif( $matchingSites.Count -ne 1 )
{
Throw "$($matchingSites.Count) PVS sites found matching `"$siteName`" found - must be unique"
}
}
elseif( $allPVSSites.Count -eq 1 )
{
$siteName = $allPVSSites[0].Name
}
else
{
Throw "$($allPVSSites.Count) PVS sites found - must specify unique site name via -sitename"
}
$pvsdeviceParameters.Add( 'SiteName' , $siteName )
[array]$allPVSCollections = @( Get-PvsCollection )
if( ! $allPVSCollections -or ! $allPVSCollections.Count )
{
Throw "No PVS Collections found"
}
if( $PSBoundParameters[ 'CollectionName' ] )
{
[array]$matchingCollections = @( $allPVSCollections.Where( { $_.Name -eq $CollectionName } ) )
if( ! $matchingCollections -or ! $matchingCollections.Count )
{
if( $create -and $siteName )
{
if( ! ( $newCollection = New-PvsCollection -CollectionName $collectionName -SiteName $siteName -Description "Created by script $(Get-Date -Format G)" ) )
{
Throw "Failed to create PVS collection `"$collectionName`" in site `"$siteName`""
}
}
else
{
Throw "No PVS Collection `"$CollectionName`" found"
}
}
elseif( $matchingCollections.Count -ne 1 )
{
Throw "$($matchingCollections.Count) PVS Collections found matching `"$CollectionName`" found - must be unique"
}
}
elseif( $allPVSCollections.Count -eq 1 )
{
$CollectionName = $allPVSCollections[0].Name
}
else
{
Throw "$($allPVSCollections.Count) PVS Collections found - must specify unique Collection name via -Collectionname"
}
$pvsdeviceParameters.Add( 'CollectionName' , $CollectionName )
[array]$disks = @( Get-PvsDiskLocator -SiteName $siteName )
if( ! $disks -or ! $disks.Count )
{
Throw "No PVS disks found in site $siteName"
}
[array]$matchingDisks = @( $disks.Where( { $_.Name -eq $diskName } ) )
[string]$storeName = $null
if( ! $matchingDisks -or ! $matchingDisks.Count )
{
Throw "Found no disk in site $siteName for $diskName"
}
elseif( $matchingDisks.Count -ne 1 )
{
Throw "Found $($matchingDisks.Count) disks in site $siteName for $diskName"
}
else
{
$storeName = $matchingDisks[0].StoreName
}
## if no OU specified see if we have enough info to select a unique one otherwise error
if( ! $PSBoundParameters[ 'OU' ] )
{
$OUs = New-Object -TypeName System.Collections.Generic.List[string]
Get-PvsDevice -DiskLocatorName $matchingdisks[0].DiskLocatorName -SiteName $sitename -StoreName $storename | Where-Object { $_.DomainName -ne $null -and ( ! $domain -or $_.DomainName -eq $domain ) } | . { Process `
{
$device = $_
$searcher = [adsisearcher]"(&(objectCategory=Computer)(name=$($device.DeviceName)))"
Try
{
if( $machine = $searcher.FindOne() )
{
$OUs.Add( ($machine.Path -split ',' , 2)[-1] )
}
}
Catch
{
Throw $_
}
}}
if( ! $OUs -or ! $OUs.Count )
{
Throw "Unable to find any OUs from existing devices - use -OU"
}
$groups = Group-Object -InputObject $OUs
if( $groups.Count -ne 1 )
{
Throw "Failed to find unique OU, found $($groups.Count) ($($groups | Select-Object -ExpandProperty Name))"
}
[string[]]$fields = $OUs[0] -split ','
$OU = $( For( [int]$index = $fields.Count - 1 ; $index -ge 0 ; $index-- )
{
if( $fields[ $index ] -match '^OU=(.*)$' )
{
$Matches[1]
}
}) -join '/'
}
## need to figure out the vCenter connection uid if not specified
$hypervisor = $null
if( ! $PSBoundParameters[ 'hypervisorConnectionName' ] )
{
## connection may not use fqdn so need to match either
[string[]]$nonFQDNviServers = $viServers | ForEach-Object { ( $_ -split '\.' )[0] }
ForEach( $connection in (Get-ChildItem -Path 'XDHyp:\Connections' @ddcParams))
{
ForEach( $hypervisorAddress in $connection.hypervisorAddress )
{
if( $hypervisorAddress -match '^https?://([^/]+)/' )
{
if( $Matches[1] -in $viServers -or $Matches[1] -in $nonFQDNviServers )
{
if( $hypervisor )
{
Write-Warning -Message "Already found $viServers in hypervisor connection `"$($hypervisor.Name)`" and now in `"$($connection.Name)`""
}
$hypervisor = $connection
$HypervisorConnectionUid = $hypervisor.HypervisorConnectionUid
}
}
}
}
if( $HypervisorConnectionUid -eq [GUID]::Empty )
{
Throw "Failed to find hypervisor connect for $($viServers -join ' , ')"
}
}
else
{
if( ! ( $hypervisor = Get-BrokerHypervisorConnection -Name $hypervisorConnectionName @ddcParams | Where-Object HypHypervisorType -eq 'vcenter' ) )
{
Throw "Failed to find hypervisor connection `"$hypervisorConnectionName`""
}
elseif( $hypervisor -is [array] -and $hypervisor.Count -gt 1 )
{
Throw "Found $($hypervisor.Count) hypervisor connections for `"$hypervisorConnectionName`""
}
if( ! $hypervisor -or ! ( $HypervisorConnectionUid = $hypervisor.HypervisorConnectionUid.Guid ) )
{
Throw "Failed to get hypervisor connection uid"
}
}
}
## if we are creating more than 1 item then we need a pattern for the name that we can figure out what are the next machines to create (doesn't exist in PVS, AD, Citrix or VMware)
$newMachines = New-Object -TypeName System.Collections.Generic.List[string]
if( $count -gt 1 -or $vmName -match '#' )
{
if( $vmName -notmatch '^([^#]*)(#+)([^#]*)$' )
{
Throw "Must have # characters in machine name when creating more than one"
}
[string]$padding = '0' * $Matches[2].Length
[string]$prefix = $Matches[1]
[string]$suffix = $Matches[3]
[string]$machinePattern = "$prefix*"
if( $suffix.Length )
{
$machinePattern += "$suffix*"
}
[hashtable]$ADMachines = @{}
[hashtable]$pvsDevices = @{}
[hashtable]$brokerMachines = @{}
[hashtable]$VMs = @{}
$searcher = [adsisearcher]"(&(objectCategory=Computer)(name=$machinePattern))"
$searcher.FindAll() | . { Process `
{
$ADMachines.Add( [string]$_.properties.name , $_.properties )
}}
Write-Verbose -Message "Got $($ADMachines.Count) AD computers matching $machinePattern"
VMware.VimAutomation.Core\Get-VM -Name $machinePattern -ErrorAction SilentlyContinue | . { Process `
{
$VMs.Add( $_.Name , $_ )
}}
Write-Verbose -Message "Got $($VMs.Count) VMs matching $machinePattern"
Get-BrokerMachine -HostedMachineName $machinePattern @ddcParams | . { Process `
{
$brokerMachines.Add( $_.HostedMachineName , $_ )
}}
Write-Verbose -Message "Got $($brokerMachines.Count) broker machines matching $machinePattern"
## can't pattern match with Get-PVsDevice
[hashtable]$existingPVSdeviceParameters = $pvsdeviceParameters.Clone()
$existingPVSdeviceParameters.Remove( 'Description' )
Get-PvsDevice @existingPVSdeviceParameters | . { Process `
{
if( $_.DeviceName -like $machinePattern )
{
$pvsDevices.Add( $_.DeviceName , $_ )
}
}}
Write-Verbose -Message "Got $($pvsDevices.Count) PVS devices matching $machinePattern"
## Find first machine available if no $startNumber specified
[string]$lastMachine = $null
[int]$machineNumber = $(if( $PSBoundParameters[ 'startNumber' ] ) { $startNumber } else { 1 })
do
{
[string]$thisMachine = "{0}{1:$padding}{2}" -f $prefix , $machineNumber , $suffix
if( $nopad -or $thisMachine.Length -eq $vmname.Length )
{
Write-Verbose -Message "Trying machine $thisMachine - got $($newMachines.Count) so far"
if( ! $ADMachines[ $thisMachine ] -and ! $VMs[ $thisMachine ] -and ! $pvsDevices[ $thisMachine ] -and ! $brokerMachines[ $thisMachine ] )
{
Write-Verbose -Message "$thisMachine not found so adding"
$newMachines.Add( $thisMachine )
$lastMachine = $thisMachine
}
$machineNumber++
}
else
{
Throw "Unable to find available machine names - got $($newMachines.Count) out of $count but now got $thisMachine which doesn't match pattern $vmname, last was $lastMachine"
}
} while( $newMachines.Count -lt $count ) ## TODO how do we detect machine exhaustion? String length?
}
else
{
$newMachines.Add( $vmName )
}
Write-Verbose -Message "Got list of $($newMachines.Count) machines:"
$newMachines | Write-Verbose
[int]$newBrokerMachineCount = 0
$tag = $null
$newCatalog = $null
$template = $null
## TODO alternative to using vSphere templates
if( $PSBoundParameters[ 'templateName' ] )
{
if( -not ( $template = Get-Template -Name $templateName ) )
{
Throw "Unable to find template $templateName"
}
if( $template -is [array] )
{
Throw "$($template.Count) templates matched $templateName"
}
}
elseif( $PSBoundParameters[ 'clone' ] )
{
if( -not ( $template = VMware.VimAutomation.Core\Get-VM -Name $clone ) ) ## not strictly a template but we'll deal with it differently for New-VM
{
Throw "Unable to find VM $clone to clone"
}
if( $template -is [array] )
{
Throw "$($template.Count) VMs matched clone $clone"
}
}
else
{
Throw "Must use either -templateName or -clone to specify source"
}
$results = New-Object -TypeName System.Collections.Generic.List[psobject]
$datastoreForNewVM = $null
[hashtable]$newvmParameters = @{}
if( $PSBoundParameters[ 'datastore' ] )
{
if( ! ($datastoreForNewVM = Get-Datastore -Name $datastore ) )
{
Throw "Unable to find datastore `"$datastore`""
}
$newvmParameters.Add( 'Datastore' , $datastoreForNewVM )
}
if( $PSBoundParameters[ 'templateName' ] )
{
$newvmParameters.Add( 'Template' , $templateName )
}
else
{
$newvmParameters.Add( 'MemoryMB' , $template.MemoryMB )
$newvmParameters.Add( 'NumCpu' , $template.NumCpu )
$newvmParameters.Add( 'CoresPerSocket' , $template.CoresPerSocket )
$newvmParameters.Add( 'BootDelayMillisecond' , $template.BootDelayMillisecond )
$newvmParameters.Add( 'DiskMB' , 1 )
$newvmParameters.Add( 'DiskStorageFormat' , 'Thin' )
$newvmParameters.Add( 'HardwareVersion' , ($template | Select-Object -ExpandProperty HardwareVersion -First 1) )
$newvmParameters.Add( 'GuestId' , $template.GuestId )
if( ! $PSBoundParameters[ 'description' ] )
{
$description = "Cloned from $($template.Name) by script"
}
}
ForEach( $newMachine in $newMachines )
{
$result = $null
if( $noNewVM -or ! $PSCmdlet.ShouldProcess( $newMachine , 'Create VM' ) )
{
if( ! ( $newVM = VMware.VimAutomation.Core\Get-VM -Name $newMachine ) )
{
Throw "Unable to find VM $newMachine"
}
}
else
{
$resourcePool = $null
if( $PSBoundParameters[ 'resourcePoolName' ] )
{
$resourcePool = Get-ResourcePool -Name $resourcePoolName
}
else
{
if( $existingVM )
{
$resourcePool = $existingVM.ResourcePool
}
else
{
$resourcePool = (Get-ResourcePool|Select-Object -First 1)
}
}
$folder = $null
if( $PSBoundParameters[ 'folderName' ] )
{
[string]$parent = 'vm' ## root folder
$folder = Get-Folder -Name $parent -ErrorAction Stop
ForEach( $folderElement in $folderName.Split( '\' ) )
{
if( ! [string]::IsNullOrEmpty( $folderElement ) )
{
if( ! ($folder = Get-Folder -Name $folderElement -Location $parent -ErrorAction SilentlyContinue ))
{
if( $create )
{
if( ! ( $folder = New-Folder -Name $folderElement -Location $parent ) )
{
Throw "Failed to create sub folder `"$folderElement`" of parent `"$parent`" of folder path `"$folderName`""
}
}
else
{
Throw "Unable to find sub folder `"$folderElement`" of parent `"$parent`" of folder path `"$folderName`""
}
}
$parent = $folderElement
}
}
if( $folder -is [array] -and $folder.Count -gt 1 )
{
Throw "Found $($folder.Count) folders `"$folderName`" - `"$($folders.Name -join '" , "' )`""
}
}
else
{
if( $existingVM )
{
$folder = $existingVM.folder
}
}
if( ! $PSBoundParameters[ 'description' ] )
{
if( $existingVM -and ! [string]::IsNullOrEmpty( $existingVM.Notes ) )
{
$description = $existingVM.Notes
}
}
elseif( $description.IndexOf( '%' ) -ne $description.LastIndexOf( '%' ) )
{
$description = [Environment]::ExpandEnvironmentVariables( $description ) -replace '%time%' , (Get-Date -Form T) -replace '%date%' , (Get-Date -Form d)
}
## TODO make async so we can create several in parallel
try
{
if( ! ( $newVM = New-VM -Name $newMachine -Description $description -ResourcePool $resourcePool -Location $folder @newvmParameters ) )
{
Throw "Failed to create $newMachine from $($template.Name)"
}
elseif( $PSBoundParameters[ 'clone' ] )
{
## Remove the auto added hard disk and NICs as we'll add from template
$newVM | Get-HardDisk | Remove-HardDisk -DeletePermanently -Confirm:$false
$newVM | Get-NetworkAdapter | Remove-NetworkAdapter -Confirm:$false
## if EFI then set it
$spec = New-Object -TypeName VMware.Vim.VirtualMachineConfigSpec
$spec.Firmware = $template.ExtensionData.Config.Firmware
$spec.BootOptions = $template.ExtensionData.Config.BootOptions
$spec.Flags = $template.ExtensionData.Config.Flags
$newVM.ExtensionData.ReconfigVM( $spec )
##$newVM = VMware.VimAutomation.Core\Get-VM -Id $newVM.Id
## need to copy hard drives and other hardware
if( -Not ( Copy-VMHardwareToVM -chosenTemplate $template -cloneVM $newVM ) )
{
Write-Warning -Message "Problem cloning hardware from $($template.Name) to $($newVM.Name)"
}
elseif( -Not ( Copy-VMDisks -chosenTemplate $template -cloneVM $newVM ) )
{
Write-Warning -Message "Problem copying disks from $($template.Name) to $($newVM.Name)"
}
}
}
catch
{
Throw "Failed to create $newMachine from $($template.Name): $_"
}
}
if( ! ( $NICs = Get-NetworkAdapter -VM $newVM ) )
{
Throw "Failed to get any NICs for VM $($newVM.Name)"
}
if( $NICs -is [array] -and $NICs.Count -gt 1 )
{
if( $theNIC = $NICs | Where-Object NetworkName -match $networkName )
{
if( $theNIC -is [array] )
{
Throw "Found $($theNIC.Count) NICs out of $($NICs.Count) matching $networkName so can't figure which one to set in PVS"
}
else
{
$NICs = $theNIC
}
}
else
{
Throw "Found no NICs out of $($NICs.Count) matching $networkName so can't set MAC address in PVS"
}
}
if( $PSBoundParameters[ 'slotnumber' ] )
{
[int]$existingSlotNumber = -1
try
{
$existingSlotNumber = $NICS.ExtensionData.SlotInfo.PciSlotNumber
}
catch
{
## no slot yet so we will need to create one
}
if( $slotNumber -ne $existingSlotNumber )
{
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$device = New-Object VMware.Vim.VirtualDeviceConfigSpec
$device.Operation = [VMware.Vim.VirtualDeviceConfigSpecOperation]::edit
$device.Device = $NICs.ExtensionData
## if never booted then may not have slot number
if( $null -eq $device.Device.SlotInfo )
{
$device.Device.SlotInfo = New-Object -TypeName VMware.Vim.VirtualDevicePciBusSlotInfo
Write-Verbose -Message "$($newVM.Name) did not have slot info so added"
}
$device.Device.SlotInfo.PciSlotNumber = $slotNumber
$spec.deviceChange = @( $device )
$newVM.ExtensionData.ReconfigVM( $spec )
if( ! $? )
{
Write-Error -Message "Problem changing slot from $existingSlotNumber to $slotNumber in $($vmToChange.Name)"
}
else
{
Write-Verbose -Message "NIC PCI slot number changed from $existingSlotNumber to $slotNumber in $($vmToChange.Name)"
}
}
}
$pvsdeviceParameters.DeviceMac = $NICs.MacAddress -replace ':' , '-'
$result = $newVM
$newPVSDevice = $null
if( $PsCmdlet.ShouldProcess( $newMachine , 'Create new PVS Device' ) )
{
try
{
$pvsdeviceParameters.Description = $description
$pvsdeviceParameters.DeviceName = $newMachine
$newPVSDevice = New-PvsDevice @pvsdeviceParameters
}
catch
{
Throw "Failed to create new PVS device $($pvsdeviceParameters.DeviceName): $_"
}
if( ! $newPVSDevice )
{
Throw "Failed to create new PVS device $($pvsdeviceParameters.DeviceName)"
}
try
{
Add-PvsDiskLocatorToDevice -DiskLocatorName $diskName -DeviceId $newPVSDevice.DeviceId -SiteName $siteName -StoreName $storeName
}
catch
{
Throw "Failed to add disk $diskname to $($newPVSDevice.DeviceName): $_"
}
$newPVSDevice | Select-Object -Property *name , DeviceMac | ForEach-Object { $_.PSObject.Properties | ForEach-Object { Add-Member -InputObject $result -MemberType NoteProperty -Name $_.Name -Value $_.Value -Force } }
[hashtable]$ADParameters = @{ DeviceName = $newPVSDevice.DeviceName }
if( ! [string]::IsNullOrEmpty( $OU ) )
{
$ADParameters.Add( 'OrganizationUnit' , $OU )
}
if( ! [string]::IsNullOrEmpty( $domain ) )
{
$ADParameters.Add( 'Domain' , $domain )
}
Add-Member -InputObject $result -NotePropertyMembers $ADParameters -Force
try
{
Add-PvsDeviceToDomain @ADParameters
}
catch
{
Throw "Failed to add $($newPVSDevice.DeviceName) to AD: $_"
}
}
if( $PSBoundParameters[ 'catalog' ] )
{
## if catalogue doesn't exist, create if reqested and not already done
if( ! $newCatalog -and ! ( Get-BrokerCatalog -Name $catalog -ErrorAction SilentlyContinue @ddcParams | Where-Object Name -eq $catalog ) ) ## ensure no name matching giving multiple catalogues
{
if( ! $domain )
{
$searcher = [adsisearcher]"(&(objectCategory=Computer)(name=$($newpvsdevice.DeviceName)))"
if( ! ( $ADmachine = $searcher.FindOne() ) )
{
Write-Warning -Message "Failed to find new machine $($newPVSDevice.DeviceName) in AD"
}
else
{
$domain = ($ADmachine.properties.dnshostname -split '\.' , 2)[-1]
}
}
if( $PsCmdlet.ShouldProcess( $catalog , 'Create Machine Catalog' ) )
{
if( ! ( $newCatalog = New-BrokerCatalog -PersistUserChanges $PersistUserChanges -ProvisioningType PVS -MachinesArePhysical $false -Description "Created by script $(Get-Date -Format G)" -SessionSupport $sessionSupport -Name $catalog -AllocationType $allocationType -IsRemotePC $false -PvsAddress $pvsServer -PvsDomain $domain @ddcParams ) )
{
Write-Warning -Message "Failed to create new catalog `"$catalog`""
}
}
}
}
else
{
if( $existingBrokerMachine )
{
$catalog = $existingBrokerMachine.CatalogName
}
}
if( $newPVSDevice -and ! [string]::IsNullOrEmpty( $catalog ) )
{
if( $newCatalog )
{
$brokerCatalog = $newCatalog
}
elseif( ! ( $brokerCatalog = Get-BrokerCatalog -Name $catalog @ddcParams ) )
{
Throw "Failed to get machine catalog `"$catalog`""
}
if( $hypervisorConnection = Get-BrokerHypervisorConnection -HypHypervisorConnectionUid $HypervisorConnectionUid )
{
$hostedMachine = $null
[int]$retry = 0
## had a problem whereby new machines don't appear immediately via PS drive so retry if not seen. Sometimes about a minute before they "appear"
while( ! $hostedMachine -and $retry -lt $retries )
{
if ( $hostedMachine = Get-ChildItem -Path (Join-Path -Path 'XDHyp:\Connections' -ChildPath $hypervisorConnection.Name -Verbose:$false) -Force -Recurse -Include "$($newPVSDevice.DeviceName).vm" -Verbose:$false @ddcParams )
{
if( ! ($newBrokerMachine = New-BrokerMachine -CatalogUid $brokerCatalog.Uid -MachineName $newPVSDevice.DeviceName -InMaintenanceMode $maintenanceMode.IsPresent -HypervisorConnectionUid $HypervisorConnection.Uid -HostedMachineId $hostedMachine.Id @ddcParams ) )
{
Throw "Failed to add $($newPVSDevice.DeviceName) to machine catalog `"$catalog`""
}
else
{
Add-Member -InputObject $result -MemberType NoteProperty -Name 'Machine Catalog' -Value $brokerCatalog.Name
$newBrokerMachineCount++
if( $PSBoundParameters[ 'tagName' ] )
{
if( ! $tag -and ! ( $tag = Get-BrokerTag -Name $tagName -ErrorAction SilentlyContinue @ddcParams ) -and $PsCmdlet.ShouldProcess( $tagName , "Create Tag" ) )
{
[hashtable]$tagParameters = @{ 'Name' = $tagName }
if( $PSBoundParameters[ 'tagDescription' ] )
{
$tagParameters.Add( 'Description' , $tagDescription )
}
$tagParameters += $ddcParams
if( ! ( $tag = New-BrokerTag @tagParameters ) )
{
Write-Warning -Message "Failed to create tag `"$tagName`""
}
}
if( $tag )
{
Add-BrokerTag -InputObject $tag -Machine $newBrokerMachine
if( ! $? )
{
Write-Warning -Message "Failed to add tag `"$($tag.Name)`" to machine $($newbrokermachine.MachineName)"
}
else
{
Add-Member -InputObject $result -MemberType NoteProperty -Name 'Tag' -Value $tagName
}
}
}
}
}
else
{
$retry++
Write-Warning -Message "$(Get-Date -Format G) failed to find hypervisor connection for $($newPVSDevice.DeviceName) - retry $retry in $retrySeconds seconds"
Start-Sleep -Milliseconds ($retrySeconds * 1000)
}
}
if( ! $hostedMachine )
{
Write-Warning -Message "$(Get-Date -Format G) failed to find VM via hypervisor connection for $($newPVSDevice.DeviceName) so unable to add to catalog `"$($brokerCatalog.Name)`""
}
}
else
{
Throw "Failed to get hypervisor connection for uid $HypervisorConnectionUid"
}
}
if( ! $PSBoundParameters[ 'deliveryGroup' ] -and $existingBrokerMachine )
{
$deliveryGroup = $existingBrokerMachine.DesktopGroupName
}
$results.Add( $result )
}
if( ! [string]::IsNullOrEmpty( $deliveryGroup ) )
{
if( ! ( $existingDeliveryGroup = Get-BrokerDesktopGroup -Name $deliveryGroup @ddcParams -ErrorAction SilentlyContinue ) )
{
if( $create )
{
if( $PsCmdlet.ShouldProcess( $deliveryGroup , "Create Delivery Group" ) )
{
if( ! ( $newDeliveryGroup = New-BrokerDesktopGroup -Name $deliveryGroup -Description "Created by $env:username via script $(Get-Date -Format G)" -InMaintenanceMode $maintenanceMode.IsPresent -SessionSupport $sessionsupport -DesktopKind $desktopKind -DeliveryType $deliveryType @ddcParams ) )
{
Write-Warning -Message "Failed to create delivery group `"$deliveryGroup`""
}
else
{
## need to create default access policies otherwise get "The User configuration has been manually modified and cannot be changed by Studio" in Studio and doesn't show to users
## TODO Should look at supporting custom settings but for now set in maintenance mode so not visible to users
if( ! ( $viaAG = New-BrokerAccessPolicyRule -AllowedConnections ViaAG -Name "$($deliveryGroup)_AG" -DesktopGroupUid $newDeliveryGroup.Uid -AllowedProtocols HDX,RDP -AllowedUsers AnyAuthenticated -AllowRestart $true -IncludedSmartAccessFilterEnabled $true -IncludedUserFilterEnabled $true ) )
{
Write-Warning -Message "Failed to create AG policy rule for new desktop group `"$deliveryGroup`""
}
if( ! ( $direct = New-BrokerAccessPolicyRule -AllowedConnections NotViaAG -Name "$($deliveryGroup)_Direct" -DesktopGroupUid $newDeliveryGroup.Uid -AllowedProtocols HDX,RDP -AllowedUsers AnyAuthenticated -AllowRestart $true -IncludedSmartAccessFilterEnabled $true -IncludedUserFilterEnabled $true ) )
{
Write-Warning -Message "Failed to create Non AG policy rule for new desktop group `"$deliveryGroup`""
}
$existingDeliveryGroup = $newDeliveryGroup
}
}
}
else
{
Write-Warning -Message "Failed to find delivery group `"$deliveryGroup`""
}
}
if( $PsCmdlet.ShouldProcess( "Delivery group `"$deliveryGroup`"" , "Add $newBrokerMachineCount machines" ) )
{
Write-Verbose -Message "Adding $newBrokerMachineCount to delivery group `"$deliveryGroup`""
[int]$added = Add-BrokerMachinesToDesktopGroup -DesktopGroup $deliveryGroup -Catalog $catalog -Count $newBrokerMachineCount
if( $added -ne $count )
{
Write-Warning -Message "Added $added machines to delivery group `"$deliveryGroup`" not $count"
}
[hashtable]$deliveryGroupMembers = @{}
ForEach( $deliveryGroupMember in (Get-BrokerMachine -DesktopGroupName $deliveryGroup))
{
$deliveryGroupMembers.Add( ($deliveryGroupMember.MachineName -split '\\')[-1] , $deliveryGroupMember.DesktopGroupName )
}
Write-Verbose -Message "Got $($deliveryGroupMembers.Count) machines in delivery group `"$deliveryGroup`""
ForEach( $item in $results )
{
Add-Member -InputObject $item -MemberType NoteProperty -Name 'Delivery Group' -Value $deliveryGroupMembers[ $item.DeviceName ]
}
if( $PSBoundParameters[ 'desktopName' ] )
{
if( ! ( Get-BrokerEntitlementPolicyRule -DesktopGroupUid $existingDeliveryGroup.Uid -Name $desktopName -ErrorAction SilentlyContinue ) )
{
if( $create )
{
if( $PsCmdlet.ShouldProcess( $desktopName , 'Create Published Desktop' ) )
{
## publish a desktop
[hashtable]$desktopParameters = $ddcParams.Clone()
if( $PSBoundParameters[ 'TagName' ] )
{
$desktopParameters.Add( 'Tag' , $tagName )
}
## TODO could have a parameter to restrict user access via -IncludedUsers
if( ! ( $newPublishedDesktop = New-BrokerEntitlementPolicyRule -Description "Created by $env:username by script $(Get-Date -Format G)" -Name $desktopName -PublishedName $desktopName -DesktopGroupUid $existingDeliveryGroup.Uid -Enabled $true -IncludedUserFilterEnabled $false -IncludedUsers @() @desktopParameters ) )
{
Write-Warning -Message "Failed to create published desktop `"$desktopName`""
}
}
}
else
{
Write-Warning -Message "Desktop `"$desktopName`" does not exist"
}
}
else
{
Write-Warning -Message "Desktop `"$desktopName`" already exists"
}
}
}
}
if( $PSBoundParameters[ 'powerOnDelaySeconds' ] -and $results -and $results.Count -gt 0 )
{
[int]$millisecondDelay = $powerOnDelaySeconds * 1000
[int]$counter = 0
ForEach( $result in $results )
{
$counter++
Write-Verbose -Message "$(Get-Date -Format G): $counter / $($results.Count) : starting $($result.Name)"
if( -Not ( $started = Start-VM -VM $result.Name ) -or $started.PowerState -ne 'PoweredOn' )
{
Write-Warning -Message "Problem starting $($result.Name)"
}
elseif( $millisecondDelay -gt 0 -and $counter -ne $results.Count )
{
Start-Sleep -Milliseconds $millisecondDelay
}
}
}
if( $PVSSession )
{
Remove-PSSession -Session $PVSSession
$PVSSession = $null
}
## because based off a VMware object, it would only output the default properties for that so force it to return all properties
$results | Select-Object -Property *
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment