PowerShell v1 Script Prompting for Password - windows

I am trying to add remote server authentication to a PS1 batch file script.
So I can do this:
Copy-Item $src $destination -Credential $Creds
I created a password file that for now is in the same directory as the script. It simply contains the password string.
The line that causes the prompt:
Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File password.txt
When I remove the Read-Host command, the prompt goes away and the script executes as expected.
Question
What's the correct way to do remote server authentication?
Here is the new code in context of the script:
[...]
if(-not(Test-Path $destination)){mkdir $destination | out-null}
Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File password.txt
$PW = Get-Content password.txt | ConvertTo-Securestring
$Creds = New-Object -Typename System.Management.Automation.PSCredential -Argumentlist "SERVER02\Administrator",$PW
ForEach ($sourcefile In $(Get-ChildItem $source | Where-Object { $_.Name -match "Daily_Reviews\[\d{1,12}-\d{1,12}\].journal" }))
{
[...]
Copy-Item $src $destination -Credential $Creds
[...]
}

If you aren't worried about portability of the password file between machines, you can use this fairly secure approach:
# Capture once and store to file - DON'T PUT THIS PART IN YOUR SCRIPT
$passwd = Read-Host "Enter password" -AsSecureString
$encpwd = ConvertFrom-SecureString $passwd
$encpwd
$encpwd > $path\password.bin
# Later pull this in and restore to a secure string
$encpwd = Get-Content $path\password.bin
$passwd = ConvertTo-SecureString $encpwd
$cred = new-object System.Management.Automation.PSCredential 'john',$passwd
$cred
# NOTE: The "secret" required to rehyrdate correctly is stored in DPAPI - consequence:
# You can only rehydrate on the same machine that did the ConvertFrom-SecureString
If you need to debug this to see if $passwd is correct you can execute this while debugging:
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($passwd)
$str = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
$str

Related

How to Apply Encrypt/Decrypt in Powershell?

Im using in PS the next command:
"Password" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
This generate a Key that im saving as "Key.txt" file
Now i want to decrypt that password using this:
$password = Get-Content password.txt (or just copy-pasting the key)
$cred = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $username,($password | ConvertTo-SecureString)
BUT...
how i supose to add that to this...
$EmailFrom = "MyMail#gmail.com"
$EmailTo = "MayMail#gmail.com"
$Subject = "Test"
$Body = "this is a Test"
$SMTPServer = "smtp.gmail.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("My_USer", "My_Password");
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
I want to add it as My_Password, of course i should add a variable $password that comes from the Key.txt file for example, but then...?
Nope, storing in plain text is not good at all, but if you are not concerned about that then it's there.
You have other options, with secure / encrypted files and Windows CredMan:
Quickly and securely storing your credentials – PowerShell
To get a credential object we can either manually create one or use the Get-Credential cmdlet to prompt for the account details:
$Credential = Get-Credential
To store the credentials into a .cred file:
$Credential | Export-CliXml -Path "${env:\userprofile}\Jaap.Cred"
And to load the credentials from the file and back into a variable:
$Credential = Import-CliXml -Path "${env:\userprofile}\Jaap.Cred"
Invoke-Command -Computername 'Server01' -Credential $Credential {whoami}
Securely Store Credentials on Disk
Allow multiple users to access credentials stored using export-clixml
How to run a PowerShell script against multiple Active Directory domains with different credentials
PowerShell Credentials Manager
CredMan.ps1 is a PowerShell script that provides access to the Win32 Credential Manager API used for management of stored credentials.
https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Credentials-d44c3cde
And modules to use
https://powershellgallery.com/packages/BetterCredentials
https://powershellgallery.com/packages/CredentialManager
https://powershellgallery.com/packages/IntelliTect.CredentialManager
First we save the credentials
"Password123" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File C:\key.txt -NoNewline
Then we can use it just like this :
$SMTPClient = New-Object Net.Mail.SmtpClient("SomeServer", 587)
$SMTPClient.Credentials = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist ThisIsAUserName ,($(Get-Content C:\key.txt) | ConvertTo-SecureString)
And we can check to make sure it loaded correctly like this :
$SMTPClient.Credentials | select username, password
The output looks like this
UserName Password
-------- --------
ThisIsAUserName Password123

