Powershell and running scheduled task as other user - powershell

I have a dilema.
I am trying to set up a scheduled task in Windows that runs a powershell script as another user (a Service account that has access but no logon rights). The issue is that we have been told by our security group to not code in passwords (obviously good advice) but the connection string for the SQL seems to need it in plain text. I am getting around this by creating a password file:
$credential = Get-Credential
$credential.Password | ConvertFrom-SecureString | Set-Content e:\temp\password.txt
And then in the script converting it back to plain text (to be used in a connection string)
$password = cat E:\temp\password.txt | ConvertTo-SecureString
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
$UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
$connectionString = "Data Source=<mydatabase>;Initial Catalog='<mytable>';User ID=tng ;Password=$Unsecurepassword;"
The snag though, is that when I create the password file and run the script as myself it works great, but I can't seem to run this as a scheduled task. In past experiences I have seen where the password file probably needs created by the service account running the scheduled task, but without local log on rights, I am not sure how to create this. Any thoughts?
I tried this technet article but it appears that it still requires local log on from the other account.

Found the answer - I needed to add a key to the secure string:
When creating the file - adding in the $key:
[byte[]] $key = (1..16)
$credential = Get-Credential
$credential.Password | ConvertFrom-SecureString -key $key | Set-Content e:\temp\password.txt
And then when reading it back in:
$passwordfile = "E:\temp\password.txt"
[byte[]] $key = (1..16)
$securePassword = Get-Content $passwordfile | ConvertTo-SecureString -Key $key
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securepassword)
$UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
Answer found thanks to this link

Related

Add username and password to Powershell script

I'm trying to create a powershell script to allow my kids to reboot my Raspberry Pi from their Windows computer if need be. I've tested everything and have gotten it to work, but the only hitch is that it's prompting for a username and password. I realize the line that's doing it is:
New-SSHSession -ComputerName "myPi" -Credential (Get-Credential)
I've done some searching, but I can't seem to figure out if it's possible to replace the "(Get-Credential)" section to automatically enter the username/password.
And yes, I'm aware of the security risks. They could do much more damage to the Windows machine than they could ever do on the Pi, and the settings on the Pi are very easily restored, so no worries from my end.
Something like this should work:
$user = "someuser"
$pass = ConvertTo-SecureString -String "somepassword" -AsPlainText -Force
$creds = new-object -typename System.Management.Automation.PSCredential -argumentlist $user,$pass
New-SSHSession -ComputerName "myPi" -Credential $creds
You could also call a file that has the password encrypted in it. Note this can only be decrypted by the account it was generated on on the computer it was generated on.
$pass = "Password"
$Username = "Username"
$outfile = "c:\filelocation.xml"
$secureStringPwd = $pass | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($Username,$secureStringPwd)
$credential | Export-CliXml -Path $OutFile
Addressing Bill.
Correct, hard coding the password in the script is bad practice. Below is how I would change the first portion.
The above came from a custom script that's purpose was to create many cred accounts off a input json is why I wrote it that way.
$outfile = "c:\filelocation.xml"
Get-Credential | export-clixml -path $OutFile
You then can call the file in your script like so but this has to be done on the same user and computer that the creds file was generated on.
$Creds = Import-Clixml -Path "c:\file.xml"
New-SSHSession -ComputerName "myPi" -Credential $creds
Good point Edited -argumentlist.
Another option could be to do a 1 time setup with get-credential then convert the password to plaintext using convertfrom-securestring and then in the file you can take your password plaintext secure string and so something similar to the other answers:
$user = "someuser"
$pass = "YOUR LONG PASSWORD GUID FROM ABOVE" | convertTO-securestring
$creds = new-object -typename System.Management.Automation.PSCredential -argumentlist $user,$pass
New-SSHSession -ComputerName "myPi" -Credential $creds
This lets you do a one time setup, but avoids having multiple files or having your password appear in a readable way in the script.
If you go this way you need to do the setup FROM the account that will run the script ON the machine that will run the script, because it uses those for the encryption as far as I know.

Encrypting password or workaround

