Link to home
Start Free TrialLog in
Avatar of CES
CES

asked on

Get-TSSessions Powershell script works with single Computer Name, fails when I substitute a variable

My goal in this script to to take a CSV list of servers and find disconnected RDP sessions.  I have gotten to the point where I have the server names into a variable ($tshosts below)  and I can run my Get-TSSessions cmdlet to export to file in the format that I want.

My issue is that Once I subsitute my variable, the script does not seem to run.  I'm hoping I can get a second set of eyes on this and help me see where I'm getting it wrong.  Any help is much appreciated!

import-module ActiveDirectory
Import-Module PSTerminalServices
$serverlist = Import-Csv \\path\Computers.csv
$rdphosts=@()
    foreach($server in $serverlist)
    {
    $rdphosts +=Get-ADComputer $server.Name
    }
   
 $tshosts=$rdphosts | Select -Property Name

    foreach ($tshost in $tshosts){
      If (Test-Connection $tshost -count 1 -quiet){
      Get-TSSession -ComputerName $tshost -ErrorAction SilentlyContinue | Where-Object {$_.ConnectionState -eq 'Disconnected'} | select {$_.Server.ServerName},ConnectionState,UserAccount | Export-Csv c:\ps\finalresults.csv -NoTypeInformation
      }
      }
Avatar of oBdA
oBdA

Main error: "Select-Object -Property Name" returns an array of objects with one single property "Name", not a string array! You'd still need to refer to it inside the loop as $tshost.Name
You can use -ExpandProperty to expand the respective property.

That said, this should work:
Import-Module ActiveDirectory
Import-Module PSTerminalServices
$serverlist = Import-Csv \\path\Computers.csv
$rdphosts=@()

ForEach ($server in $serverlist) {
	$rdphosts += Get-ADComputer $server.Name
}

$tshosts = $rdphosts | Select-Object -ExpandProperty Name

$tshosts | Where-Object {Test-Connection $_ -count 1 -quiet} |
	ForEach-Object {
		$tshost = $_
		Get-TSSession -ComputerName $tshost -ErrorAction SilentlyContinue |
			Where-Object {$_.ConnectionState -eq 'Disconnected'} |
			Select-Object -Property @{n='Server'; e=$tshost}, ConnectionState, UserAccount
	}
} | Export-Csv c:\ps\finalresults.csv -NoTypeInformation

Open in new window

Avatar of CES

ASKER

Thank you  the edits were quite helpful.  Two things, however:

- The ISE is claiming that the final pipe for exporting to CSV is empty

It doesn't quite like how I am trying to pull the server name.  it is giving me the following error:
The "e" key has a type, System.Management.Automation.PSObject, that is not valid; expected types are {System.String, System.Management.Automation.ScriptBlock}

If I get rid of the pipe/export and remove the search for server name, it loks to work great, so I think it's almost there....
ASKER CERTIFIED SOLUTION
Avatar of oBdA
oBdA

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I've implemented this script in my env.

function Get-PingStatus
{
    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [Alias('Name','IP','IPAddress')]
       [string]$Computer
    )

    $PingStatus = Get-WmiObject -Query "SELECT StatusCode FROM win32_PingStatus WHERE ADDRESS = '$Computer'"

    if ($PingStatus.StatusCode -eq 0) {
        $true
    }
    else
    {
        $false
    }
} # END Get-PingStatus 

################### 
##### GLOBALS ##### 
################### 
 
# Change the keyword to an OS search term such as Server
#$Filter = 'OperatingSystem -like "*Server*"'
# get all computers from Active Directory matching the defined keword
# $ServerList = Get-ADComputer -Properties Name -Filter $Filter | Select Name | Sort-Object -Property Name
#$ServerList = Get-ADComputer -Properties Name -Filter $Filter | Select Name | Sort-Object -Property Name
$ServerList = Get-Content C:\TS_Sessions\1030PMServers.txt
$OutputFile = "C:\TS_Sessions\1030PM.htm" 
 
# Get domain name, date and time for report title 
$DomainName = (Get-ADDomain).NetBIOSName  
$Time = Get-Date -Format t 
$CurrDate = Get-Date -UFormat "%D" 

# Info for footer
$ServerTotal = ($ServerList).count

# Option to create transcript - change to $true to turn on.
$CreateTranscript = $false

# Option to send email - change to $true to turn on 
$SendEmail = $True
 
# SMTP Settings  
$smtpServer = "smtp.office365.com"
$SmtpUser = 'smtpuser'
$smtpPassword = "smtpuserpassword" 
$From = "from" 
$To = "to" 
$cc = "cc" 
$Subject = "10:30PM Shutdown - $DomainName RDP Sessions" 


###############
##### PRE #####
###############

# Start Transcript if $CreateTranscript variable above is set to $true.
if($CreateTranscript)
{
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
if( -not (Test-Path ($scriptDir + "\Transcripts"))){New-Item -ItemType Directory -Path ($scriptDir + "\Transcripts")}
Start-Transcript -Path ($scriptDir + "\Transcripts\{0:yyyyMMdd}_Log.txt"  -f $(get-date)) -Append
}
 
# Import modules - AD and PSTS which contains Get-TSSession 
Import-Module ActiveDirectory 
Import-Module PSTerminalServices 

################ 
##### MAIN ##### 
################ 

$HTML = '<head>
<style>
table {
    font-family: Segoe UI;
    border-collapse: collapse;
    width: 100%;
}
th {
    border: 1px solid black;
    background-color: #A8D3DA;
    text-align: Center;
    padding: 8px;
}

td{
    border: 1px solid black;
    text-align: left;
    padding: 8px;
}

tr:nth-child(even) {
    background-color: #dddddd;
}
</style>
</head> '

# Report Header
$Header = "<H2 align=center><font face='Segoe UI' size=4>$DomainName Servers ($ServerList) RDP Sessions as of $Time on $CurrDate (Server Time UTC +00:00)</font></H2>" 
# Report Footer
$Footer = "<H4 align=center><font 'Segoe UI' size=3 Color=Blue>Pl. Note: If the report is empty, consider there is no active/disconnected sessions available in the server.</font></H4>"
$Footer += "<H5 align=center><font 'Segoe UI' size=3 Color=Blue>-= This is an Auto Generated Eamil =-</font></H5>"
 
$HTML += "<HTML><BODY><Table border=1 cellpadding=0 cellspacing=0 width=100% id=TSHead class=sortable>
        <TR> 
            <TH><B>Server</B></TH> 
            <TH><B>User Name</B></TH> 
            <TH><B>Client Name</B></TD> 
            <TH><B>Client IP</B></TH> 
            <TH><B>Session State</B></TH> 
            <TH><B>Session Name</B></TH> 
            <TH><B>Login Time</B></TH> 
            <TH><B>Last Input Time</B></TH>
        </TR>
        " 
 
ForEach ($ServerName in $ServerList) 
{
If (Get-PingStatus $ServerName)
    {
    Try{
    $TSSessions = Get-TSSession -ComputerName $ServerName -ErrorAction SilentlyContinue | Select UserAccount,ClientName,ClientIPAddress,State,WindowStationName,LastInputTime,LoginTime
    }
    Catch{
    }
    }
# Sessions where the username is blank and ICA (Citrix) sessions are excluded from the results 
# To see every session, change the line below to ForEach($Session in $TSSessions) 
# To include ICA sessions, change it to ForEach($Session in ($TSSessions | WHERE {$_.UserAccount -ne $null})) 
ForEach($Session in ($TSSessions | WHERE {$_.UserAccount -ne $null -AND {$_.WindowStationName -notlike "*ICA*" -like "Services"}})) 
    {         
                $HTML += "<TR> 
                    <TD>$($ServerName)</TD>
                    <TD>$($Session.UserAccount)</TD>  
                    <TD>$($Session.ClientName)</TD> 
                    <TD>$(($Session.ClientIPAddress).IPAddressToString)</TD> 
                    <TD>$($Session.State)</TD> 
                    <TD>$($Session.WindowStationName)</TD> 
                    <TD>$($Session.LoginTime)</TD> 
                    <TD>$($Session.LastInputTime)</TD> 
                </TR>" 
    }
} 
 
$HTML += "<H2></Table></BODY></HTML>" 
$Header + $HTML + $Footer | Out-File $OutputFile 
 
# Send email if $SendEmail variable above is set to $true 
if($SendEmail) 
<#{ 
$message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto 
$message.Subject = $messageSubject 
$message.IsBodyHTML = $true 
 
$message.Body = $OutputFile | ConvertTo-HTML -Head $HTML 
 
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.EnableSsl = true;

$smtp.UseDefaultCredentials = false;

$smtp.Credentials = $Credentials;
$smtp.Send($message)   
} #>
{ 
$Body = Get-Content $OutputFile -RAW 
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $SmtpUser, $($smtpPassword | ConvertTo-SecureString -AsPlainText -Force)
send-MailMessage -SmtpServer $smtpserver -To $to -cc $cc  -From $from -Subject $subject -body $body  -BodyAsHtml -UseSsl -Credential $Credentials
}

Open in new window

Avatar of CES

ASKER

In my editing, I moved one of the closing brackets off of the last line, which the script seemed to care about.  I just ran it and it worked perfectly.

Thank you much!
Avatar of CES

ASKER

In my editing, I moved one of the closing brackets off of the last line, which the script seemed to care about.  I just ran it and it worked perfectly.

Thank you much!