Skip to content

Instantly share code, notes, and snippets.

@mekuls
Last active April 2, 2016 00:54
Show Gist options
  • Save mekuls/339046b682331c1289f9355985ba3368 to your computer and use it in GitHub Desktop.
Save mekuls/339046b682331c1289f9355985ba3368 to your computer and use it in GitHub Desktop.
These are some snippets of code I wrote as a proof of concept that illustrates how to run some powershell only if the user name provided is not currently logged in to the machine running the code. No production code here!

Remove-ProfileIfOffline.ps1 (Practical application)

This script is an example usage of the below Invoke-CommandIfOffline library it will delete a folder if a given username is offline

At the top of this script are two parameters, $Username and $ProfilePath. Hard code the two of them and you should be good to go.

$GLOBAL_USER_NAME = "PII\Adminitrator" # Not case sensitive
$GLOBAL_REMOVE_DIR = "C:\temp"

At the bottom of the script there is a small piece of logic to accomplish the task, change the scriptblock code if necessary to provide you with more verbose output etc.

# Begin Script
if (Test-Path $GLOBAL_REMOVE_DIR) {

	$sciptBlock = { Remove-Item -Recurse -Force -Path $GLOBAL_REMOVE_DIR }
	Invoke-CommandIfOffline -UserName $GLOBAL_USER_NAME -ScriptBlock $scriptBlock
}
# End Script

Open up a powershell window, cd to the directory that the script is in and call it..

PS>cd C:\Path\To\ScriptFolder
PS>.\Remove-ProfileIfOffline.ps1

Note that there is no output, so you'll need to monitor the outcome manually or add additional logic.

Invoke-CommandIfOffline.ps1 (General Solution)

This script contains general functions which can be used to execute commands if a given username is offline

This is a pretty flexible couple of functions, included in the script are two functions, both functions are necessary in order to get the job done. If you want to skip right ahead to an example script that would remove a local userprofile skip right to the other script at the bottom of this Document.

Get-LoggedOnUser

Will get an object representing all of the sessions that are active on a windows machine. This function also takes a UserName parameter which allows us to test whether a user is online or not.

See Get-Help Get-LoggedOnUser for more information.

Invoke-CommandIfOffline

Will call Invoke-Command with the scriptblock which is just a block of powershell shoved into a variable using curly braces {} if and only if there are no sessions belonging to the user on the computer.

This function has minimal error detection but it does check to see if the string provided as the username is a local computer account.

If the account in question is a domain account, the function will fail and there is currently no support for Domain accounts. To execute the function using a domain account, include the IgnoreAccountCheck flag and ensure that you pass in the appropriate account name.

To test an account name when your unsure.

  1. Log in to the account.
  2. Run the following in a powershell and observe the output.
$regExAntecent = '.+Domain="(.+)",Name="(.+)"$'
$loggedOnUsers = Get-WmiObject -Class Win32_LoggedOnUser
$loggedOnUser.antecedent -match $regExAntecent > $nul
$loggedOnUserName = $matches[1] + "\" + $matches[2]
Write-Host "Found User: ", $loggedOnUserName

See Get-Help Invoke-CommandIfOffline for more information and examples after sourcing the script.

To source the script run: Ensure you add the dot at the front to source it