I am bit of a lazy guy, so I have created a script that opens many applications for me. Works fine as ISE opened with Administrator credentials, also opens apps with admin creds, however some of them need a different credentials.
Is it possible, to make powershell remember typed in password each time I log in and open it? (I know that variables are stored only till ps is opened)
Thing is - I cannot store a visible password in profile/text file or in a script, as this is a jump server used by many people. Is it somehow possible to type a password once, make PS encrypt it and each time I will open PS, it will decrypt it and use? or any workaround possible around this?
edit with code:
It's the only part I would like to change
$currentPW = "some password"
$credentials = New-Object System.Management.Automation.PSCredential ("domain\username",$CurrentPW)
start "c:\application.exe" -credential $credentials
It kinda works but it would require me, to input the password everytime I log in to device, so I could go for option like:
$currentPW = read-host "Provide your password"
$credentials = New-Object System.Management.Automation.PSCredential ("domain\username",$CurrentPW)
start "c:\application.exe" -credential $credentials
but this would require me to input the password each time I log in to system and open PS as it does not remember variables after restart.
So...is it even possible to make this work?^^
You can use ConvertTo-SecureString to encrypt the password using the users account key, then save this secure string to a file to load at a later time.
This assumes you are the only one with access to the logon account (not an account with shared credentials), as anyone who can logon as the account can decrypt the file.
$username = "domain\username"
$passwordFile = "C:\folder\EncryptedPassword.txt"
#if password file exists: populate $securePwd from file contents
If (Test-Path $passwordFile) {
$pwdTxt = Get-Content $passwordFile
$securePwd = $pwdTxt | ConvertTo-SecureString
}
#if no file: prompt for password, create file and populate $securePwd
Else {
$password = Read-Host "Provide your password"
$securePwd = $password | ConvertTo-SecureString -AsPlainText -Force
$securePwd | ConvertFrom-SecureString | Set-Content $passwordFile
}
$credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $securePwd
Start-Process "c:\application.exe" -Credential $credentials
If you have PowerShell 3.0 or newer, you can also combine Get-Credential with Export-CliXml to export a PSCredential object as an XML file. Example:
Get-Credential | Export-CliXml "C:\XML Files\credential.xml"
You can then import the credentials using Import-CliXml. Example:
$credential = Import-CliXml "C:\Xml Files\credential.xml"
Note that the password is encrypted using DPAPI, so you can only import the credentials using Import-CliXml on the same computer using the same user account that was used to export the credentials using Export-CliXml.

How can I silently run a remote PowerShell command without having to hard code my password?

I am trying to run:
Invoke-Command -Computer $computer -ScriptBlock {...}
But I get the error "Access is denied" winrm error and I am hesitant to use the following:
Invoke-Command -Computer $computer -Credential $cred -ScriptBlock {...}
where $cred is:
$username = "John Doe"
$password = "ABCDEF"
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -
argumentlist $username, $secstr
It has to be run remotely and it must be silent. So I can't have the PSCredential pop up window mid script.
Can someone please show me or point me to a document that will lead me to a possible solution?
Thank you in advance.
One thing that you can do is encrypt the password and save it to disk. Then you can read that file, and convert the encrypted password to a secure string and make a credential object from that. Mind you, this must be done with the account that will be used to run the script.
'$uper$secret1' | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Set-Content .\AccountPass.txt
That will save your password to disk in a text file. If you open the text file it will look something like:
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000b584d55e9c47c942904dd30531d3ad070000000002000000000003660000c0000000100000003060266c3c4333a41e7f0e92176fb3d50000000004800000a000000010000000a2c8bbb2a3666c092004bb5e66fd440320000000636a413a6905789e0f3521cea3d8703405897cd5948da955192bcccd08990ffc1400000068c1
5f8ac088ef0972dfce7d5a20ff3bbcdac4cc
Now, the account that created the file will be the only one that can decrypt it, but that account can then run:
$Password = Get-Content .\AccountPass.txt | ConvertTo-SecureString
$Creds = New-Object System.Management.Automation.PSCredential ("$env:UserDomain\$env:UserName",$Password)
Now you have a credential object, without having to save a password in plaintext. As mentioned, the only account that can decrypt the password in the text file is the one that generated the text file, and the text file will have to be updated whenever the password is changed.

Windows Service Recovery, Unable to execute PS-script

OS: Win 2012 R2
Hi,
I tried setting up certain services so that when they fail the second time a powershell script is triggered which emails certain people. This is done through services > (specific service) > properties > recovery.
Tried nearly every conceivable combination of Program:
Powershell.exe, C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe, same as the last but with capital "P".
Commandline parameters: -NoProfile -Executionpolicy bypass C:\users\username\appdata\local\myscript.ps1, the parameters after the path to the script.
The script is not signed.
Now my script uses the Send-MailMessage and the password is saved using ConvertFrom-SecureString and I was thinking that the service/user which actually runs the script maybe couldn't decrypt the password since it was created with my admin account.
I tried logging in as the same service account that is running the processes I want to monitor and create the encrypted password file from their user and saving it in a path that don't require that user to be admin (i.e. %localappdata%) but the script still fails to trigger when I use pskill on the PID.
When executing the command manually in PS everything works and I am not prompted for anything. It does exactly what it should do.
Now I am quite new to the Windows admin scene so which user or service actually triggers the PowerShell script? Is it the same identity that is running the service, i.e. the specific service account I specified? Or is it something else?
I'll happily post the code here but it is on my other computer and I will update this later with it. Googled for hours and tried almost everything, it might be something basic I am missing however.
Thank you very much for your help - TheSwede86
Edit: Here is the code and I also tried Ronald Rink 'd-fens'suggestion and it logs the user when I manually execute the script (showing an event with my username) but not when I try to simulate service failure.
$PSEmailServer = "<address to smtp-server>"
$SMTPPort = <port>
$SMTPUsername = "<email-address to send from>"
$EncryptedPasswordFile = "<path and filename to pwd-file, currently on C:\>.securestring"
$SecureStringPassword = Get-Content -Path $EncryptedPasswordFile | ConvertTo-SecureString
$EmailCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $SMTPUsername,$SecureStringPassword
$MailTo = "<email-address to mail to>"
$MailFrom = $SMTPUsername
$hostname = Hostname
$service = Get-Service -Name "<Servicename*>" | Where-Object {$_.Status -eq "Stopped"}
$MailSubject = "ALERT: Service on $hostname has stopped and failed to restart after one attempt"
$MailBody = "$service"
$OtherEmail = "<Other email-address to send to>"
$whoami = whoami
Send-MailMessage -From $MailFrom -To $MailTo -Bcc $OtherEmail -Subject $MailSubject -Body $MailBody -Port $SMTPPort -Credential $EmailCredential -UseSsl -Priority High
Write-EventLog –LogName Application –Source “PowerShell Script Debug” –EntryType Information –EventID 1 -Message $whoami
Redacted email-addresses, SMTP-server etc.
Edit 1: Added trying to log which user who executes the script.
Edit 2: #Ronald Rink 'd-fens'
I tried the following:
$PlainPassword = "<passwordForEmailToSendFrom>"
$SecurePassword = $PlainPassword | ConvertTo-SecureString -AsPlainText -Force | Out-File -FilePath C:\temp\<filename>.securestring
I let the service fail once with above so it will convert the plaintext password to a securestring-file which I then call upon in my script; this does not work.
If I try your suggestion:
1)
$password = "<passwordForEmailToSendFrom>" | ConvertTo-SecureString -asPlainText -Force
$username = "<domain\serviceAccountWhichRunsTheService>"
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
$credential | Export-CliXml C:\temp\credential.xml
It successfully creates "credential.xml" in my chosen path
2)
$credential = Import-CliXml C:\temp\credential.xml
$decryptedCredential = "{0} - {1}" -f $credential.UserName, $credential.GetNetworkCredential().Password
$decryptedCredential | Out-File C:\temp\ServiceRecovery.txt -Append -Force
I get the password unencrypted in "ServiceRecovery.txt" and the but not SYSTEM.
I added "SYSTEM" to the local "Administrators"-group and tried again;
it just adds another line to "ServiceRecovery.txt" with the username I specified in "1" and the unencrypted password.
I was however successful when I tried your script about which user who actually runs the script and that was indeed "SYSTEM".
Sorry for my bad explanation, have sat for hours trying to get this final bit sorted but unable to do so.
Edit 3:
Thanks to #Ronald Rink 'd-fens' I solved it this way:
New-Object System.Management.Automation.PSCredential("<EmailAddressToSendFrom>", (ConvertTo-SecureString -AsPlainText -Force "<PasswordForAboveEmailAccount>")) | Export-CliXml C:\temp\MyCredential.xml
Above converts unencrypted password to encrypted using API (DPAPI) only usable for the account/machine that it is created on!
Let the service fail once with above script to generate the file with the SERVICE account
$PSEmailServer = "<smtp-address>"
$SMTPPort = <port>
$SMTPUsername = "<EmailAddressToSendFrom>"
$credpath = Import-Clixml -Path C:\temp\MyCredential.xml
$MailTo = "<EmailAddressToSendTo>"
$MailFrom = $SMTPUsername
$hostname = Hostname
$service = Get-Service -Name "<Servicename(s)>" | Where-Object {$_.Status -eq "Stopped"} | Select-Object -Property DisplayName | Out-String
$MailSubject = "ALERT: Service on $hostname has stopped and failed to restart after one attempt"
$MailBody = $service
$OtherEmail = "<AnotherEmailAddressToSendTo>"
Send-MailMessage -From $MailFrom -To $MailTo -Bcc $OtherEmail -Subject $MailSubject -Body $MailBody -Port $SMTPPort -Credential $credpath -UseSsl -Priority High
Above is the actual script that will run when the service fails
Arguments in Services > Recovery is:
Program: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Command line parameters:
-File C:\temp\myscriptname.ps1
Enable actions for stops with errors
First failure: Restart the Service
Second failure: Run a Program
It seems very much that the script you entered to be run in case of a failure is not running under the service account (nor under your admin account) but under an account of the operating system. You can verify this by logging the username from within the script when executed after your service failed (use Write-EventLog or save it from to a text file).
Here is an example of how you can verify that your script runs under the local system:
# C:\src\TEMP\ServiceRecovery.ps1
cd C:\src\TEMP\
$ENV:USERNAME | Out-File C:\src\TEMP\ServiceRecovery.txt -Append -Force
You can configure your service as shown in the following screenshots:
The service account was created like this:
PS > net user ServiceAccount P#ssw0rdP#ssw0rd /ADD
PS > net localgroup Administrators ServiceAccount /ADD
If I then stop the process by invoking Stop-Process -Name teamviewer_service -Force I can see the following name in the generated text file:
SYSTEM
This means you would have to encrypt the secure string via the SYSTEM account and not via your personal user or service user account or you have to resort to some other means on how to read your encrypted password.
Encrypting your password via the service account can be achieved by creating a script to create a password and store it in encrypted form. Put this script into your service recovery settings and make this service fail once. Then remove the script and insert your original script which will then be able to import the encrypted password.
Here you find the scripts with which I tested it:
(1) Script to encrypt credentials
# Creating a PS Credential from a Clear Text Password in Powershell
# https://blogs.technet.microsoft.com/gary/2009/07/23/creating-a-ps-credential-from-a-clear-text-password-in-powershell/
$password = "P#ssw0rdP#ssw0rd" | ConvertTo-SecureString -asPlainText -Force
$username = ".\ServiceAccount"
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
$credential | Export-CliXml C:\src\TEMP\credential.xml
(2) Script to decrypt credentials
$credential = Import-CliXml C:\src\TEMP\credential.xml
$decryptedCredential = "{0} - {1}" -f $credential.UserName, $credential.GetNetworkCredential().Password
$decryptedCredential | Out-File C:\src\TEMP\ServiceRecovery.txt -Append -Force
Now the generated text file contains
.\ServiceAccount - P#ssw0rdP#ssw0rd
Note: the first "encrypt" script contains a plain text password which is only used once for encryption. We have to go this way, in order to run under the SYSTEM account. An alternative to this might be using RunAs from SysInternals.

Expect and Spawn with PowerShell

Is there any way to do expect and spawn with powershell.
I have to parse a CLI program with powershell which is asking for password is there any way to input this password via powershell.
In perl or python even in bash you can use expect/spawn
Is there any solution in powershell ?
One way to do this would be to create a text file with the encrypted password one time, then call this file as the password in as many scripts as necessary.
Create the password file once:
$pwd = Read-Host 'Enter password for encrypting:' -AsSecureString | ConvertFrom-SecureString | Out-File -Path 'C:\SpecialFiles\CLIPassword.txt'
Then you can use it whenever it's needed:
$pass = (Get-Content -Path 'C:\SpecialFiles\CLIPassword.txt' | ConvertTo-SecureString)
$creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList (<insert CLI UserName>, $pass)
Then when you need to supply the username and password to your script, you simply pipe
$creds | <command>
or if the command supports the -Credential parameter
<command> -Credential $creds
If your command needs the user name and password entered separately, you can do that by specifying the property name:
<command> -UserName $creds.UserName -Password $creds.Password
You can use Read-Host to prompt the user for input. See here for more information.
$pass = Read-Host 'What is your password?' -AsSecureString
$decodedpass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass))
I'm sure what you want to do with spawn, but you can execute other scripts or executables by just calling them
.\MyOtherScript.ps1