PowerShell Snippets Vol 4

Welcome to my PowerShell Snippets Vol 4. A collection of PowerShell commands for tasks that I don’t perform often and can’t recall easily from memory. Those ‘I know I’ve worked this out before’ type moments. Volume 1 is available here, Volume 2 is available here and Volume 3 is available here.
In this volume you’ll find how to quickly answer ‘what is my Azure Tenant ID’, programmatically determine the underlying major PowerShell Version, get content as binary across platforms, extract property names from PSObjects and know about the retry parameters for Invoke-WebRequest and Invoke-RestMethod.

What is my Azure Tenant Id?

The Tenant Id for Office 365 / Azure Active Directory is a Globally Unique IDentifier (GUID) for the underlying Tenant associated with your organization or your domain. What is my Azure Tenant Id is something many IT Pro’s will ask when configuring aspects of Azure AD (often preview features via Microsoft Graph). There are many ways to get it, but many also rely on an authenticated session. A classic chicken and egg problem. Therefore my preferred way is this code snippet. A few lines querying the Azure AD OIDC Authorization Endpoint using a Domain Name and returning the Tenant ID. Just update the domain variable with the domain name you are looking for the Tenant Id for.

$domain = 'microsoft.com' 
$result = Invoke-RestMethod -Method Get "https://login.microsoftonline.com/$($domain)/v2.0/.well-known/openid-configuration" -ErrorAction SilentlyContinue
($result.authorization_endpoint.split("/"))[3]

I use this so often, that I got tired of finding this code snippet, so I wrote a PowerShell Module and updated my PowerShell Profile to autoload it. I’ve now published that signed module for others. You can install it from the PowerShell Gallery using Import-Module -name AzureADTenantID. It contains the cmdlet Get-AzureADTenantId and accepts the -domain parameter. It can also take input from the pipeline.

Install-Module -name AzureADTenantID
Import-Module -name AzureADTenantID

Get-AzureADTenantId -domain 'microsoft.com'
'test.com' | Get-AzureADTenantId

darrenjrobinson/AzureADTenantID

Signed PowerShell module to lookup a domain name and return the associated Azure Active Directory Tenant ID. Queries the ‘Well-Known’ Azure AD Open ID Connect (OIDC) Authorization Endpoint using a domain name and returns the TenantId Works with Windows PowerShell and PowerShell (6.x+) Install from the PowerShell Gallery on Windows PowerShell 5.1+ or PowerShell Core 6.x or PowerShell.

what is my azure tenant id

What version of PowerShell is my script running on?

When writing PowerShell Modules or Functions that will be used cross platform and thereby different versions of PowerShell I’m finding that even some of the core functions of PowerShell have differences as recent updates have breaking changes for PowerShell 6.x and above.

There are many methods to determine the version of PowerShell being run from within a script. For me at this time though, the method I’m using is to determine the major version of the PowerShell engine. In my scripts and modules I use the following code block and implement that platform/version specific nuances inside the appropriate section.

if ($PSVersionTable.PSVersion.Major -le 5) { 
   "Do Windows PowerShell specific things"
 } elseif ($PSVersionTable.PSVersion.Major -gt 5) { 
   "Do PowerShell Core / PowerShell (7+) specific things"
}

Get-Content as Binary on Windows PowerShell and PowerShell

This snippet is related to the snippet above and the variations of cmdlet parameters between versions of PowerShell. In Windows PowerShell to get graphic image data as binary the following can be used.

[Convert]::ToBase64String((Get-Content $reportImagePath -Encoding Byte))

However using PowerShell Core or PowerShell (7.x+) the Get-Content cmdlet no longer as the -Encoding Byte parameter. It has been replaced with -AsByteStream

[Convert]::ToBase64String((Get-Content $reportImagePath -AsByteStream))

Combing the variations of Get-Content based on the version of PowerShell then, the following will allow the script to work cross platform and version.

if ($PSVersionTable.PSVersion.Major -le 5) {
                $ImageData = [Convert]::ToBase64String((Get-Content $reportImagePath -Encoding Byte))
            }
elseif ($PSVersionTable.PSVersion.Major -gt 5) {
                $ImageData = [Convert]::ToBase64String((Get-Content $reportImagePath -AsByteStream))
            }

Extracting PSObject Property Names from a PSObject

Often results from third party modules with integrations with foreign systems return a PowerShell Object. Such an object will look like the one below. When building logic to take and manipulate that data I often need to quickly retrieve the property names to build my own custom PSObject.

$positions[111]


PositionId                       : POS00191
UnionAgreementLegalEntity        : 
IsPrimaryPosition                : Yes
AvailableForAssignment           : 1/01/1900 12:00:00 AM
DetailEffective                  : 9/05/2019 12:25:33 AM
UnionAgreementExpiration         : 1/01/1900 12:00:00 AM
DepartmentNumber                 : eConsulting
LaborUnionId                     : 
JobId                            : JOB00047
PayCycleId                       : 
Activation                       : 1/01/2019 12:00:00 AM
TitleId                          : e Consultant
Description                      : e Consultant
PayrollDetailEffective           : 1/01/1900 12:00:00 PM
WorkerName                       : Anna Lily
WorkerPersonnelNumber            : 513
WorkerAssignmentEnd              : 31/12/2154 11:59:59 PM
FullTimeEquivalent               : 1
ReportsToPositionId              : POS00102
WorkerAssignmentStart            : 1/01/2019 12:00:00 AM
PositionTypeId                   : FullTime
UnionAgreementName               : 
PaidByLegalEntity                : 
Retirement                       : 31/12/2154 11:59:59 PM
AnnualRegularHours               : 0
UnionAgreementEffective          : 1/01/1900 12:00:00 AM
WorkerAssignmentReasonCodeId     : 
CompensationRegionId             : C
PayrollDetailExpiration          : 1/01/1900 12:00:00 PM
ReportsToExpiration              : 31/12/2154 11:59:59 PM
ReportsToEffective               : 1/01/2019 12:00:00 AM
DetailExpiration                 : 31/12/2154 11:59:59 PM
PayPeriodOvertimeHours           : 0
AreEarningsGeneratedFromSchedule : No
DefaultEarningCodeId             : 
Schedule                         : 
IsSalaryGenerated                : No
BenefitId                        : 

The following snippet will extract all the properties and sort them allowing them to be quickly transformed into whatever logic that I’m building.

$sortedProps = [ordered] @{}
Get-Member -Type  NoteProperty -InputObject $item | Sort-Object Name | ForEach-Object { $sortedProps[$_.Name] = $bar.$($_.Name) }
    
# Create a new object that receives the sorted properties.
$barWithSortedProperties = New-Object PSCustomObject
Add-Member -InputObject $barWithSortedProperties -NotePropertyMembers $sortedProps

$barWithSortedProperties 


Activation                       : 
AnnualRegularHours               : 
AreEarningsGeneratedFromSchedule : 
AvailableForAssignment           : 
BenefitId                        : 
CompensationRegionId             : 
DefaultEarningCodeId             : 
DepartmentNumber                 : 
Description                      : 
DetailEffective                  : 
DetailExpiration                 : 
FullTimeEquivalent               : 
IsPrimaryPosition                : 
IsSalaryGenerated                : 
JobId                            : 
LaborUnionId                     : 
PaidByLegalEntity                : 
PayCycleId                       : 
PayPeriodOvertimeHours           : 
PayrollDetailEffective           : 
PayrollDetailExpiration          : 
PositionId                       : 
PositionTypeId                   : 
ReportsToEffective               : 
ReportsToExpiration              : 
ReportsToPositionId              : 
Retirement                       : 
Schedule                         : 
TitleId                          : 
UnionAgreementEffective          : 
UnionAgreementExpiration         : 
UnionAgreementLegalEntity        : 
UnionAgreementName               : 
WorkerAssignmentEnd              : 
WorkerAssignmentReasonCodeId     : 
WorkerAssignmentStart            : 
WorkerName                       : 
WorkerPersonnelNumber            : 

Invoke-RestMethod & Invoke-WebRequest Retry parameters

This is functionality I completely missed when it came to PowerShell Core. PowerShell Core (6.x) introduced the additional parameters RetryIntervalSec and MaximumRetryCount for Invoke-RestMethod and Invoke-WebRequest.
These parameters are also available is later versions of PowerShell (e.g. PowerShell 7.x+).

-MaximumRetryCount
Specifies how many times PowerShell retries a connection when a failure code between 400 and 599, inclusive or 304 is received.
Also see -RetryIntervalSec parameter for specifying number of retries.

-RetryIntervalSec
Specifies the interval between retries for the connection when a failure code between 400 and 599, inclusive or 304 is received.
Also see -MaximumRetryCount parameter for specifying number of retries.