Skip to content

Instantly share code, notes, and snippets.

@tsmarvin
Last active November 10, 2023 03:46
Show Gist options
  • Save tsmarvin/f40ad59f33b12d88dc0682771c1a3fdc to your computer and use it in GitHub Desktop.
Save tsmarvin/f40ad59f33b12d88dc0682771c1a3fdc to your computer and use it in GitHub Desktop.
TM-RandomUtility

TM-RandomUtility Module

Introduction

TM-RandomUtility is a PowerShell module that provides a collection of random utility functions. These utilities are intended to enhance the flexibility of scripting tasks in PowerShell by providing various random utilities.

This module is part of a suite of tools designed to improve and streamline the PowerShell commandline and scripting experience.
Check out the rest of the modules by visiting my page on the PowerShell Gallery.

Features

  • Get-ExceptionTypeName: Retrieves the exception type name.
  • Get-HelpFromType: Provides help information from a type.
  • Get-StringHash: Generates the hash of a string.
  • Get-SubResourceIntegrityHash: Determine the subresource integrity hash for a file.
  • New-Timer: Returns a new, running, timer object.
  • Test-ApplicationExistsInPath: Tests if an application exists in the system path.
  • Write-RandomBytesToFile: Writes random bytes to a specified file.

Requirements

Installation

Install TM-RandomUtility from the PowerShell Gallery:

Install-Module TM-RandomUtility -Scope CurrentUser -Repository PSGallery

For manual installation, download the module files and place them in a "TM-RandomUtility" folder in your PowerShell modules directory ($Env:PSModulePath).

