ExportImportRdsDeployment.psm1

Function MyRdsFunctions_FixConnectionBrokerName {
    PARAM (
        [Parameter(Mandatory = $True)]
        [string]$ConnectionBroker
    )

    #Check if "localhost" is entered
    IF ($ConnectionBroker -eq "localhost") {
        $ConnectionBroker = ("{0}.{1}" -f (Get-WmiObject win32_computersystem).DNSHostName, (Get-WmiObject win32_computersystem).Domain)
        Write-Host -ForegroundColor Cyan ("Changing ConnectionBroker from localhost to {0}" -f $ConnectionBroker)
    }

    #Check if simple computername is entered (not FQDN)
    IF ($ConnectionBroker.IndexOf('.') -eq -1) {
        $ConnectionBroker += (".{0}" -f (Get-WmiObject win32_computersystem).Domain)
        Write-Host -ForegroundColor Cyan ("Changing ConnectionBroker to FQDN: {0}" -f $ConnectionBroker)
    }
    return $ConnectionBroker
}

Function MyRdsFunctions_FixXMLFileLocation {
    PARAM (
        [Parameter(Mandatory = $True)]
        [string]$ConnectionBroker,
        [string]$XmlFile,
        [string]$FunctionName
    )
    #Filling in default value if $XmlFile is empty.
    IF ($XmlFile -eq $null -OR $XmlFile -eq "") {
        $XmlFile = ("c:\temp\{0}_{1}.xml" -f $FunctionName, $ConnectionBroker)
        Write-Host -ForegroundColor Cyan ("Changing XmlFile to default value: {0}" -f $XmlFile)
    }
    return $XmlFile
}

Function MyRdsFunctions_CheckReboot {
    param (
        $serverName
    )

    $scriptResult = Invoke-Command -ComputerName $serverName -ScriptBlock {
        try {
            $computer = $env:COMPUTERNAME
            $invokeWmiMethodParameters = @{
                Namespace    = 'root/default'
                Class        = 'StdRegProv'
                Name         = 'EnumKey'
                ComputerName = $computer
                ErrorAction  = 'Stop'
            }

            $hklm = [UInt32] "0x80000002"

            if ($PSBoundParameters.ContainsKey('Credential')) {
                $invokeWmiMethodParameters.Credential = $Credential
            }

            ## Query the Component Based Servicing Reg Key
            $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\')
            $registryComponentBasedServicing = (Invoke-WmiMethod @invokeWmiMethodParameters).sNames -contains 'RebootPending'

            ## Query WUAU from the registry
            $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\')
            $registryWindowsUpdateAutoUpdate = (Invoke-WmiMethod @invokeWmiMethodParameters).sNames -contains 'RebootRequired'

            ## Query JoinDomain key from the registry - These keys are present if pending a reboot from a domain join operation
            $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Services\Netlogon')
            $registryNetlogon = (Invoke-WmiMethod @invokeWmiMethodParameters).sNames
            $pendingDomainJoin = ($registryNetlogon -contains 'JoinDomain') -or ($registryNetlogon -contains 'AvoidSpnSet')

            ## Query ComputerName and ActiveComputerName from the registry and setting the MethodName to GetMultiStringValue
            $invokeWmiMethodParameters.Name = 'GetMultiStringValue'
            $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\', 'ComputerName')
            $registryActiveComputerName = Invoke-WmiMethod @invokeWmiMethodParameters

            $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\', 'ComputerName')
            $registryComputerName = Invoke-WmiMethod @invokeWmiMethodParameters

            $pendingComputerRename = $registryActiveComputerName -ne $registryComputerName -or $pendingDomainJoin

            ## Query PendingFileRenameOperations from the registry
            if (-not $PSBoundParameters.ContainsKey('SkipPendingFileRenameOperationsCheck')) {
                $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Control\Session Manager\', 'PendingFileRenameOperations')
                $registryPendingFileRenameOperations = (Invoke-WmiMethod @invokeWmiMethodParameters).sValue
                $registryPendingFileRenameOperationsBool = [bool]$registryPendingFileRenameOperations
            }

            ## Query ClientSDK for pending reboot status, unless SkipConfigurationManagerClientCheck is present
            if (-not $PSBoundParameters.ContainsKey('SkipConfigurationManagerClientCheck')) {
                $invokeWmiMethodParameters.NameSpace = 'ROOT\ccm\ClientSDK'
                $invokeWmiMethodParameters.Class = 'CCM_ClientUtilities'
                $invokeWmiMethodParameters.Name = 'DetermineifRebootPending'
                $invokeWmiMethodParameters.Remove('ArgumentList')

                try {
                    $sccmClientSDK = Invoke-WmiMethod @invokeWmiMethodParameters
                    $systemCenterConfigManager = $sccmClientSDK.ReturnValue -eq 0 -and ($sccmClientSDK.IsHardRebootPending -or $sccmClientSDK.RebootPending)
                }
                catch {
                    $systemCenterConfigManager = $null
                }
            }

            $isRebootPending = $registryComponentBasedServicing -or `
                $pendingComputerRename -or `
                $pendingDomainJoin -or `
                $registryPendingFileRenameOperationsBool -or `
                $systemCenterConfigManager -or `
                $registryWindowsUpdateAutoUpdate

            if ($PSBoundParameters.ContainsKey('Detailed')) {
                [PSCustomObject]@{
                    ComputerName                     = $computer
                    ComponentBasedServicing          = $registryComponentBasedServicing
                    PendingComputerRenameDomainJoin  = $pendingComputerRename
                    PendingFileRenameOperations      = $registryPendingFileRenameOperationsBool
                    PendingFileRenameOperationsValue = $registryPendingFileRenameOperations
                    SystemCenterConfigManager        = $systemCenterConfigManager
                    WindowsUpdateAutoUpdate          = $registryWindowsUpdateAutoUpdate
                    IsRebootPending                  = $isRebootPending
                }
            }
            else {
                [PSCustomObject]@{
                    ComputerName    = $computer
                    IsRebootPending = $isRebootPending
                }
            }
        }

        catch {
            Write-Warning "$Computer`: $_"
        }
    }
    return $scriptResult
}

Function MyRdsFunctions_RebootServer {
    param (
        $serverName
    )

    $scriptResult = Invoke-Command -ComputerName $serverName -ScriptBlock {
        Restart-Computer -Force
    }
}

Function Export-RDDeploymentFromConnectionBroker {
    <#
    .SYNOPSIS
 
    Export Deployment & Removes all RD Servers from a Connection Broker, except CB & Licensing role (does not uninstall a server or server role) and stores it into an XML file.
 
    .DESCRIPTION
 
    Export Deployment & Removes all RD Servers from a Connection Broker, except CB & Licensing role (does not uninstall a server or server role) and stores it into an XML file.
    Then you can reconnect the servers to another or updated Connection Broker.
 
    .PARAMETER ConnectionBroker
    Specifies FQDN of the Connection Broker.
     
    .PARAMETER XmlFile
    Specifies the XML File location to store the information. "c:\temp\AllRDServersFrom<<ConnectionBroker>>.xml" is the default.
 
    .PARAMETER RemoveServers
    Switch to specify if the servers need to be removed or not after extraction. $false is the default (for safety reasons).
 
    .INPUTS
 
    System.String. You can pipe the Connection Broker FQDN name.
 
    .OUTPUTS
 
    System.String. Returns location to the XML File.
 
    .EXAMPLE
 
    C:\PS> Export-RDDeploymentFromConnectionBroker -ConnectionBroker "MySessionBroker.mydomain.hosting" -RemoveServers
 
    .EXAMPLE
 
    C:\PS> Export-RDDeploymentFromConnectionBroker -ConnectionBroker "MySessionBroker.mydomain.hosting" -RemoveServers:$true
 
    .EXAMPLE
 
    C:\PS> Export-RDDeploymentFromConnectionBroker -ConnectionBroker "localhost" -XmlFile "c:\myshare\myDeployment.xml" -RemoveServers:$false
 
    .LINK
 
    https://blog.cloud-architect.be
    #>


    [CmdletBinding()]
    PARAM (
        [Parameter(Mandatory = $True, ValueFromPipeline = $true)]
        [string]$ConnectionBroker,
        [string]$XmlFile = "",
        [switch]$RemoveServers
    )

    # Setting ErrorActionPreference to stop script execution when error occurs
    $ErrorActionPreference = "Stop"

    #First check ConnectionBroker name
    $ConnectionBroker = MyRdsFunctions_FixConnectionBrokerName -ConnectionBroker $ConnectionBroker

    #Then check XMLFile location
    $XmlFile = MyRdsFunctions_FixXMLFileLocation -ConnectionBroker $ConnectionBroker -XmlFile $XmlFile -FunctionName AllRDServersFrom

    #Write-Test to XMLFile
    try {
        "WriteTest" | Out-File -FilePath $XmlFile -Force
    }
    catch {
        Write-Host -ForegroundColor Red ("Error while testing XMLFile location. Please verify access to the location '{0}'" -f $XmlFile)
        return $null
    }
    
    if ($RemoveServers) {
        Write-Host ("`r`n`r`n--------------") -ForegroundColor Red
        Write-Host ("WARNING!!") -ForegroundColor Red
        Write-Host ("You added the parameter '-RemoveServers'!! ") -ForegroundColor Yellow
        Write-Host ("Did you export your Collections (using 'Export-RDCollectionsFromConnectionBroker') before running this cmdlet? ") -ForegroundColor Yellow
        Write-Host ("When confirming, all Servers & the Deployment will be removed from this Connection Broker after the export!!") -ForegroundColor Yellow
        Write-Host ("--------------") -ForegroundColor Red
        do {
            $confirmation = Read-Host -Prompt "Continue? (Y/N)"
        } while ($confirmation -ne "y" -and $confirmation -ne "n")
    
        if ($confirmation -eq "n") {
            Write-Host -ForegroundColor Yellow "Aborting..."
            return
        }
    }
    

    #Next: Export Deployment Config
    $DeploymentInfo = [ordered]@{ }

    $GatewayConfig = Get-RDDeploymentGatewayConfiguration -ConnectionBroker $ConnectionBroker
    $DeploymentInfo.Add("GatewayInfo", $GatewayConfig)

    $CertificateConfig = Get-RDCertificate -ConnectionBroker $ConnectionBroker
    $DeploymentInfo.Add("CertificateInfo", $CertificateConfig)

    $allServersToRemove = Get-RDServer -ConnectionBroker $ConnectionBroker

    $allRemovedServers = @()
    
    foreach ($serverToRemove in $allServersToRemove) {
        foreach ($role in $serverToRemove.Roles) {
            IF (($role -eq "RDS-CONNECTION-BROKER") -or ($role -eq "RDS-VIRTUALIZATION") -or ($role -eq "RDS-LICENSING")) {
                Write-Host -ForegroundColor Cyan ("Skipping role '{0}' on server '{1}'" -f $role, $serverToRemove.Server)
                continue
            }

            #Create empty HashTable
            $properties = [ordered]@{ }
            Write-Host -ForegroundColor Cyan ("Exporting server '{0}' with role '{1}' from CB" -f $serverToRemove.Server, $role)

            $properties.Add("ServerName", $serverToRemove.Server)
            $properties.Add("ServerRole", $role)

            #Create Custom object using the properties
            $object = New-Object -TypeName PSObject -Prop $properties
            $object.PSObject.TypeNames.Insert(0, "ASPEX&CloudArchitect.RDS.Servers.Export")

            #Finally, add all info to the Array of collections
            $allRemovedServers += $object
        }
    }

    $DeploymentInfo.Add("ServerInfo", $allRemovedServers)

    #Export all Collection info to an XML so we can Import it later
    Write-Host -ForegroundColor Cyan ("Exporting Deployment to XMLFile '{0}' ..." -f $XmlFile)
    $DeploymentInfo | Export-CliXml -Path $XmlFile

    if ($RemoveServers) {
        #Removing servers from CB
        Write-Host -ForegroundColor Cyan ("Removing servers from deployment ...")
        foreach ($serverToRemove in $allServersToRemove) {
            foreach ($role in $serverToRemove.Roles) {
                IF (($role -eq "RDS-CONNECTION-BROKER") -or ($role -eq "RDS-VIRTUALIZATION") -or ($role -eq "RDS-LICENSING")) {
                    Write-Host -ForegroundColor Cyan ("Skipping role '{0}'" -f $role)
                    continue
                }

                Write-Host -ForegroundColor Cyan ("Removing server '{0}' with role '{1}' from CB" -f $serverToRemove.Server, $role)

                IF ($role -eq "RDS-GATEWAY") {
                    Remove-RDServer -Server $serverToRemove.Server -Role $role -ConnectionBroker $ConnectionBroker -Force -ErrorAction SilentlyContinue
                }
                ELSE {
                    Remove-RDServer -Server $serverToRemove.Server -Role $role -ConnectionBroker $ConnectionBroker -Force
                }
            }
        }
    }
    

    Write-Host -ForegroundColor Green ("All done")

    return $XmlFile
}

Function Export-RDCollectionsFromConnectionBroker {
    <#
    .SYNOPSIS
 
    Export all RDS Collections from a Connection Broker, stores it into an XML file and removes the Collections
 
    .DESCRIPTION
 
    Export all RDS Collections from a Connection Broker, stores it into an XML file and removes the Collections
    Then you can import it into another or updated Connection Broker.
 
    .PARAMETER ConnectionBroker
    Specifies FQDN of the Connection Broker.
 
    .PARAMETER XmlFile
    Specifies the XML File location to store the information. "c:\temp\AllCollectionsFrom_<<ConnectionBroker>>.xml" is the default.
 
    .PARAMETER RemoveCollections
    Switch to specify if the collections need to be removed or not after extraction. $false is the default (for safety reasons).
 
    .INPUTS
 
    System.String. You can pipe the Connection Broker FQDN name.
 
    .OUTPUTS
 
    System.String. Returns location to the XML File.
 
    .EXAMPLE
 
    C:\PS> Export-RDCollectionsFromConnectionBroker -ConnectionBroker localhost -RemoveCollections
    Exports all Collections from the localhost (ex: "MySessionBroker.mydomain.hosting") to "c:\temp\AllCollectionsFrom_MySessionBroker.mydomain.hosting.xml"
 
    .EXAMPLE
 
    C:\PS> Export-RDCollectionsFromConnectionBroker -ConnectionBroker localhost -RemoveCollections:$true
 
    .EXAMPLE
 
    C:\PS> Export-RDCollectionsFromConnectionBroker -ConnectionBroker "MySessionBroker.mydomain.hosting" -XmlFile "c:\myshare\mycollections.xml" -RemoveCollections:$false -Verbose
    Exports all Collections from "MySessionBroker.mydomain.hosting" to "c:\myshare\mycollections.xml", with full detail output
 
    .LINK
 
    https://blog.cloud-architect.be
    #>


    [CmdletBinding()]
    PARAM (
        [Parameter(Mandatory = $True, ValueFromPipeline = $true)]
        [string]$ConnectionBroker,
        [string]$XmlFile = "",
        [switch]$RemoveCollections
    )

    # Setting ErrorActionPreference to stop script execution when error occurs
    $ErrorActionPreference = "Stop"

    #First check ConnectionBroker name
    $ConnectionBroker = MyRdsFunctions_FixConnectionBrokerName -ConnectionBroker $ConnectionBroker

    #Then check XMLFile location
    $XmlFile = MyRdsFunctions_FixXMLFileLocation -ConnectionBroker $ConnectionBroker -XmlFile $XmlFile -FunctionName AllCollectionsFrom

    #Write-Test to XMLFile
    try {
        "WriteTest" | Out-File -FilePath $XmlFile -Force
    }
    catch {
        Write-Host -ForegroundColor Red ("Error while testing XMLFile location. Please verify access to the location '{0}'" -f $XmlFile)
        return $null
    }

    if ($RemoveCollections) {
        Write-Host ("`r`n`r`n--------------") -ForegroundColor Red
        Write-Host ("WARNING!!") -ForegroundColor Red
        Write-Host ("You added the parameter '-RemoveCollections'!! ") -ForegroundColor Yellow
        Write-Host ("When confirming, all Collections will be removed from this Connection Broker after the export!!") -ForegroundColor Yellow
        Write-Host ("--------------") -ForegroundColor Red
        do {
            $confirmation = Read-Host -Prompt "Continue? (Y/N)"
        } while ($confirmation -ne "y" -and $confirmation -ne "n")
    
        if ($confirmation -eq "n") {
            Write-Host -ForegroundColor Yellow "Aborting..."
            return
        }
    }

    #Create an Array of collections
    $allCollections = @()

    #Get all Collection on this Connection Broker
    $allSessionCollections = Get-RDSessionCollection -ConnectionBroker $ConnectionBroker -ErrorAction SilentlyContinue

    if ($null -eq $allSessionCollections) {
        Write-Host -ForegroundColor Red ("No collections found on ConnectionBroker {0} ... Exiting." -f $ConnectionBroker)
        return $null
    }

    Write-Host -ForegroundColor Cyan ("Found {0} collections..." -f $allSessionCollections.Count)

    #Loop through all Collections and collect information
    foreach ($sessionCollection in $allSessionCollections) {
        Write-Host -ForegroundColor Cyan ("Processing collection '{0}' ..." -f $sessionCollection.CollectionName)
        #Create empty HashTable
        $properties = [ordered]@{ }

        $properties.Add("CollectionName", $sessionCollection.CollectionName)
    
        #First, some basic Collection Config Info & add it to the HastTable
        $properties.Add("GeneralInfo", $sessionCollection)

        #If CollectionType = PersonalUnmanaged, get extra info
        if (($null -ne $sessionCollection.CollectionType) -and ($sessionCollection.CollectionType -eq [Microsoft.RemoteDesktopServices.Management.RDSessionCollectionType]::PersonalUnmanaged)) {
            $personalSessionDesktopInfo = Get-RDPersonalSessionDesktopAssignment -ConnectionBroker $ConnectionBroker -CollectionName $sessionCollection.CollectionName
            $properties.Add("PersonalSessionDesktopInfo", $personalSessionDesktopInfo)
        }

        #Next, Client Info
        $SessionCollectionInfo = Get-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $sessionCollection.CollectionName -Client
        $properties.Add("ClientInfo", $SessionCollectionInfo)

        #Next, Loadbalancing Info (suppress errors, because Collections without SessionsHosts returns an error
        $SessionCollectionInfo = Get-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $sessionCollection.CollectionName -LoadBalancing -ErrorAction SilentlyContinue
        $properties.Add("LoadBalancingInfo", $SessionCollectionInfo)

        #Next, Security Info
        $SessionCollectionInfo = Get-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $sessionCollection.CollectionName -Security
        $properties.Add("SecurityInfo", $SessionCollectionInfo)

        #Next, UserProfileDisk Info
        $SessionCollectionInfo = Get-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $sessionCollection.CollectionName -UserProfileDisk
        $properties.Add("UserProfileDiskInfo", $SessionCollectionInfo)

        #Next, Connection Info
        $SessionCollectionInfo = Get-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $sessionCollection.CollectionName -Connection
        $properties.Add("ConnectionInfo", $SessionCollectionInfo)

        #Next, UserGroup Info
        $SessionCollectionInfo = Get-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $sessionCollection.CollectionName -UserGroup
        $properties.Add("UserGroupInfo", $SessionCollectionInfo)

        #Last info: Remote Apps in this collection
        $allRemoteApps = Get-RDRemoteApp -ConnectionBroker $ConnectionBroker -CollectionName $sessionCollection.CollectionName
        $properties.Add("RemoteApps", $allRemoteApps)
    
        #Create Custom object using the properties
        $object = New-Object -TypeName PSObject -Prop $properties
        $object.PSObject.TypeNames.Insert(0, "ASPEX&CloudArchitect.RDS.Collection.Export")

        #Finally, add all info to the Array of collections
        $allCollections += $object
    }

    #Export all Collection info to an XML so we can Import it later
    Write-Host -ForegroundColor Cyan ("Exporting all collections to XMLFile '{0}' ..." -f $XmlFile)
    $allCollections | Export-CliXml -Path $XmlFile

    if ($RemoveCollections) {
        #Loop through all Collections and Remove collections
        Write-Host -ForegroundColor Cyan ("Removing all collections ...")
        foreach ($sessionCollection in $allSessionCollections) {
            Write-Host -ForegroundColor Magenta ("Removing collection '{0}' ..." -f $sessionCollection.CollectionName)
            Remove-RDSessionCollection -ConnectionBroker $ConnectionBroker -CollectionName $sessionCollection.CollectionName -Force
        }
    }

    Write-Host -ForegroundColor Green ("All done")

    return $XmlFile
}

Function Import-RDCollectionsToConnectionBroker {
    <#
    .SYNOPSIS
 
    Imports all RDS Collections from an XML export file into a Connection Broker
 
    .DESCRIPTION
 
    Imports all RDS Collections from an XML export file into a Connection Broker
    XMLfile can be created using the Export-RDCollectionsFromConnectionBroker cmdlet.
 
    .PARAMETER ConnectionBroker
    Specifies FQDN of the Connection Broker.
 
    .PARAMETER XmlFile
    Specifies the XML File location containing the export information.
 
    .INPUTS
 
    None.
 
    .OUTPUTS
 
    None.
 
    .EXAMPLE
 
    C:\PS> Import-RDCollectionsToConnectionBroker -ConnectionBroker localhost -XmlFile "c:\temp\AllCollectionsFrom_MySessionBroker.mydomain.hosting.xml"
    Imports all Collections from "c:\temp\AllCollectionsFrom_MySessionBroker.mydomain.hosting.xml" into "MySessionBroker.mydomain.hosting"
 
    .EXAMPLE
 
    C:\PS> Import-RDCollectionsToConnectionBroker -ConnectionBroker "MySessionBroker.mydomain.hosting" -XmlFile "c:\myshare\mycollections.xml" -verbose
    Exports all Collections from "c:\myshare\mycollections.xml" into "MySessionBroker.mydomain.hosting", with full detail output
 
    .LINK
 
    https://blog.cloud-architect.be
    #>


    [CmdletBinding()]
    PARAM (
        [Parameter(Mandatory = $True)]
        [string]$ConnectionBroker,
        [Parameter(Mandatory = $True)]
        [string]$XmlFile
    )
    
    # Setting ErrorActionPreference to stop script execution when error occurs
    $ErrorActionPreference = "Stop"

    #First check ConnectionBroker name
    $ConnectionBroker = MyRdsFunctions_FixConnectionBrokerName -ConnectionBroker $ConnectionBroker

    #Then check XMLFile location
    if (!(Test-Path -Path $XmlFile)) {
        Write-Host -ForegroundColor Red ("The XML file was not found at '{0}'. Stopping function" -f $XmlFile)
        return
    }

    $allCollections = Import-Clixml -Path $XmlFile

    Write-Host -ForegroundColor Cyan ("Found {0} collections in XMLFile..." -f $allCollections.Count)

    foreach ($collection in $allCollections) {
        Write-Host -ForegroundColor Cyan ("Processing collection '{0}' ..." -f $collection.CollectionName)
        Write-Verbose ("CollectionType is '{0}'" -f $collection.GeneralInfo.CollectionType)
        if (($null -eq $sessionCollection.CollectionType) -or ($collection.GeneralInfo.CollectionType -eq [Microsoft.RemoteDesktopServices.Management.RDSessionCollectionType]::PooledUnmanaged)) {
            #Create collection
            Write-Verbose ("Creating PooledUnmanaged collection...")
            $collectionSessionHosts = $collection.LoadBalancingInfo | Select-Object -ExpandProperty SessionHost
            New-RDSessionCollection -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -SessionHost $collectionSessionHosts -CollectionDescription $collection.GeneralInfo.CollectionDescription

            #Set Loadbalancing
            Write-Verbose ("Setting Loadbalancing...")
            $LoadBalanceObjectsArray = New-Object System.Collections.Generic.List[Microsoft.RemoteDesktopServices.Management.RDSessionHostCollectionLoadBalancingInstance]
            foreach ($lbInfo in $collection.LoadBalancingInfo) {
                $LoadBalanceSessionHost = New-Object Microsoft.RemoteDesktopServices.Management.RDSessionHostCollectionLoadBalancingInstance( $lbInfo.CollectionName, $lbInfo.RelativeWeight, $lbInfo.SessionLimit, $lbInfo.SessionHost )
                $LoadBalanceObjectsArray.Add($LoadBalanceSessionHost)
            }
            Set-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -LoadBalancing $LoadBalanceObjectsArray
            

            #Set RemoteApps
            Write-Verbose ("Publishing RemoteApps...")
            Write-Verbose ("Found {0} RemoteApps in Export" -f $collection.RemoteApps.Count)

            foreach ($remoteApp in $collection.RemoteApps) {
                New-RDRemoteApp -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -Alias $remoteApp.Alias -DisplayName $remoteApp.DisplayName -FilePath $remoteApp.FilePath `
                    -FileVirtualPath $remoteApp.FileVirtualPath -ShowInWebAccess $remoteApp.ShowInWebAccess -FolderName $remoteApp.FolderName -CommandLineSetting $remoteApp.CommandLineSetting `
                    -RequiredCommandLine $remoteApp.RequiredCommandLine -UserGroups $remoteApp.UserGroups -IconPath $remoteApp.IconPath -IconIndex $remoteApp.IconIndex
            }
        }
        else {
            #Create collection
            Write-Verbose ("Creating PersonalUnmanaged collection...")
            $collectionSessionHosts = $collection.LoadBalancingInfo | Select-Object -ExpandProperty SessionHost

            if (($collection.GeneralInfo.GrantAdministrativePrivilege) -and ($collection.GeneralInfo.AutoAssignPersonalDesktop)) {
                New-RDSessionCollection -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -SessionHost $collectionSessionHosts -PersonalUnmanaged -GrantAdministrativePrivilege -AutoAssignUser
            }
            elseif ($collection.GeneralInfo.GrantAdministrativePrivilege) {
                New-RDSessionCollection -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -SessionHost $collectionSessionHosts -PersonalUnmanaged  -GrantAdministrativePrivilege
            }
            elseif ($collection.GeneralInfo.AutoAssignPersonalDesktop) {
                New-RDSessionCollection -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -SessionHost $collectionSessionHosts -PersonalUnmanaged -AutoAssignUser
            }
            else {
                New-RDSessionCollection -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -SessionHost $collectionSessionHosts -PersonalUnmanaged 
            }

            #Setting Desktop Assignment
            Write-Verbose ("Setting Desktop Assignment...")
            foreach ($assignment in $collection.PersonalSessionDesktopInfo) {
                Set-RDPersonalSessionDesktopAssignment -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -User $assignment.User -Name $assignment.DesktopName
            }
        }

        #Set UPD
        Write-Verbose ("Setting UPD...")
        if ($collection.UserProfileDiskInfo.EnableUserProfileDisk) {
            Set-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -EnableUserProfileDisk `
                -MaxUserProfileDiskSizeGB $collection.UserProfileDiskInfo.MaxUserProfileDiskSizeGB -DiskPath $collection.UserProfileDiskInfo.DiskPath `
                -IncludeFolderPath $collection.UserProfileDiskInfo.IncludeFolderPath -IncludeFilePath $collection.UserProfileDiskInfo.IncludeFilePath `
                -ExcludeFolderPath $collection.UserProfileDiskInfo.ExcludeFolderPath -ExcludeFilePath $collection.UserProfileDiskInfo.ExcludeFilePath
        }

        #Set ClientInfo
        Write-Verbose ("Setting ClientInfo...")
        Set-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -ClientDeviceRedirectionOptions $collection.ClientInfo.ClientDeviceRedirectionOptions `
            -MaxRedirectedMonitors $collection.ClientInfo.MaxRedirectedMonitors -ClientPrinterRedirected $collection.ClientInfo.ClientPrinterRedirected `
            -ClientPrinterAsDefault $collection.ClientInfo.ClientPrinterAsDefault -RDEasyPrintDriverEnabled $collection.ClientInfo.RDEasyPrintDriverEnabled

        #Set SecurityInfo
        Write-Verbose ("Setting SecurityInfo...")
        Set-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -AuthenticateUsingNLA $collection.SecurityInfo.AuthenticateUsingNLA `
            -EncryptionLevel $collection.SecurityInfo.EncryptionLevel -SecurityLayer $collection.SecurityInfo.SecurityLayer

        #Set ConnectionInfo
        Write-Verbose ("Setting ConnectionInfo...")
        Set-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -DisconnectedSessionLimitMin $collection.ConnectionInfo.DisconnectedSessionLimitMin `
            -BrokenConnectionAction $collection.ConnectionInfo.BrokenConnectionAction -TemporaryFoldersDeletedOnExit $collection.ConnectionInfo.TemporaryFoldersDeletedOnExit `
            -AutomaticReconnectionEnabled $collection.ConnectionInfo.AutomaticReconnectionEnabled -ActiveSessionLimitMin $collection.ConnectionInfo.ActiveSessionLimitMin `
            -IdleSessionLimitMin $collection.ConnectionInfo.IdleSessionLimitMin

        #Set UserGroupInfo
        Write-Verbose ("Setting UserGroupInfo...")
        Set-RDSessionCollectionConfiguration -ConnectionBroker $ConnectionBroker -CollectionName $collection.CollectionName -UserGroup $collection.UserGroupInfo.UserGroup

        Write-Host -ForegroundColor Cyan ("Done.")
        
    }

    Write-Host -ForegroundColor Green ("All done")
}

Function Import-RDDeploymentToConnectionBroker {
    <#
    .SYNOPSIS
 
    Imports RDS Deployment from an XML export file into a Connection Broker
 
    .DESCRIPTION
 
    Imports RDS Deployment from an XML export file into a Connection Broker
    XMLfile must be created using the Export-RDDeploymentFromConnectionBroker cmdlet.
 
    .PARAMETER ConnectionBroker
    Specifies FQDN of the Connection Broker.
 
    .PARAMETER XmlFile
    Specifies the XML File location containing the export information.
 
    .PARAMETER RDGatewayCertPath
    Specifies the Certificate File location for the RDGateway role.
 
    .PARAMETER RDGatewayCertPassword
    Specifies the password for the certificate for the RDGateway role.
 
    .PARAMETER RDWebAccessCertPath
    Specifies the Certificate File location for the RDWebAccess role.
 
    .PARAMETER RDWebAccessCertPassword
    Specifies the password for the certificate for the RDWebAccess role.
 
    .PARAMETER RDRedirectorCertPath
    Specifies the Certificate File location for the RDRedirector role (server authentication).
 
    .PARAMETER RDRedirectorCertPassword
    Specifies the password for the certificate for the RDRedirector role.
 
    .PARAMETER RDPublishingCertPath
    Specifies the Certificate File location for the RDPublishing role (Signing RDP Files).
 
    .PARAMETER RDPublishingCertPassword
    Specifies the password for the certificate for the RDPublishing role.
 
    .INPUTS
 
    None.
 
    .OUTPUTS
 
    None.
 
    .EXAMPLE
 
    C:\PS> $RDGatewayCertPath = "C:\Temp\*************.pfx"
    C:\PS> $RDWebAccessCertPath = "C:\Temp\*************.pfx"
    C:\PS> $RDRedirectorCertPath = "C:\Temp\*************.pfx"
    C:\PS> $RDPublishingCertPath = "C:\Temp\*************.pfx"
 
    C:\PS> $RDGatewayCertPassword = ConvertTo-SecureString -String "*************" -AsPlainText -Force
    C:\PS> $RDWebAccessCertPassword = ConvertTo-SecureString -String "*************" -AsPlainText -Force
    C:\PS> $RDRedirectorCertPassword = ConvertTo-SecureString -String "*************" -AsPlainText -Force
    C:\PS> $RDPublishingCertPassword = ConvertTo-SecureString -String "*************" -AsPlainText -Force
 
    C:\PS> Import-RDDeploymentToConnectionBroker -ConnectionBroker localhost -XmlFile "C:\Temp\AllRDServersFrom_MySessionBroker.mydomain.hosting.xml" -RDGatewayCertPath $RDGatewayCertPath -RDGatewayCertPassword $RDGatewayCertPassword -RDWebAccessCertPath $RDWebAccessCertPath -RDWebAccessCertPassword $RDWebAccessCertPassword -RDRedirectorCertPath $RDRedirectorCertPath -RDRedirectorCertPassword $RDRedirectorCertPassword -RDPublishingCertPath $RDPublishingCertPath -RDPublishingCertPassword $RDPublishingCertPassword -Verbose
    Imports Deployment from "c:\temp\AllCollectionsFrom_MySessionBroker.mydomain.hosting.xml" into "MySessionBroker.mydomain.hosting", using the supplied certificates
 
    .EXAMPLE
 
    C:\PS> $RDGatewayCertPath = "C:\Temp\*************.pfx"
    C:\PS> $RDWebAccessCertPath = "C:\Temp\*************.pfx"
    C:\PS> $RDRedirectorCertPath = "C:\Temp\*************.pfx"
    C:\PS> $RDPublishingCertPath = "C:\Temp\*************.pfx"
     
    C:\PS> $RDGatewayCertPassword = ConvertTo-SecureString -String "*************" -AsPlainText -Force
    C:\PS> $RDWebAccessCertPassword = ConvertTo-SecureString -String "*************" -AsPlainText -Force
    C:\PS> $RDRedirectorCertPassword = ConvertTo-SecureString -String "*************" -AsPlainText -Force
    C:\PS> $RDPublishingCertPassword = ConvertTo-SecureString -String "*************" -AsPlainText -Force
 
    C:\PS> Import-RDDeploymentToConnectionBroker -ConnectionBroker localhost -XmlFile "C:\Temp\AllRDServersFrom_MySessionBroker.mydomain.hosting.xml" -RDGatewayCertPath $RDGatewayCertPath -RDGatewayCertPassword $RDGatewayCertPassword -RDWebAccessCertPath $RDWebAccessCertPath -RDWebAccessCertPassword $RDWebAccessCertPassword -RDRedirectorCertPath $RDRedirectorCertPath -RDRedirectorCertPassword $RDRedirectorCertPassword -RDPublishingCertPath $RDPublishingCertPath -RDPublishingCertPassword $RDPublishingCertPassword -Verbose
    Imports Deployment from "c:\myshare\mycollections.xml" into "MySessionBroker.mydomain.hosting", using the supplied certificates, with full detail output
 
    .LINK
 
    https://blog.cloud-architect.be
    #>


    [CmdletBinding()]
    PARAM (
        [Parameter(Mandatory = $True)]
        [string]$ConnectionBroker,
        [Parameter(Mandatory = $True)]
        [string]$XmlFile,
        [Parameter(Mandatory = $True)]
        [string]$RDGatewayCertPath,
        [Parameter(Mandatory = $True)]
        [SecureString]$RDGatewayCertPassword,
        [Parameter(Mandatory = $True)]
        [string]$RDWebAccessCertPath,
        [Parameter(Mandatory = $True)]
        [SecureString]$RDWebAccessCertPassword,
        [Parameter(Mandatory = $True)]
        [string]$RDRedirectorCertPath,
        [Parameter(Mandatory = $True)]
        [SecureString]$RDRedirectorCertPassword,
        [Parameter(Mandatory = $True)]
        [string]$RDPublishingCertPath,
        [Parameter(Mandatory = $True)]
        [SecureString]$RDPublishingCertPassword
    )
    
    # Setting ErrorActionPreference to stop script execution when error occurs
    $ErrorActionPreference = "Stop"

    #First check ConnectionBroker name
    $ConnectionBroker = MyRdsFunctions_FixConnectionBrokerName -ConnectionBroker $ConnectionBroker

    #Then check XMLFile location
    if (!(Test-Path -Path $XmlFile)) {
        Write-Host -ForegroundColor Red ("The XML file was not found at '{0}'. Stopping function" -f $XmlFile)
        return
    }

    if ($null -ne (Get-RDSessionCollection -ConnectionBroker $ConnectionBroker -ErrorAction SilentlyContinue)) {
        Write-Host -ForegroundColor Red ("A deployment already exists! You should start with a new Connection Broker & License Server`r`nStopping Function")
        return
    }

    $deploymentInfo = Import-Clixml -Path $XmlFile

    Write-Host -ForegroundColor Cyan ("Found {0} RD Server Roles in XMLFile..." -f $deploymentInfo.ServerInfo.Count)

    #Check for pending reboot
    Write-Host -ForegroundColor Cyan ("Checking pending reboots on servers ...")
    $displayWarning = $false
    $pendingRebootResults = @()
    
    foreach ($server in $deploymentInfo.ServerInfo) {
        $properties = [ordered]@{ }

        $properties.Add("ServerName", $server.ServerName)

        $ConnectionAvailable = $true
        $IsRebootPending = $true

        $result = MyRdsFunctions_CheckReboot -serverName $server.ServerName
        if ($null -eq $result) {
            $ConnectionAvailable = $false
            $displayWarning = $true
        }
        else {
            if ($result.IsRebootPending -eq $false) {
                $IsRebootPending = $false
            }
            else {
                $displayWarning = $true
            }
        }
        
        $properties.Add("ConnectionAvailable", $ConnectionAvailable)
        $properties.Add("IsRebootPending", $IsRebootPending)

        #Create Custom object using the properties
        $object = New-Object -TypeName PSObject -Prop $properties
        $object.PSObject.TypeNames.Insert(0, "ASPEX&CloudArchitect.RDS.Deployment.RebootPending")

        $pendingRebootResults += $object

        #$pendingRebootResults += ("{0}`t`t{1}`t`t{2}" -f $server.ServerName, $ConnectionAvailable, $IsRebootPending)
    }

    #Testing broker server
    $IsRebootPending = $true
    $result = MyRdsFunctions_CheckReboot -serverName $ConnectionBroker
    if ($result.IsRebootPending -eq $false) {
        $IsRebootPending = $false
    }
    else {
        $displayWarning = $true
    }

    #Create Custom object using the properties
    $properties = [ordered]@{ }
    $properties.Add("ServerName", $ConnectionBroker)
    $properties.Add("ConnectionAvailable", $true)
    $properties.Add("IsRebootPending", $IsRebootPending)
    $object = New-Object -TypeName PSObject -Prop $properties
    $object.PSObject.TypeNames.Insert(0, "ASPEX&CloudArchitect.RDS.Deployment.RebootPending")

    $pendingRebootResults += $object
    #$pendingRebootResults += ("{0}`t`t{1}`t`t{2}" -f $ConnectionBroker, $true, $IsRebootPending)

    Write-Host -ForegroundColor Yellow "RebootPending overview"
    Write-Host -ForegroundColor Yellow ($pendingRebootResults | Format-Table | Out-String)

    if ($displayWarning) {
        Write-Host ("`r`n`r`n--------------") -ForegroundColor Red
        Write-Host ("WARNING!!") -ForegroundColor Red
        Write-Host ("Some servers have a reboot pending (or unavailable to query for Reboot Pendings)") -ForegroundColor Yellow
        Write-Host ("If you continue, the import might fail!") -ForegroundColor Yellow
        Write-Host ("Do you want to reboot servers (Y/N)? Or Continue without reboot (C): not recommended [press Y/N/C]") -ForegroundColor Yellow
        Write-Host ("--------------") -ForegroundColor Red
        do {
            $confirmation = Read-Host -Prompt "Reboot servers (Y/N)? Or Continue without reboot (C): not recommended [press Y/N/C]"
        } while ($confirmation -ne "y" -and $confirmation -ne "n" -and $confirmation -ne "c")
        
        if ($confirmation -eq "c") {
            Write-Host -ForegroundColor Yellow "Continuing without reboots..."
        }
    
        if ($confirmation -eq "n") {
            Write-Host -ForegroundColor Yellow "Aborting..."
            return
        }

        if ($confirmation -eq "y") {
            foreach ($server in ($pendingRebootResults | Where-Object { $_.IsRebootPending -eq $true })) {
                MyRdsFunctions_RebootServer -serverName $server.ServerName
            }
    
            Write-Host -ForegroundColor Yellow "Wait until the server(s) is (are) rebooted and restart the import."
            return
        }
    }

    #start with new RD Deployment
    Write-Verbose ("Starting with new RD Deployment")
    $deploymentSessionHosts = $deploymentInfo.ServerInfo | Where-Object { $_.ServerRole -eq "RDS-RD-SERVER" } | Select-Object -ExpandProperty ServerName
    New-RDSessionDeployment -ConnectionBroker $ConnectionBroker -SessionHost $deploymentSessionHosts


    #Add Gateway servers to deployment
    Write-Verbose ("Add Gateway servers")
    $DeploymentGatewayServers = $deploymentInfo.ServerInfo | Where-Object { $_.ServerRole -eq "RDS-GATEWAY" } | Select-Object -ExpandProperty ServerName
    foreach ($gateway in $DeploymentGatewayServers) {
        Add-RDServer -ConnectionBroker $ConnectionBroker -Server $gateway -Role "RDS-GATEWAY" -GatewayExternalFqdn $deploymentInfo.GatewayInfo.GatewayExternalFqdn
    }
    #Set Gateway settings
    if (($null -ne $deploymentInfo.GatewayInfo) -and ($null -ne $deploymentInfo.GatewayInfo.LogonMethod)) {
        Write-Verbose ("Set Gateway settings")
        Set-RDDeploymentGatewayConfiguration -ConnectionBroker $ConnectionBroker -GatewayMode $deploymentInfo.GatewayInfo.GatewayMode `
            -LogonMethod $deploymentInfo.GatewayInfo.LogonMethod -UseCachedCredentials $deploymentInfo.GatewayInfo.UseCachedCredentials `
            -BypassLocal $deploymentInfo.GatewayInfo.BypassLocal -GatewayExternalFqdn $deploymentInfo.GatewayInfo.GatewayExternalFqdn -Force
    }
    
    
    #Add Web Access servers to deployment
    Write-Verbose ("Add Web Access servers")
    $DeploymentWebAccessServers = $deploymentInfo.ServerInfo | Where-Object { $_.ServerRole -eq "RDS-WEB-ACCESS" } | Select-Object -ExpandProperty ServerName
    foreach ($waServer in $DeploymentWebAccessServers) {
        Add-RDServer -ConnectionBroker $ConnectionBroker -Server $waServer -Role "RDS-WEB-ACCESS"
    }

    #Set Certificate info
    Write-Verbose ("Set Certificate info")
    if (($null -ne $deploymentInfo.GatewayInfo) -and ($null -ne $deploymentInfo.GatewayInfo.LogonMethod)) {
        Set-RDCertificate -ConnectionBroker $ConnectionBroker -Role RDGateway -Password $RDGatewayCertPassword -ImportPath $RDGatewayCertPath -Force
    }
    Set-RDCertificate -ConnectionBroker $ConnectionBroker -Role RDWebAccess -Password $RDWebAccessCertPassword -ImportPath $RDWebAccessCertPath -Force
    Set-RDCertificate -ConnectionBroker $ConnectionBroker -Role RDPublishing -Password $RDPublishingCertPassword -ImportPath $RDPublishingCertPath -Force
    Set-RDCertificate -ConnectionBroker $ConnectionBroker -Role RDRedirector -Password $RDRedirectorCertPassword -ImportPath $RDRedirectorCertPath -Force

    Write-Host -ForegroundColor Green ("All done")
}

# Setting ErrorActionPreference to stop script execution when error occurs
$ErrorActionPreference = "Stop"

Write-Host -ForegroundColor Yellow ("`r`n`r`nExportImportRdsDeployment Module 2.2 loaded")
Write-Host -ForegroundColor Cyan ("Created by Micha Wets, visit www.cloud-architect.be for more info`r`n")

Write-Host -ForegroundColor Green ("All set...`r`n`r`n")