Changing Local Admin Password Using Encrypted Password.txt

I'm trying to update a local admin account password. I don't want to pass the password in plain text, so i found a workflow (https://www.pdq.com/blog/secure-password-with-powershell-encrypting-credentials-part-2/) that will allow me to encrypt PW's.
#Change password for TestAccount
$User = 'TestAccount'
$PasswordFile = "$PsScriptRoot\Password.txt"
$KeyFile = "$PsScriptRoot\AES.key"
$key = Get-Content $KeyFile
$MyCredential = New-Object -TypeName System.Management.Automation.PSCredential ` -ArgumentList $User, (Get-Content $PasswordFile | ConvertTo-SecureString -Key $key)
$adsiUser = [adsi]"WinNT://localhost/$User,user"
$adsiUser.SetPassword($MyCredential.Password)
I received the error
Exception calling "SetPassword" with "1" argument(s): "Type mismatch.
(Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))"
So my google-fu allowed me to decrypt the password with
$decodedpassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($MyCredential.Password))
But now there's a trail of cookies in my script... is there any way to pass this as a Secure String?
Thanks,
Paul
Use the Built-in method GetNetworkCredential() to get the plain text password from the Credential Object
So Change this line:
$adsiUser.SetPassword($MyCredential.Password)
To This:
$adsiUser.SetPassword($MyCredential.GetNetworkCredential().Password)
For short Credential Save/Load Tutorial:
#To Save Credential to file
$pass = "123456" | ConvertTo-SecureString -AsPlainText -Force
$pass | ConvertFrom-SecureString | Set-Content c:\temp\pass.txt
#To Load Credential From Text
$username = "userName"
$encrypted = Get-Content c:\temp\pass.txt | ConvertTo-SecureString
$credential = New-Object System.Management.Automation.PsCredential($username, $encrypted)
#To Show the Plain text Password from the Credential Object
$credential.GetNetworkCredential().Password

Powershell multiple credential setup

I just setup a powershell to collect disk space info among a group of servers.
but I encounter an issue that while I try to authenticate from one server to another, it require different credential info.
E.g.
SQLServer01
ID: domain1\sqladmin1
PW: 123456
SQLServer02
ID: domain2\sqladmin2
PW: 654321
right now i manage to setup first one with limited power-shell experience.
$comp= Get-Content "C:\disk_info\Computers.txt"
$diskvalue = #()
#$cre = get-Credential
$username = "domain1\sqladmin1"
$password = "123456"
$secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential -ArgumentList $user, $secureStringPwd
foreach($pc in $comp)
{
$diskvalue += Get-WmiObject -Class Win32_logicaldisk -ComputerName $pc -credential $creds -Filter DriveType=3 |
Select SystemName , DeviceID , #{Name=”size(GB)”;Expression={“{0:N1}” -f($_.size/1gb)}}, #{Name=”freespace(GB)”;Expression={“{0:N1}” -f($_.freespace/1gb)}}, #{Name=”UsedSpace(GB)”;Expression={“{0:N2}” -f(($_.size - $_.FreeSpace)/1gb)}}
#$diskvalue -replace ":",""
$diskvalue | Export-Csv C:\disk_info\DiskReport.csv -NoTypeInformation
}
Yet I am trying to input another credential for some servers only. domain2\ in this case.
Computer.txt as reference
sqlserver1.domain1.com
sqlserver2.domain1.domain2.com
sqlserver3.domain1.com
the one including "domain2" would be the need of multiple credential.
If you are using the full qualified domain names for the machine in Computers.txt, you could use a simple if statement to decide which domain credentials to use. You will just need to change the $domain2Match variable at the top to your 2nd domain in the below script ($domain2Match='.domain1.domain2.com').
$comp= Get-Content "C:\disk_info\Computers.txt"
$diskvalue = #()
# Put your FQDN without the server name here for the seconded domain
$domain2Match = '.domain1.domain2.com'
# Credential 1
$username = "domain1\sqladmin1"
$password = "123456"
$secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential -ArgumentList $user, $secureStringPwd
# Credential 2
$username2 = "domain2\sqladmin2"
$password2 = "123456"
$secureStringPwd2 = $password2 | ConvertTo-SecureString -AsPlainText -Force
$creds2 = New-Object System.Management.Automation.PSCredential -ArgumentList $user2, $secureStringPwd2
foreach($pc in $comp)
{
$credsToUse = $null
If($pc -imatch $domain2Match){
# Matched use domain 2 Credential
$credsToUse = $creds2
}Else {
# No match use domain 1 Credential
$credsToUse = $creds
}
$diskvalue += Get-WmiObject -Class Win32_logicaldisk -ComputerName $pc -credential $credsToUse -Filter DriveType=3 |
Select SystemName , DeviceID , #{Name=”size(GB)”;Expression={“{0:N1}” -f($_.size/1gb)}}, #{Name=”freespace(GB)”;Expression={“{0:N1}” -f($_.freespace/1gb)}}, #{Name=”UsedSpace(GB)”;Expression={“{0:N2}” -f(($_.size - $_.FreeSpace)/1gb)}}
$diskvalue | Export-Csv C:\disk_info\DiskReport.csv -NoTypeInformation
}
You can create two Credenital Objects:
Cred 1
$username = "domain1\sqladmin1"
$password = "123456"
$secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential -ArgumentList $user, $secureStringPwd
Cred 2
$username = "domain2\sqladmin2"
$password = "123456"
$secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
$creds2 = New-Object System.Management.Automation.PSCredential -ArgumentList $user, $secureStringPwd
Then use an IF Statement along with splatting Params (will be easier and cleaner)
foreach($pc in $comp)
{
$Params = #{
Class = "Win32_logicaldisk"
ComputerName = $pc
Credential = $creds
Filter = 'DriveType=3'
}
If ($pc -eq "SQLServer02")
{
$Params["Credential"] = $creds2
}
$diskvalue += Get-WmiObject #Params | Select SystemName , DeviceID , #{Name=”size(GB)”;Expression={“{0:N1}” -f($_.size/1gb)}}, #{Name=”freespace(GB)”;Expression={“{0:N1}” -f($_.freespace/1gb)}}, #{Name=”UsedSpace(GB)”;Expression={“{0:N2}” -f(($_.size - $_.FreeSpace)/1gb)}}
#$diskvalue -replace ":",""
$diskvalue | Export-Csv C:\disk_info\DiskReport.csv -NoTypeInformation
}

PowerShell_copy-item_ToSession/FromSession

I want to copy a file from the remote server to local, and my code is
Make sure the xxx.xxx.x.xxx's connection
>
Read-Host "Enter Password" -AsSecureString | ConvertFrom-SecureString | Out-File"C:\Users\chrishchang\Desktop\powershell/remote-password.txt"
$user = get-content C:\Users\chrishchang\Desktop\powershell/remote-user.txt
$pass = get-content C:\Users\chrishchang\Desktop\powershell/remote-password.txt |
ConvertTo-securestring
&myCred = new-object -typename System.Management.Automation.PSCredential -argumentlist $user,$pass
$session = new-pssession -computername name -credential $myCred
Invoke-Command -ComputerName xxx.xxx.x.xxx -ScriptBlock { ipconfig /all } -credential $myCred
create the new file
>
$command={New-Item c:\scripts\new_file.txt -type file -force -value "This is text added to the file"}
Invoke-Command -session $session -scriptblock $command
copy the file from xxx.xxx.x.xxx to local
>
$command={Copy-Item -FromSession $session -Path "c:\scripts\new_file.txt" -Destination "C:\Users\chrishchang\desktop\"}
Invoke-Command -session $session -scriptblock $command
The error result..
enter image description here
Please give me some suggestion, I have suffered from it for a long time.
The last step (3) should be:
Copy-Item -FromSession $session -Path "c:\scripts\new_file.txt" -Destination "C:\Users\chrishchang\desktop"
Don't use Invoke-Command as the Copy-Item already uses the session.

Create network share on machine outside domain

Issue:
Create a network share on a remote machine (Win 2008 Server) from a .ps1 script and provide username and password.
This works fine where the current user has enough privileges
$share = Get-WmiObject Win32_Share -List -ComputerName 10.0.0.1
$share.create("C:\dir","foo", 0)
When targetServer is a machine where my user does not have enough privileges I need a way to provide the username and password
Get-WmiObject has a parameter -Credential, so you can use Get-Credential to prompt the user for credentials and pass them like this:
$cred = Get-Credential
$share = Get-WmiObject Win32_Share -List -Computer 10.0.0.1 -Credential $cred
$share.Create('C:\dir', 'foo', 0)
If you want to fully automate credential handling, you can build a credential object yourself:
$username = 'DOMAIN\user'
$password = 'password'
$pw = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object Management.Automation.PSCredential $username, $pw
$share = Get-WmiObject Win32_Share -List -Computer 10.0.0.1 -Credential $cred
$share.Create('C:\dir', 'foo', 0)
Storing plaintext passwords in a script is not a good practice, though, so you may want to store the encrypted password in a separate file (make sure to set restrictive permissions on that file):
PS C:\> Read-Host 'Enter password' -AsSecureString |
>> ConvertFrom-SecureString | Out-File 'C:\password.txt'
>>
Enter password: ******
PS C:\> Get-Content 'C:\password.txt'
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000a04a09e406c93741865ab010ed072699
0000000002000000000003660000c00000001000000028b14f21c501cdf629b94cea2837e8520000
000004800000a0000000100000002db774b0a8612917224e7dbfd69055340800000043f449c82f47
3e78140000007de59fcec57a1dc9b6b62272eff517b8bf5291e5
The encrypted password can then be read and decrypted like this:
$username = 'DOMAIN\user'
$pw = Get-Content 'C:\password.txt' | ConvertTo-SecureString
$cred = New-Object Management.Automation.PSCredential $username, $pw
$share = Get-WmiObject Win32_Share -List -Computer 10.0.0.1 -Credential $cred
$share.Create('C:\dir', 'foo', 0)
Note, however, that ConvertTo-SecureString encrypts/decrypts the password with the user (and I think the host) running the command. If you need to encrypt the password as one user and decrypt it as another user (or on another host) you must share a key between those users/hosts. A random key can be generated like this:
PS C:\> $numchars = 24 # 192 Bit
PS C:\> $charmap = [char]'A'..[char]'Z' +
>> [char]'a'..[char]'z' +
>> [char]'0'..[char]'9'
>>
PS C:\> [char[]]($charmap | sort {Get-Random})[1..$numchars] -join ''
b69cGXKpJaI5tLrj4lHEPN3e
Create the password file with a shared key like this:
PS C:\> $key = [char[]]($charmap | sort {Get-Random})[1..$numchars] -join ''
PS C:\> $pw = Read-Host 'Enter password' -AsSecureString |
>> ConvertFrom-SecureString -Key ([byte[]][char[]]$key)
>>
Enter password: ******
PS C:\> #"
>> Key = $key
>> Pass = $pw
>> "# | Out-File 'C:\password.txt'
>>
and read the encrypted password like this:
$username = 'DOMAIN\user'
$pass = Get-Content 'C:\password.txt' | Out-String | ConvertFrom-StringData
$pw = ConvertTo-SecureString $pass.Pass -Key ([byte[]][char[]]$pass.Key)
$cred = New-Object Management.Automation.PSCredential $username, $pw
$share = Get-WmiObject Win32_Share -List -Computer 10.0.0.1 -Credential $cred
$share.Create('C:\dir', 'foo', 0)

Resources