rds-update-rdsh-collection/Scripts/Script.ps1 (223 lines of code) (raw):

[cmdletbinding()] param( [parameter(mandatory = $true)] [string]$domain, [parameter(mandatory = $true)] [string]$username, [parameter(mandatory = $true)] [string]$password, [parameter(mandatory = $true)] [string]$collection, [parameter(mandatory = $true)] [string]$iteration, [parameter(mandatory = $true)] [int]$nServers, [parameter(mandatory = $true)] [int]$nTimeoutMinutes, [string]$sessionHostNamingPrefix= "rdsh-", [int]$vmNameStartIndex=1, [Parameter(ValueFromRemainingArguments = $true)] $extraParameters ) $title = "System Maintenance" $message = "Please save your work. You will be logged off in $nTimeoutInMinutes minute(s)." function log { param([string]$message) "`n`n$(get-date -f o) $message" } function add-server { param( [parameter(mandatory=$true)] [string]$server ) $cs = gwmi win32_computersystem; $broker = "$($cs.dnshostname).$($cs.domain)" log "adding server $server to the deployment..." add-rdserver $server -role rds-rd-server -ev e if ($e -like '*deployment*not present*') { log "trying to create rds deployment..." new-rdsessiondeployment -connectionbroker $broker -sessionhost $_ -ev e if ($e -like "*$server*has reboots pending*") { log "attempting to reboot $server..." restart-computer $server -force -wait log "attempting to create deployment with $server one more time..." new-rdsessiondeployment -connectionbroker $broker -sessionhost $_ -ea stop } elseif ($e) { throw } log "create deployment - success." } elseif ($e -like "*$server*has reboots pending*") { log "attempting to reboot $server..." restart-computer $server -force -wait log "attempting to add $server to deployment again after reboot..." add-rdserver $server -role rds-rd-server -ea stop } elseif ($e) { throw } log "successfully added '$server' to the deployment." } log "script running..." whoami # $PSBoundParameters if ($extraParameters) { log "any extra parameters:" $extraParameters } # impersonate as admin # from .\New-ImpersonateUser.ps1 in gallery https://gallery.technet.microsoft.com/scriptcenter/Impersonate-a-User-9bfeff82 # $ImpersonatedUser = @{} log "impersonating as '$username'..." Add-Type -Namespace Import -Name Win32 -MemberDefinition @' [DllImport("advapi32.dll", SetLastError = true)] public static extern bool LogonUser(string user, string domain, string password, int logonType, int logonProvider, out IntPtr token); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr handle); '@ $tokenHandle = 0 $returnValue = [Import.Win32]::LogonUser($userName, $domain, $password, 2, 0, [ref]$tokenHandle) if (!$returnValue) { $errCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error(); log "failed a call to LogonUser with error code: $errCode" throw [System.ComponentModel.Win32Exception]$errCode } else { $ImpersonatedUser.ImpersonationContext = [System.Security.Principal.WindowsIdentity]::Impersonate($tokenHandle) [void][Import.Win32]::CloseHandle($tokenHandle) log "impersonating user $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name) returnValue: '$returnValue'" } whoami try { ipmo remotedesktop -DisableNameChecking # 4>$null # 1. add new servers to the deployment # log "current list of servers in the rds deployment:" $existingServers = (get-rdserver).Server $existingServers | % { " $($_.tolower())" } $count = $vmNameStartIndex $loopcount = 0 $newServers = new-object Collections.ArrayList while($loopcount -lt $nServers) { $newServerName = ("$($sessionHostNamingPrefix)$($iteration)$($count.ToString("D2")).$($domain)").ToLower() if ($existingServers -and ($existingServers -ieq $newServerName)) { log "server $($newServerName) already exists, skipping..." } else { log "adding server $($newServerName) to rds deployment" add-server $newServerName $newServers.Add($newServerName) $loopcount++ } $count++ } # 2. add new servers to the rdsh collection # log "current list of rdsh servers in collection '$($collection)':" $existingServers = (get-rdsessionhost -CollectionName $collection).SessionHost if ($existingServers) { $existingServers | % { " $($_.tolower())" } } else { " --- no servers in the collection yet ----" } $serversToAdd = $newServers | ? { -not ($_ -in $existingServers) } if ($serversToAdd) { log "adding new servers $($serversToAdd -join '; ') to session host collection '$collection'..." add-rdsessionhost -collectionname $collection -sessionhost $serversToAdd -ea stop } # 3. put old servers in drain mode # $serversToRemove = $existingServers | ? { -not ($_ -in $newServers) } if ($serversToRemove) { $serversToRemove | % ` { log "putting server $_ in drain mode..." set-rdsessionhost -sessionhost $_ -newconnectionallowed No } } # 4. notify users they are going to be logged off in next <n> minutes # log "querying for user sessions in collection '$collection'..." $sessions = get-rdusersession -CollectionName $collection log "found total $($sessions.count) user sessions," $sessionsToLogoff = $sessions | ? { -not( $_.HostServer -in $newServers ) } log "out of those $($sessionsToLogoff.count) sessions on the servers that are to be removed..." if ($sessionsToLogoff) { $sessionsToLogoff| % ` { log "sending message to user $($_.UserName) at host $($_.HostServer)..." send-rdusermessage -hostserver $_.HostServer -unifiedsessionid $_.UnifiedSessionId -messagetitle $title -messagebody $message } } # 5. log users off # if ($sessionsToLogoff) { log "waiting $nTimeoutMinutes munites before logging users off..." start-sleep -s ($nTimeoutMinutes * 60) log "querying for user sessions again..." $sessions = get-rdusersession -CollectionName $collection log "found total $($sessions.count) user sessions at this time," $sessionsToLogoff = $sessions | ? { -not( $_.HostServer -in $newServers ) } log "out of those, $($sessionsToLogoff.count) sessions to be logged off..." if ($sessionsToLogoff) { $sessionsToLogoff | % ` { log "logging off user $($_.UserName) from host $($_.HostServer)..." invoke-rduserlogoff -hostserver $_.HostServer -unifiedsessionid $_.SessionId -force } } } # 6. remove old servers from deployment # if ($serversToRemove) { log "removing servers $($serversToRemove -join '; ') from session host collection '$collection'..." remove-rdsessionhost -sessionhost $serversToRemove -force $serversToRemove | % ` { log "removing server $_ from the deployment..." remove-rdserver $_ -role rds-rd-server -force } log "shutting down servers $($serversToRemove -join '; ')..." $creds = new-object System.Management.Automation.PSCredential ("$domain\$username", (convertto-securestring $password -asplaintext -force)) stop-computer -computer $serversToRemove -credential $creds -force } else { log "nothing to do." } } catch { log "ERROR: caught exception" throw } finally { log "remove impersonation..." $ImpersonatedUser.ImpersonationContext.Undo() } log "done. success."