Script Center > Repository > Remote Desktop Services > Configure Guest OS for Microsoft VDI (Windows PowerShell Script)
TechNet Script Center logo

Welcome to the TechNet Script Center Repository!

Each contribution is licensed to you under a License Agreement by its owner, not Microsoft. Microsoft does not guarantee the contribution or purport to grant rights to it.

Configure Guest OS for Microsoft VDI (Windows PowerShell Script)

(Microsoft)
VERIFIED AND TESTED BY THE SCRIPT CENTER TEAM
Rate it:
 
 
 
 
 
Script Code
Windows PowerShell
<# 
.Synopsis 
    Prepares a virtual machine for use with Remote Desktop Virtualization.
    
.Description 
    This script configures the operating system on the virtual machine to work with Remote Desktop Virtualization. It performs following activities:
    
        1. It enables Remote Desktop.
        2. It enables Remote Procedure Call (RPC).
        3. It adds users to the Remote Desktop Users group.
        4. Adds the Remote Desktop Virtualization Host server to the permissions list for the RDP-Tcp listener.
        5. Enable Windows Firewall to allow an exception for Remote Desktop Services.
        6. Enable Windows Firewall to allow an exception for Remote Service Management
        7. Restarts the Remote Desktop Services service or reboots the virtual machine.
    
    Note : Elevated permissions are required to execute this script.
           Cross-domain scenarios are supported by this script.
    
.Parameter RDVHost
    One or more RD Virtualization Host servers that will host the virtual machine. Should follow domain\machine format.
    
.Parameter RDUsers
    Names of the users or user groups to add to the Remote Desktop Users group. Should follow domain\groupname format.
    
.Parameter DoNotRestartService
    Optional parameter not to restart the Remote Desktop Services service.
    
.Parameter LogFile
    Optional parameter to specify a file to log events. Default logfile location is '.\Configure-VirtualMachine.log'.
    
.Example 
    PS C:\> Configure-VirtualMachine.ps1 -RDVHost testdomain\Host1, testdomain\Host2
    
    This example configures the virtual machine to run on the RD Virtualization Host servers Host1 and Host2 and restarts the Remote Desktop Services service.
    
.Example 
    PS C:\> Configure-VirtualMachine.ps1 -RDVHost testdomain\Host1, testdomain\Host2 -LogFile C:\RDVsetup.log
    
    This example configures the virtual machine to run on the RD Virtualization Host servers Host1 and Host2 and restarts the Remote Desktop Services service. Events are logged in the file 'C:\tsvsetup.log'.
    
.Example 
    PS C:\> Configure-VirtualMachine.ps1 -RDVHost testdomain\Host1, testdomain\Host2 -RDUsers testdomain\usergroup1, testdomain\usergroup2
    
    This example configures the virtual machine to run on the RD Virtualization Host servers Host1 and Host2 and adds usergroup1 and usergroup2 to the Remote Desktop Users group and restarts the Remote Desktop Services service.
    
.Example 
    PS C:\> Configure-VirtualMachine.ps1 -RDVHost testdomain\Host1 -RDUsers testdomain\usergroup1, testdomain\usergroup2 -DoNotRestartService
    
    This example configures the virtual machine to run on the RD Virtualization Host server Host1 and adds usergroup1 and usergroup2 to the Remote Desktop Users group. It does not restart the Remote Desktop Services service.

.Example 
    PS C:\> Configure-VirtualMachine.ps1 -RDVHost testdomain\Host1 -RDUsers testdomain\usergroup1, testdomain\usergroup2 -DoNotRestartService -Force
    
    This example configures the virtual machine to run on the RD Virtualization Host server Host1 and adds usergroup1 and usergroup2 to the Remote Desktop Users group. It does not restart the Remote Desktop Services service and does not validate the users and RD virtualization host server.

.Notes 
    Name     : Configure-VirtualMachine.ps1 
    
#>

param (
    [ValidatePattern("(.+)\\(.+)")]
    [Parameter(Mandatory=$TRUE, Position=0, HelpMessage="RD Virtualization Host server")]
    [string[]]
    $RDVHost,

    [ValidatePattern("(.+)\\(.+)")]
    [Parameter(Mandatory=$FALSE, Position=1, HelpMessage="Remote Desktop Users")]
    [string[]]
    $RDUsers,

    [Parameter(Mandatory=$FALSE, Position=2, HelpMessage="File to which events are to be logged")]
    [string]
    $LogFile = ".\Configure-VirtualMachine.log",
    
    [Parameter(Mandatory=$FALSE, HelpMessage="Option not to restart the Remote Desktop Services service")]
    [switch]
    $DoNotRestartService,

    [Parameter(Mandatory=$FALSE, HelpMessage="Option to ignore verification of object when credentials are not valid")]
    [switch]
    $Force
)

function Write-Log ([string]$Message = "", [string]$Type = "verbose")
{
    switch ($Type)
    {
        "error"       {Write-Error $Message}
        "warning"     {Write-Warning $Message}
        "verbose"     {Write-Verbose $Message}
        "host"        {Write-Host $Message}
        "initialize"  { "" > $LogFile; return }
    }
    
    $Message >> $LogFile
}

#   Check if the script is running with administrator/elevated privileges
function Check-Credentials
{
    $principal = New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())
    $elevated = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)  

    if (-not $elevated)
    {
        Write-Error "Please run this script in an elevated shell!"
        exit 1
    }
}

function Grant-RDPPermissions([String]$RDVHost, [bool]$isXP = $FALSE)
{
    $adsiPath = "WinNT://{0}/{1}" -f ($RDVHost -split "\\")
    if ((([ADSI]$adsiPath).Class -ne "Group") -AND ($vhost -notmatch '\$$'))
    {
        $RDVHost = "$RDVHost$"
    }

    $nameSpace = if ($isXP) {"root\cimv2"} else {"root\cimv2\terminalservices"}

    $tsAccounts = @(Get-WMIObject -Namespace $nameSpace -Query "SELECT * FROM Win32_TSAccount WHERE (TerminalName = 'RDP-TCP' OR TerminalName = 'Console') AND AccountName = '$($RDVHost.replace("\", "\\"))'")

    if ($tsAccounts -eq $NULL -or $tsaccounts.count -eq 0)
    {
        Write-Log "  $RDVHost is being added to the RDP-TCP permissions list" "verbose"
        
        $permissionSettings = @(Get-WmiObject -Namespace $nameSpace -Query "SELECT * FROM Win32_TSPermissionsSetting WHERE TerminalName = 'RDP-TCP'")
        
        foreach($setting in $permissionSettings)
        {
            $setting.addaccount("$RDVHost", 1) | Out-Null
            ${script:restartRequired} = $TRUE
        }
    }

    $tsAccounts = @(Get-WMIObject -Namespace $nameSpace -Query "SELECT * FROM Win32_TSAccount WHERE (TerminalName = 'RDP-TCP' OR TerminalName = 'Console') AND AccountName = '$($RDVHost.replace("\", "\\"))'")
    
    foreach($account in $tsAccounts)
    {
        if (($account.PermissionsAllowed -band 517) -ne 517)
        {
            Write-Log "  Granting permissions : $RDVHost" "verbose"
            
            $account.ModifyPermissions(0,1) | Out-Null
            $account.ModifyPermissions(2,1) | Out-Null            
            $account.ModifyPermissions(9,1) | Out-Null
            
            ${script:restartRequired} = $TRUE
        }
    }
}


function Configure-XP()
{
    #   1. Enable Remote Desktop.

    Write-Log "`n  Enabling Remote Desktop..." "verbose"

    $result = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -ErrorAction SilentlyContinue

    if ($result -eq $NULL)
    {
        $result = New-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -PropertyType DWORD -Value 0
    }
    elseif ($result.AllowRemoteRPC -ne 1)
    {
        $result = Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -Value 0 -Passthru
        ${script:restartRequired} = $TRUE
    }

    if ($result.fDenyTSConnections -eq 0)
    {
        Write-Log "    Done" "verbose"
    }
    else
    {
        Write-Log "Remote Desktop could not be enabled" "error"
        $anyFailures = $TRUE
    }



    #   2. Enable RPC.

    Write-Log "`n  Enabling RPC..." "verbose"
    
    $result = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name AllowRemoteRpc -ErrorAction SilentlyContinue

    if ($result -eq $NULL)
    {
        $result = New-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name AllowRemoteRPC -PropertyType DWORD -Value 1
    }
    elseif ($result.AllowRemoteRPC -ne 1)
    {
        $result = Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name AllowRemoteRpc -Value 1 -Passthru
        ${script:restartRequired} = $TRUE
    }
    
    if ($result.AllowRemoteRpc -eq 1)
    {
        Write-Log "    Done" "verbose"
    }
    else
    {
        Write-Log "Remote Desktop could not be enabled" "error"
        $anyFailures = $TRUE
    }


    #   3. Add users to the Remote Desktop Users group.

    if (($RDUsers -ne $NULL) -AND ($RDUsers.Count -gt 0))
    {
        Write-Log "`n  Adding users to Remote Desktop Users group..." "verbose"

        $RDUsers | foreach { 
            net localgroup 'Remote Desktop Users' $_ /add 2> $Null > $Null
            if ($LASTEXITCODE -eq 1)
            {
                Write-Log "      Failed to add user $_ to 'Remote Desktop Users' group..." "error"
            }
        }
    
        Write-Log "    Done" "verbose"
    }



    #   4. Grant RD Virtualization Host servers permissions in the RDP-TCP listener.

    Write-Log "`n  Granting RD Virtualization Host server RDP-TCP permissions..." "verbose"

    $RDVHost | %{Grant-RDPPermissions $_}
    
    Write-Log "    Done" "verbose"



    #   5. Enable Windows Firewall to allow an exception for Remote Desktop.

    Write-Log "`n  Enabling firewall for 'remote desktop'..." "verbose"

    netsh firewall set service type=REMOTEDESKTOP mode=ENABLE profile=ALL 2> $Null > $Null
    if ($LASTEXITCODE -eq 0)
    {
        Write-Log "    Done" "verbose"
    }
    else
    {
        Write-Log "Firewall could not be enabled for 'remote desktop'" "error"
        $anyFailures = $TRUE
    }



    #   6. Enable Windows Firewall to allow an exception for Remote Service Management.

    Write-Log "  Enabling firewall for 'remote service management'..." "verbose"

    netsh firewall set service remoteadmin enable subnet 2> $Null > $Null
    if ($LASTEXITCODE -eq 0)
    {
        Write-Log "  Done" "verbose"
    }
    else
    {
        Write-Log "Firewall could not be enabled for 'remote service management'" "error"
        $anyFailures = $TRUE
    }



    #   7. Restart the Remote Desktop Services service.

    if ((-not $DoNotRestartService) -AND (${script:restartRequired}))
    {
        Write-Log "Rebooting machine..." "verbose"

        shutdown /r /t 10
    }
}


function Configure-PostXP()
{
    #   1. Enable Remote Desktop.

    Write-Log "`n  Enabling Remote Desktop..." "verbose"

    $result = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -ErrorAction SilentlyContinue

    if ($result -eq $NULL)
    {
        $result = New-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -PropertyType DWORD -Value 0
    }
    elseif ($result.AllowRemoteRPC -ne 1)
    {
        $result = Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -Value 0 -Passthru
        ${script:restartRequired} = $TRUE
    }

    if ($result.fDenyTSConnections -eq 0)
    {
        Write-Log "    Done" "verbose"
    }
    else
    {
        Write-Log "Remote Desktop could not be enabled" "error"
        $anyFailures = $TRUE
    }



    #   2. Enable RPC.

    Write-Log "`n  Enabling RPC..." "verbose"

    $result = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name AllowRemoteRpc -ErrorAction SilentlyContinue

    if ($result -eq $NULL)
    {
        $result = New-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name AllowRemoteRPC -PropertyType DWORD -Value 1
    }
    elseif ($result.AllowRemoteRPC -ne 1)
    {
        $result = Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name AllowRemoteRpc -Value 1 -Passthru
        ${script:restartRequired} = $TRUE
    }
    
    if ($result.AllowRemoteRpc -eq 1)
    {
        Write-Log "    Done" "verbose"
    }
    else
    {
        Write-Log "Remote Desktop could not be enabled" "error"
        $anyFailures = $TRUE
    }


    #   3. Add Users to the Remote Desktop Users group.

    if (($RDUsers -ne $NULL) -AND ($RDUsers.Count -gt 0))
    {
        Write-Log "`n  Adding users to the Remote Desktop Users group..." "verbose"

        $RDUsers | foreach { 
            net localgroup 'Remote Desktop Users' $_ /add 2> $Null > $Null
            if ($LASTEXITCODE -eq 1)
            {
                Write-Log "      Failed to add user $_ to the Remote Desktop Users group..." "error"
            }
        }
    
        Write-Log "    Done" "verbose"
    }



    #   4. Grant RD Virtualization Host servers permissions in the RDP-TCP listener.

    Write-Log "`n  Granting RD Virtualization hosts RDP-TCP listener permissions..." "verbose"

    $RDVHost | %{Grant-RDPPermissions $_}

    Write-Log "    Done" "verbose"



    #   5. Enable Windows Firewall to allow an exception for Remote Desktop.

    Write-Log "`n  Enabling firewall for 'remote desktop'..." "verbose"

    netsh advfirewall firewall set rule group="remote desktop" new enable=yes 2> $Null > $Null
    if ($LASTEXITCODE -eq 0)
    {
        Write-Log "    Done" "verbose"
    }
    else
    {
        Write-Log "Firewall could not be enabled for 'remote desktop'" "error"
        $anyFailures = $TRUE
    }



    #   6. Enable Windows Firewall to allow an exception for Remote Service Management.

    Write-Log "  Enabling firewall for 'remote service management'..." "verbose"

    netsh advfirewall firewall set rule group="remote service management" new enable=yes 2> $Null > $Null
    if ($LASTEXITCODE -eq 0)
    {
        Write-Log "  Done" "verbose"
    }
    else
    {
        Write-Log "Firewall could not be enabled for 'remote service management'" "error"
        $anyFailures = $TRUE
    }



    #   7. Restart the Remote Desktop Services service.

    if ((-not $DoNotRestartService) -AND (${script:restartRequired}))
    {
        Write-Log "Restarting the Remote Desktop Services service..." "verbose"

        $termService = Get-Service "TermService"

        Write-Log "  Stopping TermService..." "verbose"
        $termService.Stop()
        Start-Sleep 5

        Write-Log "  Starting TermService..." "verbose"
        $termService.Start()
        Start-Sleep 5
    }
}

