Compatibilidad para agregar credenciales a funciones de PowerShell

Nota:

La versión original de este artículo apareció en el blog escrito por @joshduffney. Este artículo se ha editado para su inclusión en este sitio. El equipo de PowerShell agradece a Josh que comparta este contenido con nosotros. Visite su blog en duffney.io.

En este artículo se muestra cómo agregar parámetros de credencial a las funciones de PowerShell y su utilidad. Un parámetro de credencial sirve para permitir la ejecución de la función o el cmdlet como un usuario diferente. El uso más común es ejecutar la función o el cmdlet como una cuenta de usuario con privilegios elevados.

Por ejemplo, el cmdlet New-ADUser tiene un parámetro Credential, que podría proporcionar credenciales de administrador de dominio para crear una cuenta en un dominio. Suponiendo que la cuenta normal que ejecuta la sesión de PowerShell no tenga ya ese acceso.

Creación de 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 un parámetro a una función que se ejecuta como la cuenta de usuario en ese objeto de credencial. Hay varias maneras de crear un objeto de credencial. La primera de ellas es usar el cmdlet de PowerShell Get-Credential. Cuando se ejecuta sin parámetros, le pide un nombre de usuario y una contraseña. O bien, puede llamar al cmdlet con algunos parámetros opcionales.

Para especificar el nombre de dominio y el nombre de usuario de antemano, puede usar los parámetros Credential o UserName. Al usar el parámetro UserName, también se le pedirá que proporcione un valor Message. En el código siguiente se muestra cómo usar el cmdlet. También puede almacenar el objeto de credencial en una variable para que pueda usar la credencial varias veces. En el ejemplo siguiente, 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 puede usar el método interactivo para crear los objetos de credencial que se muestran 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. A continuación, pase la cadena segura y el nombre de usuario al método System.Management.Automation.PSCredential().

Use 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 advirtiendo 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 ejemplo siguiente, la variable $password contiene la cadena segura $Cred que 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 credencial, puede agregar parámetros de credencial a las funciones de PowerShell.

Adición de un parámetro de credencial

Al igual que con cualquier otro parámetro, comience agregándolo al bloque param de su función. Se recomienda asignar un nombre al parámetro $Credential porque eso es lo que usan los cmdlets de PowerShell existentes. El tipo del parámetro debe ser [System.Management.Automation.PSCredential].

En el ejemplo siguiente se muestra el bloque de parámetros de 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 de trabajo; sin embargo, puede añadirle más elementos para mejorar su eficacia.

  • Agregue el atributo de validación [ValidateNotNull()] para comprobar el valor que se pasa a Credential. Si el valor del parámetro es NULL, este atributo impide 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 $Credential en [System.Management.Automation.PSCredential]::Empty. Su función podría estar pasando este objeto $Credential a los cmdlets de PowerShell existentes. La provisión de un valor NULL al cmdlet llamado dentro de la función produce un error. Al proporcionar un objeto de credencial vacío, se evita este error.

Sugerencia

Algunos cmdlets que aceptan un parámetro de credencial no admiten [System.Management.Automation.PSCredential]::Empty como deberían. Consulte la sección Trabajo con cmdlets heredados para obtener una solución alternativa.

Uso de parámetros de credencial

En el ejemplo siguiente se muestra cómo usar parámetros de credencial. En este ejemplo se muestra una función llamada Set-RemoteRegistryValue, que está fuera de The Pester Book. Esta función define el parámetro de credencial mediante las técnicas descritas en la sección anterior. La función llama a Invoke-Command utilizando la variable $Credential creada por la función. Esto le permite cambiar el usuario que ejecuta Invoke-Command. Dado 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
}

En las secciones siguientes se muestran diferentes métodos para proporcionar credenciales a Set-RemoteRegistryValue.

Solicitud de credenciales

El uso de Get-Credential entre paréntesis () en el entorno de ejecución hace que Get-credential se ejecute en primer lugar. Se le pide un nombre de usuario y una contraseña. Puede usar los parámetros Credential o UserName de Get-credential para rellenar previamente el nombre de usuario y el dominio. En el ejemplo siguiente se utiliza una técnica denominada expansión para pasar parámetros a la función Set-RemoteRegistryValue. Para obtener más información sobre la expansión, consulte el artículo Acerca de expansión.

$remoteKeyParams = @{
    ComputerName = $env:COMPUTERNAME
    Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
    Name = 'EnableRemoteManagement'
    Value = '1'
}

Set-RemoteRegistryValue @remoteKeyParams -Credential (Get-Credential)

Obtención de una credencial en el entorno de ejecución

El uso de (Get-Credential) parece complicado. Normalmente, cuando se usa el parámetro Credential 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

Se piden credenciales

Nota:

Para establecer el valor del registro que se muestra, en estos ejemplos se supone que tiene las características de servidor web de Windows instaladas. Ejecute Install-WindowsFeature Web-Server y Install-WindowsFeature web-mgmt-tools si es necesario.

Suministro de credenciales en una variable

También puede rellenar una variable de credencial de antemano y pasarla al parámetro Credential de la función Set-RemoteRegistryValue. Use este método con herramientas de integración continua e implementación continua (CI/CD) como Jenkins, TeamCity y Octopus Deploy. Para obtener un ejemplo de uso de Jenkins, consulte la entrada de blog de Hodge Automatización con Jenkins y PowerShell en Windows, parte 2.

En este ejemplo se usa 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

En este ejemplo, la cadena segura se crea con una contraseña de texto no cifrado. Todo el proceso de CI/CD que se menciona anteriormente tiene un método seguro para proporcionar esa contraseña en el entorno de ejecución. Al usar esas herramientas, reemplace la contraseña de texto sin formato por la variable definida en la herramienta de CI/CD que use.

Ejecución sin credenciales

Puesto 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

Trabajo con cmdlets heredados

No todos los cmdlets admiten objetos de credencial ni permiten credenciales vacías. En su lugar, el cmdlet requiere parámetros de nombre de usuario y contraseña como cadenas. Hay varias maneras de solucionar esta limitación.

Uso de if-else para administrar las credenciales vacías

En este escenario, el cmdlet que desea ejecutar no acepta un objeto de credencial vacío. En este ejemplo se agrega el parámetro Credential a Invoke-Command solo si no está vacío. De lo contrario, ejecuta 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
        }
    }
}

Uso de expansión para administrar credenciales vacías

En este ejemplo se usa el parámetro de expansión para llamar al cmdlet heredado. El objeto $Credential se agrega condicionalmente a la tabla hash de expansión y evita la necesidad de repetir el bloque de script de Invoke-Command. Para obtener más información sobre la expansión dentro de las funciones, consulte la entrada de blog Parámetros de expansión 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
}

Trabajo con contraseñas de cadena

El cmdlet Invoke-Sqlcmd es un ejemplo de cmdlet que acepta una cadena como contraseña. Invoke-Sqlcmd permite ejecutar instrucciones insert, update y delete de SQL simples. Invoke-Sqlcmd requiere un nombre de usuario y una contraseña de texto no cifrado en lugar de un objeto de credencial más seguro. En este ejemplo se muestra cómo extraer el nombre de usuario y la contraseña de un objeto de credencial.

La función Get-AllSQLDatabases de este ejemplo llama al cmdlet Invoke-Sqlcmd para consultar en todas las bases de datos de SQL Server. La función define un parámetro Credential con el mismo atributo que se usa 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, tiene que utilizar el método GetNetworkCredential() del objeto $Credential. Los valores se extraen en variables que se agregan a una tabla hash que se utiliza para expandir 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

Administración de credenciales de aprendizaje continuo

La creación y el almacenamiento de objetos de credencial de forma segura puede ser difícil. Los recursos siguientes pueden ayudarle a mantener las credenciales de PowerShell.