2

I want to be able to log into VIServers using PowerShell and have it ask for the credentials the first time the script runs, then save those credentials in the password.txt file and have the VIServer just use that password.txt file stored locally on the user's computer if the user runs the script again. The pain point is that the credential prompt keeps popping up again and again while the user wants to run the script several times.

I am able to use the following code from another answer posted here on Stackoverflow (Link: http://www.adminarsenal.com/admin-arsenal-blog/secure-password-with-powershell-encrypting-credentials-part-1)

and it works:

    Read-Host "Enter Password" -AsSecureString | ConvertFrom-SecureString |
               Out-File "G:\dev\Password.txt"

    $pass = Get-Content "G:\dev\Password.txt" | ConvertTo-SecureString

    $User = "MyUserName"
    $File = "G:\dev\Password.txt"
    $MyCredential = New-Object -TypeName System.Management.Automation.PSCredential
                    -ArgumentList $User, (Get-Content $File | ConvertTo-SecureString)

I found the following from a vmware blog (Link: http://blogs.vmware.com/PowerCLI/2011/11/have-you-seen-powerclis-credential-store-feature.html)

And here is the code from vmware blog(with some explanation):

To use the credential store, I do the following:

New-VICredentialStoreItem -Host 192.168.10.10 -User "Andrey" -Password "my favorite password"

Now I can type just:

Connect-VIServer 192.168.10.10

When I don’t specify user and/or password, Connect-VIServer checks the credential store, finds my newly stored credential and uses it.

By default the credential store file is stored under the user profile directory. It is encrypted. If I got you interested, check “help *VICredentialStoreItem” for details.

-Andrey Anastasov, PowerCLI Architect

=============AND NOW MY MODIFIED VERSION OF THE VIServer code========== $Hostname = 192.168.10.10

New-VICredentialStoreItem -Host $Hostname -User $User -Password $pass

Am I on the right track?

What should I do to type the credentials only 1 time and then just have the script call that $creds variable instead of having to type in the credentials every time?

user4833159
  • 21
  • 1
  • 5
  • You might decide to check if there's a result in `Get-VICredentialStoreItem` with your username, if there's none, get credentials from `Read-Host` and save in there, otherwise skip. ("Get-VICredentialStoreItem" should be there, due to Powershell naming conventions for type management functions) – Vesper Jul 07 '15 at 07:12

3 Answers3

1

The problem I see with most answers to this question is that the password is handled and stored in plain text. Security-wise, this is a big no-no. PowerShell can keep these credentials secure both in memory as well as when stored. A secure XML file can be created that can only be accessed by the user that created it and only on the machine it was creates on. I didn't create all of this script, I found pieces online at different locations, so I have no names to credit.

#Store-Credentials
#This script collects the credentials used by my other scripts and saves them into secure XML files.
#The XML files can only be imported by my user account, and only from the machine it was created on specifically.
#This script needs to be run any time you change your password, obviously...

    $counter = 0
    $again = $true

    while($again){
        if($counter -ge 3){
            Write-Warning -Message ('You have entered your password {0} times incorrectly' -f $counter)
            Write-Warning -Message ('Please wait until {0} to try again to avoid risking locking yourself out.' -f $((Get-Date).AddMinutes(+15).ToShortTimeString()))
            Start-Sleep -Seconds 30
        }

        # Get username and password from user...
        $username = Read-Host -Prompt 'Please enter your SSO using full path (Domain\SSO)'
        $password = Read-Host -AsSecureString -Prompt 'Please enter your password'

        try{
            $creds = New-Object System.Management.Automation.PSCredential $username,$password

            # Get the current domain
            $domain = 'LDAP://{0}' -f $creds.GetNetworkCredential().Domain

            # Try to get the username and password from the network...
            $username = $creds.GetNetworkCredential().UserName
            $password = $creds.GetNetworkCredential().Password

        }
        catch{
            Write-Warning -Message ('There was a problem with what you entered: {0}' -f $_.exception.message)
            continue
        }

        # Check against the domain to authenticate the user.
        $UserObject = New-Object System.DirectoryServices.DirectoryEntry($domain,$username,$password)
        # If we get a result back with a name property then we're good to go and we can store the credential.
        if($UserObject.name){
            Write-Host "Saving credentials..."
            Export-Clixml -InputObject $creds -Path $env:userprofile\SSOCreds.xml
           
            #Check for stored credentials...
            $creds_stored = Test-Path -Path $env:userprofile\SSOCreds.xml

                If ($creds_stored -eq $true)
                    {Write-Host "Credentials saved."}
                Else
                    {Write-Host "There was a problem writing the file...  your credentials were not saved."}
           
            $again = $false
            Remove-Variable password -Force
        }
        else{
            $counter++

            Write-Warning -Message ('The password you entered for {0} was incorrect.  Attempts {1}. Please try again.' -f $userName,$counter)
        }
    }

Once the credentials are stored, you can pull them in by using the following piece by including it at the beginning of your other scripts

#Check for stored credentials to log onto the vCenter...
        $creds_stored = Test-Path -Path $env:userprofile\SSOCreds.xml

        If ($creds_stored -eq $true) {

            Write-Host "Stored credentials found." -ForegroundColor Cyan
            $creds = Import-Clixml -Path $env:userprofile\SSOCreds.xml
       
        }
        Else {

            $creds = Get-Credential -Message "Please enter your credentials to acces the vCenter using full path (Domain\SSO)."
            Write-Host "If you would like to store your credentials, use the Store-Credentials script." -ForegroundColor Cyan
       
        }

        #Connect-ViServer server.domain.com
        Connect-VIServer -Server $vCenter -Credential $creds -Force
Rob Tullis
  • 11
  • 1
0

The simplest approach that I can see, based on what you provided is to wrap that command inside another script. One that checks the credential store and THEN prompts for your creds if the entry you're looking for doesn't exist.

Can you give me information on what your workflow looks like?

user35731
  • 1
  • 1
  • Sure: 1) Open Script in PS ISE 2) Navigate to correct directory where script is located 3) Run script in ISE 4) I get prompt to input credentials -> I enter username and password (Password is hidden) 5) the script goes into a VIServer, extracts names of all the VMs inside a specified VIServer in the script 6) For now, that is all, the goal is to keep this script running daily and compare the VM list with another list generated internally (for work). 7) Automation would be ideal, for now, launching manually - still in testing phase Thank you for your help! – user4833159 Jul 07 '15 at 18:20
0

First Save the credential to disk, like this:

$credential = Get-Credential
$credential.Password | ConvertFrom-SecureString | Set-Content c:\temp\Cred.txt

Then Load it from the disk and create a $Credential Variable like this:

$username = "Domain\UserName"
$encrypted = Get-Content C:\Temp\Cred.txt | ConvertTo-SecureString
$credential = New-Object System.Management.Automation.PsCredential($username, $encrypted)

Then You can use a Function that receive the $credential input:

function Connect-vCenter
{
    Param (

    [Parameter(Mandatory = $True)]
    $vCenterServer, 
    [System.Management.Automation.PSCredential]$Credential
    )

    if ($Credential)
    {
        Add-PSSnapin VMware.VimAutomation.Core
        Connect-VIServer $vCenterServer -Credential $Credential
    }
    else
    {
        Add-PSSnapin VMware.VimAutomation.Core
        Connect-VIServer $vCenterServer
    }

}

To Run it:

Connect-vCenter -vCenterServer vCenter -Credential $Credential

Of Course only the user that encrypt the credential can use it, if you want however to encrypt it with different key (less secure) you can add -Key Parameter like this:

$Key = [Byte]1..16
$credential.Password | ConvertFrom-SecureString -Key $Key | Set-Content c:\temp\Cred.txt

To Decrpyt:

$encrypted = Get-Content C:\Temp\Cred.txt | ConvertTo-SecureString -Key $Key
$credential = New-Object System.Management.Automation.PsCredential($username, $encrypted)
Avshalom
  • 8,657
  • 1
  • 25
  • 43