Windows Service Recovery, Unable to execute PS-script - powershell

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.

Related

Import Password -credential

I am trying to send a file to email.
I found this code and it worked
but I want to add the password automatically
is there any solution ??
$MyEmail = "tests21#gmail.com"
$Password = "TEST2132"
$SMTP= "smtp.gmail.com"
$To = "TEST212#gmail.com"
$Subject = "LOG FILE!"
$Creds = (Get-Credential -Credential "$MyEmail")
$attachment = "C:\TEST.txt"
Start-Sleep 2
Send-MailMessage -To $to -From $MyEmail -Subject $Subject -Attachment $attachment -SmtpServer $SMTP -Credential $Creds -UseSsl -Port 587 -DeliveryNotificationOption never
Non-secure method
Here is how you create a credential object from a password string.
# Some email credentials
$username = 'Username'
$password = 'Password' | ConvertTo-SecureString -AsPlainText -Force
$Creds = [PSCredential]::New($username,$password)
As it was pointed in the comments, setting a password directly in the script is insecure since anybody that could get access to the script would see the password.
Secret management module method
This method requires a little bit of preparation but is the best in my opinion since the password is retrieved from a secret vault instead of being readily available in your script.
Prerequisites
Modules
Install the following modules
Install-Module Microsoft.PowerShell.SecretManagement
Install-Module SecretManagement.JustinGrote.CredMan
Vault
Create a vault to store the email credentials and possibly many others in the future.
Register-SecretVault -ModuleName SecretManagement.JustinGrote.CredMan -Name 'Default'
Save the credentials into the vault.
$username = 'Username'
$password = 'Password' | ConvertTo-SecureString -AsPlainText -Force
$Creds = [PSCredential]::New($username, $password)
Set-Secret -Name 'SomeEmailCreds' -Secret $Creds -Vault Default
How to use
Once every prerequisites is filled, you can get the credentials from the local vault by using the Get-Secret statement. The Set-Secret should be nowhere in your script and you should use it only if you need to update your credentials at some point.
$Creds = Get-Secret -Vault Default -Name 'SomeEmailCreds'
Notes
Note that the vault is "per user" and "per machine" so it should be created on the user and the machine it will run from.
SecretManagement also have other modules than the CredsMan one by Justin Grote which use all sort of different vault so there are implementation for Azure key vaults, 1password, etc

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.

Powershell and running scheduled task as other user

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

Powershell 5.0 Invoke-Command Start Service with Credential

We have a problem with a Service on a Server. So we decided to write a PS-Script that a "normal" User without Admin privileges can start this Service. I have practiced now 2 Day's on this little Script. I'm a newbie (Apprentice) in PS but im glad that it works when I run it as an Admin. But why the heck not as an User?
I have generated the "Secure" Password as follow:
"P#ssword1" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File "C:\Temp\Password.txt"
I took the SecureString and pasted it in my Script that looks like this:
$User = "DOMAIN\USER"
$PwHash = "01000000d08c9ddf0....."
$MyCredential=New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, ($PWHash | ConvertTo-SecureString)
Invoke-Command -ComputerName "MyServer" -ScriptBlock {Get-Service -Name "MyService" | Set-Service -Status Running} -Credential ($MyCredential)
The failure pops up by the $MyCredential row:
ConvertTo-SecureString: Key in specific Status is not valid.
I have nowhere read that for an ConvertTo... cmd are Admin rights needed.
Enable-PSRemoting is active on the specific Server.
Thanks for your time and engagement
Dirty.Stone
IMHO, you're going about this all wrong. This is an example of the kind of task you would use JEA (Just Enough Admin) for. Create a constrained, delegated session on the target server, configured with a function for starting or restarting that service and running under a local account that has permission to control the service, and then grant the non-admin users permission to use that session.

Send-MailMessage : The SMTP server requires a secure connection... The server response was: 5.5.1 Authentication Required.

Alright so as the title says, I get this error when trying to send email via PowerShell:
Send-MailMessage : The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.5.1 Authentication Required.
I have looked at numerous questions related to the same issue. But I can't seem to make my script work:
#Email alerts when a user gets locked out
##############################################################################
$pass = Get-Content .\securepass.txt | ConvertTo-SecureString -AsPlainText -Force
$name = "sender#gmail.com"
$cred = New-Object System.Management.Automation.PSCredential($name,$pass)
##############################################################################
$From = "sender#gmail.com"
$To = "recipient#domain.org"
$Subject = "User Locked Out"
$Body = "A user has been locked out of his/her account."
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
Send-MailMessage -From $From -to $To -Subject $Subject `
-Body $Body -SmtpServer $SMTPServer -port $SMTPPort `
-Credential $cred -UseSsl
##############################################################################
I have logged into the Gmail account from the machine that will be running the script. I have also enabled Access for less secure apps from the Google account manager. I do get this to work just fine if I prompt for the credentials using the -Credential (Get-Credential) instead of calling for the $cred variable.
Is there something I am missing?
Thanks,
Dan
If the file contains the encrypted password it's better to read it like this (without the parameters -AsPlainText and -Force):
$pass = Get-Content .\securepass.txt | ConvertTo-SecureString
Demonstration:
PS C:\> $sec = 'foobar' | ConvertTo-SecureString -AsPlainText -Force
PS C:\> $sec
System.Security.SecureString
PS C:\> $txt = $sec | ConvertFrom-SecureString
PS C:\> $txt
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000615ce070639b9647a5e05d42b41d373
0000000000200000000001066000000010000200000001614c19281e7c0b076cceb38e284b0f18b
c0d813ea40ed055dde96fd9ccb6977000000000e8000000002000020000000a10c7019eb224c3c6
387ba03bcd94993a50e0c468248284bbce4d235b11f1b94100000002421a5d7102de13c46ccc1db
c4921287400000000412332ecb500828f4403f3e225089c629369744bad62609b528ed0a7318abf
512c9b6a8884c43b3adc8a13d5d21a9ed27e56702bcc7db094da9d9d4c02dfa74
PS C:\> $sec2 = $txt | ConvertTo-SecureString
PS C:\> $sec2
System.Security.SecureString
PS C:\> $cred = New-Object Management.Automation.PSCredential 'foo', $sec2
PS C:\> $cred.GetNetworkCredential().Password
foobar
Beware though that encryption of secure strings is tied to the user and host encrypting them, meaning you can't decrypt a secure string on another host or as another user.
I found my answer after looking at my passwords file content, thanks to Angsgar. The securepass.txt had the encrypted contents inside, not plaintext. What I did was replace it with the actual password that will then be encrypted when setting my $pass variable. All is good now!
Ansgar's answer is a good generic answer, but for Google, they have multiple SMTP servers that you can use. smtp.google.com requires authentication, but not all of them do.
From Google's doc, if your site is a G Suite site and you will always be sending from a specific IP address, you can specify the address in G Suite configuration and then use the G Suite SMTP relay at smtp-relay.gmail.com. This is only available to G Suite users, and requires either authentication or a static IP. At our site, we have an internal SMTP server that we use for these sorts of emails which relays to Google's G Suite SMTP relay server.
If you are sending email only to Google or G Suite addresses, you can specify aspmx.l.google.com as your SMTP address. This is known as the restricted Gmail SMTP server.