Adicionar suporte a Credential às funções do PowerShell
Observação
A versão original deste artigo foi publicada no blog escrito por @joshduffney. Este artigo foi editado para inclusão neste site. A equipe do PowerShell agradece a Josh por compartilhar este conteúdo conosco. Confira o blog dele em duffney.io.
Este artigo mostra como adicionar parâmetros de credencial às funções do PowerShell e por que é conveniente fazer isso. Um parâmetro de credencial permite que você execute a função ou cmdlet como um usuário diferente. Ele é comumente usado para executar a função ou cmdlet como uma conta de usuário com privilégios elevados.
Por exemplo, o cmdlet New-ADUser
tem um parâmetro Credential, para o qual você pode fornecer credenciais de administrador de domínio a fim de criar uma conta em um domínio. Supondo que sua conta normal que está executando a sessão do PowerShell ainda não tenha esse acesso.
Criar um objeto de credencial
O objeto PSCredential representa um conjunto de credenciais de segurança, como nome de usuário e senha. O objeto pode ser passado como um parâmetro para uma função executada como a conta de usuário nesse objeto de credencial. Há algumas maneiras de criar um objeto de credencial. A primeira maneira é usar o cmdlet do PowerShell Get-Credential
. Quando você faz a execução sem parâmetros, é solicitado um nome de usuário e uma senha. Ou é possível chamar o cmdlet com alguns parâmetros opcionais.
Para especificar o nome de domínio e nome de usuário com antecedência, você pode usar os parâmetros Credential ou UserName. Ao usar o parâmetro UserName, você também deve fornecer um valor de Message. O código a seguir demonstra o uso do cmdlet. Você também pode armazenar o objeto de credencial em uma variável para poder usar a credencial várias vezes. No exemplo abaixo, o objeto de credencial é armazenado na variável $Cred
.
$Cred = Get-Credential
$Cred = Get-Credential -Credential domain\user
$Cred = Get-Credential -UserName domain\user -Message 'Enter Password'
Às vezes, não será possível usar o método interativo de criação de objetos de credencial mostrado no exemplo anterior. A maioria das ferramentas de automação requer um método não interativo. Para criar uma credencial sem interação do usuário, crie uma cadeia de caracteres segura que contenha a senha. Depois, passe a cadeia de caracteres segura e o nome de usuário para o método System.Management.Automation.PSCredential()
.
Use o seguinte comando para criar uma cadeia de caracteres segura que contém a senha:
ConvertTo-SecureString "MyPlainTextPassword" -AsPlainText -Force
Tanto o parâmetro AsPlainText quanto o Force são obrigatórios. Sem esses parâmetros, você recebe uma mensagem avisando que não deve passar texto sem formatação para uma cadeia de caracteres segura. O PowerShell retorna esse aviso porque a senha de texto sem formatação é registrada em vários logs. Depois de criar uma cadeia de caracteres segura, você precisa passá-la para o método PSCredential()
a fim de criar o objeto de credencial. No exemplo a seguir, a variável $password
contém a cadeia de caractere segura e $Cred
contém o objeto de credencial.
$password = ConvertTo-SecureString "MyPlainTextPassword" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ("username", $password)
Agora que você sabe como criar objetos de credencial, pode adicionar parâmetros de credencial às funções do PowerShell.
Adicionar um parâmetro de credencial
Assim como qualquer outro parâmetro, comece adicionando-o ao bloco param
de sua função.
Recomendamos nomear o parâmetro como $Credential
porque é isso que os cmdlets existentes do PowerShell usam. O tipo do parâmetro deve ser [System.Management.Automation.PSCredential]
.
O exemplo a seguir mostra o bloco de parâmetro para uma função chamada Get-Something
. Ela tem dois parâmetros: $Name
e $Credential
.
function Get-Something {
param(
$Name,
[System.Management.Automation.PSCredential]$Credential
)
O código nesse exemplo é suficiente para ter um parâmetro de credencial funcional. no entanto, você pode adicionar alguns elementos para torná-lo mais robusto.
Adicione o atributo de validação
[ValidateNotNull()]
para verificar se o valor está sendo passado para Credential. Se o valor do parâmetro for nulo, esse atributo impedirá que a função seja executada com credenciais inválidas.Adicione
[System.Management.Automation.Credential()]
. Isso permite que você passe um nome de usuário como uma cadeia de caracteres e tenha um prompt interativo para a senha.Defina um valor padrão do parâmetro
$Credential
para[System.Management.Automation.PSCredential]::Empty
. Sua função você pode estar passando esse objeto$Credential
para os cmdlets do PowerShell existentes. Fornecer um valor nulo ao cmdlet chamado dentro de sua função causa um erro. Para evitar isso, forneça um objeto de credencial vazio.
Dica
Alguns cmdlets que aceitam um parâmetro de credencial não dão suporte a [System.Management.Automation.PSCredential]::Empty
como deveriam. Confira a seção Lidar com cmdlets herdados para obter uma solução alternativa.
Usar parâmetros de credencial
O exemplo a seguir demonstra como usar parâmetros de credencial. Este exemplo mostra uma função chamada Set-RemoteRegistryValue
, extraída do The Pester Book. Essa função define o parâmetro de credencial usando as técnicas descritas na seção anterior. A função chama Invoke-Command
usando a variável $Credential
criada por ela. Isso permite que você altere o usuário que está executando Invoke-Command
. Como o valor padrão de $Credential
é uma credencial vazia, a função pode ser executada sem o fornecimento de credenciais.
function Set-RemoteRegistryValue {
param(
$ComputerName,
$Path,
$Name,
$Value,
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
$null = Invoke-Command -ComputerName $ComputerName -ScriptBlock {
Set-ItemProperty -Path $using:Path -Name $using:Name -Value $using:Value
} -Credential $Credential
}
As seções a seguir mostram métodos diferentes de fornecimento de credenciais para Set-RemoteRegistryValue
.
Solicitar credenciais
Usar Get-Credential
entre parênteses ()
durante o tempo de execução faz com que Get-credential
seja executado primeiro. Você será solicitado a informar um nome de usuário e senha. Você pode usar os parâmetros Credential ou UserName de Get-credential
para preencher previamente o nome de usuário e o domínio. O exemplo a seguir usa uma técnica chamada nivelamento a fim de passar parâmetros para a função Set-RemoteRegistryValue
. Para saber mais sobre o nivelamento, confira o artigo Sobre o nivelamento.
$remoteKeyParams = @{
ComputerName = $env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams -Credential (Get-Credential)
Usar o (Get-Credential)
parece complicado. Normalmente, quando você usa o parâmetro Credential com apenas um nome de usuário, o cmdlet solicita a senha automaticamente. O atributo [System.Management.Automation.Credential()]
habilita esse comportamento.
$remoteKeyParams = @{
ComputerName = $env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams -Credential duffney
Observação
Para definir o valor de registro mostrado, esses exemplos presumem que você possui os recursos do Servidor da Web do Windows instalados. Execute Install-WindowsFeature Web-Server
e Install-WindowsFeature web-mgmt-tools
, se necessário.
Fornecer credenciais em uma variável
Você também pode preencher uma variável de credencial com antecedência e passá-la para o parâmetro Credential da função Set-RemoteRegistryValue
. Use esse método com ferramentas de CI/CD (integração contínua/implantação contínua), como Jenkins, TeamCity e Octopus Deploy. Para obter um exemplo de uso do Jenkins, confira a postagem no blog de Hodge Automatizar com Jenkins e PowerShell no Windows – Parte 2.
Este exemplo usa o método .NET para criar o objeto de credencial e uma cadeia de caracteres segura para passar a senha.
$password = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ("duffney", $password)
$remoteKeyParams = @{
ComputerName = $env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams -Credential $Cred
Nesse exemplo, a cadeia de caracteres segura é criada usando uma senha com texto não criptografado. Todas as ferramentas de CI/CD mencionadas anteriormente têm um método seguro de fornecimento de senha em tempo de execução. Ao utilizá-las, substitua a senha de texto sem formatação pela variável definida na ferramenta de CI/CD que você usa.
Executar sem credenciais
Como o $Credential
usa como padrão um objeto de credencial vazio, você pode executar o comando sem credenciais, conforme mostrado no seguinte exemplo:
$remoteKeyParams = @{
ComputerName = $env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams
Lidar com cmdlets herdados
Nem todos os cmdlets dão suporte a objetos de credencial ou permitem credenciais vazias. Em vez disso, o cmdlet prefere usar os parâmetros de nome de usuário e senha como cadeias de caracteres. Para solucionar essa limitação, há algumas alternativas.
Usar if-else para lidar com credenciais vazias
Neste cenário, o cmdlet que você deseja executar não aceita um objeto de credencial vazio. Este exemplo adicionará o parâmetro Credential a Invoke-Command
somente se ele não estiver vazio. Caso contrário, executará o Invoke-Command
sem o parâmetro Credential.
function Set-RemoteRegistryValue {
param(
$ComputerName,
$Path,
$Name,
$Value,
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
if($Credential -ne [System.Management.Automation.PSCredential]::Empty) {
Invoke-Command -ComputerName:$ComputerName -Credential:$Credential {
Set-ItemProperty -Path $using:Path -Name $using:Name -Value $using:Value
}
} else {
Invoke-Command -ComputerName:$ComputerName {
Set-ItemProperty -Path $using:Path -Name $using:Name -Value $using:Value
}
}
}
Usar nivelamento para lidar com credenciais vazias
Este exemplo usa o nivelamento de parâmetro para chamar o cmdlet herdado. O objeto $Credential
é adicionado condicionalmente à tabela de hash de nivelamento e evita a necessidade de repetir o bloco de script Invoke-Command
. Para saber mais sobre o nivelamento dentro de funções, confira a postagem no blog Nivelar parâmetros dentro de funções avançadas.
function Set-RemoteRegistryValue {
param(
$ComputerName,
$Path,
$Name,
$Value,
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
$Splat = @{
ComputerName = $ComputerName
}
if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) {
$Splat['Credential'] = $Credential
}
$null = Invoke-Command -ScriptBlock {
Set-ItemProperty -Path $using:Path -Name $using:Name -Value $using:Value
} @splat
}
Trabalhar com cadeias de caracteres como senha
O cmdlet Invoke-Sqlcmd
é um dos que aceitam uma cadeia de caracteres como senha.
O Invoke-Sqlcmd
permite executar instruções SQL simples de inserção, atualização e exclusão. O Invoke-Sqlcmd
requer um nome de usuário e senha com texto não criptografado, em vez de um objeto de credencial mais seguro. Este exemplo mostra como extrair o nome de usuário e a senha de um objeto de credencial.
A função Get-AllSQLDatabases
neste exemplo chama o cmdlet Invoke-Sqlcmd
a fim de consultar um servidor SQL para todos os seus bancos de dados. A função define um parâmetro Credential com o mesmo atributo usado nos exemplos anteriores. Como o nome de usuário e a senha existem na variável $Credential
, você pode extrair esses valores para usar com Invoke-Sqlcmd
.
O nome de usuário está disponível na propriedade UserName da variável $Credential
. Para obter a senha, você deve usar o método GetNetworkCredential()
do objeto $Credential
. Os valores são extraídos em variáveis que são adicionadas a uma tabela de hash usada para nivelamento dos parâmetros para Invoke-Sqlcmd
.
function Get-AllSQLDatabases {
param(
$SQLServer,
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
$UserName = $Credential.UserName
$Password = $Credential.GetNetworkCredential().Password
$splat = @{
UserName = $UserName
Password = $Password
ServerInstance = 'SQLServer'
Query = "Select * from Sys.Databases"
}
Invoke-Sqlcmd @splat
}
$credSplat = @{
TypeName = 'System.Management.Automation.PSCredential'
ArgumentList = 'duffney',('P@ssw0rd' | ConvertTo-SecureString -AsPlainText -Force)
}
$Credential = New-Object @credSplat
Get-AllSQLDatabases -SQLServer SQL01 -Credential $Credential
Gerenciamento de credencial de aprendizagem contínua
Criar e armazenar objetos de credenciais com segurança pode ser difícil. Os recursos a seguir podem ajudar você a manter suas credenciais do PowerShell.