Public/Get-VMConnectConfig.ps1
#.ExternalHelp VMConnectConfig-help.xml function Get-VMConnectConfig { [CmdletBinding(DefaultParameterSetName = 'EmptyParamSet', HelpURI='https://thegraffix.github.io/VMConnectConfig/get-vmconnectconfig.html')] [Alias('gvmc')] [OutputType([System.Management.Automation.PSObject[]])] param ( [Parameter(Mandatory, ParameterSetName = 'All')] [switch]$All, [Parameter(Mandatory, ParameterSetName = 'Deleted')] [switch]$Deleted, [Parameter(Mandatory, ParameterSetName = 'Id', Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('VMId')] [ValidateNotNullOrEmpty()] [System.Guid[]]$Id, [Parameter(Mandatory, ParameterSetName = 'LiteralPath', ValueFromPipelineByPropertyName)] [Alias('FullName')] [ValidateNotNullOrEmpty()] [System.String[]]$LiteralPath, [Parameter(Mandatory, ParameterSetName = 'Name', Position = 0, ValueFromPipeline)] [SupportsWildcards()] [ValidateNotNullOrEmpty()] [System.String[]]$Name, [Parameter(Mandatory, ParameterSetName = 'Path')] [SupportsWildcards()] [ValidateNotNullOrEmpty()] [System.String[]]$Path ) begin { # This is a workaround for a bug where advanced functions can output an error if "-ErrorAction/-WarningAction Ignore" are used. if ($ErrorActionPreference -eq 'Ignore') {$ErrorActionPreference = 'Ignore'} if ($WarningPreference -eq 'Ignore') {$WarningPreference = 'Ignore'} $cimCaption = 'Virtual Machine' $cimClassName = 'Msvm_ComputerSystem' $cimNamespace = 'root/virtualization/v2' $cimPropertyList = @('Caption', 'ElementName', 'EnhancedSessionModeState', 'Name', 'StatusDescriptions') $cimVmVersionClassName = 'Msvm_VirtualSystemSettingData' [System.IO.FileInfo[]]$configFileInfoList = @() $legacyVMConnectConfigTypeDisplayPropertySet = @('Name', 'Id', 'Path', 'DateModified', 'VMExists', 'VMVersion', 'AudioCaptureRedirectionMode', 'AudioPlaybackRedirectionMode', 'ClipboardRedirection', 'DesktopSize', 'FullScreen', 'PrinterRedirection', 'RedirectedDrives', 'RedirectedPnpDevices', 'RedirectedUsbDevices', 'SaveButtonChecked', 'SmartCardsRedirection', 'UseAllMonitors', 'VMServerName' ) $modernVMConnectConfigTypeDisplayPropertySet = @('Name', 'Id', 'Path', 'DateModified', 'VMExists', 'VMVersion', 'AudioCaptureRedirectionMode', 'AudioPlaybackRedirectionMode', 'ClipboardRedirection', 'DesktopSize', 'EnablePrinterRedirection', 'FullScreen', 'PrinterRedirection', 'RedirectedDrives', 'RedirectedPnpDevices', 'RedirectedUsbDevices', 'SaveButtonChecked', 'SmartCardsRedirection', 'UseAllMonitors', 'VMServerName' ) $webAuthnVMConnectConfigTypeDisplayPropertySet = @('Name', 'Id', 'Path', 'DateModified', 'VMExists', 'VMVersion', 'AudioCaptureRedirectionMode', 'AudioPlaybackRedirectionMode', 'ClipboardRedirection', 'DesktopSize', 'EnablePrinterRedirection', 'FullScreen', 'PrinterRedirection', 'RedirectedDrives', 'RedirectedPnpDevices', 'RedirectedUsbDevices', 'SaveButtonChecked', 'SmartCardsRedirection', 'UseAllMonitors', 'VMServerName', 'WebAuthnRedirection' ) $vmHostObjArray = @() } #begin process { if ($PSCmdlet.ParameterSetName -notlike '*Path') { $null = Test-VMConfigFolder $allConfigFiles = Get-AllConfigFiles } switch ($PSCmdlet.ParameterSetName) { {$_ -eq 'All' -or $_ -eq 'Deleted' -or $_ -eq 'EmptyParamSet'} { $configFileInfoList = $allConfigFiles break } # -All or -Deleted or <no parameters used>. 'Id' { foreach ($vmGuid in $Id.Guid) { $files = @() $files = ($allConfigFiles | Where-Object {$_.Name -match "vmconnect\.rdp\.$vmGuid.*\.config"}) if (($files.Count -eq 0) -or ([System.String]::IsNullOrEmpty($files))) { $errMsg = ($MsgTable.ConfigFileNotFoundIdError -f $VMConfigFolder, $vmGuid) $errParams = @{ Category = $ErrorCatObjectNotFound Exception = New-Object -TypeName 'System.ArgumentException' -ArgumentList $errMsg Message = $errMsg } Write-Error @errParams } #if no .config file(s) found. else { foreach ($file in $files) { $configFileInfoList += $file } } } #foreach $vmGuid break } # -Id 'LiteralPath' { foreach ($literalItem in $LiteralPath) { $literalPathError = $null $resolvedLiteralPaths = @() $resolvedLiteralPaths = [System.String[]]((Resolve-Path -LiteralPath $literalItem -ErrorVariable 'literalPathError').Path | Where-Object {[System.IO.File]::Exists($_)}) if ((($resolvedLiteralPaths.Count -eq 0) -or ([System.String]::IsNullOrEmpty($resolvedLiteralPaths))) -and [System.String]::IsNullOrEmpty($literalPathError)) { # Output an error message when Resolve-Path returns 0 items and doesn't output an error message. $resolvedLiteralItemPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($literalItem) $errMsg = ($MsgTable.ConfigFileNotFoundPathError -f $resolvedLiteralItemPath) $errParams = @{ Category = $ErrorCatObjectNotFound Exception = New-Object -TypeName 'System.IO.FileNotFoundException' -ArgumentList $errMsg, $resolvedLiteralItemPath Message = $errMsg TargetObject = $resolvedLiteralItemPath } Write-Error @errParams } #if else { foreach ($resolvedLiteralPath in $resolvedLiteralPaths) { $configFileInfoList += [System.IO.FileInfo]$resolvedLiteralPath } } } #foreach $literalItem break } # -LiteralPath 'Name' { foreach ($vmName in $Name) { $files = @() try { $files = [System.String[]]((Select-Xml -Path $allConfigFiles.FullName -XPath $VMNameXPath | Where-Object {$_.Node.Value -like $vmName}).Path) } #try catch { # Do nothing. This is to fully suppress terminating errors from the Select-Xml cmdlet when the 1.0 directory exists but is empty. } #catch if (($files.Count -eq 0) -or ([System.String]::IsNullOrEmpty($files))) { $errMsg = ($MsgTable.ConfigFileNotFoundNameError -f $VMConfigFolder, $vmName) $errParams = @{ Category = $ErrorCatObjectNotFound Exception = New-Object -TypeName 'System.ArgumentException' -ArgumentList $errMsg Message = $errMsg } Write-Error @errParams } #if no .config file(s) found. else { foreach ($file in $files) { $configFileInfoList += [System.IO.FileInfo]$file } } } #foreach $vmName break } # -Name 'Path' { foreach ($item in $Path) { $pathError = $null $resolvedPaths = @() $resolvedPaths = [System.String[]]((Resolve-Path -Path $item -ErrorVariable 'pathError').Path | Where-Object {[System.IO.File]::Exists($_)}) if ((($resolvedPaths.Count -eq 0) -or ([System.String]::IsNullOrEmpty($resolvedPaths))) -and [System.String]::IsNullOrEmpty($pathError)) { # Output an error message when Resolve-Path returns 0 items and doesn't output an error message. $resolvedItemPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($item) $errMsg = ($MsgTable.ConfigFileNotFoundPathError -f $resolvedItemPath) $errParams = @{ Category = $ErrorCatObjectNotFound Exception = New-Object -TypeName 'System.IO.FileNotFoundException' -ArgumentList $errMsg, $resolvedItemPath Message = $errMsg TargetObject = $resolvedItemPath } Write-Error @errParams } #if else { foreach ($resolvedPath in $resolvedPaths) { $configFileInfoList += [System.IO.FileInfo]$resolvedPath } } } #foreach ($item in $Path) break } # -Path } #switch ($PSCmdlet.ParameterSetName) } #process end { $configFileInfoList = $configFileInfoList | Select-Object -Unique $vmHostNameList = @() [System.IO.FileInfo[]]$validConfigFileInfoList = @() foreach ($configFile in $configFileInfoList) { $configFilePath = $configFile.FullName if ((Test-VMConnectConfig -LiteralPath $configFilePath) -eq $false) { $errMsg = ($MsgTable.InvalidConfigFileError -f $configFilePath) $errParams = @{ Category = $ErrorCatInvalidArgument Exception = New-Object -TypeName 'System.ArgumentException' -ArgumentList $errMsg Message = $errMsg TargetObject = $configFilePath } Write-Error @errParams continue } $validConfigFileInfoList += $configFile try { $xmlFile = New-Object -TypeName 'System.Xml.XmlDocument' $xmlFile.Load($configFilePath) [System.String]$vmServerNameValue = $xmlFile.SelectSingleNode($VMServerNameXPath).value if (([System.String]::IsNullOrEmpty($vmServerNameValue) -eq $false) -and ($vmServerNameValue -inotin $vmHostNameList) -and ([System.String]::IsNullOrEmpty(($configFile.Name | Select-String -Pattern $GuidRegexPattern).Matches.Value) -eq $false) ) { # Only filenames with valid GUIDS should have their VMServerName value added to $vmHostNameList. $vmHostNameList += $vmServerNameValue } } #try catch { continue } #catch } #foreach ($configFile in $configFileInfoList) [System.UInt64]$freeMemoryBytes = (Get-CimInstance -ClassName Win32_OperatingSystem -Verbose:$false).FreePhysicalMemory * 1kb # Maximum number of parallel runspaces is calculated by: 25% of free memory / 100 MB # Why 100 MB? Because under average circumstances a single PowerShell runspace is ~100 MB or less. [System.Int32]$runspacePoolMax = ($freeMemoryBytes * 0.25) / 100mb if ($runspacePoolMax -lt 1) { $runspacePoolMax = 1 } $runspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1, $runspacePoolMax) $runspacePool.Open() $tcScriptBlock = { param($tcHostName) if ($tcHostName -eq $HostName) { $testConnectionResult = $true } else { $tcParams = @{ ComputerName = $tcHostName Count = 1 Quiet = $true } $testConnectionResult = Test-Connection @tcParams } if ($testConnectionResult -eq $true) { # Create parallel CIM session(s). $cimSessionOption = New-CimSessionOption -Protocol Wsman $newCimSessionParam = @{ ComputerName = $tcHostName ErrorAction = 'Stop' SessionOption = $cimSessionOption } try { $cimErrorMessage = $null $tcCimSession = New-CimSession @newCimSessionParam } #try catch { try { # Try DCOM if WSMan protocol fails. $newCimSessionParam.SessionOption = New-CimSessionOption -Protocol Dcom $tcCimSession = New-CimSession @newCimSessionParam } #try catch { # Save the error message to the psobject. $cimErrorMessage = $_.Exception.Message } #catch } #catch } #if $tcHostName is ONLINE. [pscustomobject] @{ TestConnectionHostName = $tcHostName TestConnectionResult = $testConnectionResult CimSessionObject = $tcCimSession CimErrorMessage = $cimErrorMessage } # Perform garbage collection. [System.GC]::Collect() [System.GC]::WaitForPendingFinalizers() } #$tcScriptBlock $runspaces = foreach ($vmHostName in $vmHostNameList) { # Only display "Testing connection..." message if there's more than 1x VmHostName || there's only 1x and it's not the local computer ($env:ComputerName). if ($vmHostNameList.Count -gt 1 -or ($vmHostNameList.Count -eq 1 -and $vmHostNameList[0] -ne $HostName)) { Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Testing -Message ($MsgTable.TestingConnectionMsg -f $vmHostName) } $rsParams = @{ tcHostName = $vmHostName } $psInstance = [System.Management.Automation.PowerShell]::Create().AddScript($tcScriptBlock).AddParameters($rsParams) $psInstance.RunspacePool = $runspacePool [pscustomobject] @{ Instance = $psInstance Status = $psInstance.BeginInvoke() } } #$runspaces # Display either singular or plural "Waiting for connection test/tests" verbose message (depending on the value of $vmHostNameList.Count). if ($vmHostNameList.Count -gt 1) { Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Testing -Message ($MsgTable.TestingConnectionWaitingParallelMsg) } elseif ($vmHostNameList.Count -eq 1 -and $vmHostNameList[0] -ne $HostName) { Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Testing -Message ($MsgTable.TestingConnectionWaitingMsg) } # Wait for runspaces to finish running. while ($runspaces.Status.IsCompleted -contains $false) { Start-Sleep -Milliseconds 100 } foreach ($rs in $runspaces) { $connectionResult = $rs.Instance.EndInvoke($rs.Status) $null = $rs.Instance.Dispose() $vmHostComputerName = $connectionResult.TestConnectionHostName $cimSessionObject = $connectionResult.CimSessionObject $cimErrMsg = $connectionResult.CimErrorMessage if ($connectionResult.TestConnectionResult -eq $true) { $vmHostStatus = 'Online' Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Testing -Message ($MsgTable.ConnectionSuccessMsg -f $vmHostComputerName) Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Constructing -Message ($MsgTable.CreatingCimSessionMsg -f $vmHostComputerName) } else { $vmHostStatus = 'Offline' Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Warning -Message ($MsgTable.ConnectionError -f $vmHostComputerName) } # Add the VMHostName and the Test-Connection result to [psobject[]]$vmHostObjArray. $vmHostObjArray += [pscustomobject] @{ VMHostName = $vmHostComputerName ConnectionStatus = $vmHostStatus CimSessionObject = $cimSessionObject CimErrorMsg = $cimErrMsg } } #foreach runspace foreach ($configFile in $validConfigFileInfoList) { $cimErrorMessage = $null $cimVmObject = $null $configFilePath = $configFile.FullName $currentRunspaceObject = $null $errMsg = $null $errParams = $null $vmccObjErrorReason = $null try { $xmlFile = New-Object -TypeName 'System.Xml.XmlDocument' $xmlFile.Load($configFilePath) $xmlElementList = $xmlFile.SelectNodes($XPath) [System.Object]$vmccObjVmExists = $null [System.Object]$vmccObjVmId = $null [System.String]$vmccObjVmName = $xmlFile.SelectSingleNode($VMNameXPath).value [System.String]$vmccObjVmServerName = $xmlFile.SelectSingleNode($VMServerNameXPath).value [System.Object]$vmccObjVmVersion = $null $vmIdString = ($configFile.Name | Select-String -Pattern $GuidRegexPattern).Matches.Value if ([System.String]::IsNullOrEmpty($vmIdString) -eq $false) { [System.Guid]$vmccObjVmId = $vmIdString $vmIdErrorString = " ($vmIdString)" } $currentRunspaceObject = $vmHostObjArray | Where-Object {$_.VMHostName -eq $vmccObjVmServerName} if ($null -ne $vmccObjVmId) { if ($currentRunspaceObject.ConnectionStatus -eq 'Online') { $cimSessionObj = $currentRunspaceObject.CimSessionObject $cimErrorMessage = $currentRunspaceObject.CimErrorMsg if ($null -ne $currentRunspaceObject.CimSessionObject) { $cimVmObjParams = @{ CimSession = $cimSessionObj ClassName = $cimClassName ErrorAction = 'Stop' Filter = "Caption='$cimCaption' AND ElementName='$vmccObjVmName' AND Name='$vmccObjVmId'" NameSpace = $cimNamespace Property = $cimPropertyList Verbose = $false } $cimVmVersionParams = @{ CimSession = $cimSessionObj ClassName = $cimVmVersionClassName ErrorAction = 'Stop' Filter = "ElementName='$vmccObjVmName' AND VirtualSystemIdentifier='$vmccObjVmId'" NameSpace = $cimNamespace Property = 'Version' Verbose = $false } try { $cimVmObject = Get-CimInstance @cimVmObjParams $cimVmVersionObject = Get-CimInstance @cimVmVersionParams if ($null -ne $cimVmObject) { $vmccObjVmExists = $true } else { $vmccObjVmExists = $false $vmccObjErrorReason = ($MsgTable.VmNotFoundError -f 'Get-VMConnectConfig', $vmccObjVmName, $vmIdErrorString, $vmccObjVmServerName) } if ($null -ne $cimVmVersionObject.Version) { [System.Version]$vmccObjVmVersion = ($cimVmVersionObject).Version } else { $statusDescriptions = $cimVmObject.StatusDescriptions if (($statusDescriptions.Count -gt 1) -and ([System.String]::IsNullOrEmpty($statusDescriptions[1]) -eq $false)) { # If there are multiple status descriptions, the secondary status description ($statusDescription[1]) reflects the most critical status description. # Also, ensure that the status description ends with a period. $vmccObjErrorReason = ($statusDescriptions[1] -replace '\.$') + "." } elseif ($statusDescriptions.Count -ne 0) { $vmccObjErrorReason = ($statusDescriptions[0] -replace '\.$') + "." } elseif ($vmccObjVmExists) { $vmccObjErrorReason = $MsgTable.UnknownError } } } #try catch { # If the WMI namespace "ROOT/virtualization/v2" doesn't exist, $_.Exception.NativeErrorCode will be "InvalidNameSpace". # The presence of that error code should be sufficient for determining if a computer has the Hyper-V role/feature installed. $ex = $_ if ($ex.Exception.NativeErrorCode -eq 'InvalidNameSpace') { $vmccObjErrorReason = ($MsgTable.VmHostHyperVRoleError -f $vmccObjVmServerName) } else { $vmccObjErrorReason = $ex } } #catch } # if no Cim error message. elseif ([System.String]::IsNullOrEmpty($cimErrorMessage) -eq $false) { $vmccObjErrorReason = $cimErrorMessage } else { $vmccObjErrorReason = ($MsgTable.CimSessionError -f $vmccObjVmServerName) } } #if $vmccObjVmServerName is ONLINE. elseif ($currentRunspaceObject.ConnectionStatus -eq 'Offline') { $vmccObjErrorReason = ($MsgTable.ConnectionError -f $vmccObjVmServerName) } #if $vmccObjVmServerName is OFFLINE. } #if valid GUID found in filename. else { $vmccObjErrorReason = ($MsgTable.GuidNotFoundInFilenameError -f ($configFile.Name)) } # If the .VMExists property is $false, the .config file's VMServerName was successfully queried for that VM -- and the VM was not found; only these VMs are included in the -Deleted param set. # If the .VMExists property is $true, the .config file's VMServerName was successfully queried for that VM -- and the VM was found; these VMs are included in the default <empty param set>. # If the .VMExists property is $null, the .config file's VMServerName was not able to be connected to or queried; these VMs are also included in the default <empty param set>. if ($PSCmdlet.ParameterSetName -eq 'Deleted' -and $vmccObjVmExists -ne $false) { continue } elseif ($PSCmdlet.ParameterSetName -eq 'EmptyParamSet' -and $vmccObjVmExists -eq $false) { continue } # <EmptyParamSet> if ([System.String]::IsNullOrEmpty($vmccObjVmServerName) -and [System.String]::IsNullOrEmpty($vmccObjErrorReason)) { $vmccObjErrorReason = ($MsgTable.VmServerNameNotFound -f $configFilePath) } if ([System.String]::IsNullOrEmpty($vmccObjErrorReason) -eq $false) { if ($null -ne $vmccObjVmId) { $vmIdErrorString = " ($vmccObjVmId)" } if ([System.String]::IsNullOrEmpty($vmIdString)) { Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Warning -Message ($MsgTable.FilenameGuidUndefinedError -f $vmccObjVmName, $vmccObjErrorReason) # The .Id property is used when querying the VMServerName, and the results of that query determine the values of the VMExists and VMVersion properties. # If the .Id property is missing/null then there will be no way to determine the .VMExists and .VMVersion properties. $vmccObjErrorReason = ($MsgTable.VmIdNeededToCalculatePropertiesError) } if (($null -eq $vmccObjVmExists) -and ($null -eq $vmccObjVmVersion)) { if ([System.String]::IsNullOrEmpty($vmccObjVmServerName)) { Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Warning -Message ($MsgTable.VmExistsAndVmVersionAndVmServerNameUndefinedError -f $vmccObjVmName, $vmIdErrorString, $vmccObjErrorReason) } else { Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Warning -Message ($MsgTable.VmExistsAndVmVersionUndefinedError -f $vmccObjVmName, $vmIdErrorString, $vmccObjVmServerName, $vmccObjErrorReason) } } elseif ($null -eq $vmccObjVmExists) { Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Warning -Message ($MsgTable.VmExistsUndefinedError -f $vmccObjVmName, $vmIdErrorString, $vmccObjErrorReason) } elseif ($null -eq $vmccObjVmVersion) { Write-VMVerbose -FunctionName Get-VMConnectConfig -Category Warning -Message ($MsgTable.VmVersionUndefinedError -f $vmccObjVmName, $vmIdErrorString, $vmccObjErrorReason) } } $vmConnectConfigObj = @{} $vmConnectConfigObj.Add('PSTypeName', 'VMConnectConfig') $vmConnectConfigObj.Add('Name', $vmccObjVmName) $vmConnectConfigObj.Add('Id', $vmccObjVmId) $vmConnectConfigObj.Add('Path', $configFilePath) $vmConnectConfigObj.Add('DateModified', ($configFile.LastWriteTime)) $vmConnectConfigObj.Add('VMExists', $vmccObjVmExists) $vmConnectConfigObj.Add('VMVersion', $vmccObjVmVersion) [System.Boolean]$vmccObjAudioCaptureRedirectionMode = [System.Convert]::ToBoolean($xmlFile.SelectSingleNode("//setting[@name='AudioCaptureRedirectionMode']").value) $vmConnectConfigObj.Add('AudioCaptureRedirectionMode', $vmccObjAudioCaptureRedirectionMode) [System.String]$vmccObjAudioPlaybackRedirectionMode = $xmlFile.SelectSingleNode("//setting[@name='AudioPlaybackRedirectionMode']").value $vmConnectConfigObj.Add('AudioPlaybackRedirectionMode', $vmccObjAudioPlaybackRedirectionMode) [System.Boolean]$vmccObjClipboardRedirection = [System.Convert]::ToBoolean($xmlFile.SelectSingleNode("//setting[@name='ClipboardRedirection']").value) $vmConnectConfigObj.Add('ClipboardRedirection', $vmccObjClipboardRedirection) [System.Drawing.Size]$vmccObjDesktopSize = $xmlFile.SelectSingleNode("//setting[@name='DesktopSize']").value $vmConnectConfigObj.Add('DesktopSize', $vmccObjDesktopSize) if ($xmlElementList.Count -gt 15) { [System.Boolean]$vmccObjEnablePrinterRedirection = [System.Convert]::ToBoolean($xmlFile.SelectSingleNode("//setting[@name='EnablePrinterRedirection']").value) $vmConnectConfigObj.Add('EnablePrinterRedirection', $vmccObjEnablePrinterRedirection) } [System.Boolean]$vmccObjFullScreen = [System.Convert]::ToBoolean($xmlFile.SelectSingleNode("//setting[@name='FullScreen']").value) $vmConnectConfigObj.Add('FullScreen', $vmccObjFullScreen) [System.Boolean]$vmccObjPrinterRedirection = [System.Convert]::ToBoolean($xmlFile.SelectSingleNode("//setting[@name='PrinterRedirection']").value) $vmConnectConfigObj.Add('PrinterRedirection', $vmccObjPrinterRedirection) [System.String]$vmccObjRedirectedDrives = $xmlFile.SelectSingleNode("//setting[@name='RedirectedDrives']").value $vmConnectConfigObj.Add('RedirectedDrives', $vmccObjRedirectedDrives) [System.String]$vmccObjRedirectedPnpDevices = $xmlFile.SelectSingleNode("//setting[@name='RedirectedPnpDevices']").value $vmConnectConfigObj.Add('RedirectedPnpDevices', $vmccObjRedirectedPnpDevices) [System.String]$vmccObjRedirectedUsbDevices = $xmlFile.SelectSingleNode("//setting[@name='RedirectedUsbDevices']").value $vmConnectConfigObj.Add('RedirectedUsbDevices', $vmccObjRedirectedUsbDevices) [System.Boolean]$vmccObjSaveButtonChecked = [System.Convert]::ToBoolean($xmlFile.SelectSingleNode("//setting[@name='SaveButtonChecked']").value) $vmConnectConfigObj.Add('SaveButtonChecked', $vmccObjSaveButtonChecked) [System.Boolean]$vmccObjSmartCardsRedirection = [System.Convert]::ToBoolean($xmlFile.SelectSingleNode("//setting[@name='SmartCardsRedirection']").value) $vmConnectConfigObj.Add('SmartCardsRedirection', $vmccObjSmartCardsRedirection) [System.Boolean]$vmccObjUseAllMonitors = [System.Convert]::ToBoolean($xmlFile.SelectSingleNode("//setting[@name='UseAllMonitors']").value) $vmConnectConfigObj.Add('UseAllMonitors', $vmccObjUseAllMonitors) $vmConnectConfigObj.Add('VMServerName', $vmccObjVmServerName) if ($xmlElementList.Count -eq 17) { [System.Boolean]$vmccObjWebAuthnRedirection = [System.Convert]::ToBoolean($xmlFile.SelectSingleNode("//setting[@name='WebAuthnRedirection']").value) $vmConnectConfigObj.Add('WebAuthnRedirection', $vmccObjWebAuthnRedirection) } $vmConnectConfigObj = [pscustomobject]$vmConnectConfigObj switch ($xmlElementList.Count) { 15 { $null = Update-TypeData -TypeName 'VMConnectConfig' -DefaultDisplayPropertySet $legacyVMConnectConfigTypeDisplayPropertySet -Force -Confirm:$false -WhatIf:$false break } 16 { $null = Update-TypeData -TypeName 'VMConnectConfig' -DefaultDisplayPropertySet $modernVMConnectConfigTypeDisplayPropertySet -Force -Confirm:$false -WhatIf:$false break } 17 { $null = Update-TypeData -TypeName 'VMConnectConfig' -DefaultDisplayPropertySet $webAuthnVMConnectConfigTypeDisplayPropertySet -Force -Confirm:$false -WhatIf:$false break } } #switch ($xmlElementList.Count) Write-Output $vmConnectConfigObj } #try catch { $PSCmdlet.WriteError($_) } #catch } #foreach ($configFile in $configFileInfoList) # Perform garbage collection. [System.GC]::Collect() [System.GC]::WaitForPendingFinalizers() } #end } #function Get-VMConnectConfig |