MIT License
Copyright (c) 2023 Taylor Marvin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@{
# Script module or binary module file associated with this manifest.
RootModule = 'TM-RandomUtility.psm1'
# Version number of this module.
ModuleVersion = '0.0.7'
# Supported PSEditions
CompatiblePSEditions = @('Desktop','Core')
# ID used to uniquely identify this module
GUID = 'c07a9da5-9562-42b6-8aba-1279fdb25a8e'
# Author of this module
Author = 'Taylor Marvin'
# Company or vendor of this module
CompanyName = 'N/A'
# Copyright statement for this module
Copyright = 'Taylor Marvin (2023)'
# Description of the functionality provided by this module
Description = 'Provides random utility functions.'
# Minimum version of the PowerShell engine required by this module
PowerShellVersion = '5.1'
# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @(@{ModuleName='TM-ValidationUtility'; ModuleVersion='0.0.4'; GUID='1f1eebe8-7a0b-49ae-901e-c877f090a7fc'})
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = @(
'Get-ExceptionTypeName',
'Get-HelpFromType',
'Get-StringHash',
'Get-SubResourceIntegrityHash',
'New-Timer',
'Test-ApplicationExistsInPath',
'Write-RandomBytesToFile'
)
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PSData = @{
# Tags applied to this module. These help with module discovery in online galleries.
Tags = @('Profile', 'Utility')
# A URL to the license for this module.
LicenseUri = 'https://gist.github.com/tsmarvin/f40ad59f33b12d88dc0682771c1a3fdc#file-license'
# A URL to the main website for this project.
ProjectUri = 'https://gist.github.com/tsmarvin/f40ad59f33b12d88dc0682771c1a3fdc'
# Prerelease string of this module
# Prerelease = ''
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
RequireLicenseAcceptance = $false
}
}
}
using namespace System
using namespace System.IO
using namespace System.Collections.Generic
using namespace Microsoft.PowerShell.Commands
# Ensure we're using the primary write commands from the Microsoft.PowerShell.Utility module.
Set-Alias -Name 'Write-Progress' -Value 'Microsoft.PowerShell.Utility\Write-Progress' -Scope Script
Set-Alias -Name 'Write-Debug' -Value 'Microsoft.PowerShell.Utility\Write-Debug' -Scope Script
Set-Alias -Name 'Write-Verbose' -Value 'Microsoft.PowerShell.Utility\Write-Verbose' -Scope Script
Set-Alias -Name 'Write-Host' -Value 'Microsoft.PowerShell.Utility\Write-Host' -Scope Script
Set-Alias -Name 'Write-Information' -Value 'Microsoft.PowerShell.Utility\Write-Information' -Scope Script
Set-Alias -Name 'Write-Warning' -Value 'Microsoft.PowerShell.Utility\Write-Warning' -Scope Script
Set-Alias -Name 'Write-Error' -Value 'Microsoft.PowerShell.Utility\Write-Error' -Scope Script
Set-Alias -Name 'New-TemporaryFile' -Value 'Microsoft.PowerShell.Utility\New-TemporaryFile' -Scope Script
Set-Alias -Name 'Get-FileHash' -Value 'Microsoft.PowerShell.Utility\Get-FileHash' -Scope Script
function Get-ExceptionTypeName {
<#
.SYNOPSIS
Returns the exception type name that occurred when executing a script block or command.
.DESCRIPTION
This function executes the provided command or script block and returns the full name of the exception type
that occurred during execution.
Only use this function if you know the exception will not cause further damage by running again.
.PARAMETER Command
The command to execute
.PARAMETER ScriptBlock
The script block to execute
.EXAMPLE
Get-HelpFromType -TypeNames (Get-ExceptionTypeName -Command 'Write-Error "test"')
.OUTPUTS
If the provided command causes an exception then the exception's typename will be returned.
If the provided command does NOT cause an exception then nothing is returned.
#>
[CmdletBinding(DefaultParameterSetName = 'string')]
[OutputType([Void], [string])]
param (
[Parameter(
Mandatory,
ValueFromPipeline,
ValueFromRemainingArguments,
ParameterSetName = 'string'
)]
[ValidateNotNullOrEmpty()]
[string]$Command,
[Parameter(
Mandatory,
ValueFromPipeline,
ValueFromRemainingArguments,
ParameterSetName = 'ScriptBlock'
)]
[scriptblock]$ScriptBlock
)
[ScriptBlock]$SB = switch ($PSCmdlet.ParameterSetName) {
'string' { [ScriptBlock]::Create($Command) }
'ScriptBlock' { $ScriptBlock }
}
$ErrorActionPreference = 'Stop'
try {
$SB.Invoke() | Out-Null
} catch {
return $_.Exception.GetType().FullName
}
}
function Get-HelpFromType {
<#
.SYNOPSIS
Opens the documentation for .NET types in a web browser.
.DESCRIPTION
This function opens the official Microsoft documentation for .NET types in the default web browser.
.PARAMETER Objects
Array of objects to determine the type of and fetch the documentation for.
.EXAMPLE
Get-HelpFromType -TypeNames @($Object1.GetType().FullName, ($Error[-1].Exception.GetType().FullName))
.EXAMPLE
Get-HelpFromType -Types $Error[-1].Exception.GetType()
#>
[CmdletBinding(DefaultParameterSetName = 'TypeNames')]
[OutputType([Void])]
param (
[Parameter(
Mandatory,
ValueFromPipeline,
ValueFromRemainingArguments,
ParameterSetName = 'TypeNames'
)]
[string[]]$TypeNames,
[Parameter(
Mandatory,
ValueFromPipeline,
ValueFromRemainingArguments,
ParameterSetName = 'Types'
)]
[Type[]]$Types
)
process {
[string[]]$InputTypes = switch ($PSCmdlet.ParameterSetName) {
'Types' {
[List[string]]$typeList = [List[string]]::new()
foreach ($Type in $Types) {
$typeList.Add($Type.FullName)
}
$typeList.ToArray()
}
'TypeNames' { $TypeNames }
}
foreach ($typeName in $InputTypes) {
Start-Process -FilePath "https://docs.microsoft.com/en-us/dotnet/api/$typeName"
}
}
}
function Get-StringHash {
<#
.SYNOPSIS
This function generates the hash value for a given string using a specified algorithm and text encoding.
.DESCRIPTION
Get-StringHash is a function that takes an input string, converts it to a byte array in a specified encoding
(defaulting to UTF8), and then computes the hash of these bytes using the specified algorithm (defaulting to
SHA256). The function uses PowerShell's Get-FileHash cmdlet on a MemoryStream object to calculate the hash value.
The function returns a FileHashInfo object.
.PARAMETER InputString
The string to be hashed.
.PARAMETER Algorithm
The hashing algorithm to use.
The options are SHA1, SHA256, SHA384, SHA512, and MD5. The default is SHA256.
.PARAMETER Encoding
The encoding to be used for transforming the input string into bytes.
The options are ASCII, BigEndianUnicode, Default, Latin1, Unicode, UTF7, UTF8, and UTF32.
The "Default" option uses the dotnet system default encoding.
If no argument is provided the function will default to using UTF8.
.EXAMPLE
# This example hashes the string 'TestString' using the SHA512 algorithm, and using the default encoding (UTF8).
'TestString' | Get-StringHash -Algorithm sha512
#>
[CmdletBinding()]
[OutputType([string])]
param(
[Parameter(Position = 0, Mandatory, ValueFromPipeline)]
[string]$InputString,
[Parameter(Position = 1, Mandatory = $false)]
[ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5')]
[string]$Algorithm = 'SHA256',
[Parameter(Position = 2, Mandatory = $false)]
[ValidateSet('ASCII', 'BigEndianUnicode', 'Default', 'Latin1', 'Unicode', 'UTF32', 'UTF8', 'UTF7')]
[string]$Encoding = 'UTF8'
)
# Retrieve the bytearray for the string in the specified encoding..
[byte[]]$byteArray = switch ($Encoding) {
'ASCII' { [System.Text.Encoding]::ASCII.GetBytes($InputString) }
'BigEndianUnicode' { [System.Text.Encoding]::BigEndianUnicode.GetBytes($InputString) }
'Default' { [System.Text.Encoding]::Default.GetBytes($InputString) }
'Latin1' { [System.Text.Encoding]::Latin1.GetBytes($InputString) }
'Unicode' { [System.Text.Encoding]::Unicode.GetBytes($InputString) }
'UTF32' { [System.Text.Encoding]::UTF32.GetBytes($InputString) }
'UTF8' { [System.Text.Encoding]::UTF8.GetBytes($InputString) }
'UTF7' { [System.Text.Encoding]::UTF7.GetBytes($InputString) }
}
# Turn the bytes into a memorystream and then get the hash of the stream.
try {
[MemoryStream]$stream = [MemoryStream]::new($byteArray)
[FileHashInfo]$hash = Get-FileHash -InputStream $Stream -Algorithm $Algorithm
} catch {
throw # Re-throw the error after finally.
} finally {
# Ensure we close the stream if it exists.
if ($null -ne $stream) {
$stream.Close()
}
}
return $hash
}
function Get-SubResourceIntegrityHash {
<#
.SYNOPSIS
This function generates a Subresource Integrity (SRI) hash, using openssl, for a URI or local file using the
specified hashing algorithm.
.DESCRIPTION
Get-SubResourceIntegrityHash is a function that generates a Subresource Integrity (SRI) hash.
The function can operate on a file located at a given URI (by downloading it to a temporary file) or a local file.
It uses openssl (which must be installed on the system and accessible from the system path) to generate the hash.
The hash algorithm defaults to SHA256, but can be specified.
.PARAMETER Uri
The URI of the file to generate an SRI hash for. Either this parameter or 'Path' must be specified.
.PARAMETER Path
The local file path of the file to generate an SRI hash for. Either this parameter or 'Uri' must be specified.
.PARAMETER Algorithm
The hashing algorithm to use. The options are sha1, sha256, sha384, sha512, and MD5. The default is sha256.
.EXAMPLE
# This example generates an SRI hash for Uri 'https://code.jquery.com/jquery-3.7.0.js' using the SHA512 algorithm.
Get-SubResourceIntegrityHash -Uri 'https://code.jquery.com/jquery-3.7.0.js' -Algorithm sha512
.EXAMPLE
# This example generates an SRI hash for the file located at 'C:\Temp\jquery-3.7.0.js' using the SHA384 algorithm.
Get-SubResourceIntegrityHash -Path 'C:\Temp\jquery-3.7.0.js' -Algorithm sha384
#>
[CmdletBinding()]
[OutputType([string],[Void])]
param(
[Parameter(
Position = 0,
Mandatory,
ValueFromPipeline,
ParameterSetName = 'Uri'
)]
[string]$Uri,
[Parameter(
Position = 0,
Mandatory,
ValueFromPipeline,
ParameterSetName = 'Path'
)]
[Validation.ValidatePathExists('File')]
[string]$Path,
[Parameter(Position = 1, Mandatory = $false)]
[ValidateSet('sha1', 'sha256', 'sha384', 'sha512', 'MD5')]
[string]$Algorithm = 'sha256'
)
if ((Test-ApplicationExistsInPath -ApplicationName 'openssl') -eq $false) {
Write-Error 'openssl does not exist in the Path. Cannot retrieve subresource integrity hash.'
return
}
try {
if ($PSCmdlet.ParameterSetName -eq 'Uri') {
$TempFile = New-TemporaryFile
Invoke-RestMethod -Uri $Uri -Method Get -OutFile $TempFile.FullName
$FilePath = $TempFile.FullName
} else {
$FilePath = $Path
}
$OpensslCmd = "openssl dgst -$Algorithm -binary $FilePath | openssl base64 -A"
$Result = switch ($true) {
$IsLinux { bash -c $OpensslCmd }
$IsMacOS { zsh -c $OpensslCmd }
default { & cmd.exe /c $OpensslCmd }
}
} catch {
throw # Re-throw the error after finally.
} finally {
if ($TempFile -ne [string]::Empty) {
Remove-Item -Path $TempFile.FullName -Force
}
}
return $Result
}
function New-Timer {
<#
.SYNOPSIS
Starts a new timer.
.DESCRIPTION
This function creates and starts a new stopwatch timer.
#>
[CmdletBinding()]
[OutputType([Diagnostics.Stopwatch])]
param ()
return [Diagnostics.Stopwatch]::StartNew()
}
function Test-ApplicationExistsInPath {
<#
.SYNOPSIS
Uses Get-Command to see whether the $ApplicationName exists in the path.
#>
[CmdletBinding()]
[OutputType([boolean])]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$ApplicationName
)
return ($null -ne (Get-Command $ApplicationName -CommandType Application -ErrorAction Ignore))
}
function Write-RandomBytesToFile {
<#
.SYNOPSIS
Writes random bytes to a file.
.DESCRIPTION
This function writes random bytes to a file until that file has reached the specified fileByteSize.
If the file is already greater than fileByteSize then the function exits early.
.PARAMETER fileByteSize
The desired file size in bytes
.PARAMETER fileName
The name of the file to write to, default is 'temp.rnd'
.PARAMETER directoryPath
The directory path where the file will be created or updated, default is the current directory
#>
[CmdletBinding()]
[OutputType([Void])]
param (
[Parameter(Mandatory)]
[long]$fileByteSize,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$fileName = 'temp.rnd',
[Parameter(Mandatory = $false)]
[Validation.ValidatePathExists('Folder')]
[string]$directoryPath = [Environment]::CurrentDirectory
)
[string]$filePath = Join-Path -Path $directoryPath -ChildPath $fileName
[Random]$rnd = [Random]::New()
[IO.FileInfo]$file = [IO.FileInfo]::New($filePath);
if ($file.Length -ge $fileByteSize) {
Write-Host "File size for '$($file.FullName)' already meets or exceeds $fileByteSize"
return
}
[IO.FileStream]$outputStream
try {
$outputStream = [IO.File]::Open($file.FullName, [IO.FileMode]::OpenOrCreate)
$outputStream.Seek(0, [IO.SeekOrigin]::End) | Out-Null
[long]$bytesWritten = $file.Length
do {
[byte[]]$bytes = [byte[]]::New(
(. {
if ($fileByteSize -gt ($bytesWritten + 1024)) {
1024
} else {
[int]($fileByteSize - $bytesWritten)
}
})
)
$rnd.NextBytes($bytes)
$outputStream.Write($bytes, 0, $bytes.Length)
$bytesWritten += $bytes.Length
} while ($bytesWritten -lt $fileByteSize)
Write-Host "Wrote $bytesWritten bytes to '$($file.FullName)'. FileSize: $fileByteSize"
} finally {
if ($null -ne $outputStream) { $outputStream.Dispose() }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment