administration, business, Cloud, Devices, Projects, Scripting, System Center, Technology, web, windows

Grouping and Filtering Users and Devices: A Practical Guide

Why? Because it seems I’m getting an increased number of client requests for help with this stuff. And by “this stuff” I mean how to target resources (users, devices, mostly) for various things like policies, deployments, inventory, audit logging, sign-in activity, etc.

Effectively organizing users and devices is foundational to managing policies, deployments, and security at scale. Whether you’re targeting Microsoft Intune policies, Conditional Access rules, or software deployments, how you group objects determines your flexibility and maintainability. Here’s a breakdown of the most common methods.

In case you’re wondering, I’m not focused on security boundary aspects in this article. This is about identifying or targeting resources and accounts for other purposes (refer to the first paragraph above).

1. Naming Conventions

Using standardized prefixes, suffixes, or patterns in object names (e.g., WKS-NYC-FIN-001, JSMITH, SRV-PRNT-DFW-001).

Advantages:

  • Instantly human-readable; easy to identify location, department, or type at a glance
  • Works across nearly all platforms and tools—no special features required
  • Enables simple wildcard filtering in scripts and queries

Disadvantages:

  • Brittle—renaming objects when roles/locations change is tedious and error-prone
  • Inconsistent adoption leads to gaps; relies heavily on discipline
  • Limited queryability in modern identity platforms (dynamic groups can’t always parse naming patterns)

Administrative Overhead: Simple to start, but moderate to maintain at scale due to enforcement challenges.

Notes: More commonly used for devices than users. However, user names and properties should ALWAYS follow a consistent pattern. For example, if you use the “description” property, be consistent. It can (and often will) pay off incredibly later on when you need to mine that property for operational needs.

2. AD Organizational Units (OUs)

Placing objects in a hierarchical OU structure (e.g., OU=Finance,OU=NYC,DC=contoso,DC=com).

Advantages:

  • Native GPO targeting—policies link directly to OUs
  • Clear visual hierarchy in AD tools
  • Well-understood by most Windows admins, LDAP developers

Disadvantages:

  • Objects can only exist in one OU—no multi-dimensional grouping
  • Moving objects between OUs can break GPO inheritance unexpectedly
  • Irrelevant to cloud-only (Entra-joined) devices and increasingly hybrid environments

Administrative Overhead: Simple for on-prem GPO targeting, but complex when trying to maintain parity with cloud workloads.

Notes: For those still tied to Active Directory, OU assignments should be focused on policy relevance more than visual organization, unless you have a VERY simple environment with very few custom Group Policy Objects. Example image below for Active Directory user accounts.

# active directory
Get-ADUser -Filter "*" -SearchBase "OU=Finance, OU=Users, OU=CORP, DC=contoso, DC=local"

3. Security Groups / Entra / M365 Groups

Assigning users or devices to groups, then targeting those groups for policies or access. Or using them filter inventory, audit logs, etc. for reporting, or targeting deployments.

Advantages:

  • Objects can belong to multiple groups simultaneously—flexible multi-dimensional targeting
  • Native integration with Conditional Access, Intune, RBAC, and app assignments
  • Supports nested groups for hierarchical scenarios

Disadvantages:

  • Group sprawl is real—without governance, you’ll end up with hundreds of overlapping groups
  • Static group membership requires manual updates or automation
  • Troubleshooting “why does this user have access?” becomes detective work, especially with nested groups, multi-domain or forest-trust environments

Administrative Overhead: Moderate—requires naming conventions and lifecycle management for the groups themselves.

Notes: The overhead of maintaining static assignments can be mitigated through automation. For example, when onboarding user accounts, you can assign group memberships based on role-mapping such as department, job title, location, and so on.

Azure Management Groups, Azure Subscriptions, Azure Resource Groups, and Entra ID Administrative Units were not included here since they’re intended for security delegation and cost management more than purely for inventory-oriented organization. Azure Policy assignments are kind of a gray area, and I have enough gray as it is.

Examples using PowerShell without real coffee or quality sleep:

# active directory
$groupMembers = Get-ADGroupMember -Identity "Users-Finance-Managers" -Recursive
$managers = Get-ADGroupMember -Filter { name -like "*managers*" } -Recursive
# entra id (or graph)
$entraGroup = Get-EntraGroup -Filter "DisplayName eq 'Finance-Managers'"
$cloudManagers = Get-EntraGroupMember -GroupId $entraGroup.Id |
Where-Object {$_.'@odata.type' -eq '#microsoft.graph.user'} |
Select-Object Id, DisplayName, UserPrincipalName, acountEnabled

4. Dynamic Group Membership

Groups whose membership is automatically calculated based on attribute queries (e.g., user.department -eq “Finance”).

Not applicable to Active Directory domains. Void where prohibited, taxed or regulated. Batteries not included.

Advantages:

  • Zero manual membership management—objects automatically join/leave as attributes change
  • Ideal for large, fluid environments with frequent onboarding/offboarding
  • Enables attribute-driven policy targeting without touching group memberships

Disadvantages:

  • Membership updates are not instant—can lag by minutes to hours
  • Complex rules are hard to debug; subtle syntax errors cause silent failures
  • Requires clean, consistent attribute data—garbage in, garbage out

Administrative Overhead: Moderate to complex—rule authoring is straightforward, but ensuring attribute hygiene is ongoing work.

Note: The time it takes to update membership will be affected by total number of dynamic groups in the tenant, as well as number of object changes per update cycle, and dynamic rule configurations which are complex, and/or rely on operators like Match, Contains or memberOf. See link for more.

5. Attribute-Based Filtering (Direct) / Tags

Targeting objects directly by attribute values in policies or queries (e.g., filtering by extensionAttribute1, department, city). This also extends (conceptually at least) to Azure resource tags.

Imagine if you could literally tag every single thing in your home/apartment or vehicle. Everything. And then you had some magical tool that would find anything based on that tag. “Find spatula” or “Find guitar pick” and it lit up and broadcast it’s precise location. That’s kind of what this about.

Advantages:

  • No group management overhead—filter directly on source-of-truth data
  • Changes to attributes immediately affect targeting (no group sync delay)
  • Highly granular; combine multiple attributes for precise scoping

Disadvantages:

  • Not all platforms support direct attribute filtering (Intune filters do; some legacy tools don’t)
  • Requires well-governed attribute schema and consistent data population
  • Can become opaque—”why was this device targeted?” requires querying attributes

Administrative Overhead: Simple if attributes are already populated; complex if you need to establish and enforce an attribute schema from scratch.

Notes: This is more of a late-binding approach. Very much like using Azure resource tags: It lays the groundwork for other processes to query and return matching items based on search criteria. Some common examples: job title, department, physicalDeliveryOfficeName, employeeType, description, any of the various “extension” attributes, and so on. For Azure, this could be resource tags such as (just examples): “environment”, or “cost center”.

More feeble-minded examples using PowerShell:

# azure virtual machines
$azVMs = Get-AzVM -Status | Select-Object Id,Name,ResourceGroupName,PowerState,Location,Tags
# ...filter by name pattern
$azVMs | Where-Object {$_.Name -match "addc"}
# ...filter by location
$azVMs | Where-Object {$_.Location -eq "eastus"}
# ...filter by tag + value, and powered on
#azVMs | Where-Object {$_.Tags['DomainJoined'] -eq 'contoso.com' -and $_.PowerState -eq 'VM Running'}

Lame example of Azure Virtual machine with resource tags. Just add water, makes its own sauce.

Summary

So, in conclusion: there are many ways you can model an environment to make downstream automation, and reporting easier or more flexible. MBA folks might even say “robust” or “synergistic”, or “wholistically synergistic”, which is fine as long as it means they approve your budget request. Just nod and smile, and say thank you.

MethodBest ForOverhead
Naming ConventionsQuick identification, scriptingSimple → Moderate
AD OUsOn-prem GPO targetingSimple (on-prem)
Security GroupsMulti-dimensional access & policyModerate
Dynamic GroupsAutomated membership at scaleModerate → Complex
Attribute FilteringGranular, real-time targetingSimple → Complex

Final Thoughts

Most mature environments use a combination of these methods. OUs handle legacy GPO needs, dynamic groups automate cloud policy targeting, and attributes or resource tags provide the underlying data that drives additional flexibility. The key is investing in attribute hygiene early—clean data makes every other method more effective.

Start simple, automate where you can, and resist the urge to create a group for every edge case. Establish standards which are published in a central location, and require all staff to comply or have their company photo replaced with an image of dog poo.

Whatever you do, avoid creating secondary inventory data repositories to link things together. You should have ONE and only ONE source of truth for anything. Don’t make a spreadsheet or database to identify who’s in a department, when you can just populate the “department” attribute, group or tag. Make the systems work for you, not the other way around.

If you’re wondering why I chose the bar photo above, it’s because labeling and organizing bottles is common for most bartenders. Also for hospitals, car mechanics, dentists, electricians, and many other careers. It helps you find the right things when you need them. Not paying attention to that early will lead you straight back to the bar, and, well, you know how that ends.

Disclaimer

Blah blah blah… all information provided is for informational purposes only. Use or adaptation, even derivatives of derived derivations, of anything provided herein, in whole, or in part, is without warranty of fitness or purpose of any kind, in any galaxy or spatial realm, time dimensions notwithstanding. You assume any and all risk for damages, interruptions, delays, weird looks, gossip or drink bottles thrown from passing vehicles. The author assumes no risk or liability or blame or backhanded wierdness, even smirks or unusual grunt sounds, even from creatures with more than two legs. And, no, we don’t accept coupons. I’m not an attorney, but my family could form a law firm if they could stop talking about politics and Bruce Springsteen long enough to do the paperwork. I have no idea where this is going, but I’m sure glad you made it this far! Kirk out.

Scripting, Technology

3 Easy PowerShell Scripting Tips for Coding Masochists

Tip: Read the ‘Important Notes’ section, because these are notes that are important.

1. Optimize Authentication Requests

  • What: Check for Existing Authentication Contexts before Repeating
  • How: Use Connection Context output
  • Why: Avoid repetitive, unnecessary authentication
  • Where: Your script code, Azure Automation Runbooks
  • When: Right now

Most API’s provide return data or establish a “context” once you complete the authentication process. When using cmdlets like Connect-AzAccount, Connect-Entra, Connect-ExchangeOnline, Connect-MicrosoftTeams, Connect-MgGraph, Connect-PnPOnline, and so on, you can either redirect the output of these to a variable, or use a context function to fetch them.

Why? If you run the same script or code block repeatedly, and it prompts for authentication every time, it not becomes a hassle, but it can waste time. How much this factors into time savings will depend on your environment(s) and usage patterns. Consider the following code example:

# Comment heading, because you always have one, right?

Connect-AzAccount # will force authentication every time

# ...more code...

Each time you run that, it will prompt for the Azure authentication. A small change can make it so you only get prompted the first time…

if (-not (Get-AzContext)) {
    # will only get invoked when there is no existing context
    Connect-AzAccount
}

If you happen to work with multiple tenants, you may want to add a check for the specific tenant ID as well…

$tenantId = "your_kickass_tenant_id"

if ((Get-AzContext).Tenant.Id -ne $tenantId) {
    # only invoked if context doesn't match or there is no context
    Connect-AzAccount -Tenant $tenantId 
}

More examples…

$tenantId = "your_kickass_tenant_id"
if ((Get-EntraContext).TenantId -ne $tenantId) {
    # only invoked when you haven't had coffee
    Connect-Entra -Tenant $tenantId
}

if ((Get-MgContext).TenantId -ne $tenantId) {
    # only invoked when you're paying attention, same kick-ass Tenant Id most likely
    Connect-MgGraph -TenantId $tenantId -NoWelcome
}

$spoSiteUrl = "your_kickass_sharepoint_online_site_url"
if ((Get-PnPContext).Url -ne $spoSiteUrl) {
    # only invoked when you first connect to your kick-ass sharepoint site
    Connect-PnPOnline -Url $spoSiteUrl -Interactive
}

You can also use Get-PnPConnection as an alternative. The MicrosoftTeams module doesn’t have a context-related cmdlet that I know of, which kind of sucks, like a broken vacuum cleaner. But life isn’t all bad.

2. Avoid Re-typing Credentials

  • What: Avoid Re-entering Passwords, Tenant and Subscription IDs
  • How: Store Credentials, Tenant ID’s, Subscription ID’s in Secret Vaults
  • Why: To reduce mistakes, limit security exposure
  • Where: On your computer, in Azure KeyVaults, or Azure Automation Credentials and Variables
  • When: As soon as possible

You may have noticed that some of the examples above define $tenantId or $spoSiteUrl. You may be doing this with other things like subscription Id’s, resource groups, usernames, and more. This is VERY BAD – Do NOT do that!

Any sensitive values should be stored securely so that if your scripts land in the wrong hands, they don’t hand the keys to your stolen car.

If you’re using any of the PowerShell Connect- functions that support a -Credential parameter, you can save a little time by feeding that from a credential vault. One simple way to do this is with the SecretManagement module. This works with various credential vaults like Windows Credential Manager, LastPass, 1Password, BitWarden and more.

Note: This does not circumvent safety controls like Entra Privileged Identity Management (PIM)

$myCredential = Get-Secret -Name AzureLogin123 -Vault PersonalVault

3. Suppress Unwanted Noise

  • What: Disable or reduce unneeded output
  • How: Use parameters like -NoWelcome, -WarningAction SilentlyContinue, Out-Null (or $null = ... )
  • Why: Clean output reduces processing overhead and avoids pipeline noise
  • Where: Every Connect- cmdlet or function that returns noisy output that you aren’t putting to use.
  • When: Always

Each time you connect to Microsoft Graph, it displays a welcome message that looks like the top half of a CVS receipt, only without coupons. There’s a marketing tip for Microsoft: Inject coupon codes in your API connection responses. You’re welcome.

You will also see: “NOTE: You can use the -NoWelcome parameter to suppress this message.” So, guess what: You can add -NoWelcome to quiet it down. They don’t have a -STFU parameter, but you could always wrap that yourself.

In addition to benign output, there are situations where even Warning output can make things messy. For example, within Azure Automation Runbooks, if you have output sent to a Log Analytics Workspace, the Warning output stream doesn’t need idiotic boy-who-cried-wolf warnings filling up your logs.

Some modules have their own special kind of noise, like the PnP.PowerShell module. For this one to STFU you set a variable prior to using it:

$env:PNPPOWERSHELL_UPDATECHECK = "false"

Important Notes

These notes are important.

  • As with any PowerShell content, things will change over time and some parameters may be added, replaced or removed. The examples provided herein, forthwith, notwithstanding and ipso facto, lorem ipsum are semi-valid as of 2025, December 29, anno domini, 12:25:32 PM Eastern Standard Time, planet Earth, Solar System 423499934.
  • Never run any script code, provided by humans, reptiles or AI services, in any production environment without thoroughly testing in non-production environments. Unless of course, you just don’t care about being fired or sued, or sent to a torture facility in El Salvadore.
  • References to trademark names like CVS are coincidental and imply no sponsorship, condonement, favor, agreements, contracts, eye winks, strange head nods, or thumbs-up gestures from either party. And who has time for parties. Anyhow, I have a prescription to pick up at CVS.
  • If you don’t care for humor, that’s okay. Neither do I.
Projects, Scripting, Technology, web, windows

When Santa brings you a work request: Searching GitHub Repos and Gists for Text Patterns with PowerShell

This is my feeble attempt at blogging again. Now that blogging is out of fashion and we trust AI to tell us what to think, drink and eat, it’s probably as good a time to do it as ever. So, this was prompted (pardon the pun) by a need to find any and all references in my sprawling warehouse of festering code (GitHub) that mention “Invoke-WebRequest” which might be used on a Windows machine. The goal being to make sure to include -UseBasicParsing for safety reasons.

As Matthew Dowst explains in his recent YouTube video, this is related to CVE-2025-54100 and only affects Windows PowerShell 5.1. It does not apply to PowerShell 7, and therefore doesn’t apply to PowerShell running on MacOS or Linux.

Assumptions

  • You are familiar with GitHub and have a GitHub account
  • You are familiar with PowerShell (on Windows, Mac or Linux)
  • You need to search through a bunch of old code, like me.
  • You have GitHub CLI installed and operational
  • You have no life and are too broke to do something more fun
  • Your kids are grown and moved out, so you have nothing else going on
  • You haven’t won the lottery or inherited a fortune from a dead relative
  • You are addicted to some sort of caffeinated liquid substances like me

If you meet these conditions, you’re good to go.

Why?

Aside from having no life and being addicted to coffee, sometimes you might need to quickly search through a lot of source code, which may or may not be cloned to a local place (hard drive). Matt covers this scenario in the video linked above. All my code is hiding in old GitHub repositories and gists. Oh, and I don’t have a life.

Caveates, Disclaimers, etc.

This code was tested with Windows PowerShell 5.1 and PowerShell 7.5.4 on Linux. Why both? Because it was either that or crawl in the attic to get Christmas stuff down, and it’s cold up there today. I also don’t have a life.

