0 votes

Hi everyone!

We are considering this product for our helpdesk, but one of our most used operations, is logging users off from their Citrix/terminalserver session. Has anyone tried scripting this into the Adaxes program?

Any answers would be greatly appriciated

by (960 points)
0

Hello,

Please specify in more details what you are trying to accomplish:

  • Do you want to kick all sessions of all users on a specific computer?
  • Do you want to kick all sessions of a specific user on all computers?
  • Do you want to kick a session of a specific user on a specific computer?
0

The main idea would be to find a user in the Web interface, like you do if you want to reset his password or change information, then add a button that can be clicked to log the user off terminal servers. In my mind, this would then send a request to terminal servers to search for which one has the user logged on, get that reply and log the user of that server.

Another idea would be to get a reply to list the users open sessions, then be able to select which session you want to disconnect (if a user has multiple sessions).

1 Answer

0 votes
by (216k points)

You can create a Custom Command to log a user off from all sessions on all computers:

  1. Create a new Custom Command.

  2. On the 2nd step of the Custom Command creation wizard, select User.

  3. On the 3rd step of the wizard, add the Run a program or PowerShell script action and paste the following script:

     Import-Module PSTerminalServices
     Import-Module Adaxes
    
     $username = "%username%"
     $computers = Get-AdmComputer -Filter {Enabled -eq $True}
    
     foreach ($computer in $computers)
     {
         try
         {
             $session = Get-TSSession -ComputerName $computer.DNSHostName -UserName $username -State Active
         }
         catch
         {
             continue
         }
    
         if ($session.ConnectionState -ine "Active")
         {
             continue
         }
    
         try
         {
             Stop-TSSession -Id $session.SessionID -ComputerName $computer.DNSHostName –Force
             $Context.LogMessage("User $username has been disconnected from " + $computer.Name, "Information")
         }
         catch
         {
             $Context.LogMessage($computer.Name + " : " + $_.Exception.Message, "Error")
         }
     }
    

The script uses the Terminal Services PowerShell Module that you can download here. Install it on the computer, where your Adaxes service is running.

If you want this Custom Command to be avilable from the Home Page of the Adaxes Web Interface, you need to create a Home Page Action that will execute this Custom Command as described in the Configure Home Page Actions Tutorial. See section Custom Command.

Also, see Citrix Farm Functions for Citrix-specific functionality that you can use with your script.

0

Thank you very much for your reply :)

0

Hi.

Could you help med modifying this script to work on managed domains?

Say i have domain 1, 2 and 3. I search for a user in the webinterface, find one in domain 2, click the custom task "log of user".

What i want it to do then, is check which domain this user is in, then search computers from a predefined scope, set for each domain, and then log the user of those computers.

is this possible?

0

Hello,

Do all the domains belong to the same forest?

0

No, the domains are not in the same forest.

0

Hello,

Are there any trust relationships between the domains?

If there exist trust relationships between the domains, you will need to grant Adaxes default service administrator the Query Information, Logoff and Connect permissions for the terminal servers from which you are trying to log off users. These permissions are required to successfully log off users from terminal servers.

If the trust relationships do not exist, you will have to provide credentials of users that have the necessary permissions in each of the domains. The credentials that you provide will be save to a secure storage, in which the passwords will be encrypted using the Windows Data Protection API. This means that only the user whose credentials were used to import the passwords into the secure storage will be able to read passwords in that storage

0

There are no trust relationships established between the different domains, that's one of the reasons we are testing Adaxes extensively to see that it meets all our needs.

Providing credentials is no problem, as long as they are stored securely.

0

OK, our scripting guy is already working on the script. I'll update this topic as soon as he comes up with something.

0

Hello,

Our scripting guy has finished his job. You need to complete several preparatory steps to make the script work.

Since there are no trust relationships between the domains, you cannot launch the script for all domains from the computer where your Adaxes service is installed. To launch a script in an untrusted domain, you need to launch it on a computer located in that untrusted domain with the help of PowerShell Remoting. For this purpose you need to configure a computer in each of the domains that will allow remote PowerShell sessions. The computer will be used to launch the script in that specific domain. Also, you need to install the Terminal Services PowerShell Module on each of such computers. These computers do not necessarily have to be servers, an ordinary workstation with PowerShell will be enough.

