Public/New-NSPToolShim.ps1
|
function New-NSPToolShim { <# .SYNOPSIS Writes a launcher script (the ToolShim recipe) for a module-hosted NSP tool, optionally with seed answers embedded. This is the engine behind New-NSPADShim, New-NSPNpsShim and the rest. .DESCRIPTION The shim elevates, installs NSP.Bootstrap and then -Modules from the PowerShell Gallery (or loads them from its own -ModuleRoot parameter), hands -SeedAnswers to the tool once via "<EntryFunction> -SeedAnswersJson <json> -SeedOnly", blanks them from its own file, then runs "<EntryFunction>". With no -SeedAnswers it just launches the tool, which asks its normal first-run questions. -Modules defaults to just the tool module. List sibling NSP modules too when the tool loads them, so the shim installs them up front. .PARAMETER ToolName Display name, e.g. 'AD Manager'. .PARAMETER ModuleName Module hosting the tool, e.g. NSP.ActiveDirectory. .PARAMETER ModuleMinimumVersion Oldest version of ModuleName the shim accepts. .PARAMETER EntryFunction Function that starts the tool; must accept -SeedAnswersJson and -SeedOnly. .PARAMETER Modules Modules to install, as hashtables/objects with Name and MinimumVersion. .PARAMETER SeedAnswers Answers to embed: an object/hashtable (converted to JSON) or a JSON string. .PARAMETER Company Shown in the shim's header comment. .PARAMETER GeneratedBy Shown in the shim's header comment, e.g. 'Orchestrator 4.1.0'. .PARAMETER StartInShimFolder Start the tool in the shim's own folder. Without it, an elevated shim starts in System32, as v1 shims did. For tools that work on files shipped next to the shim. .PARAMETER PauseOnExit Wait for Enter after the tool returns (or fails), so a one-shot tool's results stay on screen in the elevated window. .PARAMETER WithCmd Also write <name>.cmd next to the shim, which runs it with a double-click (a double-clicked .ps1 opens in Notepad). The shim's file name must then be plain: letters, digits, space and . _ ( ) -. .PARAMETER Path Where to write the shim. .PARAMETER Force Overwrite an existing file. .EXAMPLE New-NSPToolShim -ToolName 'AD Manager' -ModuleName NSP.ActiveDirectory -ModuleMinimumVersion 0.1.0 ` -EntryFunction Start-NSPADManager -Path C:\Temp\AD-Manager.ps1 .EXAMPLE New-NSPToolShim -ToolName 'Certificate Install' -ModuleName NSP.SSLCertificates -ModuleMinimumVersion 0.2.0 ` -EntryFunction Install-NSPSslCertificate -StartInShimFolder -PauseOnExit -WithCmd -Path .\Install-Certificate.ps1 #> [CmdletBinding(SupportsShouldProcess)] [OutputType([IO.FileInfo])] param( [Parameter(Mandatory)][string]$ToolName, [Parameter(Mandatory)][string]$ModuleName, [Parameter(Mandatory)][version]$ModuleMinimumVersion, [Parameter(Mandatory)][string]$EntryFunction, [object[]]$Modules, [object]$SeedAnswers, [string]$Company, [string]$GeneratedBy, [switch]$StartInShimFolder, [switch]$PauseOnExit, [switch]$WithCmd, [Parameter(Mandatory)][string]$Path, [switch]$Force ) $cmdPath = $null if ($WithCmd) { # The name goes into the .cmd unquoted by anything but "...", so cmd's specials (% ^ & ! ...) are kept out. $leaf = Split-Path -Leaf $Path if ($leaf -notmatch '^[A-Za-z0-9 ._()-]+\.ps1$') { throw "With -WithCmd the shim's file name must be letters, digits, space and . _ ( ) - ending in .ps1: '$leaf'." } $cmdPath = [IO.Path]::ChangeExtension($Path, '.cmd') if ((Test-Path -LiteralPath $cmdPath) -and -not $Force) { throw "$cmdPath already exists. Use -Force to overwrite." } } if (-not $Modules) { $Modules = @(@{ Name = $ModuleName; MinimumVersion = [string]$ModuleMinimumVersion }) } $moduleList = @(foreach ($m in $Modules) { $n = if ($m -is [hashtable]) { $m['Name'] } else { $m.Name } $v = if ($m -is [hashtable]) { $m['MinimumVersion'] } else { $m.MinimumVersion } if (-not $n) { throw 'Each -Modules entry needs a Name.' } [ordered]@{ Name = [string]$n; MinimumVersion = [string]$v } }) if (-not ($moduleList | Where-Object { $_.Name -eq $ModuleName })) { $moduleList += [ordered]@{ Name = $ModuleName; MinimumVersion = [string]$ModuleMinimumVersion } } $values = @{ ToolName = $ToolName ModuleName = $ModuleName ModuleMinimumVersion = [string]$ModuleMinimumVersion EntryFunction = $EntryFunction Modules = $moduleList } if ($null -ne $SeedAnswers) { $values['SeedAnswers'] = $SeedAnswers } if ($Company) { $values['Company'] = $Company } if ($GeneratedBy) { $values['GeneratedBy'] = $GeneratedBy } if ($StartInShimFolder) { $values['StartInShimFolder'] = $true } if ($PauseOnExit) { $values['PauseOnExit'] = $true } if ($PSCmdlet.ShouldProcess($Path, "Write $ToolName shim")) { New-NSPClientScript -Recipe ToolShim -Parameters $values -Path $Path -Force:$Force -Confirm:$false if ($cmdPath) { $version = $MyInvocation.MyCommand.Module.Version $cmd = @( '@echo off' "rem $ToolName - generated by NSP.ClientScripts $version. Double-click to run $leaf beside it." ('"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -File "%~dp0' + $leaf + '" %*') ) -join "`r`n" [IO.File]::WriteAllText($PSCmdlet.GetUnresolvedProviderPathFromPSPath($cmdPath),$cmd + "`r`n", (New-Object Text.ASCIIEncoding)) Get-Item -LiteralPath $cmdPath } } } |