Determining the Dominant User and Setting the ManagedBy Computer Attribute

Hi again, this is Stephen Mathews and I’m here to talk about how to determine the dominant or primary user of a Windows operating system. This insight can help administrators facilitate direct communication with the affected user when a system needs management, and can even help non-enterprise users, such as a parent questioning which child is using their computer the most.

We’ll consider the different types of login data available, show how to expose it in the various OS instruments, and then use that information to update the system’s Active Directory computer object ‘ManagedBy’ attribute.

This post uses PowerShell Version 5 on Windows 10 to illustrate examples and it references settings that may not exist in legacy OS versions. All code examples are for illustration purposes only and should be thoroughly tested in a non-production environment. This post is intended to be used within a client OS using its built-in capabilities. Additionally, it is written from an Asset Tracking perspective and is not directly addressing Security and/or User Auditing concerns.

What type of information are you after?

Are you looking for the currently logged in user on the console, remotely logged in users, the last logged in user, the dominant user, or a list of all users?

How will the information be used: for real-time troubleshooting, historical reference, or external app consumption?

Will you script a solution, if so where will you output the data? Will the script be run manually or automatically, if automatically will you use a startup or login script, or a scheduled task?

File System

The filesystem can be the quickest and most efficient way to determine the regular users of a system. By expanding the ‘UserProfile’ environmental variable’s parent directory, you can see users that have had a profile created on that machine. You can check these profile directories and see the Created, Accessed, Modified, and Written timestamps for all of the systems’ users.

Unfortunately, this can also be the most misleading. The user profile directories are mapped via a Security Identifier (SID) that is stored in the registry. If a user account’s logon name is changed, they will still map to the original folder name. Also, if a user’s profile is corrupted they may not get a local directory and be redirected to the default profile. Additionally, the timestamps may not be updating depending on your OS version and/or auditing settings for those folders. And finally, you may not have rights to see the folders or their attributes.

  • Useful for: All Users, Last Logged On (Last access time), Dominant User (Timespan between created and last access time)
  • Get-ChildItem -Path (Get-Item -Path $env:USERPROFILE).PSParentPath | Select-Object -Property Name,*time*

Registry

The registry contains all the configurations and settings for the OS. There are multiple locations in the registry to find specific information about the users. User accounts are usually stored as SIDs inside the registry and will need to be converted into account names.

  • You can resolve a SID directly inside PowerShell which you’ll see later. You can see additional examples of this in Working with SIDs. This code will be worked into a customized Select-Object property hash-table; you can read about that in Using Calculated Properties.
  • (New-Object -TypeName System.Security.Principal.SecurityIdentifier(“S-1-5-18”)).Translate([System.Security.Principal.NTAccount])

  • $Object | Select-Object -Property SID,@{Name=”Account”;Expression={((New-Object -TypeName System.Security.Principal.SecurityIdentifier($_.SID)).Translate([System.Security.Principal.NTAccount])).Value}}

The registry is a sensitive part of the OS and can be corrupted. This risk of corruption leads many organizations to strictly limit and audit registry access. Certain registry settings may change only during startup and/or login, meaning the data may be stale while it’s being queried.

  • Useful for: All Users
  • Get-ChildItem -Path “HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\” -Recurse

  • Useful for: Currently logged on users:
  • reg query HKU

  • Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\hivelist

WMI

Windows Management Instrumentation is an infrastructure that exposes the OS to management. You can find current configuration settings, then get and set those properties.

WMI queries can be difficult to construct and may be resource intensive to the point of resource exhaustion. Take precautions to test the retrieving and setting of WMI components in a test environment before using inside production. Access to WMI may be restricted and audited for the same reasons as the registry.

  • Useful for: All Users, Last Logged On (Last use time), Currently Logged On (Loaded)
  • Get-WmiObject -Class Win32_UserProfile | Select-Object -Property SID,LocalPath,Loaded,LastUseTime,@{Name=”Account”;Expression={((New-Object -TypeName System.Security.Principal.SecurityIdentifier($_.Name)).Translate([System.Security.Principal.NTAccount])).Value}}

  • Useful for: Currently Logged On (Console)
  • Get-WmiObject -Class Win32_ComputerSystem | Select-Object -Property *ername*

ADSystemInfo

This is an overlooked tool to identify current system Active Directory network settings. The ‘UserName’ property will report the currently logged in user. It requires network connectivity to return settings and there’s no inherent way to run it remotely.

  • Useful for: Currently Logged On (Console)
  • $ADSystemProps = @(“ComputerName”,”DomainDNSName”,”DomainShortName”,”ForestDNSName”,”IsNativeMode”,”PDCRoleOwner”,”SchemaRoleOwner”,”SiteName”,”UserName”)

    $ADSystemInfo = New-Object -ComObject “ADSystemInfo”

    foreach ($ADSystemProp in $ADSystemProps) {

    $Value = $ADSystemInfo.GetType().InvokeMember($ADSystemProp, “GetProperty”, $Null, $ADSystemInfo, $Null)

    $ADSystemInfo | Add-Member -MemberType NoteProperty -Name $ADSystemProp -Value $Value -Force

    }

    $ADSystemInfo