Your results may vary. Test thoroughly in non-production environments on non-production code for non-production use. Or test it on mission critical life-supporting systems in production, because you ignore warnings like this anyway. Don’t blame me if it crashes anything important.

Searching through Code

There’s quite a few options for searching content within GitHub Repositories and Gists, from the obvious web portal search tools, to various PowerShell scripts and modules (sort of), REST API, and my recent favorite: GitHub CLI.

After sampling a dozen or so scripts, which were close, but not quite what I was looking for, I searched PowerShell Gallery (using AI assistance) but using Find-PsResource -Tag 'github' didn’t find more than a handful, but still not what I was looking for. That’s not to say there aren’t any, but
in a time crunch, I wasn’t able to locate something close enough. So I started playing with GitHub CLI.

PowerShell: Searching Repos

The source code: Get it here instead of copying from this blog.

function Search-GitHubRepository {
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true)][string]$SearchValue,
        [parameter(Mandatory = $false)][string]$Owner,
        [parameter(Mandatory = $false)][switch]$Summary
    )
    try {
        if ([string]::IsNullOrEmpty($SearchValue)) {
            throw "No SearchValue was provided"
        }
        if (-not (Get-Command "gh")) {
            throw "Install GitHub CLI first."
        }
        if ($IsLinux) {
            $cmd = "gh"
        } else {
            $cmd = "gh.exe"
        }

        $ghArgs = @('search', 'code', $SearchValue)

        if (![string]::IsNullOrEmpty($Owner)) {
            $ghArgs += "--owner=$Owner"
        }
        $ghArgs += @('--json', 'repository,path,url,textMatches')

        Write-Verbose "Command: $cmd $($arglist -join ' ')"
        $textmatches = & $cmd $ghArgs | ConvertFrom-Json
        Write-Verbose "$($textmatches.count) matches found" -ForegroundColor Cyan
        if ($Summary.IsPresent) {
            $textmatches | Select-Object -Property @{l = 'Repository'; e = { $_.repository.nameWithOwner } } |
                Select-Object -Property Repository | Sort-Object -Unique -Property Repository
        } else {
            $textmatches |
                Select-Object -Property path,url,@{l = 'Repository'; e = { $_.repository.nameWithOwner } },
                    @{l = 'Text'; e = { $_.textMatches.fragment } } |
                Sort-Object -Property Repository
        }
    } catch {
        [pscustomobject]@{
            Status   = "Error"
            Message  = $_.Exception.Message
            Trace    = $_.Exception.ScriptStackTrace
            Category = $_.Exception.CategoryInfo.Activity
        }
    }
}

Example:

Search-GitHubRepository -SearchValue "Invoke-WebRequest" -Owner "username" -Summary

This returns names of Repositories and matching items to provide a summary of matches.

Search-GitHubRepository -SearchValue "Invoke-WebRequest" -Owner "username"

This returns repository and file names and matching content as well.

PowerShell: Search Gists

The source: Get it here

function Search-GitHubGist {
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$true)][string]$SearchValue,
        [parameter(Mandatory=$false)][switch]$IncludeContent,
        [parameter(Mandatory=$false)][int]$Limit = 100
    )

    try {
        if ([string]::IsNullOrEmpty($SearchValue)) {
            throw "No SearchValue was provided"
        }
        if (-not (Get-Command "gh")) {
            throw "Install GitHub CLI first."
        }
        if ($IsLinux) {
            $cmd = "gh"
        } else {
            $cmd = "gh.exe"
        }

        $ghArgs = @('gist', 'list', '--filter', $SearchValue, '--include-content', '--limit', $Limit)
        $gists = & $cmd @ghArgs
        <#
        Filter results to map lines to properties as follows:

        b5db0c256f73f300eaea8c50d7973f9d boxstarter_sample2.txt
            BoxStarter Examples
                Invoke-WebRequest https://chocolatey.org/install.ps1 -UseBasicParsing | iex

        No spaces at the beginning of the line = id and filename
        4 spaces at the beginning of the line = description
        8 spaces at the beginning of the line = matching content
        #>
        $results = @()
        for ($i = 0; $i -lt $gists.Count; $i++) {
            $line = $gists[$i]
            if (![string]::IsNullOrEmpty($line)) {
                if (-not $line.StartsWith(" ")) {
                    # Line with no leading spaces = id and filename
                    $gistId      = $line.Substring(0, 32)
                    $filename    = $line.Substring(33)
                    $description = ""
                    $content     = ""

                    # Check next lines for description (4 spaces) and content (8 spaces)
                    if ($i + 1 -lt $gists.Count -and $gists[$i + 1].StartsWith("    ") -and -not $gists[$i + 1].StartsWith("        ")) {
                        $description = $gists[$i + 1].Trim()
                        if ($i + 2 -lt $gists.Count -and $gists[$i + 2].StartsWith("        ")) {
                            $content = $gists[$i + 2].Trim()
                        }
                    }

                    $results += [pscustomobject]@{
                        id          = $gistId
                        filename    = $filename
                        gistname    = $description
                        content     = $content
                    }
                }
            }
        }

        $results | foreach-object {
            $gistId   = $_.id
            $filename = $_.filename
            Write-Verbose "gist id: $gistId - filename: $filename"
            $gistContent = gh gist view $gistId --raw
            if ($IncludeContent.IsPresent) {
                Write-Verbose "Including content in results"
                $gistContent | select-string -Pattern $SearchValue -List |
                    select-object -Property @{l='gistId';e={$gistId}}, @{l='filename';e={$filename}}, @{l='line';e={$_.LineNumber}}, @{l='match';e={$_.Line}}
            } else {
                $gistContent | select-string -Pattern $SearchValue -List |
                    select-object -Property @{l='gistId';e={$gistId}}, @{l='filename';e={$filename}}, @{l='line';e={$_.LineNumber}}
            }
        }
    } catch {
        [pscustomobject]@{
            Status   = "Error"
            Message  = $_.Exception.Message
            Trace    = $_.Exception.StackTrace
            Category = $_.Exception.CategoryInfo.Activity
        }
    }
}

