Technology, Scripting

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.

Scripting, Technology

Semi-Dynamic Static PowerShell Function Definitions for a Dynamically Semi-Static World

It’s been a while since I posted anything to my blog. I needed a break. But I needed a break from the break. I hope you don’t mind. This is admittedly an “edge case” scenario. The “need” (with air quotes) for this occurred within fairly uncommon set of circumstances, but I thought the methodology might be useful in other cases. Or maybe not. But if it’s of some help to you, that makes it worth the effort for me.

Eating a Chick-Fil-A lunch at Mall of America

For this particular case, we had a somewhat generic function that queries records from a data source (Log Analytics, Graph, relational database, etc.) using an input parameter to specify which customer or project to filter on. Because the project or customer names are more recognizable and easier to remember than their corresponding Identifier, we wanted to generate a pseudo function (i.e. a wrapper) that used the customer or project name. If you inhale enough paint fumes, you might see an analog to how DNS works (names vs. IP addresses).

Basically: generate a set of functions using names that make them easier for users to remember. Why? Because IT was built on laziness and forgetfulness. I read that somewhere once.

This example starts with an array of paired values, wherein each pair defines [name]=[identifier], such as “Contoso=3”. Then we iterate the array and parse (split) each pair to define a function wrapper as a string. Then we use Invoke-Expression to evaluate the string version of the function into a PowerShell function definition. This is a very old trick to anyone familiar with LISP, but not commonly used elsewhere as far as I’ve seen.

Aside from remembering to escape certain characters like $ the rest is fairly straightforward.

After processing, you will have functions named: Contoso, Fabrikam, IGZX and ABCD (using the example above).

Rather than trying to remember that Fabrikam is ID number 14 and typing:

You can type:

This is obviously a very basic example, but shows the potential for building user-friendly-er function names from an existing function without having to write explicit functions for each.

Some relevant notes:

  • This example is obviously not focused on performance or resource efficiency. The array could be any construct you prefer, as long as it provides a logical mapping. You might also be able to accomplish this using Alias definitions instead of functions.
  • Others may have discovered this same approach already, but my Google searching and AI prompting didn’t seem to find anything quite the same.
  • For the PowerShell snobs: I realize the alias name doesn’t follow the approved Verb-Noun format. This is just one example.
  • I have no idea what I’m doing, but most of it seems to work.

If you find this useful, please click the Like button below. If you hate it, politely let me know why, or how it could be done a better way. Thank you for reading!