Hello

We are looking to optimize one of our most used scripts. The script is kinda slow when working against a domain containing over 15 terminal servers. We are using the same script against several domains, therefore we can not specify the names or ou's of the servers.

So if anyone has any suggestions on how to make this script work faster, it would be greatly appreciated!

Import-Module Adaxes

$credentialDirectoryPath = "C:\Credentials" 

$targetUserName = "%username%"
# Get name of the user's domain
$domainName = $Context.GetObjectDomain("%distinguishedName%")

# Get credentials for the domain
if(!(Test-Path -Path $credentialDirectoryPath))
{
    $Context.LogMessage("The credentials folder was not found. Make sure that $credentialDirectoryPath exists.", "Error") 
    return
}
$directory = Get-ChildItem -Path $credentialDirectoryPath -Filter $domainName
if(!$directory)
{
    $Context.LogMessage("The credentials folder for domain $domainName was not found.", "Error") 
    return
}

# Read credentials for the domain from the file
$file = Get-ChildItem -Path $directory.FullName
if(!$file)
{
    $Context.LogMessage("The credentials file for domain $domainName was not found.", "Error") 
    return
}

$userName = (Get-Content -Path $file.FullName)[0]
$passwordEncryptedString = (Get-Content -Path $file.FullName)[1]
$password = ConvertTo-SecureString -String $passwordEncryptedString
$credential = New-Object System.Management.Automation.PsCredential($userName,$password)

# Get all computers from the user's domain
$computers = Get-AdmComputer -Filter {(Enabled -eq $True) -and (operatingSystem -like "*Server*") -and (name -like "*MF*" -or name -like "*ctx*" -or name -like "*xap*" -or name -like "*TS*")} `
    -AdaxesService localhost -Server $domainName

# Create a remote PowerShell session
$session = New-PSSession $file.Name -Authentication Negotiate -Credential $credential 
foreach ($computer in $computers) {
    $result = Invoke-Command -Session $session -ArgumentList $computer, $targetUserName -Scriptblock {
        param($computer, $targetUserName)
        Import-Module PSTerminalServices
        try
        {
            $session = Get-TSSession -ComputerName $computer.DNSHostName -UserName $targetUserName
            if($session) 
            {
                return "User has a " + $session.State + " session on " + $computer.Name
            }
        }
        catch
        {
            continue
        }
    }
    if($result)
    {
        $Context.LogMessage($result, "Information")
    }
}
Remove-PSSession $session
by (960 points)
by (216k points)
0

Hello,

We've given our script guy the task to test the script and see whether it is possible to improve it. I'll update the post as soon as he comes up with something.

1 Answer

by (216k points)
0 votes

Hello,

Our script guy has come up with a certain performance improvement. However, keep in mind that often the time required for a script to run depends on your environment. In this particular case the performance of the script may depend on whether all of the computers that are polled are available. If some of the computers are unavailable (e.g. powered off), the script will still try to connect to them, and this involves the standard timeout required to identify that the computer is down.

Here's the updated version of the script. Instead of using the Invoke-Command cmdlet in a foreach loop, it passes an array of computers to the Invoke-Command cmdlet, and only connection to the computers is performed in the foreach loop.

Import-Module Adaxes

$credentialDirectoryPath = "C:\Credentials"

$targetUserName = "%username%"
# Get name of the user's domain
$domainName = $Context.GetObjectDomain("%distinguishedName%")

# Get credentials for the domain
if(!(Test-Path -Path $credentialDirectoryPath))
{
    $Context.LogMessage("The credentials folder was not found. Make sure that $credentialDirectoryPath exists.", "Error")
    return
}
$directory = Get-ChildItem -Path $credentialDirectoryPath -Filter $domainName
if(!$directory)
{
    $Context.LogMessage("The credentials folder for domain $domainName was not found.", "Error")
    return
}

# Read credentials for the domain from the file
$file = Get-ChildItem -Path $directory.FullName
if(!$file)
{
    $Context.LogMessage("The credentials file for domain $domainName was not found.", "Error")
    return
}

$userName = (Get-Content -Path $file.FullName)[0]
$passwordEncryptedString = (Get-Content -Path $file.FullName)[1]
$password = ConvertTo-SecureString -String $passwordEncryptedString
$credential = New-Object System.Management.Automation.PsCredential($userName,$password)

# Get all computers from the user's domain
$computers = Get-AdmComputer -Filter {(Enabled -eq $True) -and (operatingSystem -like "*Server*") -and (name -like "*MF*" -or name -like "*ctx*" -or name -like "*xap*" -or name -like "*TS*")} `
    -AdaxesService localhost -Server $domainName

# Create a remote PowerShell session
$session = New-PSSession $file.Name -Authentication Negotiate -Credential $credential
$result = Invoke-Command -Session $session -ArgumentList $computers, $targetUserName -Scriptblock {
    param($computers, $targetUserName)
    Import-Module PSTerminalServices

    $sessionsInfo = @()
    foreach($computer in $computers)
    {
        try
        {
            $session = Get-TSSession -ComputerName $computer.DNSHostName -UserName $targetUserName 
            if($session)
            {
                $sessionsInfo += "User has a " + $session.State + " session on " + $computer.Name
            }
        }
        catch
        {
            continue
        }
    }
    return $sessionsInfo
}
Remove-PSSession $session

if($result -eq $NULL)
{
    $Context.LogMessage("No session information for the user.", "Information") # TODO: modify me
    return
}

foreach($sessionInfo in $result)
{
    $Context.LogMessage($sessionInfo, "Information")
}
by (960 points)
0

Thank you very much!

We added a bit to the script to avoid offline servers, as per your suggestion. These two together made the script execute about 15 sec faster :)

Here is the complete script:

Import-Module Adaxes

$credentialDirectoryPath = "C:\Credentials"

$targetUserName = "%username%"
# Get name of the user's domain
$domainName = $Context.GetObjectDomain("%distinguishedName%")

# Get credentials for the domain
if(!(Test-Path -Path $credentialDirectoryPath))
{
    $Context.LogMessage("The credentials folder was not found. Make sure that $credentialDirectoryPath exists.", "Error")
    return
}
$directory = Get-ChildItem -Path $credentialDirectoryPath -Filter $domainName
if(!$directory)
{
    $Context.LogMessage("The credentials folder for domain $domainName was not found.", "Error")
    return
}

# Read credentials for the domain from the file
$file = Get-ChildItem -Path $directory.FullName
if(!$file)
{
    $Context.LogMessage("The credentials file for domain $domainName was not found.", "Error")
    return
}

$userName = (Get-Content -Path $file.FullName)[0]
$passwordEncryptedString = (Get-Content -Path $file.FullName)[1]
$password = ConvertTo-SecureString -String $passwordEncryptedString
$credential = New-Object System.Management.Automation.PsCredential($userName,$password)

# Get all computers from the user's domain
$computers = Get-AdmComputer -Filter {(Enabled -eq $True) -and (operatingSystem -like "*Server*") -and (name -like "*MF*" -or name -like "*ctx*" -or name -like "*xap*" -or name -like "*TS*")} `
    -AdaxesService localhost -Server $domainName

# Create a remote PowerShell session
$session = New-PSSession $file.Name -Authentication Negotiate -Credential $credential
$result = Invoke-Command -Session $session -ArgumentList $computers, $targetUserName -Scriptblock {
    param($computers, $targetUserName)
    Import-Module PSTerminalServices

    $sessionsInfo = @()
    foreach($computer in $computers)
    {    
    $online = Test-Connection -Cn $computer.DNSHostName -BufferSize 16 -Count 1 -ea 0 -quiet
    if($online){
        try
        {
            $session = Get-TSSession -ComputerName $computer.DNSHostName -UserName $targetUserName 
            if($session)
            {
                $sessionsInfo += "User has a " + $session.State + " session on " + $computer.Name
            }
        }
        catch
        {
            continue
        }
      }
    }
    return $sessionsInfo
}
Remove-PSSession $session

if($result -eq $NULL)
{
    $Context.LogMessage("User has no active sessions", "Information") 
    return
}

foreach($sessionInfo in $result)
{
    $Context.LogMessage($sessionInfo, "Information")
}
by (216k points)
0

Hello,

Well, we think that this is as much performance gain as you can get in this case. Thank you for your update on the script. ;)

Related questions

I'd like to run a powershell script Before Updating a User Account, which I can check what the exchange mailbox permissions before and after the change. I need to be able to both report on ... - not SID's or DN's. What's the best way to do that? Thanks, Jay

asked Oct 2 by jaymallery (60 points)
0 votes
1 answer

Hi, would it be possible to use Value references in embedded scripts with functions? Like, I have several flows, using one external shared file with PowerShell functions. If I ... "%lastname%").Replace("sAMAccountName", "%sAMAccountName%").Replace(" ", "")) }

asked Sep 23 by wintec01 (2.3k points)
0 votes
1 answer

Let's say that I have a scheduled task that adds a high-level roles-based AD group to a user. As part of that same task, I'd like to run a powershell script to collect all ... the task to type in the "parent" group name, and pass it to the script that way?

asked Sep 22 by 3Jake (190 points)
0 votes
1 answer

I haven't seen a version to know the syntax.

asked Sep 4 by mightycabal (1.2k points)
0 votes
1 answer

I am creating a script triggered by a buisness rule. The rule is triggered 'before createing a user' This script checks for a duplicate user. If the script ... { $Context.LogMessage("Duplicate check: no duplicates found for %displayName%","Information") }

asked Sep 4 by mightycabal (1.2k points)
0 votes
1 answer