Example:

Search-GitHubGist -SearchValue "Invoke-WebRequest"

This returns a summary of Gists and filenames with matching content, but not the actual matching content in the output.

Search-GitHubGist -SearchValue "Invoke-WebRequest" -IncludeContent

This returns all matching Gists and filenames as well as matching line numbers and content portions for each.

Conclusion

You might find some mistakes, or notice that I conveniently forgot to include support for filtering Gists by Public or Secret (GitHub CLI supports that, by the way). If you think this blows chunks, please be kind, but any feedback is welcome, especially if it includes winning lottery numbers.

You can get a lot done when you don’t have enough money to do something else. Coffee helps too.
Go to the following link for more information on the GitHub CLI Comment Reference

Cloud, Technology, web

Square Wheels Syndrome: Getting Comfortable with Defects

Cloud platforms, social media, discussion forums, online shopping; it seems that no matter where you go there are issues with the design and behavior that never seem to get resolved. Even worse, are when people go beyond adapting to the issues into defending them as if they cannot, or should not, be improved.

I’m not sure where to begin. It’s like walking into a food court when I’m starving. Except that none of the menus are up to date, and none of the dishes are complete. “We’re still waiting on the chicken, fries, lettuce and tomato. But we can provide you with pickles, mustard and relish for now. The rest is on the road map.”

Whether it’s ambiguous “consumption-based” billing (some consumption, some flat-rate), inconsistent UX features, or mismatched UX vs CLI pairings, it can feel like navigating a labyrinth. API docs that leave you with more questions than answers. Multiple channels for submitting feedback that seem to lead nowhere. Support cases that take scenic routes through departments.

You’re not imagining things: it’s a little messy out there.

If only the left wing had flaps too!

That’s on our road map!

Landing gear?”

Those only come with license subscriptions.

I’m kidding, of course. Though the more realistic answer might be:

Based on customer feedback those features have been simplified. You’ll get used to it!

What really makes me scratch my head is how quickly we adapt to these quirks. It reminds me of an old saying about getting comfortable riding in a car with square wheels. The occupants become so conditioned to the bumps that they not only stop complaining, they start defending them as “the way it should be.”

I call this Square Wheels Syndrome (SWS).

Common symptoms include: accepting situations that contradict the very guidelines created by those who are breaking them. Side effects may include defending the status quo as if it’s optimal, mild confusion, and occasionally an urge to give your computer screen a stern talking-to.

So, in this security compliance report, the main category shows severity level ‘Low’, but when I expand it, every item in that group shows severity level ‘High’. That seems inconsistent.

It’s always been that way! What’s the big deal!?” (translation: no need to fix it because we’re used to it now)

Let’s translate that into a Mars colony scenario:

Do we really need to keep buying these oxygen bottles when the dome should provide enough for us to breathe?

It’s always been that way! You’ll adjust.

Contemporary SWS examples include: waiting 24 hours for logs to expose security events, waiting 24 hours to get package deployment status to remote devices, overbooking passengers on flights, and “upgrading” to a new version that removes features customers really liked in the previous version.

First off, the UI (or UX, whatever you prefer) often lacks consistency. Don’t believe me? Browse through your cloud subscriptions and see how many of these elements actually match across services:

  • Menu structure and style
  • Input controls (lists, combo boxes, checkboxes, radio buttons)
  • Pagination methods: Prev/Next, More…, Page numbers, Up/Down, Auto-extend on scroll
  • Breadcrumb menus: Do they persist on page reload?
  • Labels: Settings, Options, Preferences, Features (pick one!)
  • Filters: bubbles, lists, popups, buttons, checkboxes, tree lists
  • Visual elements: Emojis, symbols, galleries, “+”/”-” indicators

Add a point for each one that’s consistent across all services within your cloud subscription from the same vendor. For example, across Azure, M365, Intune, Entra ID, SharePoint Online, Teams, OneDrive, Exchange Online, Defender, and so on.

I hear from customers all the time: “it looks like they hired 20 different teams to build this, and they never talked to each other.” Not far from the truth, actually.

Some other examples:

Google Photos – Still waiting on that revolutionary invention called “tags.” Sure would make it easier to build albums to share. Feedback submitted… still waiting!

Amazon – The shopping portal looks slick, but wouldn’t it be nice if sorting by “customer reviews” considered both rating and review count? Five stars with one review probably shouldn’t outrank four stars with 20,000 reviews.

Social Media Platforms – Customizing your timeline feels like adjusting those thermostats that don’t actually control anything. The algorithm decides what you see, and it seems to have a preference for content that makes you type furiously.

Developer Tools – Is it too much to ask for navigation menus that follow the same patterns across different sections of the same product?

These are just a handful of examples that I hear about from clients, friends, neighbors, and yes, even strangers at checkout lines.

So, if you’re building digital products, please do us all a favor: make early decisions about your UX patterns and stick to them as you build your next cool gift to the world. Your users will thank you for not making them adapt to square wheels.

And if you’re a user suffering from SWS, maybe it’s time to speak up. After all, round wheels really do make for a smoother ride.

Cheers!

Society, Technology

Words and Meanings

Warning: I’m going to sound like an old curmudgeon. A very cranky, old curmudgeon. But hear me out, please?

I graduated high school in the 1980’s, and went to college in the 1990’s. Some would argue that makes me a “baby boomer”, or “boomer” for short. I would argue that the time period ascribed to that “generation” is insanely incorrect and meaningless, but that’s for another discussion. I consider myself more of a “post boomer” or “pre gen Z”, which could be “zoomer”.