Event Logs

Event logs are the record keepers of all activities on the system. As such they are the definitive source for tracking the login process. Logging can be turned on or off per provider and the logging level can be tailored based upon the event type: Critical, Error, Warning, Information, and Verbose. The ‘UserID’ property is typically set to the SID of the account creating the event, this is automatically translated for you in the Event Viewer. If the individual Event Log does not populate the ‘UserID’ property, you can parse the event message text with a SID to find events.

  • Get-WinEvent -LogName “Microsoft-Windows-GroupPolicy/Operational” | Select-Object -First 1 -Property *


  • Get-WinEvent -LogName Security | Select-Object -First 1 -Property *

The event log filter can be difficult to configure and a poorly created filter may be resource intensive to the point of resource exhaustion. Access to certain logs may be restricted and not all event logs record the same information in their properties. Furthermore, the logs may be collected into a central repository, making them unavailable or lacking significant detail to make an accurate determination.

In the first example it uses the Group Policy Operational log and groups the events by ‘UserID’, the second example events do not populate the ‘UserID’ property and need the message data to be parsed for matching SIDs; the list of SIDs were defined from the Win32_UserProfiles class.

  • Useful for: All Users, Dominant User (Count)
  • Get-WinEvent -LogName “Microsoft-Windows-GroupPolicy/Operational” | Group-Object -Property UserID | Sort-Object -Property Count -Descending | Select-Object -Property Count,Name,@{Name=”Account”;Expression={((New-Object -TypeName System.Security.Principal.SecurityIdentifier($_.Name)).Translate([System.Security.Principal.NTAccount])).Value}}

  • $SecurityEvents = Get-WinEvent -FilterHashTable @{LogName=”Security”;ID=4624}

    $WMIUserProfiles = Get-WmiObject -Class Win32_UserProfile

    foreach ($WMIUser in $WMIUserProfiles) {

    $WMIUser | Add-Member -MemberType NoteProperty -Name Account -Value ((New-Object -TypeName System.Security.Principal.SecurityIdentifier($WMIUser.SID)).Translate([System.Security.Principal.NTAccount])).Value

    $WMIUser | Add-Member -MemberType NoteProperty -Name Events -Value ($SecurityEvents | Where-Object {($_.Properties).Value -contains $WMIUser.SID}).Count

    $WMIUser | Select-Object -Property Events,Account,SID

    }

System Center Configuration Manager

For those of you with SCCM, it does the hard work for you in its Asset Intelligence feature set. Click to read more about the SMS_SystemConsoleUser Client WMI Class that calculates the dominant user for you. Here are a couple of screen shots.


Using the information

In this example, we want to update the Active Directory computer object ‘ManagedBy’ attribute with the dominant user. In order for this to happen we have to edit the default permissions to that attribute in the Organizational Unit where the computer object resides. Step two utilizes a script to perform the update, there are easier ways to do this, however we want to utilize a process that is as intrinsic as possible to the OS.

  • On the OU where the computer objects are, add permissions for SELF for Descendent Computer objects and select “Write ManagedBy”.
  • #Create the script below and feed it the ‘UserName’ determined from the above solutions
    $DomUser = “UserName”

    #Set Filter strings for locating objects in AD
    $strComputerFilter = “(&(objectCategory=Computer)(name=” + $env:COMPUTERNAME + “))” #get current computer name from environment variable
    $strFilter = “(&(objectCategory=User)(samaccountName=$DomUser))” #username set to $DomUser defined above

    $objDomain = New-Object System.DirectoryServices.DirectoryEntry
    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = $objDomain
    $objSearcher.PageSize = 1000
    $objSearcher.Filter = $strFilter
    $objSearcher.SearchScope = “Subtree”

    #find LDAP path for User
    $ADUser = $objSearcher.FindAll()

    #create PowerShell ADSI object for User
    $ADSIUser=[ADSI]$ADUser.path

    #find LDAP path for Computer
    $objSearcher.Filter = $strComputerFilter
    $computer = $objSearcher.FindAll()

    #create PowerShell ADSI object for Computer
    $ADComputer=[ADSI]$computer.path

    #set attributes on computer AD object
    $ADComputer.managedby = $ADSIUser.distinguishedname
    #$ADComputer.employeeid = $ADSIUser.employeeID
    $ADComputer.setinfo()

  • Then configure the scheduled task to run as System with Highest Priority.



In closing, I hope this explains the different types of login information that can be collected, exposes those information locations for you to use, and inspires you to keep track of your assets. A special thanks to Mike Kanofsky who created the script and found the permissions required to update the ‘ManagedBy’ computer object attribute and to Kevin Kasalonis for his SCCM expertise. Thanks for reading!

References:

SMS_SystemConsoleUser Client WMI Class

https://msdn.microsoft.com/en-us/library/cc143513.aspx