Skip to main content

Pulling the Last Logged on User from Your Clients

Yesterday on PowerShell.com, I had the opportunity to help an IT Pro pull the last logged on user from every one of their client machines.  What we discovered is that there are two places in the registry that hold this information. 

For the last local account, we extracted the name from:

HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUsername

For the last domain user:

HKLM:\Software\Microsoft\windows\currentVersion\Authentication\LogonUI\LastLoggedOnUser

Just to add a little, and since it was right there, here is the last SID:

HKLM:\Software\Microsoft\windows\currentVersion\Authentication\LogonUI\LastLoggedOnUserSid

To do this one at a time would have been to time consuming.  Also, what about the clients that were not online?  How are you going to record them?  Sitting in my Windows Server 2012 R2 class right now is an IT Pro with over 70,000 clients.  This would have been a nightmare to perform manually.  I am estimating that it would have taken about a minute per client to remote in, grab this information, and then move on. That means a total of 48 days of continuous work or 145 standard working days.  I went ahead and enclosed this code inside of one of my tool templates to solve some issues and make this a bit of a more robust experience.  Check out the help file.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

 

Function Get-LastLoggedOnUser

{

[CmdletBinding()]

Param(

   

    [parameter(Mandatory=$true,

                ValueFromPipeline=$true,

                ValueFromPipelineByPropertyName=$true)]

    [String[]]

    $ComputerName

 

   

    )

 

BEGIN

{

    # Place all private functions here.

 

    # Main Object Creation

    Function New-CustomObject

    {

        $Obj = New-Object -TypeName PSObject -Property @{

            "ComputerName" = $Null

            "Online" = $False

            "TimeStamp" = $Null

            "DomainUser" = $Null

            "DomainSID" = $Null

            "LocalUser" = $Null

        }

        $obj.PSObject.TypeNames.Insert(0,'Object')

        Write-Output $Obj

    } # END: Function New-CustomObject

 

} # END: BEGIN BLOCK

 

PROCESS

{

    # Get a fresh copy of the output object.

    $CustomeObject = New-CustomObject

 

    # Cycle through each client and process.

    ForEach ($C in $ComputerName)

    {

        Write-Verbose "Connecting to: $C"

 

        # Initialize the output data.

        $Data = $CustomeObject

 

        # Add the name of the current client being processed to the Object.

        $Data.ComputerName = $C

 

        # Add the current timestamp.

        $Data.TimeStamp = Get-Date

     

        Try

        {

            # Establish the PowerShell Session

            $SO = New-PSSessionOption -OpenTimeout 500 -NoMachineProfile

            $SessionHash = @{

                "ComputerName" = $C

                "SessionOption" = $SO

                "ErrorAction" = "Stop"

            }

         

            # Establish the new session.

            $S = New-PSSession @SessionHash

 

            # Execute on the remote client.

            $Hash = @{

                "Session" = $S

            }

            $Data = Invoke-Command -Session $S -ScriptBlock {

                Param ($Obj, $Fixed)

 

     

                # Set the ComputerName.

                $Obj.ComputerName = HostName

           

                # Mark the client as being "Online"

                $Obj.Online = $True

 

 

 

                $Splat = @{

                 "Path" = "HKLM:\Software\Microsoft\windows\currentVersion\Authentication\LogonUI"

                 "ErrorAction" = "SilentlyContinue"

                }

                # Get the last Domain User to log on.

                $Obj.DomainUser = Get-ItemProperty @Splat -Name LastLoggedOnUser |

                    Select -ExpandProperty LastLoggedOnUser

               

                # Get the last Domain SID to log on.

                $Obj.DomainSID = Get-ItemProperty @Splat -Name LastLoggedOnUserSid |

                    Select -ExpandProperty LastLoggedOnUserSid

 

                $Splat = @{

                 "Path" = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"

                 "ErrorAction" = "SilentlyContinue"

                }

 

                # Get the last local account to log on.

                $Obj.LocalUser = Get-ItemProperty @Splat -Name DefaultUserName |

                Select-Object -ExpandProperty DefaultUsername

 

                # End of remote commands. -------------------------------------

                # Return the object to the calling client.

                Write-Output $Obj

            } -ArgumentList $Data

       

            # Remove the PS Session.

            $S | Remove-PSSession

 

            Write-Verbose "Finished processing: $C"

        } # END: TRY BLOCK

        Catch

        {

        Write-Verbose "Error connecting to: $C."

        $Data.ComputerName = $C

        } # END: CATCH BLOCK

        

        # Write the data to the pipeline.

        Write-Output $Data

    }

} # END: PROCESS BLOCK

 

END

{

 

} # END: END BLOCK

 

<#

.SYNOPSIS

Gets the last logged on Domain and local user on a client.

 

.DESCRIPTION

Gets the last logged on domain user and the last local account used to log

on to a client.

 

.PARAMETER ComputerName

The name of the client to recover drive information from.

 

.EXAMPLE

Get-LastLoggedOnUser -ComputerName PHX-cl1, PHX-CL2

 

LocalUser      : admin

TimeStamp      : 4/9/2015 3:40:51 PM

DomainUser     : MCTExpert\jyoder

Online         : True

ComputerName   : PHX-CL1

DomainSID      : S-1-5-21-442550829-505139508-2737514282-1605

PSComputerName : lon-cl1

RunspaceId     : 0bba2b2c-97df-4e32-94dc-cbc045db7a92

 

LocalUser      : admin

TimeStamp      : 4/9/2015 3:40:52 PM

DomainUser     : MCTExpert\administrator

Online         : True

ComputerName   : PHX-CL2

DomainSID      : S-1-5-21-442550829-505139508-2737514282-500

PSComputerName : LON-CL2

RunspaceId     : 9f122008-d0da-4d40-9d4a-a81f2ba4c7b3

 

Returns information concerning the last logged on users for clients PHX-CL1

and PHX-CL2.

 

.EXAMPLE

"PHX-cl1", "PHX-CL2"  | Get-LastLoggedOnUser

 

Returns information concerning the last logged on users for clients PHX-CL1

and PHX-CL2.

 

.EXAMPLE

"PHX-cl1", "PHX-CL2"  | Get-LastLoggedOnUser | Export-CSV -Path D:\PS\Offline.csv

 

Exports the Results to a CSV File.

 

Import-CSV -Path D:\PS\Offline.csv | Where-Object Online -eq $False |

    Get-LastLoggedOnUser | Export-CSV -Path D:\PS\Offline2.csv

 

Imports the CSV file and filters for all clients that were offline during the

previous run of this command.  The previous offline clients are processed again

and a CSV file is generated so any clients that are still offline can be

processed again.

 

.NOTES

Requirements:

- PowerShell Remoting is enabled.

- You are logged on with credentials that allow you to remote to other clients.

===============================================================================

== Cmdlet: Get-LastLoggedOnUser                                              ==

== Author: Jason A. Yoder                                                    ==

== Company: MCTExpert of Arizona                                             ==

== Date: April 10, 2015                                                       ==

== Copyright: All rights reserved.                                           ==

== Version: 1.0.0.0                                                          ==

== Legal: The user assumes all responsibility and liability for the usage of ==

== this PowerShell code.  MCTExpert of Arizona, Its officers, shareholders,  ==

== owners, and their relatives are not liable for any damages.  As with all  ==

== code, review it and understand it prior to usage.  It is recommended that ==

== this code be fully tested and validated in a test environment prior to    ==

== usage in a production environment.                                        ==

==                                                                           ==

== Does this code make changes: NO                                           ==

===============================================================================

#>

} # End Get-LastLoggedOnUser 

 

Comments

Popular posts from this blog

Adding a Comment to a GPO with PowerShell

As I'm writing this article, I'm also writing a customization for a PowerShell course I'm teaching next week in Phoenix.  This customization deals with Group Policy and PowerShell.  For those of you who attend my classes may already know this, but I sit their and try to ask the questions to myself that others may ask as I present the material.  I finished up my customization a few hours ago and then I realized that I did not add in how to put a comment on a GPO.  This is a feature that many Group Policy Administrators may not be aware of. This past summer I attended a presentation at TechEd on Group Policy.  One organization in the crowd had over 5,000 Group Policies.  In an environment like that, the comment section can be priceless.  I always like to write in the comment section why I created the policy so I know its purpose next week after I've completed 50 other tasks and can't remember what I did 5 minutes ago. In the Group Policy module for PowerShell V3, th

Return duplicate values from a collection with PowerShell

If you have a collection of objects and you want to remove any duplicate items, it is fairly simple. # Create a collection with duplicate values $Set1 = 1 , 1 , 2 , 2 , 3 , 4 , 5 , 6 , 7 , 1 , 2   # Remove the duplicate values. $Set1 | Select-Object -Unique 1 2 3 4 5 6 7 What if you want only the duplicate values and nothing else? # Create a collection with duplicate values $Set1 = 1 , 1 , 2 , 2 , 3 , 4 , 5 , 6 , 7 , 1 , 2   #Create a second collection with duplicate values removed. $Set2 = $Set1 | Select-Object -Unique   # Return only the duplicate values. ( Compare-Object -ReferenceObject $Set2 -DifferenceObject $Set1 ) . InputObject | Select-Object – Unique 1 2 This works with objects as well as numbers.  The first command creates a collection with 2 duplicates of both 1 and 2.   The second command creates another collection with the duplicates filtered out.  The Compare-Object cmdlet will first find items that are diffe

How to list all the AD LDS instances on a server

AD LDS allows you to provide directory services to applications that are free of the confines of Active Directory.  To list all the AD LDS instances on a server, follow this procedure: Log into the server in question Open a command prompt. Type dsdbutil and press Enter Type List Instances and press Enter . You will receive a list of the instance name, both the LDAP and SSL port numbers, the location of the database, and its status.