Created
October 20, 2013 19:26
-
-
Save shaunhess/7074085 to your computer and use it in GitHub Desktop.
Reading the LastWriteTime of a registry key using Powershell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
At work recently I needed to pull together some information from the registry of a few thousand machines and include the last time the key had been updated. Lately I've been turning to Powershell more and more for my day to day tasks and this time was no different. However this simple task turned out to not be so easy, and it all revolved around acquiring the LastWriteTime of the registry keys. | |
Digging through WMI and .NET proved less fruitful than I had hoped, so off to Google I went. It seemed everyone had the solution if you wanted to query the machine locally, but with thousands of hosts in my Enterprise that wasn't going to work. Plus, who doesn't enjoy a good challenge. One particular script was very useful in pointing my team in the right direction, posted by Tim Medin over at blog.securitywhole.com. So we decided to adapt and modify his script to work with remote hosts. | |
I've only tested against a few machines at home, but I wanted to share it while I had time. I will update the post if I find issues in the production environment. And as always please feel free to weigh in and provide comments! | |
Download Script | |
function Get-RegKeyLastWriteTime { | |
<# | |
.SYNOPSIS | |
Retrieves the last write time of the supplied registry key | |
.DESCRIPTION | |
The Registry data that a hive stores in containers are called cells. A cell | |
can hold a key, a value, a security descriptor, a list of subkeys, or a | |
list of key values. | |
Get-RegKeyLastWriteTime retrieves the LastWriteTime through a pointer to the | |
FILETIME structure that receives the time at which the enumerated subkey was | |
last written. Values do not contain a LastWriteTime property, but changes to | |
child values update the parent keys lpftLastWriteTime. | |
The LastWriteTime is updated when a key is created, modified, accessed, or | |
deleted. | |
.PARAMETER ComputerName | |
Computer name to query | |
.PARAMETER Key | |
Root Key to query | |
HKCR - Symbolic link to HKEY_LOCAL_MACHINE \SOFTWARE \Classes. | |
HKCU - Symbolic link to a key under HKEY_USERS representing a user's profile | |
hive. | |
HKLM - Placeholder with no corresponding physical hive. This key contains | |
other keys that are hives. | |
HKU - Placeholder that contains the user-profile hives of logged-on | |
accounts. | |
HKCC - Symbolic link to the key of the current hardware profile | |
.PARAMETER SubKey | |
Registry Key to query | |
.EXAMPLE | |
Get-RegKeyLastWriteTime -ComputerName testwks -Key HKLM -SubKey Software | |
.EXAMPLE | |
Get-RegKeyLastWriteTime -ComputerName testwks1,testwks2 -SubKey Software | |
.EXAMPLE | |
Get-RegKeyLastWriteTime -SubKey Software\Microsoft | |
.EXAMPLE | |
"testwks1","testwks2" | Get-RegKeyLastWriteTime -SubKey Software\Microsoft ` | |
\Windows\CurrentVersion | |
.NOTES | |
NAME: Get-RegKeyLastWriteTime | |
AUTHOR: Shaun Hess | |
VERSION: 1.0 | |
LASTEDIT: 01JUL2011 | |
LICENSE: Creative Commons Attribution 3.0 Unported License | |
(http://creativecommons.org/licenses/by/3.0/) | |
.LINK | |
http://www.shaunhess.com | |
#> | |
[CmdletBinding()] | |
param( | |
[parameter( | |
ValueFromPipeline=$true, | |
ValueFromPipelineByPropertyName=$true)] | |
[Alias("CN","__SERVER","Computer","CNAME")] | |
[string[]]$ComputerName=$env:ComputerName, | |
[string]$Key = "HKLM", | |
[string]$SubKey | |
) | |
BEGIN { | |
switch ($Key) { | |
"HKCR" { $searchKey = 0x80000000} #HK Classes Root | |
"HKCU" { $searchKey = 0x80000001} #HK Current User | |
"HKLM" { $searchKey = 0x80000002} #HK Local Machine | |
"HKU" { $searchKey = 0x80000003} #HK Users | |
"HKCC" { $searchKey = 0x80000005} #HK Current Config | |
default { | |
"Invalid Key. Use one of the following options: | |
HKCR, HKCU, HKLM, HKU, HKCC"} | |
} | |
$KEYQUERYVALUE = 0x1 | |
$KEYREAD = 0x19 | |
$KEYALLACCESS = 0x3F | |
} | |
PROCESS { | |
foreach($computer in $ComputerName) { | |
$sig0 = @' | |
[DllImport("advapi32.dll", SetLastError = true)] | |
public static extern int RegConnectRegistry( | |
string lpMachineName, | |
int hkey, | |
ref int phkResult); | |
'@ | |
$type0 = Add-Type -MemberDefinition $sig0 -Name Win32Utils ` -Namespace | |
RegConnectRegistry -Using System.Text -PassThru | |
$sig1 = @' | |
[DllImport("advapi32.dll", CharSet = CharSet.Auto)] | |
public static extern int RegOpenKeyEx( | |
int hKey, | |
string subKey, | |
int ulOptions, | |
int samDesired, | |
out int hkResult); | |
'@ | |
$type1 = Add-Type -MemberDefinition $sig1 -Name Win32Utils ` | |
-Namespace RegOpenKeyEx -Using System.Text -PassThru | |
$sig2 = @' | |
[DllImport("advapi32.dll", EntryPoint = "RegEnumKeyEx")] | |
extern public static int RegEnumKeyEx( | |
int hkey, | |
int index, | |
StringBuilder lpName, | |
ref int lpcbName, | |
int reserved, | |
int lpClass, | |
int lpcbClass, | |
out long lpftLastWriteTime); | |
'@ | |
$type2 = Add-Type -MemberDefinition $sig2 -Name Win32Utils ` | |
-Namespace RegEnumKeyEx -Using System.Text -PassThru | |
$sig3 = @' | |
[DllImport("advapi32.dll", SetLastError=true)] | |
public static extern int RegCloseKey( | |
int hKey); | |
'@ | |
$type3 = Add-Type -MemberDefinition $sig3 -Name Win32Utils ` | |
-Namespace RegCloseKey -Using System.Text -PassThru | |
$hKey = new-object int | |
$hKeyref = new-object int | |
$searchKeyRemote = $type0::RegConnectRegistry($computer, $searchKey, ` [ref]$hKey) | |
$result = $type1::RegOpenKeyEx($hKey, $SubKey, 0, $KEYREAD, ` [ref]$hKeyref) | |
#initialize variables | |
$builder = New-Object System.Text.StringBuilder 1024 | |
$index = 0 | |
$length = [int] 1024 | |
$time = New-Object Long | |
#234 means more info, 0 means success. Either way, keep reading | |
while ( 0,234 -contains $type2::RegEnumKeyEx($hKeyref, $index++, ` $builder, [ref] $length, $null, $null, $null, [ref] $time) ) | |
{ | |
#create output object | |
$o = "" | Select Key, LastWriteTime, ComputerName | |
$o.ComputerName = "$computer" | |
$o.Key = $builder.ToString() | |
# TODO Change to use the time api | |
$o.LastWriteTime = (Get-Date $time).AddYears(1600).AddHours(-4) | |
$o | |
#reinitialize for next time through the loop | |
$length = [int] 1024 | |
$builder = New-Object System.Text.StringBuilder 1024 | |
} | |
$result = $type3::RegCloseKey($hKey); | |
} | |
} | |
} # End Get-RegKeyLastWriteTime function |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Sean. Stumbled upon the fantastic script you published way back in 2011. Been searching for a solution for weeks.
I tried this and it works perfectly when my local computer is used as a 'ComputerName'. Any other remote machine will return no data. Been trying to debug this without much success. Would you have time is debug this? I do have approx 20k VMs in my org that I need to read. And this would be the perfect script to incorporate as a function. Thanks, Clifton