Skip to content

Commit

Permalink
Merge pull request #212 from lorengordon/rdcb-cleanup
Browse files Browse the repository at this point in the history
Moves cleanup of stale session hosts to scheduled task on RDCB node
  • Loading branch information
lorengordon authored Aug 31, 2019
2 parents 359883f + dfffbec commit e99b637
Show file tree
Hide file tree
Showing 36 changed files with 555 additions and 195 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.3.6
current_version = 0.4.0
commit = True
message = Bumps version to {new_version}
tag = False
Expand Down
4 changes: 3 additions & 1 deletion psmodules/P3RemoteAccess/P3RemoteAccess.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ Copyright = '(c) 2018 Maintainers of plus3it/cfn. All rights reserved.'
# ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
# RequiredModules = @()
RequiredModules = @(
"RemoteDesktop"
)

# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()
Expand Down
27 changes: 26 additions & 1 deletion psmodules/P3RemoteAccess/P3RemoteAccess.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ function global:Update-RDMS {
Process{
Write-Debug "Starting Update-RDMS"
Write-Debug "Import RDS cmdlets"
Import-Module RemoteDesktop
$ConnectionBrokers = Get-RDServer | Where-Object {$_.Roles -contains "RDS-CONNECTION-BROKER"}
$ServerManagerXML = "$env:USERPROFILE\AppData\Roaming\Microsoft\Windows\ServerManager\Serverlist.xml"
Write-Debug "Find active Connection Broker"
Expand Down Expand Up @@ -59,3 +58,29 @@ function global:Update-RDMS {
}
End{}
}

function global:Clear-RDSessionHost {
Param(
[Parameter(Mandatory=$true, ValueFromPipeLine=$true)]
[Microsoft.RemoteDesktopServices.Management.RDServer[]]
$SessionHost,

[Parameter(Mandatory=$false)]
[string]
$ConnectionBroker = [System.Net.DNS]::GetHostByName('').HostName
)
BEGIN {
$Role = "RDS-RD-SERVER"
}
PROCESS {
ForEach ($instance in $SessionHost)
{
Write-Verbose "Removing RD Session Host, $($instance.SessionHost), from the collection, ${CollectionName}..."
Remove-RDSessionHost -SessionHost $instance.SessionHost -ConnectionBroker $ConnectionBroker -Force

Write-Verbose "Removing ${Role} role from $($instance.SessionHost)..."
Remove-RDServer -Role $Role -Server $instance.SessionHost -ConnectionBroker $ConnectionBroker -Force
}
}
END {}
}
272 changes: 272 additions & 0 deletions psmodules/P3Utils/P3Utils.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,275 @@ function global:Invoke-RetryCommand {
}
End {}
}

function global:Test-RetryNetConnection {
Param(
[Parameter(Mandatory=$true, ValueFromPipeLine=$true)]
[string[]]
$ComputerName,

[Parameter(Mandatory=$false)]
[string]
$CheckExpression = '$? -and $Return.Result.TcpTestSucceeded',

[Parameter(Mandatory=$false)]
[int]
$Tries = 5,

[Parameter(Mandatory=$false)]
[int]
$InitialDelay = 2, # in seconds

[Parameter(Mandatory=$false)]
[int]
$MaxDelay = 32 # in seconds
)
BEGIN {
$ValidComputers = @()
$StaleComputers = @()
}
PROCESS {
ForEach ($computer in $ComputerName)
{
Write-Verbose ("Testing connectivity to server: {0}" -f $computer)
try
{
$null = Invoke-RetryCommand -Command Test-NetConnection -ArgList @{ComputerName=$computer; CommonTCPPort="RDP"} -CheckExpression $CheckExpression -Tries $Tries
Write-Verbose ("Successfully connected to server: {0}" -f $computer)
Write-Output @{ ValidComputers = $computer }
$ValidComputers += $computer
}
catch
{
Write-Verbose ("Server is not available, marked as stale: {0}" -f $computer)
Write-Output @{ StaleComputers = $computer }
$StaleComputers += $computer
}
}
}
END {
Write-Verbose "Valid Computers:"
$ValidComputers | ForEach-Object { Write-Verbose "* $_" }
Write-Verbose "Stale Computers:"
$StaleComputers | ForEach-Object { Write-Verbose "* $_" }
}
}

