Agregar soporte de credenciales a las funciones de PowerShell
Nota
La versión original de este artículo apareció en el blog escrito por @joshduffney. Este artículo ha sido editado para su inclusión en este sitio. El equipo de PowerShell agradece a Josh por compartir este contenido con nosotros. Consulte su blog en duffney.io.
Este artículo le muestra cómo agregar parámetros de credenciales a las funciones de PowerShell y por qué desea hacerlo. Un parámetro de credencial sirve para permitirle ejecutar la función o el cmdlet como un usuario diferente. El uso más común es ejecutar la función o cmdlet como una cuenta de usuario elevada.
Por ejemplo, el cmdlet New-ADUser tiene un parámetro Credential, que le permite proporcionar credenciales de administrador de dominio para crear una cuenta en un dominio. Suponiendo que su cuenta normal que ejecuta la sesión de PowerShell aún no tiene ese acceso.
Creando objeto de credencial
El objeto PSCredential representa un conjunto de credenciales de seguridad, como un nombre de usuario y una contraseña. El objeto se puede pasar como parámetro a una función que se ejecuta como cuenta de usuario en ese objeto de credencial. Hay algunas formas de crear un objeto de credencial. La primera forma de crear un objeto de credencial es utilizar el cmdlet de PowerShell Get-Credential. Cuando ejecuta sin parámetros, le solicita un nombre de usuario y contraseña. O puede llamar al cmdlet con algunos parámetros opcionales.
Para especificar el nombre de dominio y el nombre de usuario con anticipación, puede utilizar los parámetros Credencial o Nombre de usuario. Cuando utiliza el parámetro Nombre de usuario, también debe proporcionar un valor de Mensaje. El siguiente código demuestra el uso del cmdlet. También puede almacenar el objeto de credencial en una variable para poder utilizar la credencial varias veces. En el siguiente ejemplo, el objeto de credencial se almacena en la variable $Cred.
$Cred = Get-Credential
$Cred = Get-Credential -Credential domain\user
$Cred = Get-Credential -UserName domain\user -Message 'Enter Password'
A veces, no se puede utilizar el método interactivo de creación de objetos de credenciales que se muestra en el ejemplo anterior. La mayoría de las herramientas de automatización requieren un método no interactivo. Para crear una credencial sin interacción del usuario, cree una cadena segura que contenga la contraseña. Luego pase la cadena segura y el nombre de usuario al método System.Management.Automation.PSCredential().
Utilice el siguiente comando para crear una cadena segura que contenga la contraseña:
ConvertTo-SecureString "MyPlainTextPassword" -AsPlainText -Force
Se requieren los parámetros AsPlainText y Force. Sin esos parámetros, recibirá un mensaje advirtiéndole que no debe pasar texto sin formato a una cadena segura. PowerShell devuelve esta advertencia porque la contraseña de texto sin formato se registra en varios registros. Una vez que haya creado una cadena segura, deberá pasarla al método PSCredential() para crear el objeto de credencial. En el siguiente ejemplo, la variable $password contiene la cadena segura $Cred contiene el objeto de credencial.
$password = ConvertTo-SecureString "MyPlainTextPassword" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ("username", $password)
Ahora que sabe cómo crear objetos de credenciales, puede agregar parámetros de credenciales a sus funciones de PowerShell.
Agregar un parámetro de credencial
Al igual que cualquier otro parámetro, comienza agregándolo en el bloque param de tu función. Se recomienda nombrar el parámetro $Credential porque eso es lo que usan los cmdlets de PowerShell existentes. El tipo de parámetro debe ser [System.Management.Automation.PSCredential].
El siguiente ejemplo muestra el bloque de parámetros para una función llamada Get-Something. Tiene dos parámetros: $Name y $Credential.
function Get-Something {
param(
$Name,
[System.Management.Automation.PSCredential]$Credential
)
El código de este ejemplo es suficiente para tener un parámetro de credencial funcional; sin embargo, hay algunas cosas que puede agregar para hacerlo más sólido.
Agregue el atributo de validación
[ValidateNotNull()]para comprobar que el valor se pasa a Credencial. Si el valor del parámetro es nulo, este atributo evita que la función se ejecute con credenciales no válidas.Agregue
[System.Management.Automation.Credential()]. Esto le permite pasar un nombre de usuario como una cadena y tener una solicitud interactiva para la contraseña. -
Establezca un valor predeterminado para el parámetro
$Credentialen[System.Management.Automation.PSCredential]::Empty. Es posible que su función esté pasando este objeto$Credentiala cmdlets de PowerShell existentes. Proporcionar un valor nulo al cmdlet llamado dentro de su función provoca un error. Proporcionar un objeto de credencial vacío evita este error.
Consejo útil
Algunos cmdlets que aceptan un parámetro de credencial no admiten [System.Management.Automation.PSCredential]::Empty como deberían. Consulte la sección Manejo de cmdlets heredados para obtener una solución alternativa.
Usando parámetros de credenciales
El siguiente ejemplo demuestra cómo utilizar los parámetros de credenciales. Este ejemplo muestra una función llamada Set-RemoteRegistryValue, que no está en The Pester Book. Esta función define el parámetro de credencial utilizando las técnicas descritas en la sección anterior. La función llama a Invoke-Command usando la variable $Credential creada por la función. Esto le permite cambiar el usuario que ejecuta Invoke-Command. Debido a que el valor predeterminado de $Credential es una credencial vacía, la función se puede ejecutar sin proporcionar credenciales.
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
}
Las siguientes secciones muestran diferentes métodos para proporcionar credenciales a Set-RemoteRegistryValue.
Solicitar credenciales
El uso de Get-Credential entre paréntesis () en tiempo de ejecución hace que Get-credential se ejecute primero. Se le solicitará un nombre de usuario y contraseña. Puede utilizar los parámetros Credential o UserName de Get-credential para completar previamente el nombre de usuario y el dominio. El siguiente ejemplo utiliza una técnica llamada splatting para pasar parámetros a la función Set-RemoteRegistryValue. Para obtener más información sobre Splatting, consulte el artículo about_Splatting.
$remoteKeyParams = @{
ComputerName = $env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams -Credential (Get-Credential)
Usar (Get-Credential) parece engorroso. Normalmente, cuando utiliza el parámetro Credencial solo con un nombre de usuario, el cmdlet solicita automáticamente la contraseña. El atributo [System.Management.Automation.Credential()] habilita este comportamiento.
$remoteKeyParams = @{
ComputerName = $env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams -Credential duffney
Nota
Para establecer el valor de registro que se muestra, estos ejemplos suponen que tiene instaladas las funciones del servidor web de Windows. Ejecute Install-WindowsFeature Web-Server e Install-WindowsFeature web-mgmt-tools si es necesario.
Proporcionar credenciales en una variable.
También puede completar una variable de credencial con anticipación y pasarla al parámetro Credential de la función Set-RemoteRegistryValue. Utilice este método con herramientas de integración continua/implementación continua (CI/CD) como Jenkins, TeamCity y Octopus Deploy. Para ver un ejemplo de uso de Jenkins, consulte la publicación del blog de Hodge Automatización con Jenkins y PowerShell en Windows - Parte 2.
Este ejemplo utiliza el método .NET para crear el objeto de credencial y una cadena segura para pasar la contraseña.
$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
Para este ejemplo, la cadena segura se crea utilizando una contraseña de texto sin cifrar. Todos los CI/CD mencionados anteriormente tienen un método seguro para proporcionar esa contraseña en tiempo de ejecución. Cuando utilice esas herramientas, reemplace la contraseña de texto sin formato con la variable definida dentro de la herramienta CI/CD que utiliza.
Ejecutar sin credenciales
Dado que $Credential tiene como valor predeterminado un objeto de credencial vacío, puede ejecutar el comando sin credenciales, como se muestra en este ejemplo:
$remoteKeyParams = @{
ComputerName = $env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams
Manejo de cmdlets heredados
No todos los cmdlets admiten objetos de credenciales ni permiten credenciales vacías. En cambio, el cmdlet desea que los parámetros de nombre de usuario y contraseña sean cadenas. Hay algunas formas de solucionar esta limitación.
Usando if-else para manejar credenciales vacías
En este escenario, el cmdlet que desea ejecutar no acepta un objeto de credencial vacío. Este ejemplo agrega el parámetro Credential a Invoke-Command solo si no está vacío. De lo contrario, ejecuta el Invoke-Command sin el 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 splatting para manejar credenciales vacías
En este ejemplo se utiliza la dispersión de parámetros para llamar al cmdlet heredado. El objeto $Credential se agrega condicionalmente a la tabla hash para salpicar y evita la necesidad de repetir el bloque de script Invoke-Command. Para obtener más información sobre las funciones internas de Splatting, consulte la publicación del blog Parámetros de Splatting dentro de funciones avanzadas.
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
}
Trabajar con contraseñas de cadena
El cmdlet Invoke-Sqlcmd es un ejemplo de un cmdlet que acepta una cadena como contraseña. Invoke-Sqlcmd le permite ejecutar instrucciones simples de inserción, actualización y eliminación de SQL. Invoke-Sqlcmd requiere un nombre de usuario y una contraseña en texto claro en lugar de un objeto de credencial más seguro. Este ejemplo muestra cómo extraer el nombre de usuario y la contraseña de un objeto de credencial.
La función Get-AllSQLDatabases en este ejemplo llama al cmdlet Invoke-Sqlcmd para consultar un servidor SQL para todas sus bases de datos. La función define un parámetro Credencial con el mismo atributo utilizado en los ejemplos anteriores. Dado que el nombre de usuario y la contraseña existen dentro de la variable $Credential, puede extraer esos valores para usarlos con Invoke-Sqlcmd.
El nombre de usuario está disponible en la propiedad UserName de la variable $Credential. Para obtener la contraseña, debe utilizar el método GetNetworkCredential() del objeto $Credential. Los valores se extraen en variables que se agregan a una tabla hash utilizada para dividir los parámetros en 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
Gestión de credenciales de aprendizaje continuo
Crear y almacenar objetos de credenciales de forma segura puede resultar complicado. Los siguientes recursos pueden ayudarle a mantener las credenciales de PowerShell.
- BetterCredentials
- Bóveda de claves de Azure
- Proyecto de bóveda
- Módulo de gestión secreta

