Olá pessoal,
Muitas vezes quando estou escrevendo alguma função(ou cmdlet) ou criando um modulo em algum projeto, surge a necessidade de aumentar ainda mais a flexibilidade de uma função, por exemplo, caso o valor do parametro 1 seja X, será solicitado outro parâmetro como mandatório, porém caso o valor seja Y, não tera esse requerimento. O powershell tem essa funcionalidade ha algum tempo, é chamda de Parametro Dinâmico. Para citar a documentação oficial:
Parâmetros dinâmicos são parâmetros de um cmdlet, uma função ou um script que estão disponíveis somente sob determinadas condições.
Por exemplo, vários cmdlets de provedor têm parâmetros que estão disponíveis somente quando o cmdlet é usado na unidade do provedor ou em um caminho específico da unidade do provedor. Por exemplo, o parâmetro de codificação está disponível nos
Add-Content
Get-Content
Set-Content
cmdlets, e somente quando ele é usado em uma unidade do sistema de arquivos.Você também pode criar um parâmetro que aparece somente quando outro parâmetro é usado no comando Function ou quando outro parâmetro tem um determinado valor.
Os parâmetros dinâmicos podem ser úteis, mas são usados somente quando necessário, pois podem ser difíceis para os usuários descobrirem. Para localizar um parâmetro dinâmico, o usuário deve estar no caminho do provedor, usar o parâmetro ArgumentList do
Get-Command
cmdlet ou usar o parâmetro Path deGet-Help
.
Mas como colocar isso em pratica? Vamos começar com o esqueleto da função, no nosso exemplo ela irá se chamar Add-ManagementGroup
,
function Add-ManagementGroup
{
[CmdletBinding()]
Param(
)
BEGIN {}
PROCESS {}
END {}
}
Vamos agora começar a declarar nossos parâmetros, no nosso exemplo vamos criar 3, InputObject que deverá ser uma string JSON valida(para isso vamos adicionar uma validação de script ao parâmetros, em seguida outro chamado Name, que nao poderá ser nulo ou vazio e por fim Level, que só aceitará 3 valores como corretos(Federal, State e City).
Param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
[ValidateScript({ConvertFrom-Json -InputObject $PSitem })]
$InputObject,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
$Name,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[ValidateSet('Federal','State','City')]
$Level
)
Agora podemos criar nosso parametro dinâmico fora do bloco Param() criando um novo bloco DynamicParam {}
dentro dele Vamos criar uma condição que será verdadeira caso o valor de Level seja igual a City. Como estamos usando ValidateSet()
e ValidateNotNullOrEmpty()
para determinar o que estamos esperando exatamente em termos de valor, reduzimos a quantidade de possiveis erros na logica. Para criar um novo parametro dinâmico, precisamos primeiro declarar seus atributos com um objeto do tipo System.Management.Automation.ParameterAttribute
, onde podemos dizer se é mandatório, sua posição pardão, e assim por diante. Em seguida devemos criar uma collection de System Attributes da seguinte com o seguinte typename System.Collections.ObjectModel.Collection[System.Attribute]
. O proximo passo é adicionar nosso atributo a Collection que acamabos de criar.
Em seguida iremos criar um objeto do tipo System.Management.Automation.RuntimeDefinedParameter
onde iremos informar qual o Nome do parametro, o tipo de valor aceito e a collection de atributos definida anteriormente. Em seguida iremos criar um objeto to tipo System.Management.Automation.RuntimeDefinedParameterDictionary
para criar o dicionário de parametros e adicionar nosso parametro recém declarado a ele informando seu nome novamente. Por fim utilizamos a funcão return
para retornar o dicionário de parametros com todos os parametros dinamicos declarados.
Mas como fica isso em Código? O resultado final do Parametro Dinâmico irá ser declarado dessa maneira:
DynamicParam {
if($Level -eq 'City'){
$SegmentAttribute = New-Object -TypeName System.Management.Automation.ParameterAttribute
$SegmentAttribute.Position = 3
$SegmentAttribute.Mandatory = $true
#create an attributecollection object for the attribute we just created.
$attributeCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute]
#add our custom attribute
$attributeCollection.Add($SegmentAttribute)
#add our paramater specifying the attribute collection
$SegmentParameter = New-Object System.Management.Automation.RuntimeDefinedParameter('SegmentName', [String], $attributeCollection)
#expose the name of our parameter
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('SegmentName', $SegmentParameter)
return $paramDictionary
}
}
Por fim podemos utilizar o bloco PROCESS
para definir a logica que iremos utilizar. Em nosso exemplo iremos simplismente utilizar um bloco switch
para tratar cada valor de Level de forma individual:
PROCESS {
switch($level){
'Federal' {
#Add Logic for Federal
}
'State' {
#Add Logic for State
}
'City' {
#Add Logic for City
}
default {}
}
}
Essa é uma técnica muito interessante para criar logicas e comandos e deixá-los mais flexiveis. Pessoalmente gosto de simplificar o maximo possivel enquanto mantenho a flexibilidade. No exemplo acima, ao invés de escrever 3 funcões diferentes que fazem basicamente a mesma funçao, reduzimos para apenas 1 que aborda todos os casos de uso do exemplo.
Função completa:
function Add-ManagementGroup
{
[CmdletBinding()]
Param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
[ValidateScript({ConvertFrom-Json -InputObject $PSitem })]
$InputObject,
[Parameter(Mandatory=$true)]
[Alias("BUName")]
[ValidateNotNullOrEmpty()]
$Name,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[ValidateSet('Root','Federal','State','City')]
$Level
)
DynamicParam {
if($Level -eq 'City'){
$SegmentAttribute = New-Object -TypeName System.Management.Automation.ParameterAttribute
$SegmentAttribute.Position = 4
$SegmentAttribute.Mandatory = $true
#create an attributecollection object for the attribute we just created.
$attributeCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute]
#add our custom attribute
$attributeCollection.Add($SegmentAttribute)
#add our paramater specifying the attribute collection
$SegmentParameter = New-Object System.Management.Automation.RuntimeDefinedParameter('SegmentName', [String], $attributeCollection)
#expose the name of our parameter
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('SegmentName', $SegmentParameter)
return $paramDictionary
}
}
BEGIN {}
PROCESS {
switch($level){
'Federal' {
#Add Logic for Federal
}
'State' {
#Add Logic for State
}
'City' {
#Add Logic for City
}
default {}
}
}
END {}
}
Dúvidas? Sugestões? Comente!
Até a próxima!
4 Comments
como acesso e trato depois o valor do parâmetro dinâmico no PROCESS?
Olá Lucas, como uma variavel normal, caso nao esteja presente o valor será nulo.
como crio um parâmetro dinâmico após a existência, ou valor, de outro parâmetro dinâmico?
Pode comparar o valor da variavel a nulo, por exemplo se for uma string:
if(-not([string]::IsNullOrEmpty($parametro)){...}