function global:Edit-AclIdentityReference {
Param(
[Parameter(Mandatory=$true)]
[System.Security.AccessControl.DirectorySecurity]
$Acl,

[Parameter(Mandatory=$false)]
[string[]]
$IdentityReference,

[Parameter(Mandatory=$false)]
[string]
$IdentityFilter = "(?i).*\\.*[$]$",

[Parameter(Mandatory=$false)]
[string]
$FileSystemRights = "FullControl",

[Parameter(Mandatory=$false)]
[string]
$InheritanceFlags = "ContainerInherit, ObjectInherit",

[Parameter(Mandatory=$false)]
[string]
$PropagationFlags = "None",

[Parameter(Mandatory=$false)]
[string]
$AccessControlType = "Allow"
)
BEGIN {
$AclIdentities = @($Acl.Access.IdentityReference.Value) | Where-Object { $_ -match $IdentityFilter }

# Test if ACL contains only matching identity references
$DiffIdentities = Compare-Object $IdentityReference $AclIdentities

# Skip further processing if there are no differences
if (-not $DiffIdentities)
{
Write-Verbose "ACL contains only matching identities, no changes needed..."
return
}

# Identity in ACL but not in $IdentityReference; need to remove
$RemoveIdentities = $DiffIdentities | Where-Object { $_.SideIndicator -eq "=>" } | ForEach-Object { $_.InputObject }

# Identity in $IdentityReference but not in ACL; need to add
$AddIdentities = $DiffIdentities | Where-Object { $_.SideIndicator -eq "<=" } | ForEach-Object { $_.InputObject }

# Remove rules for identity references not present in $IdentityReference
foreach ($Rule in $Acl.Access)
{
$Identity = $Rule.IdentityReference.Value

if ($Identity -in $RemoveIdentities)
{
Write-Verbose "Identity is NOT VALID, removing rule:"
Write-Verbose "* Rule Identity: $Identity"
$null = $Acl.RemoveAccessRule($Rule)
}
}

# Add rules for identity references in $IdentityReference that are missing from the ACL
foreach ($Identity in $AddIdentities)
{
Write-Verbose "Adding missing access rule to ACL:"
Write-Verbose "* Rule Identity: $Identity"
$null = $Acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule(
$Identity,
$FileSystemRights,
$InheritanceFlags,
$PropagationFlags,
$AccessControlType
)))
}

# Output the new ACL
Write-Verbose ($Acl.Access | Out-String)
Write-Output $Acl
}
}

function global:Get-File {
Param (
[Parameter(Mandatory=$true)]
[System.URI]
$Source,

[Parameter(Mandatory=$true)]
[System.IO.FileInfo]
$Destination,

[Parameter(Mandatory=$false)]
[ValidateSet("Ssl3","SystemDefault","Tls","Tls11","Tls12")]
[String]
$SecurityProtocol = "Tls12",

[Parameter(Mandatory=$false)]
[Switch]
$MakeDir,

[Parameter(Mandatory=$false)]
[Switch]
$OverWrite
)
try {
$ResolvedDestination = [System.IO.FileInfo]$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Destination)
$TempFile = New-TemporaryFile

Write-Verbose "Retrieving file:"
Write-Verbose "* Source: ${Source}"
Write-Verbose "* Destination: ${ResolvedDestination}"
Write-Verbose "* Temporary Destination: ${TempFile}"

try
{
Write-Verbose "Attempting to retrieve file using .NET method..."
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::$SecurityProtocol
(New-Object Net.WebClient).DownloadFile("$Source","$TempFile")
}
catch
{
try
{
Write-Verbose $PSItem.ToString()
Write-Verbose ".NET method failed, attempting BITS transfer method..."
Start-BitsTransfer -Source $Source -Destination $TempFile -ErrorAction Stop
}
catch
{
Write-Verbose $PSItem.ToString()
$PSCmdlet.ThrowTerminatingError($PSItem)
}
}

If (-not $ResolvedDestination.Directory.Exists -and $MakeDir) {
$null = New-Item -Path $ResolvedDestination.Directory -ItemType Directory
}

Move-Item $TempFile $ResolvedDestination -Force:$OverWrite -ErrorAction Stop
Write-Verbose "Retrieved file successfully!"
Write-Output (Get-ChildItem $ResolvedDestination)
}
finally
{
if (Test-Path $TempFile)
{
Remove-Item -Path $TempFile -Force
}
}
}

