Batman
Batman

Reputation: 6353

Execute cmdlet through variable?

I want to use an if \ else statement to determine which cmdlet to run while keeping the same params for both commands:

For example I have this call:

    New-AzureRmResourceGroupDeployment `
        -ResourceGroupName $ResourceGroup `
        -TemplateFile $TemplateUri `
        -TemplateParameterFile $TemplateParamFile 

But I want to use a variable to determine the verb:

    $myVerb = if ($booleanTest) {"Test"} else {"New"}
    [$myVerb]-AzureRmResourceGroupDeployment `
         -ResourceGroupName $ResourceGroup `
         -TemplateFile $TemplateUri `
         -TemplateParameterFile $TemplateParamFile 

OR something like this:

    $command = if ($booleanTest) {"Test-AzureRmResourceGroupDeployment"} else {"New-AzureRmResourceGroupDeployment"}
    $command `
         -ResourceGroupName $ResourceGroup `
         -TemplateFile $TemplateUri `
         -TemplateParameterFile $TemplateParamFile 

I tried the $command version but it failed with this:

At C:\Users\Administrator\Dropbox\projects\deloitte\Suncore\Dev\scripts\az-d eploy.ps1:36 char:13 + -ResourceGroupName $ResourceGroup + ~~~~~~~~~~~~~~~~~~ Unexpected token '-ResourceGroupName' in expression or statement. At C:\Users\Administrator\Dropbox\projects\deloitte\Suncore\Dev\scripts\az-d eploy.ps1:36 char:32 + -ResourceGroupName $ResourceGroup + ~~~~~~~~~~~~~~

Upvotes: 3

Views: 189

Answers (4)

mklement0
mklement0

Reputation: 437197

To complement the existing helpful answers:

  • Invoke-Expression should always be the last resort and is not needed here. With Invoke-Expression, it is tricky to get the quoting right, and its use can be a security risk (execution of arbitrary commands passed as a string, analogous to eval in POSIX-like shells).

  • Splatting (Get-Help about_Splatting) is always worth considering:

    • it is a more robust alternative to multi-line parameter-passing with line continuations, as Mark's answer explains.
    • it allows incremental, conditional construction of the set of parameter values as well as their use in multiple invocations.
  • In the case at hand, however, since only the command name is variable, Patrick's answer based on &, the call operator is simplest (see Get-Help about_Operators).


Generally, you need & whenever the command name is not an unquoted literal (e.g., notepad foo.txt works, but 'notepad' foo.txt doesn't).

To put it differently: you need &, if your command name is:

  • in quotes (whether '...' or "..."); e.g., 'notepad'
  • or is a variable reference; e.g., $cmdName
  • or is the result of an expression (e.g., ('get' + '-item')

& is needed in theses case in order to tell PowerShell that your intent is to invoke a command as opposed to evaluating an expression; without &, such tokens would be interpreted as starting an expression; see Get-Help about_Parsing to learn about PowerShell's two fundamental parsing modes, argument mode and expression mode.


While it may not be the most readable solution, you can therefore even combine an expandable string with an embedded subexpression ($(...) - again, see Get-Help about_Operators) to get away without the need for an aux. variable:

& "$( if ($booleanTest) {'Test'} else {'New'} )-AzureRmResourceGroupDeployment" `
     -ResourceGroupName $ResourceGroup `
     -TemplateFile $TemplateUri `
     -TemplateParameterFile $TemplateParamFile 

Upvotes: 2

iRon
iRon

Reputation: 23633

Using Splatting as suggested by Mathias R. Jessen:

Function Do-AzureRmResourceGroupDeployment ([ValidateSet("Test", "New")]$Verb, $ResourceGroupName, $TemplateFile, $TemplateParameterFile) {
    $PSBoundParameters.Remove("Verb")
    & "$Verb-AzureRmResourceGroupDeployment" @PSBoundParameters 
}

Upvotes: 0

Patrick Meinecke
Patrick Meinecke

Reputation: 4173

I don't recommend using this over the other answer but to directly answer your question, add the invocation operator &

$command = if ($booleanTest) {
    "Test-AzureRmResourceGroupDeployment"
} else {
    "New-AzureRmResourceGroupDeployment"
}

& $command `
     -ResourceGroupName $ResourceGroup `
     -TemplateFile $TemplateUri `
     -TemplateParameterFile $TemplateParamFile 

Upvotes: 3

Mark Wragg
Mark Wragg

Reputation: 23355

To do exactly what you are describing you'd need to wrap the whole command as a string and then call it with Invoke-Expression. For Example:

$MyCommand = "$myVerb-AzureRmResourceGroupDeployment -ResourceGroupName $ResourceGroup -TemplateFile $TemplateUri"
Invoke-Expression $MyCommand

But I think this isn't a very clear way to write a script. A better option would be to use Splatting, which is where you create a hashtable of the parameters that you can then send the cmdlet via a special @ character with the variable name. For example:

$AzureParams = @{ 
    ResourceGroupName = $ResourceGroup
    TemplateFile = $TemplateUri
    TemplateParameterFile = $TemplateParamFile 
}

If ($booleanTest) {
    Test-AzureRmResourceGroupDeployment @AzureParams
} Else {
    New-AzureRmResourceGroupDeployment @AzurParams
}

This also has the benefit of avoiding using the backtick character, which is generally encouraged as it's hard to spot and easy to break.

Upvotes: 6

Related Questions