function Test-ADObject ([string[]]$adEntries, [string[]]$Type)
{
    $toExit = $FALSE
    foreach($entry in $adEntries)
    {
        $adsiPath = "WinNT://{0}/{1}" -f ($entry -split "\\")
        try 
        {
            [ADSI]$adsiPath | Select-Object Name | Out-Null
        }
        catch
        {
            $throwError = !${script:Force}
            
            if ($Type -contains "Computer")
            {
                $adsiPath = "WinNT://{0}/{1}$" -f ($entry -split "\\")                
                try
                {
                    [ADSI]$adsiPath | Select-Object Name | Out-Null
                    $throwError = $FALSE
                }
                catch { }
            }
            
            if ($throwError)
            {
                $toExit = $TRUE
                Write-Log "Existence of specified object '$entry' could not be verified. $($_.Exception.InnerException.Message.Trim())" "error"                
            }
            continue
        }
        
        $adsiObj = [ADSI]$adsiPath
        
        if ($adsiObj.Name -eq $NULL)
        {
            $toExit = $TRUE
            Write-Log "Specified object '$entry' does not exist" "error"
        }
        elseif($Type -notcontains $adsiObj.Class)
        {
            $toExit = $TRUE
            Write-Log "Specified object '$entry' is invalid. Specify an object of type $([string]::Join(" or ", $Type))" "error"
        }
    }
    
    if ($toExit)
    {
        exit 1
    }
}

Write-Log "" "initialize"
Check-Credentials

Write-Log "Preparing virtual machine for the RD Virtualization Host server: " "verbose"

Write-Log "  RDV Host  : $([string]::Join(", ", $RDVHost))" "verbose"
Test-ADObject $RDVHost @("Computer", "Group")

if ($RDUsers.Count -gt 0)
{
    Write-Log "  RD Users  : $([string]::Join(", ", $RDUsers))" "verbose"
    Test-ADObject $RDUsers @("User", "Group")
}

$restartRequired = $FALSE
$anyFailures = $FALSE

$version = New-Object System.Version 6, 0, 0, 0
if ([System.Environment]::OsVersion.Version -lt $version)
{
    Configure-XP
}
else
{
    Configure-PostXP
}

if ($anyFailures)
{
    Write-Log "ERROR : There were one more errors preparing the virtual machine for the RD Virtualization Host server" "error"
}
else
{
    Write-Log "Virtual machine was prepared for use with the RD Virtualization Host server" "verbose"
    Write-Host "This virtual machine has been successfully configured for use with RD Virtualization."
}
Platforms
Windows Server 2008 R2 No
Windows Server 2008 No
Windows Server 2003 No
Windows 7 Yes
Windows Vista Yes
Windows XP Yes
Windows 2000 No
For online peer support, join The Official Scripting Guys Forum! To provide feedback or report bugs in sample scripts, please start a new discussion on the Discussions tab for this script.
Disclaimer The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.
  • Script logic error
    1 Posts | Last Post: February 01, 2010
    • When running this script on a Windows XP machine, there is a line of code that calls the Grant-RDPPermissions function within the Configure-XP function is missing the parameter which tells the function that it is XP and should be changed from this:
      $RDVHost | %{Grant-RDPPermissions $_}
      To this:
      $RDVHost | %{Grant-RDPPermissions $_ $TRUE}

      If left unchanged, the script uses the wrong WMI namespace for Windows XP.