That aside, one of the many social trends I’ve witnessed, has been the gradual dismissal of certain rote educational exercises. Among them: word meanings.

This is admittedly a very broad subject, so I will be careful to pick my battles here, and cite examples for each. What I’m aiming for is to gain some appreciation for the relationship between things, actions or concepts, and their respective names. And that those words quite often have immutable definitions. This seems to be almost completely lost on the American youth today. And I don’t place most of the blame on them.

Where this manifests most often is in conversation, online or in person. But it also finds its way into official (business, government, organizational, etc.) documentation, such as policies, procedures, contracts, warranties (a kind of contract) and more. So many times I see “notwithstanding” used incorrectly, but that’s not what this is about.

A little background…

The genesis of this blog post was being reminded of something a former coworker, I’ll call Chuck, once said about this very topic. Someone, I’ll call Mark, asked him a question, and he answered it with the correct terminology. But the terms were unfamiliar to the person asking, and Mark smirked and commented that the terms are dumb. To which Chuck replied, “Everyone thinks that names don’t matter, until it’s something they care about.” Mark then asked, “Like what?”. Now, knowing Mark was a big fan of the Hip Hop music of the time (1990’s), Chuck said, “what do you think of that rap group Run GMC“. Mark quickly corrected him, “It’s Run DMC!” . After a few seconds, Chuck said, “So, to me, DMC might sound dumb. But to you it matters. Unless we agree to use the correct names, out of respect for each other, neither of us will take the other seriously.” They got along very well after that.

Incidentally, Chuck and Mark (not their real names) are both long deceased now. When you get older you’re often entertained by little memories like this one. Unless you spent too much time drinking and getting high, or getting hit on the head. But I digress.

Here’s a few examples of where things seem to be going off the rails lately.

Example 1 – Names of things

Regarding tools: A ratchet set is NOT the same as a socket wrench. They are distinctly different things, and sockets are also sold without the wrench. They are often sold together and are used together, but they are different things. Like hammers and nails, wrenches and bolts (or nuts), and so on.

The thing on many walls you connect electrical things to, is not a “plug”, but a “receptacle” (I’ll accept “socket” or “outlet” too). The “plug” is the part at the end of the wire with the prongs, which you insert into the receptacle.

A stove is not a range, which is also not an oven. A stove is the top where you often cook with frying pans. The lower part with the door, where you bake things like cookies and your weed stash is called the oven. A range is the appliance which has either a stove or an oven, or both.

Most everything has an official name. Like the plastic tips on the ends of your shoelaces (unless you’re in Florida and wearing Crocs). They’re called “aglets” by the way.

A web browser is not a web site. A cursor is not a mouse.

Assault and Battery are not the same things, even though they often happen together.

IT people should recognize terms like “radio button”, “checkbox”, “drop-down menu”, “slider”, “button” and “text box” as well as “taskbar” or “panel”, “Start menu”, “launcher”, “pop-up”, “file”, “folder”, “application” and “shortcut”. I’m sure some of you have heard non-IT users get those names mixed up a few times.

When you’re explaining to a nurse, EMT or a doctor where the pain is, you probably can say “my left little toe” rather than “my little boofoo“. It matters when it matters.

Example 2 – Job Titles

While the job titles in the IT world have been smeared like toddlers doing finger-painting, the names DO have real meaning. An “analyst” is supposed to “analyze”. Kind of a noun-to-verb thing. And an “engineer” is supposed to “engineer” things (noun > verb, again). So, when you see a position posted for a “programmer analyst” that really means it involves analyzing and programming (writing software code of some sort).

“Systems Administrator” or “sysadmin” is technically a meaningless term. Almost anything you point at in the IT world is a system of some kind.

An “architect” is supposed to “architect” things, but the verb form is actually not a real thing. Look it up in the Merriam-Webster’s dictionary. More on this later.

The trend today is to make the names as abstract as possible, giving the employer more flexibility with work assignments. “Programmer-Analyst”, “Business Analyst”, “Technical Analyst”, or “Architect-Engineer” are all sufficiently ambiguous to cover installing applications and configuring routing tables, or even updating spreadsheets.

Example 3 – Nouns and Verbs

There’s a very common trend today with turning nouns into verbs, sometimes called “verbing“. I think it started with “Google” (i.e. “Google it!”), or Facebook (“Friend me!”). But that linguistic cancer has spread to the following stupid terms:

Vision it!

Action it!

Template it!

You might notice these are often used by marketing and sales people. I’ll leave that right there.

What I Propose

I would like to see primary schools (those are elementary schools for Americans), teach a subject on “proper names”. Not just nouns, pronouns, adjectives and verbs, etc. Those are definitely important. But knowing basic names for things will not only help them later in life when it comes to jobs, but also when talking to repair people when they have to get things fixed (cars, houses, dishwashers, phones, computers, pets, etc.).

Why? Because the less stupid or ignorant you sound, the less likely those service providers are to taking advantage of you.

Example 1: “That big white metal thing with the glass door is leaking water!” Repair person: “You mean the washing machine?

Example 2: “I don’t know where the Start button is” Service tech: “It’s the Windows icon button on the bottom left

You might roll your eyes in disbelief, but I have seen these examples literally many times. And many more that are far worse.

So, if our schools can’t step up, then I would ask parents with young children: please, at least take the time to teach them that everything has a name. If you don’t have time to go into teaching them the names, teach them to read enough to use a dictionary.

BitBucket, humor, Society, Technology

20-20-2024 hours to go (2004 to 2024)

