DSCResources/VE_XD7Common/VE_XD7Common.psm1
Import-LocalizedData -BindingVariable localizedData -FileName Resources.psd1; #region Private Functions function AddInvokeScriptBlockCredentials { <# .SYNOPSIS Adds the required Invoke-Command parameters for loopback processing with CredSSP. #> [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory)] [System.Collections.Hashtable] $Hashtable, [Parameter(Mandatory)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential ) process { $Hashtable['ComputerName'] = $env:COMPUTERNAME; $Hashtable['Credential'] = $Credential; $Hashtable['Authentication'] = 'Credssp'; } } #end function AddInvokeScriptBlockCredentials function GetHostname { [CmdletBinding()] [OutputType([System.String])] param ( ) process { $globalIpProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties(); if ($globalIpProperties.DomainName) { return '{0}.{1}' -f $globalIpProperties.HostName, $globalIpProperties.DomainName; } else { return $globalIpProperties.HostName; } } #end process } #end function GetHostname function GetRegistryValue { <# .SYNOPSIS Returns a registry string value. .NOTES This is an internal function and shouldn't be called from outside. This function enables registry calls to be unit tested. #> [CmdletBinding(SupportsShouldProcess)] param ( # Registry key name/path to query. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [Alias('Path')] [System.String] $Key, # Registry value to return. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [System.String] $Name ) process { $itemProperty = Get-ItemProperty -Path $Key -Name $Name -ErrorAction SilentlyContinue; if ($itemProperty.$Name) { return $itemProperty.$Name; } return ''; } } #end function GetRegistryValue function StartWaitProcess { <# .SYNOPSIS Starts and waits for a process to exit. .NOTES This is an internal function and shouldn't be called from outside. #> [CmdletBinding(SupportsShouldProcess)] [OutputType([System.Int32])] param ( # Path to process to start. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [System.String] $FilePath, # Arguments (if any) to apply to the process. [Parameter()] [AllowNull()] [System.String[]] $ArgumentList, # Credential to start the process as. [Parameter()] [AllowNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, # Working directory [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $WorkingDirectory = (Split-Path -Path $FilePath -Parent) ) process { $startProcessParams = @{ FilePath = $FilePath; WorkingDirectory = $WorkingDirectory; NoNewWindow = $true; PassThru = $true; }; $displayParams = '<None>'; if ($ArgumentList) { $displayParams = [System.String]::Join(' ', $ArgumentList); $startProcessParams['ArgumentList'] = $ArgumentList; } Write-Verbose ($localizedData.StartingProcess -f $FilePath, $displayParams); if ($Credential) { Write-Verbose ($localizedData.StartingProcessAs -f $Credential.UserName); $startProcessParams['Credential'] = $Credential; } if ($PSCmdlet.ShouldProcess($FilePath, 'Start Process')) { $process = Start-Process @startProcessParams -ErrorAction Stop; } if ($PSCmdlet.ShouldProcess($FilePath, 'Wait Process')) { Write-Verbose ($localizedData.ProcessLaunched -f $process.Id); Write-Verbose ($localizedData.WaitingForProcessToExit -f $process.Id); $process.WaitForExit(); $exitCode = [System.Convert]::ToInt32($process.ExitCode); Write-Verbose ($localizedData.ProcessExited -f $process.Id, $exitCode); } return $exitCode; } #end process } #end function StartWaitProcess function FindXDModule { <# .SYNOPSIS Locates a module's manifest (.psd1) file. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $Name = 'Citrix.XenDesktop.Admin', [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $Path = 'C:\Program Files\Citrix\XenDesktopPoshSdk\Module\Citrix.XenDesktop.Admin.V1' ) process { $module = Get-ChildItem -Path $Path -Include "$Name.psd1" -File -Recurse; if (-not $module) { # If we have no .psd1 file, search for a .psm1 (for StoreFront) $module = Get-ChildItem -Path $Path -Include "$Name.psm1" -File -Recurse; } return $module.FullName; } #end process } #end function FindModule function TestXDModule { <# .SYNOPSIS Tests whether Powershell modules or Snapin are available/registered. #> [CmdletBinding()] param ( [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $Name = 'Citrix.XenDesktop.Admin', [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $Path = 'C:\Program Files\Citrix\XenDesktopPoshSdk\Module\Citrix.XenDesktop.Admin.V1', [Parameter()] [System.Management.Automation.SwitchParameter] $IsSnapin ) process { if ($IsSnapin) { if (Get-PSSnapin -Name $Name -Registered) { return $true; } } else { if (FindXDModule -Name $Name -Path $Path) { return $true; } } return $false; } #end process } #end TestModule function AssertXDModule { <# .SYNOPSIS Asserts whether all the specified modules are present, throwing if not. #> [CmdletBinding()] param ( [Parameter()] [ValidateNotNullOrEmpty()] [System.String[]] $Name, [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $Path = 'C:\Program Files\Citrix\XenDesktopPoshSdk\Module\Citrix.XenDesktop.Admin.V1', [Parameter()] [System.Management.Automation.SwitchParameter] $IsSnapin ) process { foreach ($moduleName in $Name) { if (-not (TestXDModule -Name $moduleName -Path $Path -IsSnapin:$IsSnapin)) { ThrowInvalidProgramException -ErrorId $moduleName -ErrorMessage $localizedData.XenDesktopSDKNotFoundError; } } #end foreach module } #end process } #end function AssertXDModule function GetXDBrokerMachine { <# Searches for a registered Citrix XenDesktop machine by name. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [System.String] $MachineName ) if ($MachineName.Contains('.')) { ## Attempt to locate the machine by FQDN $brokerMachine = Get-BrokerMachine -DNSName $MachineName -ErrorAction SilentlyContinue; } elseif ($MachineName.Contains('\')) { ## Otherwise attempt to locate the machine by DomainName\NetBIOSName $brokerMachine = Get-BrokerMachine -MachineName $MachineName -ErrorAction SilentlyContinue; } else { ## Failing all else, perform a wildcard search $brokerMachine = Get-BrokerMachine -MachineName "*\$MachineName" -ErrorAction SilentlyContinue; } if ($brokerMachine -eq $null) { Write-Error -ErrorId 'MachineNotFound' -Message ($localizedData.MachineNotFoundError -f $Machine); return; } elseif (($brokerMachine).Count -gt 1) { Write-Error -ErrorId 'AmbiguousMachineReference' -Message ($localizedData.AmbiguousMachineReferenceError -f $MachineName); return; } else { return $brokerMachine; } } function TestXDMachineIsExistingMember { <# Tests whether a machine is an existing member of a list of FQDN machine members. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [System.String] $MachineName, [Parameter()] [System.String[]] $ExistingMembers ) if ((-not $MachineName.Contains('\')) -and (-not $MachineName.Contains('.'))) { Write-Warning -Message ($localizedData.MachineNameNotFullyQualifiedWarning -f $MachineName); $netBIOSName = $MachineName; } elseif ($MachineName.Contains('\')) { $netBIOSName = $MachineName.Split('\')[1]; } if ($ExistingMembers -contains $MachineName) { return $true; } elseif ($ExistingMembers -like '{0}.*' -f $netBIOSName) { return $true; } else { return $false; } } #end function TestXDMachine function TestXDMachineMembership { <# Provides a centralised function to test whether machine membership of a Machine Catalog or Delivery Group are correct - evaluating FQDNs, DOMAINNAME\NETBIOS and NETBIOS name formats. #> param ( [Parameter(Mandatory)] [System.String[]] $RequiredMembers, [Parameter(Mandatory)] [ValidateSet('Present','Absent')] [System.String] $Ensure, [Parameter()] [System.String[]] $ExistingMembers ) process { $isInCompliance = $true; foreach ($member in $RequiredMembers) { if (TestXDMachineIsExistingMember -MachineName $member -ExistingMembers $ExistingMembers) { if ($Ensure -eq 'Absent') { Write-Verbose ($localizedData.SurplusMachineReference -f $member); $isInCompliance = $false; } } else { if ($Ensure -eq 'Present') { Write-Verbose ($localizedData.MissingMachineReference -f $member); $isInCompliance = $false; } } } #end foreach member return $isInCompliance; } #end process } #end function TestXDMachineMembers function ResolveXDBrokerMachine { <# Returns a machine machine from an existing collection of Citrix XenDesktop machines assigned to a Machine Catalog or Delivery Group #> param ( [Parameter(Mandatory)] [System.String] $MachineName, [Parameter(Mandatory)] [AllowNull()] [System.Object[]] $BrokerMachines ) $brokerMachine = $null; foreach ($machine in $brokerMachines) { ## Try matching on DNS name if (($machine.DNSName -eq $MachineName) -or ($machine.MachineName -eq $MachineName)) { return $machine; } elseif ((-not $MachineName.Contains('\')) -and ($machine.MachineName -match '^\S+\\{0}$' -f $MachineName)) { ## Try matching based on DOMAIN\NETBIOS name return $machine } } #end foreach machine return $null; } #end function ResolveXDBrokerMachine function ThrowInvalidOperationException { <# .SYNOPSIS Throws terminating error of category NotInstalled with specified errorId and errorMessage. #> param( [Parameter(Mandatory)] [System.String] $ErrorId, [Parameter(Mandatory)] [System.String] $ErrorMessage ) $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument; $exception = New-Object -TypeName 'System.InvalidOperationException' -ArgumentList $ErrorMessage; $errorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $exception, $ErrorId, $errorCategory, $null; throw $errorRecord; } function ThrowInvalidProgramException { <# .SYNOPSIS Throws terminating error of category NotInstalled with specified errorId and errorMessage. #> param( [Parameter(Mandatory)] [System.String] $ErrorId, [Parameter(Mandatory)] [System.String] $ErrorMessage ) $errorCategory = [System.Management.Automation.ErrorCategory]::NotInstalled; $exception = New-Object -TypeName 'System.InvalidProgramException' -ArgumentList $ErrorMessage; $errorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $exception, $ErrorId, $errorCategory, $null; throw $errorRecord; } #end function ThrowInvalidProgramException function ThrowOperationCanceledException { <# .SYNOPSIS Throws terminating error of category InvalidOperation with specified errorId and errorMessage. #> param( [Parameter(Mandatory)] [System.String] $ErrorId, [Parameter(Mandatory)] [System.String] $ErrorMessage ) $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidOperation; $exception = New-Object -TypeName 'System.OperationCanceledException' -ArgumentList $ErrorMessage; $errorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $exception, $ErrorId, $errorCategory, $null; throw $errorRecord; } #end function ThrowOperationCanceledException function TestXDInstalledRole { <# .SYNOPSIS Tests whether a Citrix XenDesktop 7.x role is installed. #> [CmdletBinding()] [OutputType([System.Boolean])] param ( ## Citrix XenDesktop 7.x role to query. [Parameter(Mandatory)] [ValidateSet('Controller','Studio','Storefront','Licensing','Director','DesktopVDA','SessionVDA')] [System.String] $Role ) process { if (GetXDInstalledRole -Role $Role) { return $true; } return $false; } #end process } #end function TestXDRole function GetXDInstalledRole { <# .SYNOPSIS Returns installed Citrix XenDesktop 7.x installed product by role. #> [CmdletBinding()] [OutputType([System.String])] param ( ## Citrix XenDesktop 7.x role to query. [Parameter(Mandatory)] [ValidateSet('Controller','Studio','Storefront','Licensing','Director','DesktopVDA','SessionVDA')] [System.String] $Role ) process { $installedProducts = Get-ItemProperty 'HKLM:\SOFTWARE\Classes\Installer\Products\*' -ErrorAction SilentlyContinue | Where-Object { $_.ProductName -like '*Citrix*' -and $_.ProductName -notlike '*snap-in' } | Select-Object -ExpandProperty ProductName; switch ($Role) { 'Controller' { $filter = 'Citrix Broker Service'; } 'Studio' { $filter = 'Citrix Studio'; } 'Storefront' { $filter = 'Citrix Storefront'; } 'Licensing' { $filter = 'Citrix Licensing'; } 'Director' { $filter = 'Citrix Director(?!.VDA Plugin)'; } 'DesktopVDA' { $filter = 'Citrix Virtual Desktop Agent'; } 'SessionVDA' { $filter = 'Citrix Virtual Desktop Agent'; } } $result = $installedProducts -match $filter; if ([System.String]::IsNullOrEmpty($result)) { return $false; } else { return $result; } } #end process } #end functoin GetXDInstalledProduct function ResolveXDSetupMedia { <# .SYNOPSIS Resolve the correct installation media source for the local architecture depending on the role. #> [CmdletBinding()] [OutputType([System.String])] param ( ## Citrix XenDesktop 7.x role to install/uninstall. [Parameter(Mandatory)] [ValidateSet('Controller','Studio','Storefront','Licensing','Director','DesktopVDA','SessionVDA')] [System.String] $Role, ## Citrix XenDesktop 7.x installation media path. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [System.String] $SourcePath ) process { $architecture = 'x86'; if ([System.Environment]::Is64BitOperatingSystem) { $architecture = 'x64'; } switch ($Role) { 'DesktopVDA' { $installMedia = 'XenDesktopVdaSetup.exe'; } 'SessionVDA' { $installMedia = 'XenDesktopVdaSetup.exe'; } Default { $installMedia = 'XenDesktopServerSetup.exe'; } } $sourceArchitecturePath = Join-Path -Path $SourcePath -ChildPath $architecture; $installMediaPath = Get-ChildItem -Path $sourceArchitecturePath -Filter $installMedia -Recurse -File; if (-not $installMediaPath) { throw ($localizedData.NoValidSetupMediaError -f $installMedia, $sourceArchitecturePath); } return $installMediaPath.FullName; } #end process } #end function ResolveXDSetupMedia #endregion Private Functions |