MonthAugust 2015

Powershell Dynamic IP Helper

As an IT guy, it’s very helpful for me to be able to remote to a machine outside of the current network in order to do testing — e.g., if the client is having weird DNS issues or routing issues, or if I’m doing work on a public facing web site.  I’ve got my home machine set up in such a manner that after jumping through a few hoops, I can remotely control it from wherever I am.

I don’t pay for a static IP address on a monthly basis, it seems pretty exorbitantly priced — my present ISP wants $15/mo for the privilege.  That said, I’ve been fairly lucky in that they don’t change my IP address much.  When they do, however, it messes up a few things — as I self-host this blog (among other things), the name server needs to be updated with the new IP address.  It used to be that you could use things like dyndns.org and the like, but at this point all of those have ceased being free, and I never liked the .dyndns.org suffix on any of the domain names anyways.

What I did to overcome this was write a small Powershell script that uses one of the public IP check websites, grabs the current IP, and if it has changed since the last time it grabbed it, sends it to me via e-mail.  The next incarnation may make use of my hosting provider’s API to automatically update the DNS entry, but I haven’t gotten that far yet.

I set this script to run as a scheduled task every 30 minutes and it has been serving me well now for over a year and a half, so I figured it may be helpful to someone else.  See below.  If there’s anything odd you feel I did and are curious about, drop a comment and I’ll explain.  Alternately, if there’s a better way to do anything I’ve done, I’d love to learn it — drop some knowledge in the comments section to help me out!

Two prerequisites — first being you must make a credential file and put it in the same directory that the script is executing from, and this file can *easily* be reversed by anyone who grabs a copy of the file.  As such, I don’t recommend using your primary personal e-mail account for this.  This is a pretty minor step, and you only have to do it once — here’s how you do it.  Run powershell and input the following commands:

"Your E-mail Password Goes Here" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File "$pwd\credentials.txt"

Second prerequisite is that if you need to create the Event Log source that the script is going to write to — OR comment out the event logging portion of the script as it will throw errors if the log doesn’t exist.  The reason this isn’t in the script is that the script was written to run in non-elevated user context, and creating an event log requires an elevated session.  So, run the following from an elevated/administrator Powershell session to prepare your event log for the events or comment out with a # before each Write-EventLog line:

new-eventlog -source "IP Checker" -logname Application

Then, once you’ve gotten all of the pre-requisites completed, put your username, credential file location, and e-mail address info into the script below and you’ll be off to the races.

#Define credentials for e-mail later on
$username = "your_email_address@goes_here"
$password = cat $pwd\credentials.txt | convertto-securestring
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password

#Remove username and password from memory as they are no longer needed and I'm paranoid.
Clear-Variable username
Clear-Variable password

#Get the old IP from the text file in this directory
$OLDIP = Get-Content $pwd\IP.txt -totalcount 1

#Provide a somewhat useful event to indicate whether the IP.txt was blank or not.
If (!$OLDIP) {
  Write-EventLog -Logname Application -Source "IP Checker" -EntryType Information -EventId 3001 -Message "IP Address not received from IP.txt.  Script will behave as though IP address has changed."
}

#Get the current IP from ifconfig.me
$NEWIP_DIRTY = (Invoke-WebRequest ifconfig.me/ip).Content

#If IP was not retrieved, sleep 180 seconds and retry repeatedly until an IP is retrieved.
If (!$NEWIP_DIRTY) {
  Do {
    Write-EventLog -Logname Application -Source "IP Checker" -EntryType Information -EventId 3001 -Message "IP Address not received from ifconfig.me/ip.  Sleeping for 180 seconds and trying again."
    Start-Sleep -s 180
    $NEWIP_DIRTY = (Invoke-WebRequest ifconfig.me/ip).Content
  } while(!$NEWIP_DIRTY)
}

#Remove the trailing newline character to sanitize the content of the variable for the upcoming test
$NEWIP = $NEWIP_DIRTY.TrimEnd("`n")

#Write information to Event Log for Tracking/troubleshooting
Write-EventLog -Logname Application -Source "IP Checker" -EntryType Information -EventId 3000 -Message "Retrieved old IP from IP.txt: $OLDIP`nCurrent IP after sanitization is: $NEWIP"

#If the IP has changed, e-mail it to me
If($NEWIP -ne $OLDIP) {
  Write-EventLog -Logname Application -Source "IP Checker" -EntryType Information -EventId 3999 -Message "IP flagged as having changed.  Previous IP: $OLDIP, Current IP: $NEWIP"
  Send-MailMessage -SmtpServer your.mailserver.com -Port 587 -To "target@email.address" -Subject "New IP Address!" -Body "IP Address has changed.  Previous IP: $OLDIP, current IP: $NEWIP" -From "from@email.address" -UseSsl -Credential $cred
  Out-File -filepath $pwd\IP.txt -InputObject $NEWIP
}

Last Logged On User (PowerShell)

So, one of the things I’ve been working on is using SCCM and the Desired Configuration Management (DCM) functionality to test ‘compliance’ on desired configuration items (CIs) at one of the client sites I’m assigned.  As you can likely guess based on several of my other postings, most of these configuration settings are related to Outlook 2010.  If you’ve ever worked with SCCM DCM CIs that use PowerShell to check the contents of registry keys that exist in HKEY_CURRENT_USER, you’ve likely dealt with a few interesting issues:

  •  SCCM runs PowerShell deployed through the DCM interface via the SYSTEM account, therefore HKEY_CURRENT_USER is useless — you must instead loop through HKEY_USERS.  Your SYSTEM account does not have a users registry hive, and even if it did, I’m pretty sure that’s not the registry you want to be checking! 🙂
  • HKEY_USERS is fine, so long as you strip out unimportant profiles for your script — e.g., existing profiles that are part of the imaging process, local administrator profiles, etc.
  • HKEY_USERS is fine, even if a machine has more than one user — so long as it can be reasonably assumed that the last logged in user is the person whose compliance you are interested in.
  • HKEY_USERS is not accessible to normal users programmatically via PowerShell, so to test your scripts you must invoke PowerShell as the SYSTEM account, interactively.  I use PsExec for this, but I’m sure there’s other ways of doing such.

This creates a few interesting issues that require some thought in order to ensure that the code runs perfectly against all, say, 1,600 client computers — AND returns legitimate compliance results.

Thankfully, someone (Brian Wilhite) has done the legwork of putting together a solid PowerShell cmdlet that determines the last logged in user and whether that user is still logged in.  If you need something like this, take a look at Script – Get-LastLogon

I’ve now incorporated that into several of my CI scripts in order to do checks against data in the HKEY_USERS key while confirming that the user account whose registry I’m targeting corresponds to the user that has most recently logged in to the machine, and it’s done wonders to help sanitize the compliance results.

I always like uncovering someone else’s hard work, especially when it is precisely something I was thinking of writing myself.  I get the feeling Brian’s script is better than what I would have come up with!

Anyway, hope this helps anyone else who has found themselves in a similar situation.  I had such good results using this script I couldn’t help but post about how useful it had become for me.

Outlook – Cached Mode & Public Folder Favorites?

So, in my series of things I’m doing with Configuration Items / Configuration Baselines / etc. in SCCM, I’ve been tasked with creating a script that will check whether any profile on the target machine has been configured for Cached Mode w/ Public Folder Favorites caching enabled.

Typical to Outlook 2010 settings, this is stored in a binary encoded registry key deep in the Profiles location under HKCU.  Anyone who has worked with compliance settings in SCCM using Powershell will know that SCCM runs the Powershell script as the SYSTEM account, and not as the locally logged in user — as such, HKCU is a bust.  You must instead loop through HKEY_USERS in order to get the data.

This script does a recursive search through HKU, pulls the location of Outlook profiles where the 00036601 value is found (the specific value that contains the binary encoded representation of the configuration objects we are trying to report on), then checks the specific bytes to validate against the known value for Cached Mode and then the known value for Cached Mode w/ Public Folder Favorite Caching enabled.

If you just want to determine whether someone is in cached mode, you can remove the second bit check from the script — the first bit stays the same when Cached Mode is enabled regardless.

Here’s a quick reference to the bytes and their respective definition:

Byte 0 = 128 
  Cached Mode is Enabled
Byte 0 = 0 
  Cached Mode is not Enabled

Byte 1 = 29
  Public Folder Favorites are being Cached
Byte 1 = 25
  Public Folder Favorites are not Cached

To wrap, here’s the code snippet — note that this is a work in progress and while I’ve tested locally, it’s still pending further testing before I classify it as being 100%.  Use at your own risk, YMMV, etc.

I will update the post once I’ve done deeper testing to ensure it gets the right value across the board.

#Check client Outlook configuration settings for public folders cached mode

#Silence Errors to ensure sane output regardless of run circumstances
$ErrorActionPreference = 'silentlycontinue' 

#Create PS Drive to HKEY Users registry
$null = New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS 

#Define the source registry path to begin the search, including wildcard
$REG_LOC = "HKU:\*\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\"

#Define the value name we are looking for
$ConnectModeValueName="00036601"

#Set the initial state of the output of the script
$Output="Check not run"

#Recursively search through the registry location previously specified and set the variable with the results
$REG_Hits = (get-childitem -recurse $REG_LOC | ?{$_.Property -contains $ConnectModeValueName})

#If the cached mode is found, check the specific binary value to ensure it complies with best practices
If ($REG_Hits) {
 $Profile_Key = $REG_Hits.Name
 $Profile_Key = $Profile_Key -replace "HKEY_USERS", "HKU:"
 $Key_Data_0 = (Get-ItemProperty $Profile_Key -name 00036601)."00036601"[0]
 $Key_Data_1 = (Get-ItemProperty $Profile_Key -name 00036601)."00036601"[1]
 If ($Key_Data_0 -eq 128 -and $Key_Data_1 -eq 29) { $Output = "Enabled" }
}

#Remove PS Drive definition
$null = Remove-PSDrive HKU

#Provide output for SCCM
$Output

Default Resolution with no monitor?

I have a slightly more complicated than normal setup for my gaming PC — it runs via HDMI through a Denon receiver (3310CI, if you’re curious), and then into my big screen DLP (73″ Mitsubishi WD73640, if you’re curious).  It works pretty well, my GPU has overscan correction so the small amount of overscan is easily remedied.

The problem I was having was that when I turned the receiver off every night, the computer would revert back to 1024×768, moving all of my windows to the top-left corner and shrinking each window to fit the 1024×768 screen.  This, while minor, is definitely a nuisance when you have to re-size every open window every day.

I did some googling and found out that there are registry settings that control the resolution of the virtual monitor that is created when no other monitors exist, and was quickly able to solve this problem.  It took quite some deep googling to uncover the solution, so I felt I’d document it and post it as well in the hopes that it spreads further through the ether.

The key in question is:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Configuration

Under this key, you will see many devices (one for each monitor that has ever connected to your PC).  The ones we’re concerned with are ones that follow this naming convention:

SIMULATED_10DE_13C0_00000002_00000000_200100^291C550750C24B5F6E0897D9EE3B912B

The key part is the “SIMULATED” portion of the key name.  Under this key will be another key (or keys) named 00 (and 01, 02, 03, etc.).  Expand each key and look for the following DWORDs:

PrimSurfSize.cx = 1024
PrimSurfSize.cy = 768

When you see those values, this indicates that this virtual monitor is configured with a resolution of 1024×768 — the very resolution we are trying to avoid.  Change those entries to match your desired resolution.  In my case, 1920×1080.

Note that the entry for all of these values assumes you are entering in hexadecimal notation, so when you enter the value, click the radio button for ‘decimal’ to ensure that it is entered in the proper format, or this will not work as expected!

Expand the 00 key (and, if they exist, the 01, 02, and 03 keys) and you will likely see another nested 00 key (or 01, 02, 03, etc.).  In that key, you will see the following DWORDs:

ActiveSize.cx = 1024
ActiveSize.cy = 768
PrimSurfSize.cx = 1024
PrimSurfSize.cy = 768

Change those to match the desired resolution.

Do the same for any other SIMULATED monitors that are listed, and reboot your computer.  Do some testing by unplugging/replugging monitors — you should now see that the virtual monitor used by Windows when you have no monitor connected reflects the resolution you have set, and no longer drops you down to 1024×768.

In my experience, this persists through GPU driver upgrades, but a core OS upgrade (e.g., the one from Windows 8.1 to Windows 10 that I just did) requires you to re-configure the setting.

If you have any questions or concerns, drop a comment below!

 

Outlook – On Fast Networks / On Slow Networks

So, one of the clients I do business with was looking for a way to use SCCM to generate a compliance baseline including some key Outlook 2010 performance metrics they uncovered in their environment.

One of the settings they wanted to check was whether the “Connect to Microsoft Exchange using HTTP” “On fast networks, connect using HTTP first, then connect using TCP/IP” and “On slow networks, connect using HTTP first, then connect using TCP/IP” checkboxes were enabled.

Anyone who has worked with Outlook and Powershell likely knows that Outlook loves to store data in binary encoded keys, which are not trivial to decipher using standard methods.  Additionally, SCCM CIs run as the SYSTEM account and not as the logged on user’s account, so checking settings directly through HKCU is not an option.

To work through this, I wrote a Powershell script that loops through the USERS key, looks for an Outlook Profile Registry set, then checks the value of that bit and returns back whether the setting is enabled or disabled.  It’s a fairly simplistic script and I’ve commented it fairly well to indicate what each line does.  We’ve run this across about 1,600 machines at this point and the results seem sound, however, with any script you download from the internet, test small first to make sure it does nothing unexpected in your environment.  I take no responsibility if you copy/paste, then run this and it does anything you’d prefer it didn’t.

If you have any suggestions on how to improve the script, let me know in the comments below!

#Check client Outlook configuration settings for connect over HTTP/S and fast/slow link settings

#Silence Errors to ensure sane output regardless of run circumstances
$ErrorActionPreference = 'silentlycontinue' 

#Create PS Drive to HKEY Users registry
$null = New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS 

#Define the source registry path to begin the search, including wildcard
$REG_LOC = "HKU:\*\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\"

#Define the value name we are looking for
$ConnectModeValueName="00036623"

#Set the initial state of the output of the script
$Output="Disabled"

#Recursively search through the registry location previously specified and set the variable with the results
$REG_Hits = (get-childitem -recurse $REG_LOC | ?{$_.Property -contains $ConnectModeValueName})

#If the connect mode is found, check the specific binary value to ensure it complies with best practices
If ($REG_Hits) {
 $Profile_Key = $REG_Hits.Name
 $Profile_Key = $Profile_Key -replace "HKEY_USERS", "HKU:"
 $Key_Data = (Get-ItemProperty $Profile_Key -name 00036623)."00036623"[0]
 If ($Key_Data = 43) { $Output = "Enabled" }
}

#Remove PS Drive definition
$null = Remove-PSDrive HKU

#Provide output for SCCM
$Output

© 2018 TJ in IT

Theme by Anders NorénUp ↑