0 votes

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)
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

0 votes
by (216k points)

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")
}
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")
}
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

0 votes
1 answer

Hi, is it possible, or planned to add another action to show messages? This command is showing during execution in the web interface & in logging area. ... to the end users only a few information. Something like $Context.LogMessage $Context.OutputMessage

asked Nov 12 by wintec01 (1.5k points)
0 votes
1 answer

Hi, I have this Script to check if a condition is met # The condition is met if $Context.ConditionIsMet is set to $True. $Context.ConditionIsMet = $False $inputString = " ... or issue with this match function using in a "if script returns true" condition?

asked Oct 31 by wintec01 (1.5k points)
0 votes
1 answer

Hi, would it be possible to script a workstation in AD and also directly from our local SCCM environment ?

asked Oct 28 by ddesmedt (40 points)
0 votes
1 answer

Trying to create a custom command to run a powershell script, but I keep getting an error saying it can't find the file or path supplied for launching powershell. ... and I can launch powershell from this location using Command Prompt. Am I missing something?

asked Oct 24 by cstaub (100 points)
0 votes
1 answer

The rule runs but since the first name and last name are passed as parameters, I only get the sequential # as a userID without the initials.

asked Oct 24 by curtisa (290 points)
3,548 questions
3,239 answers
8,232 comments
547,814 users