
Name: Moe
Email:
Posts by Moe:
Comparing files via checksum with Powershell
August 1st, 2009Have you ever had to push Gigabytes of files accross the network, knowing that the same version of many of those files already existed on the other end, but you didn’t have an immediate way of checking which ones were already in place?
If so, we can mitigate that problem somewhat with checksums.
Below is a simple function that will give you the checksum for a given file. For the sake of space and time, I’ve kept the function pretty minimal.
function Get-Checksum($file, $crypto_provider) {
if ($crypto_provider -eq $null) {
$crypto_provider = new-object 'System.Security.Cryptography.MD5CryptoServiceProvider';
}
$file_info = get-item $file;
trap { ;
continue } $stream = $file_info.OpenRead();
if ($? -eq $false) {
return $null;
}
$bytes = $crypto_provider.ComputeHash($stream);
$checksum = '';
foreach ($byte in $bytes) {
$checksum += $byte.ToString('x2');
}
$stream.close() | out-null;
return $checksum;
}
Let’s look at it in further detail.
It takes in the full path to a file as an argument, and an optional argument of your crypto provider of choice. If none is specified, it will default to using The MD5 crypto provider.
function Get-Checksum($file, $crypto_provider) {
if ($crypto_provider -eq $null) {
$crypto_provider = new-object 'System.Security.Cryptography.MD5CryptoServiceProvider';
}
Next, get the file, then attempt to read it in as a stream. If we are unable to open it, the function simply returns null instead of processing it further.
$file_info = get-item $file;
trap { ;
continue } $stream = $file_info.OpenRead();
if ($? -eq $false) {
return $null;
}
Next, call the ComputeHash method of the provider on the stream to create a byte array, and then create our empty checksum. For each byte, convert it to hexadecimal, then add it to the checksum.
Read the rest of this entry “
Disabling Admin UAC with Powershell
July 31st, 2009Well apparently some people don’t like User Account Control getting in the way of the Administrator account(s), and so they need a way to disable this annoying
behavior. Here’s a quick function I threw together today at the request of a friend, that will do just that. Please excuse the formatting…
function Set-AdminUAC([int] $value = 0) {
$computers = @($input);
if ($computers.count -lt 1) {
$computers += @('.');
}
$results = @{};
$hkey_lm = 'LocalMachine';
$hkey_cr = 'ClassesRoot';
$path_system = 'Software\Microsoft\Windows\CurrentVersion\Policies\System';
$path_clsid = 'CLSID';
$id_notify = '{FD6905CE-952F-41F1-9A6F-135D9C6622CC}';
$prompt_admin = 'ConsentPromptBehaviorAdmin';
foreach ($computer in $computers) {
$success = $false;
trap { ;
continue } $reg_lm = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hkey_lm, $computer);
if ($? -eq $false) {
$results.$computer = $success;
continue;
}
$reg_cr = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hkey_cr, $computer);
$key_system = $reg_lm.OpenSubKey($path_system, $true);
if ($key_system -ne $null) {
foreach ($index in 0..1) {
$value_admin = $key_system.GetValue($prompt_admin);
if ($value_admin -eq $value) {
$success = $true;
break;
}
else {
trap { ;
continue } $key_system.SetValue($prompt_admin, $value, 'Dword') | out-null;
if ($?) {
$key_system.Flush() | out-null;
$key_clsid = $reg_cr.OpenSubKey($path_clsid, $true);
$subkeys = @($key_clsid.GetSubKeyNames());
if (($key_clsid -ne $null) -and ($subkeys -contains $id_notify)) {
$key_clsid.DeleteSubKey($id_notify) | out-null;
$key_system.Flush() | out-null;
$key_clsid.Close() | out-null;
}
}
}
}
$key_system.Close() | out-null;
}
$reg_lm.Close() | out-null;
$reg_cr.Close() | out-null;
$results.$computer = $success;
}
return $results;
}
Checking for pending reboots with PowerShell
July 29th, 2009I found out the hard way a few days ago that our old, tried-and-true method of determining if there were pending reboots had changed with the latest Windows Server OS, Server 2008. In the old days (last year), we could check for pending reboots on Windows 2003 simply by looking at the ‘PendingFileRenameOperations’ property in the registry, but now with Server 2008, times have changed.
I have now tasked myself with spending quality time with a Server 2008 VM and a copy of ProcessMonitor, so as to see if I can find a new way to determine this sometimes-critical piece of information. Unfortunately, I have not yet found a 100% reliable way of getting this information, but I’d like to share what I’ve found so far.
function Get-PendingReboot($computer = '.') {
$hkey = 'LocalMachine';
$path_server = 'SOFTWARE\Microsoft\ServerManager';
$path_control = 'SYSTEM\CurrentControlSet\Control';
$path_session = join-path $path_control 'Session Manager';
$path_name = join-path $path_control 'ComputerName';
$path_name_old = join-path $path_name 'ActiveComputerName';
$path_name_new = join-path $path_name 'ComputerName';
$pending_rename = 'PendingFileRenameOperations';
$pending_rename_2 = 'PendingFileRenameOperations2';
$attempts = 'CurrentRebootAttempts';
$computer_name = 'ComputerName';
$num_attempts = 0;
$name_old = $null;
$name_new = $null;
$reg= [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hkey, $computer);
$key_session = $reg.OpenSubKey($path_session);
if ($key_session -ne $null) {
$session_values = @($key_session.GetValueNames());
$key_session.Close() | out-null;
}
$key_server = $reg.OpenSubKey($path_server);
if ($key_server -ne $null) {
$num_attempts = $key_server.GetValue($attempts);
$key_server.Close() | out-null;
}
$key_name_old = $reg.OpenSubKey($path_name_old);
if ($key_name_old -ne $null) {
$name_old = $key_name_old.GetValue($computer_name);
$key_name_old.Close() | out-null;
$key_name_new = $reg.OpenSubKey($path_name_new);
if ($key_name_new -ne $null) {
$name_new = $key_name_new.GetValue($computer_name);
$key_name_new.Close() | out-null;
}
}
$reg.Close() | out-null;
if ((($session_values -contains $pending_rename) `
-or ($session_values -contains $pending_rename_2)) `
-or (($num_attempts -gt 0) -or ($name_old -ne $name_new))) {
return $true;
}
else {
return $false;
}
}
Base64 encoding and decoding with PowerShell
July 19th, 2009A few days ago, I needed to perfom some simple obfuscation on a string. Since I had used Base-64 encoding for this purpose in the past, I needed to find a quick way to perform Base-64 encoding and decoding with PowerShell. Surprisingly, I couldn’t find a blog post or tutorial detailing a simple, straight-forward way of performing this task, though I knew it was easy to do. Since it took me a while to sort it out, I figured that it would be a good idea to throw together a couple of functions for public consumption.
First we have the encode function.
function ConvertTo-Base64($string) {
$bytes = [System.Text.Encoding]::UTF8.GetBytes($string);
$encoded = [System.Convert]::ToBase64String($bytes);
return $encoded;
}
Then, we have the decode function.
function ConvertFrom-Base64($string) {
$bytes = [System.Convert]::FromBase64String($string);
$decoded = [System.Text.Encoding]::UTF8.GetString($bytes);
return $decoded;
}
Simple, straight forward. Also notice that I used UTF8 for the initial encoding. Feel free to substitute for UNICODE or whatever, as per your requirements. For a list of what’s available
Read the rest of this entry “
Verifying valid email addresses with PowerShell…sort of…
July 11th, 2009I was recently asked to whip up a quick function that would verify email addresses from a list, and extract the ones that were valid and unique. Apparently, someone had run a report which should have resulted in a list of email addresses, sent it to the requester, and then left for the day. Unfortunately, they ran it incorrectly, and the results were a mix of email addresses, names, LDAP paths, partial home addresses, and the like…in fact, tens of thousands of lines worth.
Now, if your familiar with email address standards, you know that there’s a very complex set of characters which would be considered valid for an email address. I’ve seen huge multi-line RegExes designed to verify *almost* any valid email address, and fortunately for me, I’m not interested in showing you any of those here. The fact is, they all fall short in some way. Instead, I’m going to show you a simple RegEx that should catch a majority of modern email addresses. The key is to know who’s standard you’re parsing for, and the company that I did this for has a strict one-or-more-word-characters along with one-or-more-dots standard on either side of the “@”.
Let’s look at it.
function Get-ValidAddresses() {
$lines = @($input);
$addresses = @();
$addresses_unique = @();
$pattern_address = '(?<Address>[\w\.\+]+@[\w\.]+)';
$pattern_replace = '(\A\.+|\.+\Z)';
foreach ($line in $lines) {
if ($line -match $pattern_address) {
$addresses += @($matches.Address);
}
}
$addresses = $addresses -replace $pattern_replace, '';
$addresses_unique = @($addresses | sort-object -unique);
return $addresses_unique;
}
First, we take in the piped input and assign it to an array. I like to avoid using the default/built-in variables where possible (a practice that was reinforced by what I read in Perl Best Practices a few years ago). Anyway, then set up a couple of arrays to store our email addresses in. After that, we set up our Regular Expressions for verifying which email addresses we believe to be valid.
Read the rest of this entry “
Getting a list of installed Windows Components remotely with PowerShell
July 11th, 2009Here we go again! Last time, we covered how to get a relatively accurate list of installed programs with PowerShell. This time around, we’re going to show you how to get a list of Windows Components and their installation states with PowerShell. Let’s begin by showing you the finished product, as a function. Then, we’ll cover how it works, piece by piece.
function Get-InstalledComponents($computer = '.') {
$components_installed = @();
$reg_paths = @('SOFTWARE\Microsoft\Windows\CurrentVersion'`
+ '\Setup\Oc Manager\Subcomponents');
$reg_paths += @('SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion'`
+ '\Setup\Oc Manager\Subcomponents');
$hkey = 'LocalMachine';
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hkey, $computer);
foreach ($reg_path in $reg_paths) {
$reg_key = $reg.OpenSubKey($reg_path);
if ($reg_key -eq $null) {
continue;
}
$names = $reg_key.GetValueNames();
foreach ($name in $names) {
$value = $reg_key.GetValue($name);
if ($value -gt 0) {
$components_installed += @($name);
}
}
$reg_key.close();
}
$reg.close();
if ($components_installed.count -lt 1) {
trap { ;
continue } $features = @(get-wmiobject -class 'Win32_ServerFeature' `
-computer $computer -erroraction 'Stop');
foreach ($feature in $features) {
$components_installed += @($feature.name);
}
}
return ($components_installed | sort);
}
As before, we take in the computer name as an argument, and if there isn’t one, we assume that the computer in question is the local machine (denoted by the “.”).
Read the rest of this entry “
Getting a list of installed programs remotely with PowerShell (part 2)
December 14th, 2008As an alternative to using WMI, another way of retrieving a list of installed programs on a remote machine is to use the [Microsoft.Win32.RegistryKey] class.
function Get-InstalledPrograms($computer = '.') {
$programs_installed = @{};
$error_action = 'Stop';
$reg_uninstall_paths = @('Software\Microsoft\Windows'`
+ '\CurrentVersion\Uninstall');
$reg_uninstall_paths += @('Software\Wow6432Node\Microsoft'`
+ '\Windows\CurrentVersion\Uninstall');
$pattern_valid_name = '^{[\w\W}]+\Z';
trap { ;
continue } $wmi_output = @(get-wmiobject -class 'Win32_Product'`
-computer $computer -ErrorAction $error_action);
if ($?) {
foreach ($product in $wmi_output) {
$name = $product.Name;
$version = $product.Version;
if ($name -ne $null) {
$programs_installed.$name = $version;
}
}
}
else {
$hkey = 'LocalMachine';
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hkey, $computer);
foreach ($reg_uninstall_path in $reg_uninstall_paths) {
$reg_uninstall_key = $registry.OpenSubKey($reg_uninstall_path);
if ($reg_uninstall_key -eq $null) {
continue;
}
$key_names = $reg_uninstall_key.GetSubKeyNames();
foreach ($key_name in $key_names) {
$key_properties = $reg_uninstall_key.OpenSubKey($key_name);
$name = $key_properties.GetValue('DisplayName');
$version = $key_properties.GetValue('DisplayVersion');
if (($key_name -imatch $pattern_valid_name) `
-and ($name -ne $null)) {
$programs_installed.$name = $version;
}
$key_properties.close();
}
$reg_uninstall_key.close();
}
$registry.close();
}
return $programs_installed;
}
We start with the original function shell, and add error checking that allows it to fall through to check the remote registry if the WMI call fails. Here’s an overview of the additions:
$reg_uninstall_paths = @('Software\Microsoft\Windows'`
+ '\CurrentVersion\Uninstall');
$reg_uninstall_paths += @('Software\Wow6432Node\Microsoft'`
+ '\Windows\CurrentVersion\Uninstall');
$pattern_valid_name = '^{[\w\W}]+\Z';
First, we set our registry paths to look in with the array $reg_uninstall_paths. The ‘Wow6432Node’ key is present on 64-bit boxes, and I’ve found that you need to check both locations in order to get an accurate view of everything that’s installed on a 64-bit machine.
After that, we set up a regular expression as $pattern_valid_name, which will be used to limit which keys under the Uninstall key that we use to compile our list from. Our requirements for the regex are that we need a pattern that begins with an open-brace, followed by mutiple word and non-word characters, as well as a closing brace. We also have to consider that the closing-brace may not be the last character of the key name, as names that end in expressions like “}_is1″ are quite common.
$hkey = 'LocalMachine';
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hkey, $computer);
foreach ($reg_uninstall_path in $reg_uninstall_paths) {
$reg_uninstall_key = $registry.OpenSubKey($reg_uninstall_path);
if ($reg_uninstall_key -eq $null) {
continue;
}
$key_names = $reg_uninstall_key.GetSubKeyNames();
foreach ($key_name in $key_names) {
$key_properties = $reg_uninstall_key.OpenSubKey($key_name);
$name = $key_properties.GetValue('DisplayName');
$version = $key_properties.GetValue('DisplayVersion');
if (($key_name -imatch $pattern_valid_name) `
-and ($name -ne $null)) {
$programs_installed.$name = $version;
}
$key_properties.close();
}
$reg_uninstall_key.close();
}
$registry.close();
Once we fetch the remote registry key, we get the subkeys and parse out the keys that we can’t use, keeping the DisplayName and DisplayVersion of the ones that we consider valid. After we finish, we perform cleanup by closing each of the keys that we opened.
Accessing the function should result in near-identical results as before.
Getting a list of installed programs remotely with PowerShell (part 1)
November 28th, 2008Something that I’ve found useful, and has been often needed, is a way to get an accurate list of the programs installed on a remote machine. Initially, I would have thought that something so simple would have had it’s own cmdlet. It appears that I was mistaken. In an effort to prevent people from reinventing the wheel, I present to you a function which will handle this:
function Get-InstalledPrograms($computer = '.') {
$programs_installed = @{};
$win32_product = @(get-wmiobject -class 'Win32_Product' -computer $computer);
foreach ($product in $win32_product) {
$name = $product.Name;
$version = $product.Version;
if ($name -ne $null) {
$programs_installed.$name = $version;
}
}
return $programs_installed;
}
The basic rundown is as follows:
function Get-InstalledPrograms($computer = '.') {
First, we take in the computer name as an argument, and if there isn’t one, we assume that the computer in question is the local machine (”.” is treated as the local machine by WMI). Then, we create a hash to store the program information in.
$win32_product = @(get-wmiobject -class 'Win32_Product' -computer $computer);
foreach ($product in $win32_product) {
$name = $product.Name;
$version = $product.Version;
if ($name -ne $null) {
$programs_installed.$name = $version;
}
}
We use the “Win32_Product” WMI object to get information about the installed programs on the machine, itterating over each entry and pulling out the name and version information. We skip NULL entries, in part because they’re not useful to us, and in part because they’ll wreck our hash. And yes, you will occasionally find entries that have NULL set as the program name, so it’s important to account for these instances.
Finally, the program returns the hash containing the results of our efforts. Calling the function should look something like this:
PS > $programs = Get-InstalledPrograms 'devtest001'
PS > $programs
Name Value
---- -----
Microsoft Windows Software ... 6.0.6000.0
Microsoft SQL Server Compac... 3.5.5386.0
Java(TM) 6 Update 13 6.0.130
Administrative Templates fo... 1.0.0
Windows Mobile 5.0 SDK R2 f... 5.00.1700.5.14343.06
...
So that’s it. In part 2 of this article, I’ll show you how you can retrieve this information by using [Microsoft.Win32.RegistryKey] instead of WMI, which is useful in instances where WMI in general, or “Win32_Product” in particular are unavailable to you.




