Watch Demo×
×

See NinjaOne in action!

By submitting this form, I accept NinjaOne's privacy policy.

How to Monitor User Logins in Windows Systems with PowerShell

Key takeaways

  • Automated user monitoring: This PowerShell script automates the monitoring of user logins on Windows systems.
  • Customizable checks: Tailor the script to check for specific users or any logged-in user.
  • NinjaOne RMM integration: Integrates with NinjaOne RMM for enhanced customization and data sourcing.
  • Operational efficiency: Ensures necessary user accounts are active for application functionality.
  • Security enhancement: Aids in maintaining security by monitoring authorized and unauthorized logins.
  • Flexibility: Adapts to different runtime environments through environment variable handling.
  • Real-Time alerts: Provides immediate alerts based on user login status.
  • Compatibility: Supports Windows 10 and Windows Server 2012 R2 onwards.
  • Easy integration: Can be integrated into existing IT management tools for cohesive system monitoring.
  • Compliance support: Helps in ensuring compliance with security protocols by tracking user activities.

Background

The provided script is a PowerShell tool designed for IT professionals and Managed Service Providers (MSPs) to monitor user logins on Windows-based systems. Its relevance stems from the necessity to ensure that specific user accounts are active or to verify if any user is logged in, particularly for legacy applications requiring authenticated user sessions. The script stands out for its customization options and integration with NinjaOne RMM, our remote monitoring and management platform.

The script:

<#
.SYNOPSIS
    Alert if no user is logged in, or if a specified user is not logged in. It can also accept a custom text field name to identify the user to check.
.DESCRIPTION
    Alert if no user is logged in, or if a specified user is not logged in. It can also accept a custom text field name to identify the user to check.
    
    Intended for legacy apps that are non-functional without an actual user account signed in.
.EXAMPLE
    (No Parameters)
    itAdmin alternateAdmin tuser is currently signed in!

PARAMETER: -CheckUser "itAdmin"
    Enter in the user you'd like to alert on. ex. to alert on user itAdmin use -CheckUser "itAdmin"
.EXAMPLE
    -CheckUser "itAdmin"
    itAdmin is currently signed in!

PARAMETER: -CustomFieldName "ReplaceMeWithAnyTextCustomField"
    The name of a text custom field to grab the CheckUser value from.
.EXAMPLE
    -CustomFieldName "ReplaceMeWithAnyTextCustomField"
    itAdmin is currently signed in!

.OUTPUTS
    None
.NOTES
    Minimum OS Architecture Supported: Windows 10, Windows Server 2012 R2
    Release Notes: Renamed script and added Script Variable support
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
    Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
#>

[CmdletBinding()]
param (
    [Parameter()]
    [Switch]$ActiveOnly = [System.Convert]::ToBoolean($env:userMustBeActive),
    [Parameter()]
    [String]$CheckUser,
    [Parameter()]
    [String]$CustomFieldName
)

