Devices, Scripting, Technology, windows

100-level 101

By my semi-quasi scientific reasoning, I estimate that this scenario has occurred in my presence approximately 34.75 times in the past 10 years. That number could be completely fictitious, but you have to prove me wrong, so good luck.

Anyhow, it happened yesterday, and today I had to actually apply it again myself, so it reminded me to blabber about it again, here, on my blabber blog. Remember, this is 100-level 101 stuff, so if you start rolling your eyes, I warned you already.

Challenge: You need to confirm a registry key is set on a remote client, RIGHT THIS FREAKING SECOND. The registry key is under one of the users who uses that machine. You only know the following:

  • The machine name
  • The user’s first and last name

Caveats: You are logged onto one of the domain controllers. You do not have Configuration Manager. You only have a keyboard, a mouse, a brain, a pair of eyeballs, and possibly a sleeping dog and angry cat nearby. Nothing else. Clothing is optional.


  • You ping the remote computer (e.g. “DT001”) and it responds with a happy wave and a smile.
  • You open trusty, old, bearded REGEDIT.exe and click File / Connect Network Registry. You enter the computer name (e.g. “DT001”). It tells you to **** off.
  • You apply some wax to your mustache and curl the ends neatly, crack your knuckles and continue. If you don’t have a mustache, use someone else’s for now.
  • Open a PowerShell console
  • Type: Get-Service RemoteRegistry -ComputerName DT001
  • It returns some information, including Status = “Stopped”
  • You attempt to start it: Get-Service RemoteRegistry -ComputerName DT001 | Start-Service. But it tells you to **** off.
  • You crack your knuckles once more and dawn a sinister look, like Daniel Day Lewis in There Will Be Blood
  • Set-Service RemoteRegistry -ComputerName DT001 -StartupType Manual
  • Get-Service RemoteRegistry -ComputerName DT001 | Start-Service
  • So far, so good. Go back to REGEDIT and connect successfully
  • You open HKEY_USERS and see a bunch of SID stuff, like “S-1-5-21-1234567890-0987654321-234234234234-1234, but you don’t know which one is related to the desired user account
  • Your dog reminds you that you are currently logged onto a domain controller.
  • You know the user is “Jimmy Jerkweed”, so you search for him using Get-ADUser -Filter ‘Name -like “*Jerkweed*”‘ | select *
  • You find one with a SID property that matches the registry key names and dive in

The Short Version

  • ping DT001
  • Set-Service RemoteRegistry -ComputerName DT001 -StartupType Manual
  • Get-Service RemoteRegistry -ComputerName DT001 | Start-Service
  • Regedit.exe / Connect Network Registry / DT001
  • Get-ADUser -Filter ‘Name -like “*jerkweed*”‘ | select SID

Way too many times, this would stop at the second bullet (above). The technician would insist that either a firewall, or anti-virus, were blocking access. Or maybe there was a problem with the machine. Not so.

  • By default, the Remote Registry service is disabled. Therefore, it cannot be forced to start, especially remotely.
  • Without this service running, you cannot connect to the registry from a different machine on the network, regardless of your privileges.
  • In most cases, by default, as a user with direct (or indirect) administrative rights on the remote machine, you can change the service startup type property from “disabled” to “manual”, allowing you to then start it, even remotely.
  • When using a Windows workstation, or member server (not a domain controller), you can also run the Get-ADxxxx cmdlets, if you have RSAT installed and enabled. If you don’t, and can’t, you can install the AdsiPS powershell module and do the same using Get-AdsiUser.


Personal, Projects, Technology

ud-cmwt, conferences and doggy poo bags


A few people asked what I was blabbering about on Twitter recently that mentioned “ud-cmwt“. I promised I would elaborate, alas, I procrastinate, but here’s the nutshell:

Millions of years ago, before the last ice age melted and uncovered what would become Mitch McConnell and Keith Richards, there was a quasi web-project, oh, never mind. I’m too tired to be stupid. Wait. You’re never too tired to be stupid. I should say, too stupid to be tired.

Anyhow: it’s a revised/revamped/retooled CMWT built on Adam Driscoll’s fantabulously increditastical PowerShell module: UniversalDashboard (Community Edition). It’s at 0.0.5 on PowerShell Gallery, but it’s only a scaffold framework right now. It lets you poke around and list users, groups, computers, packages, applications, os images, and a bunch of SQL information (server, database, tables, etc.) – linked to ConfigMgr, AD, AzureAD and SQL Server, just like the old CMWT was (except for AAD part).

Yes, it’s one more project I’m piling on my pile of over-piled project piles. But it’s really fun. And 0.0.6 will add drill-down searching and detail views like CMWT had. Phase 3 will add manipulation gyration to the extrapolation of interpolation. hmm. Here are some screen shots to wow you with…

Upcoming Conferences

PowerShell Saturday – Raleigh

I’ll be presenting at PowerShell Saturday in Raleigh NC on Saturday, September 21, 2019. Yes, you read correctly: I’m actually getting in front of a large group of people and speaking. Some would say torturing is a better word, but I hope it’s at least mildly entertaining. Tickets are still available, but are going fast, so don’t wait too long!

Aside from me, there are quite a few incredible people presenting at the conference, so please do not make up your mind on my participation. Do it for the awesome stuff you’ll learn from the other awesome people. But if you attend my session, please stop by afterwards and say hello?

MMS Jazz – New Orleans

I will also be (tentatively) speaking at two (2) , yes, holy shit, at MMS Jazz Edition in New Orleans, November 10 – 14, 2019. The schedule is posted, but may change as things solidify. Tickets are still available, but not for much longer. Just take a look at the list of speakers and you’ll be looking for where your jaw fell off, right next to mine. I’m still pinching myself.

Doggy Poo Bags

So, one of other other human pets walking their dog masters around the neighborhood started bagging their master’s poo and leaving the bags where they tied them up. I’m not kidding. Little green bags all over the place.

One of the neighbors assumed it was me, but I kindly corrected his malformed perceptions. I showed him my custom doggy poo bags I purchased in a crate-sized package from Amazon. No one else uses them in my hood. I’m saving the planet, one pile at a time. Of course, they go into a landfill, sealed in plastic, for the next 5000 years, just like nature intended.


I’ve been toying with podcasting, solo format for now. I ran out of space on SoundCloud, so when I get time, I plan on finding a more suitable place to store and host them from. As if you don’t already have enough noise in your life. I’ll add some more.

Thank you for reading!

Projects, Scripting, Technology

Building Blocks: PowerShell module rollbacks

What is a “roll back” you ask? (I know you didn’t really ask, but for those that wanted to ask…) in general terms, it is rolling back to a previous version of some piece of software, in this case a PowerShell module. For example, going from module version 1.2 back to 1.1.

A customer asked me, “What’s the best way to roll back to a specific version of a PowerShell module?

I said, “As a consultant, the answer is ‘it depends’“, ha ha! Just kidding. Well, kind of kidding. Okay, not really kidding, but all kidding aside… The process usually follows this workflow (assuming this is a public module, which you do not own/maintain):

Rollback Scenarios

Reminder: Because this happens so often, it’s like struggling with a USB plug – – – Whenever you are working with installing, updating or removing PowerShell modules, open the PowerShell console using “Run as administrator”. Alternatively, you can manage them under your “user” scope alone.

For the following examples, I’m using the PowerShell module: dbatools. There is nothing wrong (as far as I’ve seen) with the latest version, but I’m going to roll it back to a previous version to demonstrate my incoherent blabbering.

Scenario A – Old Version Still Installed

If the PowerShell module was updated using Update-Module, there’s a good chance that the prior version(s) are still installed on the local system. To confirm, use Get-Module <modulename> -ListAvailable.

In this example, I have two (2) versions installed (1.0.15 and 1.0.20). I want to uninstall the newer version (1.0.20) and leave only 1.0.15 installed.

I would normally use Uninstall-Module <modulename> -RequiredVersion <bad-version> or in this example: Uninstall-Module dbatools -RequiredVersion 1.0.20, as shown below.

You may get an error saying another module is “dependent” upon the one you’re trying to remove (see example above). If so, make note of the dependent module, uninstall it, then try the first uninstall again. Once you have the version you want, you can reinstall the dependent module (assuming it’s not actually dependent on the version you just uninstalled, doh!!)

After all this fuss, it now shows dbatools version 1.0.15 installed.

Scenario B – PS Gallery

If only the newest version (the bad version) is installed, check to see if the prior version is still available on the PowerShell Gallery. You can do this using Find-Module <modulename> -AllVersions.

Warning: dbatools lists pretty much every version since inception, so the list is very long.

If the results show the version you want/need, simply uninstall the current module and install the specific version from the PS Gallery.

Tip: This method supports rolling back to as far back as the author maintains in the PS Gallery. If they chose to unlist a particular version that you need, this won’t work, and you’re on to scenario C below.

Scenario C – GitHub Repository

If the prior version you need is no longer available on the PowerShell Gallery, the next place to look is on the “Project site” or GitHub repository. In some cases, this isn’t possible, but thankfully, it’s more often available than not.

Go to the GitHub site, open the repository, confirm the version, and the branch, and click the Clone or Download button, then click Download Zip. Extract the ZIP file contents somewhere.

Keep in mind that the folder structure provided by the GitHub ZIP download is not the same as what PowerShell modules require in the default path environment. Use the following command to display the current module path…

(Get-Module <name> -ListAvailable).Path

Note the version number in the path string. You will need to “spoof” this to match the version you downloaded so the PowerShell environment will properly recognize it. For this example, just pretend it shows “…\1.0.20\…” and “…\1.0.15\…” doesn’t exist.

Navigate to the parent folder (e.g. the module name itself, “dbatools”), such as “c:\Program Files\WindowsPowerShell\Modules\dbatools”

Create a new sub-folder for the version you want (i.e. “1.0.15”)

Open the ZIP file, drill-down under the first root-level folder, to see the main files and folders. Extract the contents from there into that new module path folder on your hard drive.

IMPORTANT: This extract/copy process will place more than is really needed, but it’s okay. PowerShell will only load what it needs and ignore what it doesn’t need.

If there is not GitHub (or other) repository available, or the version is no longer available for some reason, you’re on to scenario D below.

Scenario D – F**k it

That’s right, just F**K it. Yell out obscenities, and claim you have Tourette syndrome. After you calm down, search for alternative sources:

  • Other systems which still have the older module version installed (copy the folders/files)
  • System or file backups which you could pilfer to get the older module files back. Use the $env:PATH variable to guide you towards the folder and file location(s).
  • Call a friend who might have an older version installed somewhere, and threaten them with fresh doughnuts or cold beer, until they give in.

If that doesn’t work, go to a gym and beat up a punching bag for an hour.


As it turned out, they’d built a PowerShell-based automation process using internal scripts, and modules available on the PowerShell Gallery. Nothing unusual about that; it is what it was intended for. However, they had also built-in an automatic “update all modules” task at the beginning of their script.

This is a major no-no, because it violates basic “change control” rules. Every change (emphasis on “every“) should (read: must) be tested prior to applying in a production environment. Making the update process part of the production workflow automatically breaks that rule. And in their case, the module they were using was updated to deprecate a parameter on a particular function, which crashed their particular process.

Be careful not to confuse what I’m saying with automated CI/CD pipelines (dev > test > prod). This is merging external changes into a production environment; skipping dev and test entirely. In a nutshell, if you follow standard change control practices, you should rarely, if ever, encounter this situation.

Long story short (like I’m any good at short stories), they couldn’t locate a local copy of the older version and didn’t have a suitable backup to search, but the older version of the module was available in PS Gallery, so they went with scenario B.

Then the angry pack of wolves climbed in through the bedroom window in the middle of the night and ate every single one of them. Oh wait, wrong story…

And they lived happily ever after. The end.

Cloud, Scripting, Technology

Building Blocks: GitHub Issues via PowerShell

The PowerShell module “PowerShellForGitHub” contains a powerful collection of functions to let you interact with, and manage, your GitHub goodies. (Note: read the Configuration section carefully before using). I won’t repeat the installation and configuration part since they already took care of that just fine.

After playing around with it, I found one useful way to leverage this is to query the open issues for my repos, and feed selected information to other things like e-mail, Teams, and so forth. Since it’s just providing a pipeline of information, you can send it off anywhere your mind can imagine.