To configure a computer:

  1. Install the Terminal Services PowerShell Module on the computer. You can download it here. You need the ZIP file, do not download the MSI package. To install the module, just unpack the contents of the ZIP file to %WINDIR%\System32\WindowsPowerShell\v1.0\Module, where %WINDIR% is the Windows directory.

  2. Enable Remote PowerShell. To do this, you need to execute the Enable-PSRemoting -force command from the PowerShell console.

  3. Also, you also need to configure PowerShell execution policy to disable blocking and prompts for execution of commands from the module. To do this, execute the Set-ExecutionPolicy Bypass -force command from the PowerShell console.

  4. Make sure that the computer is trusted for delegation. To do this:

    • Open Adaxes Administration Console.
    • Expand the service node that represents your service.
    • Navigate to the computer account you need and right-click it.
    • Select Properties from the context menu.
    • In the dialog box that appears, switch to the Delegation tab.
    • Make sure that the radio button is switched to Trust this computer for delegation to any service.

Then, since there are no trust relationships between the domains, you need to add the computers that you've prepared to accept remote PowerShell sessions to trusted hosts on the computer where your Adaxes service is installed. To do this, execute the following command line on the computer where your Adaxes service is installed:
winrm s winrm/config/client '@{TrustedHosts="RemoteComputer1,RemoteComputer2"}'
where RemoteComputer1,RemoteComputer2 are Fully Qualified Domain Names (FQDNs) of the computers that you've configured to accept remote PowerShell sessions in each of your domains.

Now you have to prepare a CSV file with credentials required to connect to each of your domains. The first line should contain the following headers:
DomainName, ComputerName, UserName, Password
Then, you should specify credentials for each of your domains in the following format:

  • 1st column - domain name,
  • 2nd column - the name of the computer that you've configured to accept remote PowerShell sessions per the above instructions,
  • 3rd column - user name of a user who has the Query Information, Logoff and Connect privileges for the computers located in that domain,
  • 4th column - password for the account specified in the 3rd column.

There should be one line for each domain. Here's an example of such a file:

DomainName, ComputerName, UserName, Password
domain1.com,computer.domain1.com,Administrator@domain1.com,password
domain2.com,computer.domain2.com,administrator@domain2.com,password

Now, you need to import the credentials into a secure storage. To do this:

  1. Copy the following script and save it to a file with the .ps1 exetnsion, say, Import-Credentinals.ps1.

     Param($csvFilePath, [switch]$deleteOldCredentials) # Run the script with command line parameter -csvFilePath (CVS file path) and optional parameter -deleteOldCredentials
    
     $scriptDirectoryPath = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition)
    
     # Create a directory for files with credentials
     $credentialDirectoryPath = $scriptDirectoryPath + "\Credentials"
     if ($deleteOldCredentials)
     {
         if ((Test-Path -Path $credentialDirectoryPath))
         {
             Get-Item -Path $credentialDirectoryPath | Remove-Item -Force -Recurse
         }
     }
    
     if (!(Test-Path -Path $credentialDirectoryPath))
     {
         New-Item -ItemType directory -Path $credentialDirectoryPath | Out-Null
     }
     $credentialDirectory = Get-Item -Path $credentialDirectoryPath
    
     # Import credentials from the CSV file
     $domainsCSV = Import-Csv $csvFilePath
     $fileCount = 0
     foreach ($domainCSV in $domainsCSV)
     {    
         $directoryPath = $credentialDirectory.FullName + "\" + $domainCSV.DomainName
         if (!(Test-Path -Path $directoryPath))
         {
             New-Item -Type directory -Path $directoryPath | Out-Null
         }
         $directory =  Get-Item -Path $directoryPath
    
         $filePath = $directory.FullName + "\" + $domainCSV.ComputerName
         if((Test-Path -Path $filePath))
         {
             Get-Item -Path $filePath | Remove-Item -Force -Recurse
         }
         $file = New-Item $filePath -Type file
    
         Set-Content -Path $file.FullName -Value $domainCSV.UserName
         ConvertTo-SecureString  $domainCSV.Password -AsPlainText -Force | ConvertFrom-SecureString | Add-Content $file
    
         $fileCount++
     }
    
     Write-Host "Import complete, $fileCount row(s) processed"
    
  2. Open Windows Command Prompt (cmd.exe).

  3. Run the saved script with the credentials of Adaxes default service administrator (the user that you specified during Adaxes installation) using the following command line:
    RunAs /user:DOMAIN\DefaultServiceAdmin "PowerShell -file <full_script_file_path> -csvfilepath <full_csv_file_path> -deleteOldCredentials"
    where

    • DOMAIN\DefaultServiceAdmin - user name of Adaxes default service administrator,
    • <full_script_file_path> - full path to the script that you saved on the 1st step,
    • <full_csv_file_path> - full path to the CSV file with credentials,
    • -deleteOldCredentials - an optional parameter that specifies whether to purge credentials from the secure storage before importing credentials from the CSV file. When this parameter is not specified, the script will just import the new credentials. If the CSV file contains credentials for a domain, credentials for which are already in the secure storage, the credentials will be updated with data from the CSV file.

The script will create a subfolder called Credentials in the same folder that you saved your CSV file to. The Credentials subfolder will contain a set of folders and files containing credentials and names of computers required to connect to each of the domains. After this you no longer need the csv file. The passwords will be encrypted using the Windows standard Data Protection API, which means that only the user whose credentials were used to launch the PowerShell script (Adaxes default service administrator) will be able to read passwords in those files. Since all scripts are launched in Adaxes with the credentials of the default service administrator, the passwords saved in the secure storage will be accessible for use by Adaxes.

Now, you can create a Custom Command for logging users off:

  1. Create a new Custom Command.

  2. On the 2nd step of the Custom Command creation wizard, select User.

  3. On the 3rd step of the wizard, add the Run a program or PowerShell script action and paste the following script:

     Import-Module Adaxes
    
     $credentialDirectoryPath = "C:\PowershellScript\Credentials" # TODO: modify me
    
     $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") # TODO: modify me
         return
     }
     $directory = Get-ChildItem -Path $credentialDirectoryPath -Filter $domainName
     if(!$directory)
     {
         $Context.LogMessage("The credentials folder for domain $domainName was not found.", "Error") # TODO: modify me
         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") # TODO: modify me
         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*")} `
         -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
    
         foreach ($computer in $computers)
         {
             try
             {
                 $session = Get-TSSession -ComputerName $computer.DNSHostName -UserName $targetUserName -State Active
             }
             catch
             {
                 continue
             }
    
             if ($session.ConnectionState -ine "Active")
             {
                 continue
             }
    
             try
             {
                 Stop-TSSession -Id $session.SessionID -ComputerName $computer.DNSHostName –Force
                 return "User $targetUserName has been successfully disconnected from " + $computer.Name
             }
             catch
             {
                 return $computer.Name + " : " + $_.Exception.Message
             }
         }
     }
     Remove-PSSession $session
    
     if($result)
     {
         $Context.LogMessage($result, "Information")
     }
    

    In the script, $credentialDirectoryPath specifies the path to the directory that contains a secure storage with credentials to log on to your domains.

  4. Finish creation of the Custom Command.

When executed on a user, the Custom Command will connect to the computer that you specified for the user's domain in the CSV file using the credentials that you provided. When connected, the Custom Command will launch a script on that computer that will log the user off from all sessions.

If you want this Custom Command to be avilable from the Home Page of the Adaxes Web Interface, you need to create a Home Page Action that will execute this Custom Command as described in the Configure Home Page Actions Tutorial. See section Custom Command.

0

Thanks for the script.

It seems to be a bit off though. Every time i run it on a user, i get a reply: User User has been successfully logged off from RemoteComputer1.
RemoteComputer1 being the computer specified in the TrustedHosts and as remote powershell receiver. It shows this message even if the user is not connected to that machine, and it does not log the user of other servers.

0

We found a solution for this, altered the script a bit :)

0

Hello,

Can you post your updated script here?

0

Here is the script:

Import-Module Adaxes

$credentialDirectoryPath = "C:\Credentials" # TODO: modify me

$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") # TODO: modify me
    return
}
$directory = Get-ChildItem -Path $credentialDirectoryPath -Filter $domainName
if(!$directory)
{
    $Context.LogMessage("The credentials folder for domain $domainName was not found.", "Error") # TODO: modify me
    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") # TODO: modify me
    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*")} `
    -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) 
            {
                Stop-TSSession -Id $session.SessionID -ComputerName $computer.DNSHostName –Force
                return "User $targetUserName has been successfully logged off from " + $computer.Name
            }
        }
        catch
        {
            continue
        }
    }
    if($result)
    {
        $Context.LogMessage($result, "Information")
    }
}
Remove-PSSession $session
0

Hi again

We've added another managed domain and followed the guidelines from the last domain we added. But this time we're getting a error when trying to execute this script (still works fine against the other managed domain):

Invoke-Command : Exception calling "GetSessions" with "0" argument(s): "Access is denied"
At line:1 char:15

  • Invoke-Command <<<< -Session $session -ScriptBlock {get-tssession -computername Terminalserver1}
  • CategoryInfo : NotSpecified: (:) [Invoke-Command], MethodInvocationException
  • FullyQualifiedErrorId : DotNetMethodException

Any ideas?

0

Hello,

The error message means that the user with the credentials of which you are trying to establish the remote PowerShell connection does not have sufficient privileges to use PowerShell remoting on the computer that you trying to connect to.

Please make sure that you've done all the preparatory steps mentioned in Citrix/terminalserver Logoff. In particular, make sure that the user with the credentials of which you trying to establish the remote connection has the Query Information, Logoff and Connect privileges for the computers located in the domain and that the computer that you trying to connect to is trusted for delegation and properly configured to allow PowerShell Remoting.

0

The user specified in credentials is a domain admin account, so it should have all the permissions needed.

I've done all the preparatory steps mentioned.

The computer is trusted for Delegation and properly configured to allow PowerShell Remoting.

0

If the computer that you are trying to connect to is running a client version of Windows (Windows XP, Windows Vista, Windows 7 or Windows 8), this can be caused by the AllowRemoteRPC registry entry not being set appropriately. See the following article by Microsoft on how to fix it: http://technet.microsoft.com/en-us/libr ... S.10).aspx. Windows XP will require a reboot for changes to that registry key to be effective; Windows Vista, 7 and 8 should not.

0

This registry setting is already set to 1.

Our Adaxes server (Adaxes01.domain1.local) sets up a remote powershell session to RemotePS.domain2.local. In that session it runs: Get-TSSession -ComputerName TerminalServer1 (.domain2.local). It then throws the error specified.

But if i directly from RemotePS.domain2.local open powershell and run Get-TSSession -ComputerName TerminalServer1 (.domain2.local) it outputs the correct info.

0

No ideas? :(

0

Hello,

Our scripting guys are currently working on this, however no success so far. They can't even reproduce the issue. Such an error should not appear if all the preparatory steps have been performed. They have a couple more ideas to check. We'll update you as soon as they're done checking.

0

Try the following:

  1. Find the following line in the script:

     $session = New-PSSession $file.Name -Authentication Negotiate -Credential $credential
  2. Change Negotiate to CredSSP
    You should receive the following line:

     $session = New-PSSession $file.Name -Authentication CredSSP -Credential $credential
  3. Save the modified Custom Command and try to execute it. Our script guys report that this should help.

0

Se attachment for the error i then got

0

I've also tried running winrm set winrm/config/client/auth @{CredSSP="true"} on both the Adaxes server and the server it is connecting to, but still no luck

0

To allow using the CredSSP authentication type, you need to perform the following steps:

  1. On the computer where Adaxes is installed, launch Windows PowerShell.

  2. To allow passing credentials via the CredSSP authentication mechanism, execute the following PowerShell command:

     Enable-WSManCredSSP -Role Client -DelegateComputer comp1.domain.com -Force

    where comp1.domain.com is the computer that will be used to execute the script in the untrusted domain (the one where you have the Terminal Services PowerShell Module installed).

  3. Also, launch Windows PowerShell on the computer that you specified in the previous step.

  4. To allow accepting credentials via the CredSSP authentication mechanism, execute the following PowerShell command:

     Enable-WSMaCredSSP -Role Server –Force

For additional information, you can take a look at the following article by Microsoft: http://blogs.technet.com/b/heyscripting ... edssp.aspx.

0

I have finally resolved this... and this is a bit stupid, but the script to generate the credential file, did not include @domainname.com in the username. Why it didn't for this registration, i don't know. But the solution was adding @domainname.com to the username and voila!

0

Hello,

If you didn't include the domain part of the username in the CSV file, then it will not be included in the credentials files generated by the script. As we've mentioned in our initial post with instructions, the CSV file format should be as follows:

DomainName, ComputerName, UserName, Password
domain1.com,computer.domain1.com,administrator@domain1.com,password
domain2.com,computer.domain2.com,administrator@domain2.com,password

In the above sample, the username is specified with its domain part.

No related questions found

3,326 questions
3,025 answers
7,723 comments
544,675 users