PS>cd C:\PathTo\ScriptDir\
PS>. .\Remove-ProfilesNotLoggedIn.ps1
PS>Get-Help Invoke-CommandIfOffline
<#
.SYNOPSIS
Get's a full list of users that are currently logged
into a system.
.DESCRIPTION
Leverages WMI calls to extract user and session details
.PARAMETER UserName
(Optional) The name of the user account 'COMPUTER\USERNAME' to filter for.
.EXAMPLE
PS> Get-LoggedOnUser
Session : 4039626
User : PII\lukem
Type : Interactive
Auth : NTLM
StartTime : 3/30/2016 6:04:00 PM
Session : 4039597
User : PII\GeorgeT
Type : Interactive
Auth : NTLM
StartTime : 3/30/2016 6:20:00 PM
.EXAMPLE
PS> Get-LoggedOnUser -UserName "PII\lukem"
Session : 4039626
User : PII\lukem
Type : Interactive
Auth : NTLM
StartTime : 3/30/2016 6:04:00 PM
.NOTES
Function Name : Get-LoggedOnUser
Author : Luke Muccillo luke@lukemuccillo.com
Requires : PowerShell V2
Call this function whenever you need to pull a list of users from a windows machine.
NB// I never tested this script in a domain environment so YMMV.
#>
function Get-LoggedOnUser {
param (
[string]$UserName
)
$regExAntecent = '.+Domain="(.+)",Name="(.+)"$'
$regExDependent = '.+LogonId="(\d+)"$'
$logontypes = @{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$loggedOnUsers = @(Get-WmiObject -Class Win32_LoggedOnUser)
$logonSessions = @(Get-WmiObject -Class Win32_LogonSession)
$sessionUserMap = @{}
foreach ($loggedOnUser in $loggedOnUsers) {
$loggedOnUser.antecedent -match $regExAntecent > $nul
# Name Value
# ---- -----
# 2 lukem
# 1 PII
# 0 \\.\root\cimv2:Win32_Account.Domain="PII",Name="lukem"
$loggedOnUserName = $matches[1] + "\" + $matches[2]
$loggedOnUser.dependent -match $regExDependent > $nul
# Name Value
# ---- -----
# 1 4039626
# 0 \\.\root\cimv2:Win32_LogonSession.LogonId="4039626"
$session = $matches[1]
$sessionUserMap[$session] += $loggedOnUserName
}
$allSessionObjects = @()
foreach ($logonSession in $logonSessions) {
$loggedOnUserName = $sessionUserMap[$logonSession.LogonId]
$logonType = $logonTypes[$logonSession.LogonType.ToString()]
$authPackage = $logonSession.Authenticationpackage
$startTime = [Management.ManagementDateTimeConverter]::ToDateTime($logonSession.StartTime)
$o = New-Object -TypeName PSObject
$o | Add-Member -MemberType NoteProperty -Name "Session" -Value $logonSession.LogonId
$o | Add-Member -MemberType NoteProperty -Name "UserName" -Value $loggedOnUserName
$o | Add-Member -MemberType NoteProperty -Name "Type" -Value $logonType
$o | Add-Member -MemberType NoteProperty -Name "Auth" -Value $authPackage
$o | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $startTime
$allSessionObjects += $o
}
if (-not [string]::IsNullOrEmpty($UserName)) {
return $allSessionObjects | Where { $_.UserName -ieq $UserName }
}
return $allSessionObjects
}
<#
.SYNOPSIS
Checks to see if a user (COMPUTER\USERNAME) is logged in and, if the user is not logged
in, executes a script block.
.DESCRIPTION
This function is designed with the same idea as the Invoke-Command cmdlet. Except
in this function we also check to see if the user (COMPUTER\USERNAME) is currently
logged into the system prior to execution.
.PARAMETER UserName
[string]
The name of the user account 'COMPUTER\USERNAME' to filter for.
.PARAMETER ScriptBlock
[ScriptBlock]
The block of powershell code that needs to be executed.
.PARAMETER IgnoreAccountCheck
[switch]
If this value is **not** provided, the function will check to see if a local account exists with the UserName value provided.
If this value is provided, the function will skip the check to see if a local account exists with the UserName value provided.
.EXAMPLE
PS> $theScriptBlock = { Remove-Item -Recurse -Force -Path 'C:\Users\lukem' -WhatIf }
PS> Invoke-CommandIfOffline -Username "PII\lukem" -ScriptBlock $theScriptBlock
.EXAMPLE
# This example illustrates what happens when the user is online.
PS>Invoke-CommandIfOffline -UserName "Pii\lukem" -ScriptBlock { Write-Host "Hello World!" }
[!] The user is currently online. We cannot invoke the command at this time.
.EXAMPLE
# This example illustrates what happens when the user name provided is not a local account.
PS>Invoke-CommandIfOffline -UserName "BLAH" -ScriptBlock { Write-Host "Hello World!" }
Invoke-CommandIfOffline : [!] The user name provided does not exist.
At line:1 char:1
+ Invoke-CommandIfOffline -UserName "BLAH" -ScriptBlock { Write-Host "H ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.EXAMPLE
# This example illustrates a successful execution
PS>Invoke-CommandIfOffline -UserName "Pii\Administrator" -ScriptBlock { Write-Host "Hello World!" }
Hello World!
.NOTES
Function Name : Invoke-CommandIfOffline
Author : Luke Muccillo luke@lukemuccillo.com
Requires : PowerShell V3
Call this function whenever
1. You need to run a command (and)
2. You first want to ensure that a user is not online
NB// I never tested this script in a domain environment so YMMV.
#>
function Invoke-CommandIfOffline {
param(
[Parameter(Mandatory=$true)]
[string]$UserName,
[Parameter(Mandatory=$true)]
[ScriptBlock]$ScriptBlock,
[Switch]$IgnoreAccountCheck
)
if (-not $IgnoreAccountCheck) {
# Error detection that checks to see if a local account
# exists with the username provided.
$allUserAccounts = Get-WmiObject -Class Win32_UserAccount
$allUserCaptionNames = $allUserAccounts |
Select -ExpandProperty Caption
if ($allUserCaptionNames -inotcontains $UserName) {
$validUserNames = [string]::Join(",", $allUserCaptionNames)
Write-Error "[!] The user name provided does not exist."
Write-Error "[i] Valid local accounts: ", $allUserCaptionNames
}
}
$currentUserSessions = @(Get-LoggedOnUser -UserName $UserName)
if ($currentUserSessions.Count -ine 0) {
Write-Output "[!] The user is currently online. We cannot invoke the command at this time."
return
}
Invoke-Command -ScriptBlock $ScriptBlock
}
$GLOBAL_USER_NAME = "PII\Administrator" # Not case sensitive
$GLOBAL_REMOVE_DIR = "C:\temp"
<#
.SYNOPSIS
Get's a full list of users that are currently logged
into a system.
.DESCRIPTION
Leverages WMI calls to extract user and session details
.PARAMETER UserName
(Optional) The name of the user account 'COMPUTER\USERNAME' to filter for.
.EXAMPLE
PS> Get-LoggedOnUser
Session : 4039626
User : PII\lukem
Type : Interactive
Auth : NTLM
StartTime : 3/30/2016 6:04:00 PM
Session : 4039597
User : PII\GeorgeT
Type : Interactive
Auth : NTLM
StartTime : 3/30/2016 6:20:00 PM
.EXAMPLE
PS> Get-LoggedOnUser -UserName "PII\lukem"
Session : 4039626
User : PII\lukem
Type : Interactive
Auth : NTLM
StartTime : 3/30/2016 6:04:00 PM
.NOTES
Function Name : Get-LoggedOnUser
Author : Luke Muccillo luke@lukemuccillo.com
Requires : PowerShell V2
Call this function whenever you need to pull a list of users from a windows machine.
NB// I never tested this script in a domain environment so YMMV.
#>
function Get-LoggedOnUser {
param (
[string]$UserName
)
$regExAntecent = '.+Domain="(.+)",Name="(.+)"$'
$regExDependent = '.+LogonId="(\d+)"$'
$logontypes = @{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$loggedOnUsers = @(Get-WmiObject -Class Win32_LoggedOnUser)
$logonSessions = @(Get-WmiObject -Class Win32_LogonSession)
$sessionUserMap = @{}
foreach ($loggedOnUser in $loggedOnUsers) {
$loggedOnUser.antecedent -match $regExAntecent > $nul
# Name Value
# ---- -----
# 2 lukem
# 1 PII
# 0 \\.\root\cimv2:Win32_Account.Domain="PII",Name="lukem"
$loggedOnUserName = $matches[1] + "\" + $matches[2]
$loggedOnUser.dependent -match $regExDependent > $nul
# Name Value
# ---- -----
# 1 4039626
# 0 \\.\root\cimv2:Win32_LogonSession.LogonId="4039626"
$session = $matches[1]
$sessionUserMap[$session] += $loggedOnUserName
}
$allSessionObjects = @()
foreach ($logonSession in $logonSessions) {
$loggedOnUserName = $sessionUserMap[$logonSession.LogonId]
$logonType = $logonTypes[$logonSession.LogonType.ToString()]
$authPackage = $logonSession.Authenticationpackage
$startTime = [Management.ManagementDateTimeConverter]::ToDateTime($logonSession.StartTime)
$o = New-Object -TypeName PSObject
$o | Add-Member -MemberType NoteProperty -Name "Session" -Value $logonSession.LogonId
$o | Add-Member -MemberType NoteProperty -Name "UserName" -Value $loggedOnUserName
$o | Add-Member -MemberType NoteProperty -Name "Type" -Value $logonType
$o | Add-Member -MemberType NoteProperty -Name "Auth" -Value $authPackage
$o | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $startTime
$allSessionObjects += $o
}
if (-not [string]::IsNullOrEmpty($UserName)) {
return $allSessionObjects | Where { $_.UserName -ieq $UserName }
}
return $allSessionObjects
}
<#
.SYNOPSIS
Checks to see if a user (COMPUTER\USERNAME) is logged in and, if the user is not logged
in, executes a script block.
.DESCRIPTION
This function is designed with the same idea as the Invoke-Command cmdlet. Except
in this function we also check to see if the user (COMPUTER\USERNAME) is currently
logged into the system prior to execution.
.PARAMETER UserName
[string]
The name of the user account 'COMPUTER\USERNAME' to filter for.
.PARAMETER ScriptBlock
[ScriptBlock]
The block of powershell code that needs to be executed.
.PARAMETER IgnoreAccountCheck
[switch]
If this value is **not** provided, the function will check to see if a local account exists with the UserName value provided.
If this value is provided, the function will skip the check to see if a local account exists with the UserName value provided.
.EXAMPLE
PS> $theScriptBlock = { Remove-Item -Recurse -Force -Path 'C:\Users\lukem' -WhatIf }
PS> Invoke-CommandIfOffline -Username "PII\lukem" -ScriptBlock $theScriptBlock
.EXAMPLE
# This example illustrates what happens when the user is online.
PS>Invoke-CommandIfOffline -UserName "Pii\lukem" -ScriptBlock { Write-Host "Hello World!" }
[!] The user is currently online. We cannot invoke the command at this time.
.EXAMPLE
# This example illustrates what happens when the user name provided is not a local account.
PS>Invoke-CommandIfOffline -UserName "BLAH" -ScriptBlock { Write-Host "Hello World!" }
Invoke-CommandIfOffline : [!] The user name provided does not exist.
At line:1 char:1
+ Invoke-CommandIfOffline -UserName "BLAH" -ScriptBlock { Write-Host "H ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.EXAMPLE
# This example illustrates a successful execution
PS>Invoke-CommandIfOffline -UserName "Pii\Administrator" -ScriptBlock { Write-Host "Hello World!" }
Hello World!
.NOTES
Function Name : Invoke-CommandIfOffline
Author : Luke Muccillo luke@lukemuccillo.com
Requires : PowerShell V3
Call this function whenever
1. You need to run a command (and)
2. You first want to ensure that a user is not online
NB// I never tested this script in a domain environment so YMMV.
#>
function Invoke-CommandIfOffline {
param(
[Parameter(Mandatory=$true)]
[string]$UserName,
[Parameter(Mandatory=$true)]
[ScriptBlock]$ScriptBlock,
[Switch]$IgnoreAccountCheck
)
if (-not $IgnoreAccountCheck) {
# Error detection that checks to see if a local account
# exists with the username provided.
$allUserAccounts = Get-WmiObject -Class Win32_UserAccount
$allUserCaptionNames = $allUserAccounts |
Select -ExpandProperty Caption
if ($allUserCaptionNames -inotcontains $UserName) {
$validUserNames = [string]::Join(",", $allUserCaptionNames)
Write-Error "[!] The user name provided does not exist."
Write-Error "[i] Valid local accounts: ", $allUserCaptionNames
}
}
$currentUserSessions = @(Get-LoggedOnUser -UserName $UserName)
if ($currentUserSessions.Count -ine 0) {
Write-Output "[!] The user is currently online. We cannot invoke the command at this time."
return
}
Invoke-Command -ScriptBlock $ScriptBlock
}
# Begin Script
if (Test-Path $GLOBAL_REMOVE_DIR) {
$scriptBlock = { Remove-Item -Recurse -Force -Path $GLOBAL_REMOVE_DIR }
Invoke-CommandIfOffline -UserName $GLOBAL_USER_NAME -ScriptBlock $scriptBlock
}
# End Script
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment