Aggiungere il supporto delle credenziali alle funzioni di PowerShell

Nota

La versione originale di questo articolo è apparsa nel blog scritto da @joshduffney. Questo articolo è stato modificato per essere incluso in questo sito. Il team di PowerShell ringrazia Josh per averne condiviso il contenuto. Visitare il suo blog all'indirizzo duffney.io.

Questo articolo illustra come aggiungere i parametri delle credenziali alle funzioni di PowerShell e i motivi per cui eseguire questa operazione. I parametri delle credenziali consentono di eseguire la funzione o il cmdlet come altro utente. L'uso più comune consiste nell'eseguire la funzione o il cmdlet come un account utente con privilegi elevati.

Ad esempio, il cmdlet New-ADUser ha un parametro Credential che consente di specificare le credenziali di amministratore di dominio per creare un account in un dominio, supponendo che il normale account che esegue la sessione di PowerShell non abbia già tale accesso.

Creazione di un oggetto credenziali

L'oggetto PSCredential rappresenta un set di credenziali di sicurezza, come ad esempio un nome utente e una password. L'oggetto può essere passato come parametro a una funzione che viene eseguita con l'account utente in tale oggetto credenziali. È possibile creare un oggetto credenziali in diversi modi. Il primo modo per creare un oggetto credenziali consiste nell'usare il cmdlet di PowerShell Get-Credential. Quando l'esecuzione avviene senza parametri, viene richiesto un nome utente e una password. In alternativa, è possibile chiamare il cmdlet con alcuni parametri facoltativi.

Per specificare il nome di dominio e il nome utente in anticipo, è possibile usare i parametri Credential o UserName. Quando si usa il parametro UserName, è necessario specificare anche un valore Message. Il codice seguente illustra l'uso del cmdlet. È anche possibile archiviare l'oggetto credenziali in una variabile in modo da poter usare più volte le credenziali. Nell'esempio seguente l'oggetto credenziali viene archiviato nella variabile $Cred.

$Cred = Get-Credential
$Cred = Get-Credential -Credential domain\user
$Cred = Get-Credential -UserName domain\user -Message 'Enter Password'

In alcuni casi non è possibile usare il metodo interattivo per la creazione di oggetti credenziali descritto nell'esempio precedente. La maggior parte degli strumenti di automazione richiede un metodo non interattivo. Per creare credenziali senza interazione dell'utente, creare una stringa sicura contenente la password. Passare quindi la stringa sicura e il nome utente al metodo System.Management.Automation.PSCredential().

Usare il comando seguente per creare una stringa sicura che contiene la password:

ConvertTo-SecureString "MyPlainTextPassword" -AsPlainText -Force

Sono necessari entrambi i parametri AsPlainText e Force. Senza questi parametri, viene visualizzato un messaggio di avviso che indica di non passare testo normale in una stringa sicura. PowerShell restituisce questo avviso perché la password in testo normale viene registrata in diversi log. Una volta creata una stringa sicura, è necessario passarla al metodo PSCredential() per creare l'oggetto credenziali. Nell'esempio seguente la variabile $password contiene la stringa sicura $Cred contenente l'oggetto credenziali.

$password = ConvertTo-SecureString "MyPlainTextPassword" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ("username", $password)

Ora che si è appreso come creare oggetti credenziali, è possibile aggiungere i parametri delle credenziali alle funzioni di PowerShell.

Aggiunta di un parametro Credential

Come per qualsiasi altro parametro, iniziare aggiungendolo al blocco param della funzione. È consigliabile denominare il parametro $Credential perché è il nome che viene usato dai cmdlet di PowerShell esistenti. Il tipo del parametro deve essere [System.Management.Automation.PSCredential].

Nell'esempio seguente viene illustrato il blocco di parametri per una funzione denominata Get-Something che contiene due parametri: $Name e $Credential.

function Get-Something {
    param(
        $Name,
        [System.Management.Automation.PSCredential]$Credential
    )

Il codice in questo esempio è sufficiente per avere un parametro di credenziali funzionante, tuttavia esistono alcuni elementi che è possibile aggiungere per renderlo più affidabile.

  • Aggiungere l'attributo di convalida [ValidateNotNull()] per verificare che il valore venga passato a Credential. Se il valore del parametro è Null, questo attributo impedisce l'esecuzione della funzione con credenziali non valide.

  • Aggiungere [System.Management.Automation.Credential()]. In questo modo è possibile passare un nome utente come stringa e avere un prompt interattivo per la password.

  • Impostare un valore predefinito per il parametro $Credential su [System.Management.Automation.PSCredential]::Empty. La funzione potrebbe passare questo oggetto $Credential ai cmdlet di PowerShell esistenti. Se si specifica un valore Null per il cmdlet chiamato all'interno della funzione, viene generato un errore. Per evitare questo errore, specificare un oggetto credenziali vuoto.

Suggerimento

Alcuni cmdlet che accettano un parametro delle credenziali non supportano [System.Management.Automation.PSCredential]::Empty come dovrebbero. Per una soluzione alternativa, vedere la sezione Gestione dei cmdlet legacy.

Uso dei parametri delle credenziali

L'esempio seguente illustra come usare i parametri delle credenziali. In questo esempio viene illustrata una funzione denominata Set-RemoteRegistryValue, presa dal The Pester Book. Questa funzione definisce il parametro delle credenziali usando le tecniche descritte nella sezione precedente. La funzione chiama Invoke-Command usando la variabile $Credential creata dalla funzione. In questo modo è possibile modificare l'utente che esegue Invoke-Command. Poiché il valore predefinito di $Credential è una credenziale vuota, la funzione può essere eseguita senza specificare le credenziali.

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
}

Le sezioni seguenti illustrano metodi diversi per specificare le credenziali per Set-RemoteRegistryValue.

Richiesta di credenziali

Se si usa Get-Credential tra parentesi () in fase di esecuzione, Get-credential viene eseguito per primo. Vengono richiesti un nome utente e una password. Per prepopolare il nome utente e il dominio, è possibile usare i parametri Credential o UserName di Get-credential. Nell'esempio seguente viene usata una tecnica denominata splatting per passare i parametri alla funzione Set-RemoteRegistryValue. Per altre informazioni sullo splatting, vedere l'articolo Informazioni sullo splatting.

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

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

Ottenere credenziali in fase di esecuzione

L'uso di (Get-Credential) sembra complesso. In genere, quando si usa il parametro Credential con solo un nome utente, il cmdlet richiede automaticamente la password. L'attributo [System.Management.Automation.Credential()] abilita questo comportamento.

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

Set-RemoteRegistryValue @remoteKeyParams -Credential duffney

Richiesta di credenziali

Nota

Per impostare il valore del Registro di sistema visualizzato, in questi esempi si presuppone che le funzionalità Server Web di Windows siano installate. Eseguire Install-WindowsFeature Web-Server e Install-WindowsFeature web-mgmt-tools se necessario.

Specificare le credenziali in una variabile

È anche possibile popolare anticipatamente una variabile credenziali e passarla al parametro Credential della funzione Set-RemoteRegistryValue. Usare questo metodo con strumenti di integrazione continua/distribuzione continua (CI/CD), come ad esempio Jenkins, TeamCity e Octopus Deploy. Per un esempio di uso di Jenkins, vedere il post di blog di Hodge Automating with Jenkins and PowerShell on Windows - Part 2.

In questo esempio viene usato il metodo .NET per creare l'oggetto credenziali e una stringa sicura da passare alla password.

$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

Per questo esempio, la stringa sicura viene creata usando una password non crittografata. Tutti gli strumenti CI/CD indicati in precedenza hanno un metodo sicuro per specificare la password in fase di esecuzione. Quando si usano questi strumenti, sostituire la password in testo normale con la variabile definita nello strumento CI/CD usato.

Esecuzione senza credenziali

Poiché $Credential ha come impostazione predefinita un oggetto credenziali vuoto, è possibile eseguire il comando senza credenziali, come illustrato nell'esempio seguente:

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

Set-RemoteRegistryValue @remoteKeyParams

Gestione dei cmdlet legacy

Non tutti i cmdlet supportano gli oggetti credenziali o consentono credenziali vuote. Al contrario, richiedono i parametri nome utente e password come stringhe. Esistono diversi modi per aggirare questa limitazione.

Uso di if-else per gestire le credenziali vuote

In questo scenario il cmdlet che si vuole eseguire non accetta un oggetto credenziali vuoto. In questo esempio viene aggiunto il parametro Credential per Invoke-Command solo se non è vuoto. In caso contrario, viene eseguito Invoke-Command senza il parametro 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 dello splatting per gestire le credenziali vuote

Questo esempio usa lo splatting del parametro per chiamare il cmdlet legacy. L'oggetto $Credential viene aggiunto in modo condizionale alla tabella hash per lo splatting ed evita la necessità di ripetere il blocco di script Invoke-Command. Per altre informazioni sulle funzioni di splatting, vedere il post di blog Splatting di parametri all'interno di funzioni avanzate.

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
}

Uso delle password in stringhe

Il cmdlet Invoke-Sqlcmd è un esempio di cmdlet che accetta una stringa come password. Invoke-Sqlcmd consente di eseguire semplici istruzioni SQL di inserimento, aggiornamento ed eliminazione. Invoke-Sqlcmd richiede un nome utente e una password non crittografati anziché un oggetto credenziali più sicuro. Questo esempio illustra come estrarre il nome utente e la password da un oggetto credenziali.

La funzione Get-AllSQLDatabases in questo esempio chiama il cmdlet Invoke-Sqlcmd per eseguire una query su SQL Server per tutti i relativi database. La funzione definisce un parametro Credential con lo stesso attributo usato negli esempi precedenti. Poiché il nome utente e la password sono presenti all'interno della variabile $Credential, è possibile estrarli per l'uso con Invoke-Sqlcmd.

Il nome utente è disponibile dalla proprietà UserName della variabile $Credential. Per ottenere la password, è necessario usare il metodo GetNetworkCredential() dell'oggetto $Credential. I valori vengono estratti in variabili aggiunte a una tabella hash usata per lo splatting dei parametri in 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

Gestione delle credenziali - formazione continua

La creazione e l'archiviazione di oggetti credenziali in modo sicuro possono risultare difficili. Le risorse seguenti consentono di gestire le credenziali di PowerShell.