Calling a powershell cmdlet from ps script - powershell

I have a cmdlet with the following defintion:
[CmdletBinding(DefaultParameterSetName="Path",
SupportsShouldProcess=$TRUE)]
param(
[parameter(Mandatory=$TRUE,Position=0)]
[String] $Pattern,
[parameter(Mandatory=$TRUE,Position=1)]
[String] [AllowEmptyString()] $Replacement,
[parameter(Mandatory=$TRUE,ParameterSetName="Path",
Position=2,ValueFromPipeline=$TRUE)]
[String[]] $Path,
[parameter(Mandatory=$TRUE,ParameterSetName="LiteralPath",
Position=2)]
[String[]] $LiteralPath,
[Switch] $CaseSensitive,
[Switch] $Multiline,
[Switch] $UnixText,
[Switch] $Overwrite,
[Switch] $Force,
[String] $Encoding="ASCII"
)
I put the cmdlet .ps1 file in the same folder as as a powershell script file that calls the cmdlet as following:
Invoke-Expression -Command .\Replace-FileString.ps1 "9595" "NewPort"
"c:\temp" -Overwrite
However, when I execute my ps script, I get the following error:
Invoke-Expression : A positional parameter cannot be found that
accepts argument '9595'.
How can I make it work?
Thanks.

Try:
Invoke-Expression -Command '.\Replace-FileString.ps1 "9595" "NewPort" "c:\temp" -Overwrite'
Your command includes arguments that uses quotemarks, so PS thinks that your command is over and those are new arguments(not a part of the -Command paramter).

Related

Passing Boolean powershell parameters from a non-powershell terminal

Sometimes I invoke powershell scripts from Linux bash/shell scripts like so:
pwsh MyScript.ps1 win-x64 false
And in my MyScript.ps1 file, I set up parameters like so:
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $runtime,
[Parameter()]
[bool] $singleFile = $true
)
I get an error for the second parameter:
Cannot process argument transformation on parameter 'singleFile'. Cannot convert value "System.String" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0.
I tried passing '$false' as well as 0 but it treats everything as a string. When invoking powershell scripts from outside of a PWSH terminal, how do I get it to coerce my string-boolean value into an actual Powershell boolean type?
I propose to use [switch]
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $runtime,
[Parameter()]
[switch] $singleFile
)
Write-Host $runtime
It works for me with :
pwsh ".\MyScript.ps1" "toto" -singlefile
In fact your code is working with :
pwsh ".\MyScript.ps1" toto -singleFile 1

running PS script with arguments

I know how to run scripts with parameters like script.ps1 -arcive=true
but I'm trying to run my script with parameters like script.ps1 -archive.
is that possible?
You are looking for a switch. Its the same concept as a parameter because it is a parameter.
param (
[string] $randomParameter,
[switch] $archive = $false
)
$archive will be false unless you call: script.ps1 -archive, then it will be true

Trying to code a function which takes a scriptblock with params and argument list, edits that scriptblock and then runs invoke-command

I am editing a function which it will invoke a command directly on the VM. The issue I keep running into is if someone passes a function declaration as a scriptblock, I get and error when calling create, because params() is not at the top of the scriptblock.
Trying to figure out how I can still set-fulllanguage first then execute a function with params.
function Invoke-DirectOnVM
{
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[CloudEngine.Configurations.EceInterfaceParameters]
$Parameters,
[Parameter(Mandatory = $true)]
[String[]]$VMNames,
[Parameter(Mandatory = $true)]
[Object]$VMCredential,
[Parameter(Mandatory = $true)]
[ScriptBlock]$ScriptBlock,
[Object[]]$ArgumentList = $null
)
{
Invoke-Command -VMName $localVMs -Credential $using:VMCredential -ScriptBlock ([ScriptBlock]::Create($("Import-Module OpenUpSession; Set-FullLanguage; `r`n" + $using:ScriptBlock)))
}
Remove the $using: from the scriptblock and it should work properly. I took the liberty of cleaning up the code a bit. The result looks like:
function Invoke-DirectOnVM
{
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[CloudEngine.Configurations.EceInterfaceParameters]
$Parameters,
[Parameter(Mandatory)]
[String[]]
$VMNames,
[Parameter(Mandatory)]
$VMCredential,
[Parameter(Mandatory)]
[ScriptBlock]
$ScriptBlock,
[Parameter()]
[Object[]]
$ArgumentList = $null
)
$PSBoundParameters.Remove("ScriptBlock")
Invoke-Command #PSBoundParameters -ScriptBlock ([ScriptBlock]::Create( "Import-Module OpenUpSession; Set-FullLanguage; `r`n" + $ScriptBlock ))
}

How do I run a ps1 script file passed by parameter from inside a powershell function?

I need to write a function in PowerShell that receives a bunch of parameters, one of them being a ps1 file. I need to execute this file as part of my function code, but I don't know how to do that.
This is probably a very silly detail, but I failed horribly in trying to search for this.
This is my function at the moment. I tried using Invoke-Command there but it's not working:
Function Start-Dsc {
Param(
[Parameter(Mandatory = $true)]
[string] $configurationFile,
[Parameter(Mandatory = $true)]
[string] $configurationName,
[Parameter()]
[string] $configurationData,
[Parameter(Mandatory = $true)]
[string] $computerName
)
Begin {}
Process
{
Invoke-Command -Command "$configurationFile -ConfigurationData $configurationData";
Start-DscConfiguration -Path ".\$configurationName" -ComputerName $computerName -Verbose -Wait
}
End{}
}
UPDATE:
With Bacon Bits' help, I managed to make it work. The final script is a bit different than what I initially posted though. This is the final process block:
Process
{
Invoke-Command -FilePath $configurationFile -ComputerName 'localhost';
Invoke-Expression -Command "$configurationName -ConfigurationData $configurationData";
Start-DscConfiguration -Path ".\$configurationName" -ComputerName $computerName -Verbose -Wait
}
Arguments are a separate option in Invoke-Command. Try:
Invoke-Command -Command "$configurationFile" -ArgumentList "-ConfigurationData $configurationData";
You may also need to change -Command to -FilePath.

Argument list and the pipeline

as an adjunct to this issue: How do I force declared parameters to require explicit naming? I am struggling with pipelines. Suppose I want the behaviour that this declares:
param(
$installdir,
$compilemode,
[Parameter(Position=0, ValueFromRemainingArguments=$true)] $files
)
namely, that I can call my script like this:
c:\> MyScript -installdir c:\ file-1.txt file-2.txt file-3.txt
but now I want to also be able to do it this way:
c:\> gi file-*.txt |MyScript -installdir c:\
I might think of adding a decoration to the parameter like this:
param(
$installdir,
$compilemode,
[Parameter(
Position=0,
ValueFromRemainingArguments=$true,
ValueFromPipeline=$true
)] $files
)
but what actually happens is I only get 1 argument into my parameter i.e. instead of getting an array with all the files that gi produced, I get only the first in the list.
A second way I attempted this was by using the $input variable (instead of using the ValueFromPipeline decorator), but then in trying to call the script I get the error:
The input object cannot be bound to any parameters for the command
either because the command does not take pipeline input or the input
and its properties do not match any of the parameters that take
pipeline input.
where can I go from here?
You could declare it without ValueFromRemainingArguments:
param(
[Parameter(
Position=0,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[Alias('PSPath')]
[string[]]
$files,
$installdir,
$compilemode
)
And then pass in multiple files as an array using the comma operator e.g.:
MyScript -installdir c:\ file-1.txt,file-2.txt,file-3.txt
Note: In order to accept input from commands like Get-Item and Get-ChildItem, use ValueFromPipelineByPropertyName and add a parameter alias "PSPath" that will find the PSPath property on the objects output by Get-Item/Get-ChildItem.
I have test this in ISE and it works fine:
function foo
{
param(
[Parameter(
Position=0,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[Alias('PSPath')]
[string[]]
$files,
$installdir,
$compilemode
)
process {
foreach ($file in $files) {
"File is $file, installdir: $installdir, compilemode: $compilemode"
}
}
}
foo a,b,c -installdir c:\temp -compilemode x64
ls $home -file | foo -installdir c:\bin -compilemode x86
FYI, this is a template I use all the time to create commands that take pipeline input or array input, as well as wildcard paths:
function Verb-PathLiteralPath
{
[CmdletBinding(DefaultParameterSetName="Path",
SupportsShouldProcess=$true)]
#[OutputType([output_type_here])] # Uncomment this line and specify the output type of this
# function to enable Intellisense for its output.
param(
[Parameter(Mandatory=$true,
Position=0,
ParameterSetName="Path",
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
HelpMessage="Path to one or more locations.")]
[ValidateNotNullOrEmpty()]
[SupportsWildcards()]
[string[]]
$Path,
[Alias("PSPath")]
[Parameter(Mandatory=$true,
Position=0,
ParameterSetName="LiteralPath",
ValueFromPipelineByPropertyName=$true,
HelpMessage="Literal path to one or more locations.")]
[ValidateNotNullOrEmpty()]
[string[]]
$LiteralPath
)
Begin
{
Set-StrictMode -Version Latest
}
Process
{
if ($psCmdlet.ParameterSetName -eq "Path")
{
if (!(Test-Path $Path)) {
$ex = new-object System.Management.Automation.ItemNotFoundException "Cannot find path '$Path' because it does not exist."
$category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
$errRecord = new-object System.Management.Automation.ErrorRecord $ex, "PathNotFound", $category, $Path
$psCmdlet.WriteError($errRecord)
}
# In the -Path (non-literal) case, resolve any wildcards in path
$resolvedPaths = $Path | Resolve-Path | Convert-Path
}
else
{
if (!(Test-Path $LiteralPath)) {
$ex = new-object System.Management.Automation.ItemNotFoundException "Cannot find path '$LiteralPath' because it does not exist."
$category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
$errRecord = new-object System.Management.Automation.ErrorRecord $ex, "PathNotFound", $category, $LiteralPath
$psCmdlet.WriteError($errRecord)
}
# Must be -LiteralPath
$resolvedPaths = $LiteralPath | Convert-Path
}
foreach ($rpath in $resolvedPaths)
{
if ($pscmdlet.ShouldProcess($rpath, "Operation"))
{
# .. process rpath
}
}
}
End
{
}
}