Hi guys I am wondering if there is any script that will notify with email about logged in user information like username, computer name, client ip, client name, date and time etc.
Here is the script i get from technet but it displays computer name as local host, can it output a logged file and email as attachment?
Param(
[CmdletBinding()]
[Parameter(ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]$ComputerName = 'localhost'
)
Begin {
$ErrorActionPreference = 'Stop'
}
Process {
foreach ($Computer in $ComputerName) {
try {
quser /server:$Computer 2>&1 | Select-Object -Skip 1 | ForEach-Object {
$CurrentLine = $_.Trim() -Replace '\s+',' ' -Split '\s'
$HashProps = #{
UserName = $CurrentLine[0]
ComputerName = $Computer
}
# If session is disconnected different fields will be selected
if ($CurrentLine[2] -eq 'Disc') {
$HashProps.SessionName = $null
$HashProps.Id = $CurrentLine[1]
$HashProps.State = $CurrentLine[2]
$HashProps.IdleTime = $CurrentLine[3]
$HashProps.LogonTime = $CurrentLine[4..6] -join ' '
$HashProps.LogonTime = $CurrentLine[4..($CurrentLine.GetUpperBound(0))] -join ' '
} else {
$HashProps.SessionName = $CurrentLine[1]
$HashProps.Id = $CurrentLine[2]
$HashProps.State = $CurrentLine[3]
$HashProps.IdleTime = $CurrentLine[4]
$HashProps.LogonTime = $CurrentLine[5..($CurrentLine.GetUpperBound(0))] -join ' '
}
New-Object -TypeName PSCustomObject -Property $HashProps |
Select-Object -Property UserName,ComputerName,SessionName,Id,State,IdleTime,LogonTime,Error
}
} catch {
New-Object -TypeName PSCustomObject -Property #{
ComputerName = $Computer
Error = $_.Exception.Message
} | Select-Object -Property UserName,ComputerName,SessionName,Id,State,IdleTime,LogonTime,Error
}
}
}
I guess script usage should be:
save it in a .ps1 file
call it with a -ComputerName 'hostname' parameter, allowing you to choose the targeted computer
Here, 'localhost' is the default value for this parameter.
To send an email, check the Send-MailMessage cmdlet (PS 3.0 required).
If you need to run a script as a scheduled task, set the Action fields like this:
program = C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments = -File "C:\path\to\script.ps1"
Related
The below script to get the logon users and send as email was working great but only on the console output only.
I am trying to get the result as a table so the result in the console and the email body will be like:
Server, ConnectionType, User, ID, State
PRDSVR16, rdp-tcp#44, SVC-SQL, 4, Active
PRDSVR10, rdp-tcp#27, Admin.domain, 6, Disc
SVR25-VM,console,domain.admin,8,Active
Open in new window
This is the script:
$Today = Get-Date -Format 'F'
$SessionList = "`n`nRDP Session List - " + $Today + "`n`n"
$CurrentSN = 0
# Get a list of servers from Active Directory
write-progress -activity "Getting list of servers from Active Directory" -status "... please wait ..."
$Servers = (Get-ADComputer -Filter { Enabled -eq $True -and OperatingSystem -like "*Server*" } -Properties OperatingSystem -SearchBase "OU=Data Center,DC=Company,DC=com") |
Where-Object { Test-Connection $_.Name -Count 1 -Quiet } |
Select-Object -ExpandProperty Name
$NumberOfServers = $Servers.Count
# Iterate through the retrieved list to check RDP sessions on each machine
ForEach ($Server in $Servers)
{
Write-Host "Processing $Server ..." -ForegroundColor Yellow
Write-progress -activity "Checking RDP Sessions" -status "Querying $Server" -percentcomplete (($CurrentSN / $NumberOfServers) * 100)
try
{
$SessionList += qwinsta /server:$Server |
Select-Object -Skip 1 |
% {
[PSCustomObject] #{
Type = $_.Substring(1, 18).Trim()
User = $_.Substring(19, 20).Trim()
ID = $_.Substring(41, 5).Trim()
State = $_.Substring(48, 6).Trim()
}
} |
? { $_.Type -notin 'console', 'services', 'rdp-tcp' -and $_.User -ne $null -and $_.User -ne 65536 } |
% {
"`n$Server logged in by $($_.User) on $($_.Type), session id $($_.ID) $($_.state)"
}
}
catch
{
$SessionList += "`n Unable to query " + $Server
write-host "Unable to query $Server! `n $($Error[0].Exception)" -foregroundcolor Red
}
$CurrentSN++
}
# Send the output the screen.
$SessionList + "`n`n"
$sendMailArgs = #{
From = "$env:USERNAME#$env:userdnsdomain"
To = 'SOC#domain.com'
SmtpServer = 'SMTP.domain.com'
Priority = 'High'
Body = $SessionList | Select-Object #{ N = 'Server'; E = { $Server } },
#{ N = 'User'; E = { $_.User } },
#{ N = 'LogonType'; E = { $_.Type } },
#{ N = 'ID'; E = { $_.ID } },
#{ N = 'State'; E = { $_.State } }
Subject = "$($SessionList.Count) Logged On users from $($NumberOfServers) online servers as at $($Today)"
}
Send-MailMessage #sendMailArgs
Rendering collected information in different places is way easier if you keep strict separation between data and presentation (or formatting) of said data.
For the $SessionList for example, that means doing less than what you're currently trying to do inside the loop:
$ErrorList = #()
$SessionList = foreach($server in $servers){
try{
qwinsta /server:$Server |Select-Object -Skip 1 |ForEach-Object {
[PSCustomObject] #{
Server = $server
Type = $_.Substring(1, 18).Trim()
User = $_.Substring(19, 20).Trim()
ID = $_.Substring(41, 5).Trim()
State = $_.Substring(48, 6).Trim()
}
} |Where-Object { $_.Type -notin 'console', 'services', 'rdp-tcp' -and $_.User -ne $null -and $_.User -ne 65536 }
}
catch{
$ErrorList += [pscustomobject]#{
Server = $server
ErrorRecord = $_
}
}
}
Notice how I don't construct any strings - I just create the custom objects, filter them - and then leave them as-is.
Now it becomes much easier to format the data as desired for different output media:
# For console output, simply pipe to Format-Table
$SessionList |Format-Table
if($ErrorList.Count -gt 0){
Write-Warning "The following servers had errors, please inspect"
$ErrorList |Format-Table
}
# For email output we can use `ConvertTo-Html`
$Body = $SessionList |ConvertTo-Html -As Table -Fragment
if($ErrorList.Count -gt 0){
$ErrorTable = $ErrorList |ConvertTo-Html -As Table -Fragment
$Body = $Body,$ErrorTable -join '<br />'
}
$sendMailArgs = #{
# ...
Body = ConvertTo-Html -Body $Body -Title "Session list"
BodyAsHtml = $true
# ...
}
I have a code to check on local machines logged sessions as below
Get-WmiObject win32_networkloginprofile | ? {$_.lastlogon -ne $null} | % {[PSCustomObject]#{User=$_.caption; LastLogon=[Management.ManagementDateTimeConverter]::ToDateTime($_.lastlogon)}}
Is it possible to check it for specific machines a logged sessions , even the one that have status "disconnected" ?
Obviously you need rights on the target computers:
Function Get-LoggedOnUser {
param(
[CmdletBinding()]
[Parameter(ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]$ComputerName = 'localhost'
)
begin {
$ErrorActionPreference = 'Stop'
}
process {
foreach ($Computer in $ComputerName) {
try {
quser /server:$Computer 2>&1 | Select-Object -Skip 1 | ForEach-Object {
$CurrentLine = $_.Trim() -Replace '\s+',' ' -Split '\s'
# If session is disconnected different fields will be selected
if ($CurrentLine[2] -eq 'Disc') {
[pscustomobject]#{
UserName = $CurrentLine[0];
ComputerName = $Computer;
SessionName = $null;
Id = $CurrentLine[1];
State = $CurrentLine[2];
IdleTime = $CurrentLine[3];
LogonTime = $CurrentLine[4..($CurrentLine.GetUpperBound(0))] -join ' '
}
# LogonTime = $CurrentLine[4..6] -join ' ';
}
else {
[pscustomobject]#{
UserName = $CurrentLine[0];
ComputerName = $Computer;
SessionName = $CurrentLine[1];
Id = $CurrentLine[2];
State = $CurrentLine[3];
IdleTime = $CurrentLine[4];
LogonTime = $CurrentLine[5..($CurrentLine.GetUpperBound(0))] -join ' '
}
}
}
}
catch {
New-Object -TypeName PSCustomObject -Property #{
ComputerName = $Computer
Error = $_.Exception.Message
} | Select-Object -Property UserName,ComputerName,SessionName,Id,State,IdleTime,LogonTime,Error
}
}
}
}
found this script to log off a single username
$scriptBlock = {
$ErrorActionPreference = 'Stop'
try {
## Find all sessions matching the specified username
$sessions = quser | Where-Object {$_ -match 'username'}
## Parse the session IDs from the output
#foreach($sessionsUser in $sessions){
$sessionIds = ($sessions -split ' +')[2]
Write-Host "Found $(#($sessionIds).Count) user login(s) on computer."
## Loop through each session ID and pass each to the logoff command
$sessionIds | ForEach-Object {
Write-Host "Logging off session id [$($_)]..."
logoff $_
}
#}
} catch {
if ($_.Exception.Message -match 'No user exists') {
Write-Host "The user is not logged in."
} else {
throw $_.Exception.Message
}
}
}
## Run the scriptblock's code on the remote computer
Invoke-Command -ComputerName NAME -ScriptBlock $scriptBlock
Is it possible to do the same for all users logged in session ?
I know that -match return first parameter , i tried to do " -ne $Null" but it returns a whole column with sessions instead a row and only check row [0] and the ones with actuall parameters ...
Iterate through the collection and logoff all the Id present:
$ScriptBlock = {
$Sessions = quser /server:$Computer 2>&1 | Select-Object -Skip 1 | ForEach-Object {
$CurrentLine = $_.Trim() -Replace '\s+',' ' -Split '\s'
# If session is disconnected different fields will be selected
If ($CurrentLine[2] -eq 'Disc') {
[pscustomobject]#{
UserName = $CurrentLine[0];
Id = $CurrentLine[1]
}
}
Else {
[pscustomobject]#{
UserName = $CurrentLine[0];
Id = $CurrentLine[2]
}
}
}
$Sessions | ForEach-Object {
logoff $_.Id
}
}
Invoke-Command -ComputerName gmwin10test -ScriptBlock $ScriptBlock
Instead of
$sessions = quser | Where-Object {$_ -match 'username'}
## Parse the session IDs from the output
#foreach($sessionsUser in $sessions){
$sessionIds = ($sessions -split ' +')[2]
use:
$sessionIDs = #()
$sessions = (quser) -split '`r`n'
for ($i=1;$i -lt $sessions.length;$i++){
$sessionIDs += $sessions[$i].substring(42,4).trim()
}
This way all sessions are recorded and the IDs are added to the $sessionIDs-array.
I'm using substring since the regex is not working if the session is disconnected. Also using forinstead of foreach since the first entry is the header ("ID").
I add user logon and logout tracking script
I found that some computers do not export csv as they have powershell 2.0 because append is not supported is there any alternative?
$ErrorActionPreference = 'Continue'
####**** Tracking user logon *****#####
$username = $env:USERNAME
$computername = $env:COMPUTERNAME
$ipv4 = Test-Connection -ComputerName (hostname) -Count 1 | foreach { $_.ipv4address }
$ipv6 = Test-Connection -ComputerName (hostname) -Count 1 | foreach { $_.ipv6address }
$timeformat='MM-dd-yyyy hh:mm:ss tt'
$time = (Get-Date).ToString($timeformat)
$action = 'Logon'
$filedate = 'MM-dd-yyyy'
$filename = 'CompInfo' + ' ' + $(Get-Date).ToString($filedate)
#Creates custom table and sorts the information
$table= New-Object –TypeName PSObject -Property #{
'Date/Time' = $time
'Username' = $username
'ComputerName'= $computername
'IPv4 Address' = $ipv4
'IPv6 Address' = $ipv6
'Notes/Action' = $action
} | Select date/time, username, computername, 'IPv4 Address', 'IPv6 Address', notes/action
$table | Export-Csv "d:\$env:username.csv" -NoClobber -append -NoTypeInformation
Try this
#Thanks to Dmitry Sotnikov
#https://dmitrysotnikov.wordpress.com/2010/01/19/export-csv-append/
#### Append CSV Powershell 2.0
function Export-CSV {
[CmdletBinding(DefaultParameterSetName='Delimiter',
SupportsShouldProcess=$true, ConfirmImpact='Medium')]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[System.Management.Automation.PSObject]
${InputObject},
[Parameter(Mandatory=$true, Position=0)]
[Alias('PSPath')]
[System.String]
${Path},
#region -Append (added by Dmitry Sotnikov)
[Switch]
${Append},
#endregion
[Switch]
${Force},
[Switch]
${NoClobber},
[ValidateSet('Unicode','UTF7','UTF8','ASCII','UTF32',
'BigEndianUnicode','Default','OEM')]
[System.String]
${Encoding},
[Parameter(ParameterSetName='Delimiter', Position=1)]
[ValidateNotNull()]
[System.Char]
${Delimiter},
[Parameter(ParameterSetName='UseCulture')]
[Switch]
${UseCulture},
[Alias('NTI')]
[Switch]
${NoTypeInformation})
begin
{
# This variable will tell us whether we actually need to append
# to existing file
$AppendMode = $false
try {
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Export-Csv',
[System.Management.Automation.CommandTypes]::Cmdlet)
#String variable to become the target command line
$scriptCmdPipeline = ''
# Add new parameter handling
#region Dmitry: Process and remove the Append parameter if it is present
if ($Append) {
$PSBoundParameters.Remove('Append') | Out-Null
if ($Path) {
if (Test-Path $Path) {
# Need to construct new command line
$AppendMode = $true
if ($Encoding.Length -eq 0) {
# ASCII is default encoding for Export-CSV
$Encoding = 'ASCII'
}
# For Append we use ConvertTo-CSV instead of Export
$scriptCmdPipeline += 'ConvertTo-Csv -NoTypeInformation '
# Inherit other CSV convertion parameters
if ( $UseCulture ) {
$scriptCmdPipeline += ' -UseCulture '
}
if ( $Delimiter ) {
$scriptCmdPipeline += " -Delimiter '$Delimiter' "
}
# Skip the first line (the one with the property names)
$scriptCmdPipeline += ' | Foreach-Object {$start=$true}'
$scriptCmdPipeline += '{if ($start) {$start=$false} else {$_}} '
# Add file output
$scriptCmdPipeline += " | Out-File -FilePath '$Path'"
$scriptCmdPipeline += " -Encoding '$Encoding' -Append "
if ($Force) {
$scriptCmdPipeline += ' -Force'
}
if ($NoClobber) {
$scriptCmdPipeline += ' -NoClobber'
}
}
}
}
$scriptCmd = {& $wrappedCmd #PSBoundParameters }
if ( $AppendMode ) {
# redefine command line
$scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
$scriptCmdPipeline
)
} else {
# execute Export-CSV as we got it because
# either -Append is missing or file does not exist
$scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
[string]$scriptCmd
)
}
# standard pipeline initialization
$steppablePipeline = $scriptCmd.GetSteppablePipeline(
$myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
} catch {
throw
}
}
process
{
try {
$steppablePipeline.Process($_)
} catch {
throw
}
}
end
{
try {
$steppablePipeline.End()
} catch {
throw
}
}
}
#### Append CSV Powershell 2.0
$ErrorActionPreference = 'Continue'
####**** Tracking user logon *****#####
$username = $env:USERNAME
$computername = $env:COMPUTERNAME
$ipv4 = Test-Connection -ComputerName (hostname) -Count 1 | foreach { $_.ipv4address }
$ipv6 = Test-Connection -ComputerName (hostname) -Count 1 | foreach { $_.ipv6address }
$timeformat='MM-dd-yyyy hh:mm:ss tt'
$time = (Get-Date).ToString($timeformat)
$action = 'Logon'
$filedate = 'MM-dd-yyyy'
$filename = 'CompInfo' + ' ' + $(Get-Date).ToString($filedate)
#Creates custom table and sorts the information
$table= New-Object –TypeName PSObject -Property #{
'Date/Time' = $time
'Username' = $username
'ComputerName'= $computername
'IPv4 Address' = $ipv4
'IPv6 Address' = $ipv6
'Notes/Action' = $action
} | Select date/time, username, computername, 'IPv4 Address', 'IPv6 Address', notes/action
$table | Export-Csv "D:\$env:username.csv" -NoClobber -Append -Delimiter ',' -NoTypeInformation
function get-localgroupmember {
[CmdletBinding()]
param(
[parameter(ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]$computername = $env:COMPUTERNAME
)
BEGIN {
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine
}
PROCESS{
foreach ($computer in $computername) {
$context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $computer
$idtype = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName
$group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context, $idtype, 'Administrators')
$group.Members |
select #{N='Server'; E={$computer}}, #{N='Domain'; E={$_.Context.Name}}, samaccountName
} # end foreach
} # end PROCESS
}
"Win12R2", "W12SUS" | get-localgroupmember
What I want is the output to look like the following and I want to flag the users in the admin group that are NOT part of our standard setup. Really I want to ignore the SAM accounts that are the domain accounts but flagging them for now works. What is happening is there is a looping through the SAM accounts to create this output. However when the machine is offline I need to note that too.
I also do NOT want to use a ValueFromPipeline but rather get a list of PC names from this command $allComputers = Get-ADComputer -Filter 'PasswordLastSet -ge $date' -properties PasswordLastSet | select Name and then use that variable as the source to loop through.
This is my revised code but I'm having issues creating a custom object to add to an array when there seems to be looping in the $group.Members |select #{N='Server'; E={$computer}}, #{N='Domain'; E={$_.Context.Name}}, samaccountName
function get-localgroupmember {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True,HelpMessage="Enter PC")]
[ValidateNotNullorEmpty()]
[object]$computername = $null
)
BEGIN {
$newArray = #();
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine
}
PROCESS{
foreach ($computer in $computername) {
If (Test-Connection -ComputerName $computer.name -Quiet -Count 1) {
try {
$context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $computer.name
$idtype = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName
$group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context, $idtype, 'Administrators')
$group.Members | select #{N='Server'; E={$computer.name}}, #{N='Domain'; E={$_.Context.Name}}, samaccountName
$objComputer = [pscustomobject] #{
Server = $computer.name
Domain = $group.Members | select #{N='Domain'; E={$_.Context.Name}}
Account = $Computer.samaccountName
}
} catch {
$objComputer = [pscustomobject] #{
Server = $computer.name
Domain = "Error"
Account = "Error"
}
}
} else {
$objComputer = [pscustomobject] #{
Server = $computer.name
Domain = "Off-Line"
Account = "Off-Line"
}
} $arrayNew += $objComputer
} # end foreach
} # end PROCESS
return $arrayNew
}
$date = [DateTime]::Today.AddDays(-1)
$allComputers = Get-ADComputer -Filter 'PasswordLastSet -ge $date' -properties PasswordLastSet | select Name
get-localgroupmember -computername $allComputers | Out-GridView
To be honest I would not try to output an array object like you are. There is really no need for it. Just create each object as needed, and let it output directly (you really don't need to use return as the function will pass any output down the pipeline unless you specifically tell it otherwise, with something like Write-Host, or Out-File). Also, it looks like your input wants an object (that's pretty vague), but you are then trying to loop through that object, and use each record as the name of a PC, so what you really want for input is an array of strings. In that case change your type from [object] to [string[]]. Lastly, a good bit of your code can be simplified if you just expand the Name property when creating your $AllComputers variable. Oh, I lied, this is the last thing... Your return statement is not in a valid section of your function. It would need to be something like END{ Return $arrayNew }
Then you just have to add a list of excepted accounts to not flag, or add some logic in, or something. Honestly, your code should do pretty much everything you want it to do with a little syntax fixing. Here's based on your script, where it outputs all members of the group and flags any that arn't a local account with the name 'Administrator', and are not a domain account listed as OK (defined in the BEGIN section, currently "Domain Admins" or "Workstation Admin").
function get-localgroupmember {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True,HelpMessage="Enter PC")]
[string[]]$computername
)
BEGIN {
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine
$OKAccounts = ("Workstation Admin","Domain Admins" | ForEach{[regex]::Escape($_)}) -join "|"
}
PROCESS{
foreach ($computer in $computername) {
If (Test-Connection -ComputerName $computer -Quiet -Count 1) {
try {
$context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $computer
$idtype = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName
$group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context, $idtype, 'Administrators')
$group.Members | select #{N='Server'; E={$computer}}, #{N='Domain'; E={$_.Context.Name}}, samaccountName, #{N='Flag';E={If(!(($_.Context.Name -eq $Computer -and $_.samaccountname -match "Administrator") -or ($_.context.name -ne $Computer -and $_.samaccountname -match $OKAccounts))){"X"}}}
} catch {
[pscustomobject] #{
Server = $computer
Domain = "Error"
SamAccountName = "Error"
Flag = ''
}
}
} else {
[pscustomobject] #{
Server = $computer
Domain = "Off-Line"
SamAccountName = "Off-Line"
Flag = ''
}
}
} # end foreach
} # end PROCESS
}
$date = [DateTime]::Today.AddDays(-1)
$allComputers = Get-ADComputer -Filter 'PasswordLastSet -ge $date' -properties PasswordLastSet | select -Expand Name
#$allComputers = $env:COMPUTERNAME
get-localgroupmember -computername $allComputers | Out-GridView
That should give you output something like:
Server Domain SamAccountName Flag
------ ------ -------------- ----
TMTsLab TMTsLab Administrator
TMTsLab TMTsTacoTruck.com Domain Admins
TMTsLab TMTsTacoTruck.com SomeAcct1 X
TMTsLab TMTsTacoTruck.com SomeAcct2 X
TMTsLab TMTsTacoTruck.com TMTech X
Probably better yet would be to filter out the accounts you don't want, rather than just not flag them. So change the #{N='Flag';E={If(!(($_.Context.Name -eq $Computer -and $_.samaccountname -match "Administrator") -or ($_.context.name -ne $Computer -and $_.samaccountname -match $OKAccounts))){"X"}}} bit to a Where statement, so that line would be:
$group.Members | select #{N='Server'; E={$computer}}, #{N='Domain'; E={$_.Context.Name}}, samaccountName | Where { !(($_.Server -eq $_.Domain -and $_.samaccountname -match "Administrator") -or ($_.Server -ne $_.Domain -and $_.samaccountname -match $OKAccounts)) }
You'll also want to remove the Flag = '' lines from your Catch and Else scriptblocks as well. Which then the code only returns something like:
Server Domain SamAccountName
------ ------ --------------
TMTsLab TMTsTacoTruck.com SomeAcct1
TMTsLab TMTsTacoTruck.com SomeAcct2
TMTsLab TMTsTacoTruck.com TMTech
Full function code at that point:
function get-localgroupmember {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True,HelpMessage="Enter PC")]
[string[]]$computername
)
BEGIN {
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine
$OKAccounts = ("Workstation Admin","Domain Admins" | ForEach{[regex]::Escape($_)}) -join "|"
}
PROCESS{
foreach ($computer in $computername) {
If (Test-Connection -ComputerName $computer -Quiet -Count 1) {
try {
$context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $computer
$idtype = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName
$group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context, $idtype, 'Administrators')
$group.Members | select #{N='Server'; E={$computer}}, #{N='Domain'; E={$_.Context.Name}}, samaccountName | Where{ !(($_.Server -ieq $_.Domain -and $_.samaccountname -match "Administrator") -or ($_.Server -ne $_.Domain -and $_.samaccountname -match $OKAccounts)) }
} catch {
[pscustomobject] #{
Server = $computer
Domain = "Error"
Account = "Error"
}
}
} else {
[pscustomobject] #{
Server = $computer
Domain = "Off-Line"
Account = "Off-Line"
}
}
} # end foreach
} # end PROCESS
}