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

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.

How to run GPResult on a remote client with PowerShell

In the past, to run the GPResult command, you would need to either physically visit this client, have the user do it, or use and RDP connection.  In all cases, this will disrupt the user.  First, you need PowerShell remoting enabled on the target machine.  You can do this via Group Policy . Open PowerShell and type this command. Invoke-Command –ScriptBlock {GPResult /r} –ComputerName <ComputerName> Replace <ComputerName> with the name of the target.  Remember, the target needs to be online and accessible to you.

Error icon when creating a GPO Preference drive map

You may not have an error at all.  Take a look at the drive mapping below. The red triangle is what threw us off.  It is not an error.  It is simply a color representation of the Replace option of the Action field in the properties of the drive mappings. Create action This give you a green triangle. The Create action creates a new mapped drive for users. Replace Action The Replace action gives you a red triangle.  This action will delete and recreate mapped drives for users. The net result of the Replace action is to overwrite all existing settings associated with the mapped drive. If the drive mapping does not exist, then the Replace action creates a new drive mapping. Update Action The Update action will have a yellow triangle. Update will modify settings of an existing mapped drive for users. This action differs from Replace in that it only updates settings defined within the preference item. All other settings remain as configured on the mapped drive. If the