2004 – George W. Bush is re-elected to president. Facebook launches. The Red Sox break the curse of the Bambino. Lance Armstrong pedals one testicle over the finish line to his sixth Tour de France win. Martha Stewart joins a prison gang. Okay, not exactly, but she could pull it off. A 30 year fixed rate mortgage averages 5.88%. A volatile stock market ends the year with NASDAQ at 2175, and the S&P 500 above 1200.

2024 – Donald Trump is re-elected to president. BlueSky takes off. The Chiefs and Bills lead the NFL. Jake Paul beats Mike Tyson, sort of. Dick Cheney votes Democrat. Iggy Azalea earns $9.2 Million per month on OnlyFans, and Hauk Tua girl launches her own bitcoin. 475 musicians, actors, writers, politicians, artists and athletes died, but Keith Richards is still walking around. NASDAQ is around 20,173, and S&P 500 is around 6,075. The 475 number is just a guess, but I bet it’s pretty close.

We have come so far indeed.

But wait! There’s more!

2004: Meeting with “beta testing” team for <insert major corporation name here> gathering feedback from users. Eagerly asking about usability, bugs, feature enhancements, and any concerns that might prevent losing the customer to a competitor. Responses to questions are met with follow-up questions to clarify use-cases, get examples, understand the benefit to work streams, profit margins, and ease of administration. It’s common to hear vendor reps say, “How can we better understand your needs?

2024: Online webinar with “insider preview” users team for <insert major corporation name here> to showcase new products/platforms/services. First half hour spent introducing who’s who, and losing count of how many program managers are on the call, or trying to make sense of ambiguous titles like “senior lead experience engineer”. Eagerly trying to convince users that the new features are clearly, undoubtedly, obviously superior to not just their competitors but all previous versions of the same thing. Questions are met with repeated assertions about the obvious improvements. It’s common to hear vendor reps say, “You just aren’t seeing the benefits of this new feature.”

2004: Businesses have at least one, if not several, buildings with large “server rooms“. Corners are cluttered with piles of old equipment, boxes, roles of cables, old service manuals, a couple of removed false floor tiles or ceiling panels, and a broken office chair. Obtaining a new server for a project takes days to round up the figures, write up the request forms, submit them for review, wait on the approval, then issue the P.O. and maybe in a few months can actually start building something. Creating an AD domain requires a large enough machine to run VMware and build guest machines, or enough hardware to build physically. Not to mention all the other teams required to be involved from storage, and networking to infosec.

2024: People work from home and connect to services that use servers, and have no idea where those servers actually reside. Obtaining a new server for a project takes about 14 mouse clicks and a credit card, and it’s ready to do something in about two minutes. And you don’t pay full price when you turn it off, or pay nothing if you delete it until you re-create again when needed. Creating an AD domain requires a Terraform or Bicep template and a few mouse clicks.

2004: Most Americans can’t believe we’re fighting in Iraq.

2024: Most Americans can’t believe we’re not fighting in every fast food lobby.

2004: Phones were trying to be compact and uniquely designed.

2024: Phones are trying to be TV screens that look very much the same.

2004: The IRS collected over $1.88 trillion in revenue. The

2004: An important meeting involved large tables, many chairs, bottles of water, suits, ties, dress shoes, notebooks (the kind with paper) some sort of writing tool, and multiple restroom trips. Days at the office were from 8:00 AM to 5:00 PM with a one hour lunch break. A drive-by consisted of someone walking over to your cube and standing around to ask you a favor.

2024: An important meeting involves staying on mute, without a camera, watching someone draining oxygen while reciting the words on a PowerPoint being displayed for every attendee, bottles of alcohol or coffee, underwear, t-shirt, Crocs, and never leaving the restroom. Days at the office are from whenever to whenever with multiple lunch breaks whenever. A drive-by consists of someone texting you for a favor on Teams, Slack or whatever you’re told is the superior platform of the day, until it’s renamed by the vendor.

2004: Earth has 6.47 billion humans who can’t drive and have no idea what they’re doing.

2024: Earth has 8.03 billion humans who can’t drive and have no idea what they’re doing, but they all have a podcast or a blog.

Raise your glass to 2025. I’m sure it’ll continue to blow our socks off. If you’re wearing any.

Cloud, Scripting, Technology

Install Azure VM Extensions w/PowerShell

In this installment of mind-numbing tediousness, we’ll look at the situation where you want to install the Azure Monitor Extension on a Windows or Linux VM in Azure, but can’t seem to find it in the portal. There are several ways to work around this, one of which is using PowerShell within a Cloud Shell console, but you can also do this from a local PowerShell console.

Requirements:

  • Time
  • Coffee
  • PowerShell
  • The Az modules
  • Authenticated Access
  • Azure Roles to allow f**king up, I mean, modifying Azure VMs
  • Some PowerShell code
  • More time

The Code

$vmName = ""
#Connect-AzAccount
$vm = Get-AzVm -Name $vmName -Status
if ($vm.PowerState -eq 'VM running') {
	if ($vm.OsName -match 'Windows') {
		$agentType = "Windows"
		$newAgentName = 'AzureMonitorWindowsAgent'
	} else {
		$agentType = "Linux"
		$newAgentName = 'AzureMonitorLinuxAgent'
	}
	$params = @{
		Name                   = $newAgentName
		ExtensionType          = $newAgentName
		Publisher              = $publisher
		ResourceGroupName      = $vm.ResourceGroupName
		VMName                 = $vm.Name
		Location               = $vm.Location
		TypeHandlerVersion     = '1.0'
		EnableAutomaticUpgrade = $true
		ErrorAction            = 'Stop'
	}
	Write-Output "Installing AMA $($agentType) agent on $($VMName)"
	Set-AzVmExtension @params
} else {
	Write-Output "VM $($vmName) is not running"
}

The Explanation

First, edit the script to input the VM Name. Then, if you’re running this in a Cloud Shell session: paste and run. If you’re running this locally, un-comment the Connect-AzAccount line as well. Then run.

The first condition is checking if the machine is running. If it’s powered off, it drops to the “else” block and exits.

If the machine is running, we then check the OS type to set the extension name to the appropriate value of either “AzureMonitorWindowsAgent” or “AzureMonitorLinuxAgent”.

Then we take the agent name and add that into the “splatted” parameters block, and call Set-AzVmExtension to do the installation. If you don’t want to wait, add -AsJob (or AsJob = $True, in the params block). If you choose to include AsJob you’ll get back a Job object, which you can interrogate by the status (do-while, etc.) until it completes, which can be useful if you plan to roll this into a batch deployment scenario.

Enjoy!

Scripting, Technology

10 Signs of a Great Programming Language

  1. Multiple ways to accomplish the same task
  2. Built on objects and classes
  3. Robust exception handling (try/catch, error objects, etc.)
  4. Always something to explore (depth)
  5. Documentation and community support
  6. Extensible modules/packages
  7. Platform agnostic
  8. Syntactical brevity
  9. Syntactical consistency
  10. Easy to start, challenging to master (goes with no.4)

PowerShell checks most of these, even if some modules violate no. 5 and no. 8 like a prison gang rape.

Personal, Society, Technology, Uncategorized

YouTube Catch-Up

I had a few people ask what online content I follow or subscribe to. I subscribe to about a hundred of them, but I don’t check-in with all of them at the same frequency. This is a short-list of the ones I tend to check more often than others. Enjoy!

Rick Beato – Music Writing, Performing, Theory, Industry

Curious Droid – History of technology, inventions, industry, science

Deployment Research – Johan covers OSD, ConfigMgr (SCCM), MDT, Windows 10 and much more.

David Bomball – IT education, cybersecurity, hacking, privacy, red team / blue team

Doug Finke – PowerShell, AI, automation, tips and demos, Q&A feedback sessions

Patch My PC – Monthly patching news, how-to’s related to MDM, patching, app deployment, provisioning.

Ward Carroll – Defense News, current events, history, world and defense industry insights

John Savill – Azure, Entra and M365 platform weekly updates, technical deep-dives, exam prep sessions

The Charismatic Voice – Opera singer analyzes classic rock and popular music vocalist performances

Michael Tunnell – Linux News, Product and Platform Reviews

Marques Brownlee – Tech gadget reviews: devices, computers, phones, TV’s, cars, more

Liv Boree – Philosophical discussions about game theory, science, civilization, human behavior

Andrea Borman – Linux, Linux, and more Linux from an IT veteran with a British flavor

Smarter Every Day – Science and historical discussions, education

Rob Braxman – Privacy and security around technology, hacking, education

Dave’s Garage – Windows History, Windows vs Linux Comparisons, Arduino Project Tutorials, Shop Projects, ESP32 Information and more.

Dr. Peter Attia – Drive Podcast / Medical, health, bio-medical science, pharmaceuticals, fitness.

SB Mowing – ASMR-oriented lawn care and restoration at high speed

ENCurtis – Woodworking for woodworkers to nerd-out

Cloud Management Community – Modern Endpoint Management. Technology channel for everyone interested in Modern management with Microsoft Intune, SCCM and Azure.

There are a LOT more I follow, but this is off the top of my pointy little head.

Technology

Blah Blah Linux something something Blah Blah

For several years, I’ve been reading article after article promising Linux is finally going to kill Windows, or Linux is going to take over everything, etc. The second most-common trope is which Linux distro is the “best” (whatever that means). Almost none of these offer any quantifiable metrics or scoring. It’s usually a subjective review of ease of use, default apps, and familiarity. Pffft.

Linux is customizable. If you don’t like the UX, change it. If you don’t like the package manager, switch to another. Default apps? Change them. Menu, desktop, panels, and so on: change them however you like. It’s like standing at the ice cream shop and claiming which scoop of vanilla is the best scoop of all scoops.

Someone will say they like Ubuntu or LinuxMint, and get chopped in half by the angry Fedora/RedHat, KDE, xfce or mate crowd. I call them Linux snobs. They actually ruin the community in my humble opinion. You want the platform (Linux kernel) to survive and be successful. In order to do that, it must gain users. To gain users, it must tolerate new users. Otherwise, you’re really just wishing for the platform to slowly die.

If you’re not aware, “distro-hopping” is a thing. It’s normal. It’s like shopping. You try different products on to see if they fit. Maybe they feel good for a while (weeks, months) and then you change your mind. That’s okay too. I’ve found a polite way to silence the snobs: When they ask what distro you use, just say “I’m shopping around, but right now I’m giving <<name>> a run.

My advice: Use whatever distro you like. Same goes for programming languages, food, drinks, hobbies, movies, music, books, cars, pets, whatever. Find what works for you. It’s okay to consider other people’s opinions, to a point. It’s really about the spirit in which they offer an opinion. If they make a pitch for just giving something else a try, listen. Listening isn’t an obligation to take action. It just means you’re respecting their opinion. But it has to be quid pro quo.

I’m not going to dive into Linux vs. Windows or MacOS. There’s a nauseatingly vast amount of content out there already. In 2024, most everything you need to do on a computer can be done on Linux. It might not be done the same way, but it can be done. The main difference is Linux lets you customize the absolute **** out of it to your liking. UX, apps, services, security posture, you name it.

Whether you write code, produce audio and video content, write and record music, crunch financial numbers, write documents and books, chat by text or video, or … wait… what am I saying…. 99.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999% of humans use their computer to get to a web browser. Period. So you’re that 0.0<insert a billion zeros>1% that needs more apps, congratulations. But if you’re the average human, Linux will work just fine. Pick a distro. Spin up a VM (or an old laptop) and kick the tires.

Have fun.

Remember when you got into computers because they were “fun”?

Don’t forget that.