function global:New-RepeatingTask {
Param(
[Parameter(Mandatory=$true,ValueFromPipeLine=$false,ValueFromPipeLineByPropertyName=$false)]
[String]
$Name,

[Parameter(Mandatory=$true,ValueFromPipeLine=$false,ValueFromPipeLineByPropertyName=$false)]
[String[]]
$Arguments,

[Parameter(Mandatory=$true,ValueFromPipeLine=$false,ValueFromPipeLineByPropertyName=$false)]
[String]
$User,

[Parameter(Mandatory=$true,ValueFromPipeLine=$false,ValueFromPipeLineByPropertyName=$false)]
[SecureString]
$SecurePassword,

[Parameter(Mandatory=$false,ValueFromPipeLine=$false,ValueFromPipeLineByPropertyName=$false)]
[String]
$Command = "powershell.exe",

[Parameter(Mandatory=$false,ValueFromPipeLine=$false,ValueFromPipeLineByPropertyName=$false)]
[DateTime]
$StartTime = (Get-Date).Date,

[Parameter(Mandatory=$false,ValueFromPipeLine=$false,ValueFromPipeLineByPropertyName=$false)]
[TimeSpan]
$RepetitionInterval = (New-TimeSpan -Hours 1),

[Parameter(Mandatory=$false,ValueFromPipeLine=$false,ValueFromPipeLineByPropertyName=$false)]
[TimeSpan]
$RandomDelay = (New-TimeSpan -Minutes 10),

[Parameter(Mandatory=$false,ValueFromPipeLine=$false,ValueFromPipeLineByPropertyName=$false)]
[Switch]
$Force
)
if (Get-ScheduledTask -TaskName $Name -ErrorAction SilentlyContinue)
{
if ($Force)
{
UnRegister-ScheduledTask -TaskName $Name -Confirm:$false
Write-Verbose "Force-unregistered existing job, ${Name}"
}
else
{
throw "Task already exists, ${Name}. Use '-Force' to delete it"
}
}

$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $User, $SecurePassword
$Password = $Credentials.GetNetworkCredential().Password
$Action = New-ScheduledTaskAction -Execute $Command -Argument "$Arguments"
$Trigger = New-JobTrigger -Once -At $StartTime -RepeatIndefinitely -RepetitionInterval $RepetitionInterval -RandomDelay $RandomDelay
$Settings = New-ScheduledTaskSettingsSet -MultipleInstances Parallel
Register-ScheduledTask -TaskName $Name -Action $Action -Trigger $Trigger -User $User -Password $Password -RunLevel Highest -Settings $Settings
Write-Verbose "Created scheduled job, ${Name}"

if ($StartTime.CompareTo((Get-Date)) -le 0)
{
# Start time is now or in the past, trigger job immediately
Start-ScheduledTask -TaskName $Name
Write-Verbose "Triggered job, ${Name}"
}
}
Loading

0 comments on commit e99b637

Please sign in to comment.