#requires -modules PowerShellForGitHub
function Get-GitHubRepoIssues {
  param (
    [parameter(Mandatory=$True, HelpMessage="The name of your repository")]
    [string] $RepoName,
    [parameter(Mandatory=$False, HelpMessage="GitHub site base URL")]
    [string] $BaseUrl = ""
  try {
    $issues = Get-GitHubIssue -Uri "$BaseUrl/$RepoName" -NoStatus |
      Where-Object {$_.state -eq 'open'} | 
        Sort-Object Id |
          Select Id,Title,State,Labels,Milestone,html_url
    $issues | % {         
      $labels = $null         
      if (![string]::IsNullOrEmpty($ {
        $labels = $ -join ';'
        ID     = $_.Id
        Title  = $_.Title
        State  = $_.state
        Labels = $Labels
        Milestone = $_.milestone.title
        URL    = $_.html_url
  catch {
    Write-Error $Error[0].Exception.Message

Sample output…

So, if you have a GitHub account with active repositories and issues, you might be able to glue some cool things together using PowerShell. If you have a cool example, share it in the comments below and I’ll be happy to share it on Twitter as well.


Cloud, Scripting

Microsoft Teams and PowerShell

I just started playing around with the MicrosoftTeams PowerShell module (available in the PowerShell Gallery, use Find-Module MicrosoftTeams for more information). Here’s a quick sample of how you can get started using it…

$conn = Connect-MicrosoftTeams

# list all Teams

# get a specific Team
$team = Get-Team -DisplayName "Benefits"

# create a new Team
$team = New-Team -DisplayName "TechSupport" -Description "Technical Support" -Owner ""

# add a few channels to the new Team
New-TeamChannel -GroupId $team.GroupId -DisplayName "Forms Library" -Description "Forms and Templates"
New-TeamChannel -GroupId $team.GroupId -DisplayName "Customers" -Description "Information for customers"
New-TeamChannel -GroupId $team.GroupId -DisplayName "Development" -Description "Applications and DevOps teams"

# dump properties for one Team channel
$channelId = Get-TeamChannel -GroupId $team.GroupId |
Where-Object {$_.DisplayName -eq 'Development'} |
Select-Object -ExpandProperty Id

# add a user to a Team
Add-TeamUser -GroupId $team.GroupId -User "" -Role Member

Here’s a splatted form of the above example, in case it renders better on some displays…

$conn = Connect-MicrosoftTeams

# list all Teams

# get a specific Team
$team = Get-Team -DisplayName "Benefits"

# create a new Team
$params = @{
DisplayName = "TechSupport"
Description = "Technical Support"
Owner = ""
$team = New-Team @params

# add a few channels to the new Team
# NOTE: You could form an array to iterate more efficiently
$params = @{
GroupId = $team.GroupId
DisplayName = "Forms Library"
Description = "Forms and Templates"
New-TeamChannel @params

$params = @{
GroupId = $team.GroupId
DisplayName = "Customers"
Description = "Information for customers"
New-TeamChannel @params

$params = @{
GroupId = $team.GroupId
DisplayName = "Development"
Description = "Applications and DevOps teams"
New-TeamChannel @params

# dump properties for one Team channel
$channelId = Get-TeamChannel -GroupId $team.GroupId |
Where-Object {$_.DisplayName -eq 'Development'} |
Select-Object -ExpandProperty Id

# add a user to a Team
$params = @{
GroupId = $team.GroupId
User = ""
Role = 'Member'
Add-TeamUser @params

Scripting, System Center, Technology

Captain’s Log: cmhealthcheck

I’ve consumed way way waaaaay too much coffee and tea today. Great for getting things done, not great for my future health.

CMHealthCheck 1.0.8 is in the midst of being waterboarded, kicked, beaten, tasered and pepper-sprayed to make it squeal. I’m close to a final release. Among the changes in testing:

  • Discovery Methods
  • Boundary Groups
  • Site Boundaries
  • Packages, Applications, Task Sequences (just summary), Boot Images (summary), etc.
  • User and Device Collections
  • SQL Memory allocation (max/pct)
  • Fixed “Local Groups” bug
  • Fixed “Local Users” bug
  • Enhanced Logical Disks report
  • Fixed “Installed Software” sorting issue
  • Fixed “Services” sorting issue
  • Fixed null-reference issues with “Installed Hotfixes”

Still in the works:

  • Sorting issue with ConfigMgr Roles installation table
  • Local Group Members listing
  • More details for Discovery Methods
  • Client Settings
  • ADR’s
  • Deployment Summary
  • Enhancements to the HTML reporting features

Stay tuned for more.

Note: The current posted version (as of 3/8/19) is 1.0.7, which is what will install if you use Install-Module.

To load the 1.0.8 test branch, go to the GitHub repo, change the branch drop-down from “master” to 1.0.8 (or whatever the other name happens to be at the time) and then use the Download option to get the .ZIP file. Then extract to a folder, and use Import-Module to import the .psd1 file and start playing.

Projects, Scripting, System Center, windows



UPDATE: 1/14/2019 – version 1901.13.2 was posted to address a problem with the previous upload.  Apparently, I posted an out-of-date build initially, so I’ll call this the “had another cup of coffee build”.

Dove-tailing from the previous idiotic blog post, I’ve taken some time off to retool, rethink, redesign and regurgitate “skattertools” as a single PowerShell module.  The new version blends PoSHServer into the module and removes the need to perform a separate install for the local web listener.  The first version of this is 1901.13.1 (as in 2019, 01=January 13th, 1st release).

How to Install and Configure sktools

  • Open a PowerShell console using Run as Administrator
  • Type: Install-Module sktools
  • Type: Import-Module sktools
  • Type: Install-SkatterTools (this creates a default “sktools.txt” configuration file in your “Documents” folder)
  • Type: Start-SkatterTools
  • Open your browser and navigate to http://localhost:8080

This next part is only temporary, and will be improved upon soon:

  • Once the web console is open, expand “Support” and click “Settings” and modify to suit your Configuration Manager site environment.
  • Close and reopen the PowerShell console (still “Run as Administrator”)
  • Type: Start-SkatterTools
  • Refresh your web browser session

Work will continue until morale is eliminated.  Easter eggs are included, sort of.  Thoughts, feedback, bug reports, enhancement requests, angry snarky comments, are all welcome.  Enjoy!