begin {
    # If script variables are used overwrite the existing variables.
    if ($env:userToCheckFor -and $env:userToCheckFor -notlike "null") { $CheckUser = $env:userToCheckFor }
    if ($env:retrieveUserFromCustomFieldName -and $env:retrieveUserFromCustomFieldName -notlike "null") { $CustomFieldName = $env:retrieveUserFromCustomFieldName }

    # This function is to make it easier to parse Ninja Custom Fields.
    function Get-NinjaProperty {
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
            [String]$Name,
            [Parameter()]
            [String]$Type,
            [Parameter()]
            [String]$DocumentName
        )

        if($PSVersionTable.PSVersion.Major -lt 3){
            throw "PowerShell 3.0 or higher is required to retrieve data from custom fields. https://ninjarmm.zendesk.com/hc/en-us/articles/4405408656013"
        }

        # If we're requested to get the field value from a Ninja document we'll specify it here.
        $DocumentationParams = @{}
        if ($DocumentName) { $DocumentationParams["DocumentName"] = $DocumentName }

        # These two types require more information to parse.
        $NeedsOptions = "DropDown","MultiSelect"

        # Grabbing document values requires a slightly different command.
        if ($DocumentName) {
            # Secure fields are only readable when they're a device custom field
            if ($Type -Like "Secure") { throw "$Type is an invalid type! Please check here for valid types. https://ninjarmm.zendesk.com/hc/en-us/articles/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality" }

            # We'll redirect the error output to the success stream to make it easier to error out if nothing was found or something else went wrong.
            Write-Host "Retrieving value from Ninja Document..."
            $NinjaPropertyValue = Ninja-Property-Docs-Get -AttributeName $Name @DocumentationParams 2>&1

            # Certain fields require more information to parse.
            if ($NeedsOptions -contains $Type) {
                $NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2>&1
            }
        }
        else {
            # We'll redirect error output to the success stream to make it easier to error out if nothing was found or something else went wrong.
            $NinjaPropertyValue = Ninja-Property-Get -Name $Name 2>&1

            # Certain fields require more information to parse.
            if ($NeedsOptions -contains $Type) {
                $NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2>&1
            }
        }

        # If we received some sort of error it should have an exception property and we'll exit the function with that error information.
        if ($NinjaPropertyValue.Exception) { throw $NinjaPropertyValue }
        if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions }

        # This switch will compare the type given with the quoted string. If it matches, it'll parse it further; otherwise, the default option will be selected.
        switch ($Type) {
            "Attachment" {
                # Attachments come in a JSON format this will convert it into a PowerShell Object.
                $NinjaPropertyValue | ConvertFrom-Json
            }
            "Checkbox" {
                # Checkbox's come in as a string representing an integer. We'll need to cast that string into an integer and then convert it to a more traditional boolean.
                [System.Convert]::ToBoolean([int]$NinjaPropertyValue)
            }
            "Date or Date Time" {
                # In Ninja Date and Date/Time fields are in Unix Epoch time in the UTC timezone the below should convert it into local time as a datetime object.
                $UnixTimeStamp = $NinjaPropertyValue
                $UTC = (Get-Date "1970-01-01 00:00:00").AddSeconds($UnixTimeStamp)
                $TimeZone = [TimeZoneInfo]::Local
                [TimeZoneInfo]::ConvertTimeFromUtc($UTC, $TimeZone)
            }
            "Decimal" {
                # In ninja decimals are strings that represent a decimal this will cast it into a double data type.
                [double]$NinjaPropertyValue
            }
            "Device Dropdown" {
                # Device Drop-Downs Fields come in a JSON format this will convert it into a PowerShell Object.
                $NinjaPropertyValue | ConvertFrom-Json
            }
            "Device MultiSelect" {
                # Device Multi-Select Fields come in a JSON format this will convert it into a PowerShell Object.
                $NinjaPropertyValue | ConvertFrom-Json
            }
            "Dropdown" {
                # Drop-Down custom fields come in as a comma-separated list of GUIDs; we'll compare these with all the options and return just the option values selected instead of a GUID.
                $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name"
                $Options | Where-Object { $_.GUID -eq $NinjaPropertyValue } | Select-Object -ExpandProperty Name
            }
            "Integer" {
                # Cast's the Ninja provided string into an integer.
                [int]$NinjaPropertyValue
            }
            "MultiSelect" {
                # Multi-Select custom fields come in as a comma-separated list of GUID's we'll compare these with all the options and return just the option values selected instead of a guid.
                $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name"
                $Selection = ($NinjaPropertyValue -split ',').trim()

                foreach ($Item in $Selection) {
                    $Options | Where-Object { $_.GUID -eq $Item } | Select-Object -ExpandProperty Name
                }
            }
            "Organization Dropdown" {
                # Turns the Ninja provided JSON into a PowerShell Object.
                $NinjaPropertyValue | ConvertFrom-Json
            }
            "Organization Location Dropdown" {
                # Turns the Ninja provided JSON into a PowerShell Object.
                $NinjaPropertyValue | ConvertFrom-Json
            }
            "Organization Location MultiSelect" {
                # Turns the Ninja provided JSON into a PowerShell Object.
                $NinjaPropertyValue | ConvertFrom-Json
            }
            "Organization MultiSelect" {
                # Turns the Ninja provided JSON into a PowerShell Object.
                $NinjaPropertyValue | ConvertFrom-Json
            }
            "Time" {
                # Time fields are given as a number of seconds starting from midnight. This will convert it into a datetime object.
                $Seconds = $NinjaPropertyValue
                $UTC = ([timespan]::fromseconds($Seconds)).ToString("hh\:mm\:ss")
                $TimeZone = [TimeZoneInfo]::Local
                $ConvertedTime = [TimeZoneInfo]::ConvertTimeFromUtc($UTC, $TimeZone)

                Get-Date $ConvertedTime -DisplayHint Time
            }
            default {
                # If no type was given or not one that matches the above types just output what we retrieved.
                $NinjaPropertyValue
            }
        }
    }

    function Get-User {
        $quser = quser.exe
        $quser -replace '\s{2,}', ',' -replace '>' | ConvertFrom-Csv
    }

}process {

    # If a custom field name is provided grab the text and overwrite the $CheckUser variable with it.
    if ($CustomFieldName) { 
        if (-not ($CheckUser)) {
            try{
                $CheckUser = Get-NinjaProperty -Name $CustomFieldName
            }catch{
                Write-Warning "$($_.ToString())"
            }

            if(-not $CheckUser){
                Write-Warning "The custom field $CustomFieldName was empty!" 
            }
        }
    }

    if($CheckUser){
        Write-Host "Checking for $CheckUser."
    }else{
        Write-Host "Checking if any user is logged in."
    }

    # This will work whether or not your looking for any user to be logged in ($CheckUser = $null) or a specific one.
    $LoggedInUsers = Get-User
    if ($ActiveOnly) {
        $Test = $LoggedInUsers | Where-Object { $_.USERNAME -match $CheckUser } | Where-Object { $_.STATE -like "Active" }
    }
    else {
        $Test = $LoggedInUsers | Where-Object { $_.USERNAME -match $CheckUser } 
    }

    # If we didn't find the user or users we were looking for error out and exit 1
    if (!$Test) {
        Write-Error "The user(s) you are checking for is not currently logged in!"
        $LoggedInUsers | Format-Table USERNAME, STATE, "LOGON TIME", SESSIONNAME | Out-String | Write-Host
        exit 1
    }
    else {
        # Otherwise exit 0
        Write-Host "$($LoggedInUsers.USERNAME) is currently signed in!"
        $LoggedInUsers | Format-Table USERNAME, STATE, "LOGON TIME", SESSIONNAME | Out-String | Write-Host
        exit 0
    }
    
}end {
    
    
    
}

 

Access over 300+ scripts in the NinjaOne Dojo

Get Access

Detailed breakdown

The script operates in several stages:

  • Initialization: It begins by setting up parameters that control its behavior, such as checking only active users or focusing on a specific user.
  • Environment Variable Handling: It checks for environment variables that might override its parameters, ensuring adaptability in different runtime environments.
  • Custom Field Retrieval: The script can fetch user information from custom fields defined, providing flexibility in sourcing login data.
  • User Login Retrieval: Utilizing the quser command, it retrieves the list of logged-in users, a core functionality of the script.
  • User Check and Alert: Depending on the parameters set, the script either checks for any logged-in user or a specific user and alerts accordingly.

Potential use cases

Imagine an IT administrator responsible for a network where specific applications require an admin user to be constantly logged in. Using this script, they can set up automated checks to ensure that the necessary user accounts are active, thereby maintaining application functionality and system security.

Comparisons

Traditional methods of user login monitoring might involve manual checks or basic scripting without customization options. This PowerShell script surpasses these methods with its integration with NinjaOne RMM, customization, and automatic alerting capabilities.

FAQs

Q: How does this script improve security?
A: By ensuring necessary user accounts are active and monitoring unauthorized logins.

Q: Can it be customized for non-admin users?
A: Absolutely, the script can be tailored to monitor any user account.

Q: Is it compatible with all Windows versions?
A: It supports Windows 10 and Windows Server 2012 R2 and above.

Implications

The script’s ability to monitor user logins is vital for maintaining security protocols, ensuring compliance, and preventing unauthorized access. It also aids in troubleshooting and operational maintenance by providing real-time user login data.

Recommendations

  • Regularly update the script to keep pace with system updates.
  • Customize the script parameters according to specific IT environment needs.
  • Integrate the script with existing IT management tools for a cohesive monitoring approach.

Final thoughts

Incorporating this PowerShell script into your IT infrastructure, especially when combined with NinjaOne, enhances your ability to monitor user logins effectively. NinjaOne’s platform provides an additional layer of management and security, making the script an invaluable part of a comprehensive IT management strategy.

Next Steps

Building an efficient and effective IT team requires a centralized solution that acts as your core service deliver tool. NinjaOne enables IT teams to monitor, manage, secure, and support all their devices, wherever they are, without the need for complex on-premises infrastructure.

Learn more about NinjaOne Remote Script Deployment, check out a live tour, or start your free trial of the NinjaOne platform.

Categories:

You might also like

NinjaOne Terms & Conditions

By clicking the “I Accept” button below, you indicate your acceptance of the following legal terms as well as our Terms of Use:

  • Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms.
  • Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party.
  • Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library belonging to or under the control of any other software provider.
  • Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations.
  • Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks.
  • Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script.
  • EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).