Skip to content

Instantly share code, notes, and snippets.

@mklement0
Last active November 22, 2019 15:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mklement0/9c494216681c4c25d17abda2b60cdff9 to your computer and use it in GitHub Desktop.
Save mklement0/9c494216681c4c25d17abda2b60cdff9 to your computer and use it in GitHub Desktop.
Get-CmdletMinPsVersion: PowerShell function for determining the minimum PowerShell version required to run given standard cmdlets.
function Get-MinPsVersionForCmdlet {
<#
.SYNOPSIS
Determines the minimum PowerShell version required for a given standard cmdlet.
.DESCRIPTION
NOTE:
* v3 or higher is required to run this function.
* Only cmdlets with help topics defined in the GitHub
PowerShell-documentation repository at
https://github.com/PowerShell/PowerShell-Docs
can be examined.
* In general, this INCLUDES the cmdlets that ship with PowerShell Core.
* It EXCLUDES:
* inherently Windows-only cmdlets such as Set-WinSystemLocale
(some of which are tied to an *OS* version rather than a PowerShell
version)
* cmdlets that have not yet or may never be ported to
PowerShell Core, such as Set-Culture.
* If a given cmdlet is not available on this machine, it must be specified
CASE-EXACTLY.
CAVEATS:
The minimum PowerShell version that supports a given cmdlet (i.e., the version
in which a give cmdlet was introduced) is derived from the version-specific
help-topic links at https://github.com/PowerShell/PowerShell-Docs
The lowest version represented there is v3, so in order to determine whether
a cmdlet was available in v2 already, the text of the v3 help topic must
be searched for a hint that the cmdlet was introduced in v3.
From the *absence* of such a hint, this function infers v2 availability, but
note that this method is not foolproof, because not all cmdlet help topics
explicitly state in what version they were introduced.
.PARAMETER Name
The name of one or more cmdlets whose minimum version requirement to
determine.
Wildcards are supported.
If the cmdlet exists on the local machine, you may also specify an alias
for it for brevity.
If the cmdlet does NOT exist, you must specify it CASE-EXACTLY.
.EXAMPLE
Get-MinPsVersionForCmdlet Get-FileHash
Name MinVersion
---- ----------
Get-FileHash 4.0
.EXAMPLE
Get-MinPsVersionForCmdlet Get-Content
Name MinVersion Comment
---- ---------- -------
Get-Content 2.0 CAVEAT: v2 availability inferred from absence of version note in v3 topic
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[SupportsWildCards()]
[string[]] $Name
)
begin {
function getCmdlet($rawName) {
# See if the command can be found.
$cmd = Get-Command -EA SilentlyContinue -Name $rawName
if (-not $cmd) {
# Pass the input name through, because it could be the name of a cmdlet
# that happens not to be available in the PS edition running this function.
# !! GitHub's filename search *should* be case-INsensitive, but, as of 3 Apr 2018,
# !! it apparently requires camel-case *transitions* to be case-exact.
# !! e.g., to match `Get-CimClass.md`, you'd have to specify `CimClass` or `cimClass` or `CImClass` or `CImCLASS`,
# !! but not `cimclass` or `CIMclass` or `cimcLASS`.
# !! It's too complex and brittle to try to *guess* these transitions and auto-correct them here,
# !! so we simply issue a warning.
Write-Warning "Cmdlet $rawName not found on this machine. If it is a cmdlet in a later version / different edition, it will still be found, but be sure to specify it CASE-EXACTLY (or at least with case-exact camel-case transitions)."
$rawName
} else{
# As a courtesy, resolve an alias to the underlying cmdlet name.
if ($cmd.ResolvedCommand) { $cmd = $cmd.ResolvedCommand }
$cmd.Name
}
}
function getMinVersion($name) {
$nameEsc = [uri]::EscapeDataString($name)
# Derive the minimum version from the lowest-versioned help topic for
# the specified cmdlet at https://github.com/PowerShell/PowerShell-Docs/tree/staging/reference
# Note:
# * The lowest version maintained there is v3.0, so to determine whether a given cmdlet
# is available in v2, more work is needed.
# * The help topics for Windows-only cmdlets tied to specific versions are NOT maintained on GitHub;
# e.g., Set-WinSystemLocale can be found at https://docs.microsoft.com/en-us/powershell/module/international/set-winsystemlocale
# with version query parameters reflecting a specific *OS* version, e.g., ?view=win10-ps
[string[]] $uris = (Invoke-WebRequest "https://github.com/PowerShell/PowerShell-Docs/search?utf8=%E2%9C%93&q=filename%3A${nameEsc}+extension%3Amd+path%3Areference&type=Code").Links.href -match '/reference/'
if (-not $uris) {
Write-Error "No help topics found for cmdlet ${name}."
} else {
$uris | Write-Verbose
$verStrings = $uris -replace ('.*?/(\d+(?:\.\d+)?)/.*?/{0}\b.*\.md' -f [regex]::escape($name)), '$1' -notmatch '/'
$minVer = [version] ('{0}.0' -f [int]::MaxValue)
$minVerUri = ''; $i = 0
foreach ($verString in $verStrings) {
wv $verString
$ver = [version] $(if ($verString -match '\.') { $verString } else { $verString + '.0' })
if ($ver -lt $minVer) { $minVer = $ver; $minVerUri = 'https://github.com/' + $uris[$i] }
++$i
}
$comment = $null
if ($minVer.Major -eq 3) {
# If the minimum version is 3, the cmdlet may have been introduced in v2 or even v1 -
# we can't tell by version-specific URIs, because the minimum version online is v3.
# The best we can do is to search the v3 version link's *content* for a string
# indicating that the cmdlet was introduced in v3 - this is not foolproof, as
# the cmdlet topics do not contain that information consistently - see https://github.com/PowerShell/PowerShell-Docs/issues/2270
Write-verbose "Minimum version reflected in links is v3; must dig deeper, via $minVerUri"
if ((Invoke-WebRequest $minVerUri).RawContent -notmatch 'cmdlet (?:is|was) introduced in .*?PowerShell 3.0') {
$minVer = [version] '2.0'
$comment = 'CAVEAT: v2 availability inferred from absence of version note in v3 topic'
}
}
if ($minVer.Major -eq [int]::MaxValue) {
# 'n/a', 'No help topics named for the cmdlet found.'
Write-Error "No help topics found for cmdlet ${name}."
} else {
# !!
if ($name -match '-cim' -and $minVer.Major -eq 6) {
Write-Warning "Generally, CIM CMDLETS WERE INTRODUCED IN V3. As of 3 Apr 2018, https://github.com/PowerShell/PowerShell-Docs only contains v6 help topics, however."
}
$minVer, $comment
}
}
}
# Windows PowerShell: Supports only SSL3 and TLS 1.0 by default, which doesn't work with GitHub
# To avoid a "Could not create SSL/TLS secure channel" error, we must add TLS 1.1 and TLS 1.2 explicitly
if ($PSVersionTable.PSEdition -eq 'Desktop') {
[Net.ServicePointManager]::SecurityProtocol += 'Tls11, Tls12'
}
}
process {
foreach ($n in $Name) {
foreach ($cmdletName in getCmdlet $n) { # due to wildcard support, getCmdlet may return multiple cmdlets
$minVer, $comment = getMinVersion $cmdletName
if ($minVer) {
$oht = [ordered] @{
Name = $cmdletName
MinVersion = $minVer
}
if ($comment) {
$oht['Comment'] = $comment
}
[pscustomobject] $oht
}
}
}
}
}
# If this script is invoked directly - as opposed to being dot-sourced in order
# to define the embedded function for later use - invoke the embedded function,
# relaying any arguments passed.
if (-not ($MyInvocation.InvocationName -eq '.' -or $MyInvocation.Line -eq '')) {
Get-MinPsVersionForCmdlet @Args
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment