How to change user credentials of windows service from command line?
sc.exe config "Service Name" obj= "DOMAIN\User" password= "password" type= own
See Shortcut Setting Log-On Credentials for Windows Services ยป jonathanmalek.com.
#MattT points out that on Windows Server 2008R2 you have to add type= own, but prior to that version it isn't necessary.
In PowerShell 3+, you can avoid escaping the arguments with the stop-parsing symbol: --%
sc.exe --% config "Service Name" obj= "DOMAIN\User" password= "password" type= own
I simply called WMI from powershell to do this.
$Svc = Get-WmiObject win32_service -filter "name='ServiceName'"
$Svc.Change($Null, $Null, $Null, $Null, $Null, $Null, "User", "Password")
Don't forget to restart the service afterwards:
Stop-Service -Name 'ServiceName'
Start-Service -Name 'ServiceName'
For more fun with WMI and services, see
Win32_Service Class
Using WMI results in non-encrypted communication between your machine and the machine you are changing the service credentials on. So your new password can be sniffed quite easily. You just have to parse the WMI blob send over the network. By now I found no really secure way to change a service accounts password remotely with a tool.
For those who are wondering how to pass a secure password:
$credentials = Get-Credential -UserName 'Domain\username' -Message 'Enter password below'
$service = Get-WmiObject win32_service -filter "name='SERVICE_NAME'"
$service.Change($null,$null,$null,$null,$null,$null,$credentials.username,($credentials.Password | ConvertFrom-SecureString))
Related
Is it possible to use the New-Service command to create a service using a gMSA account? I tried creating the credentials with a blank password but it fails because ConvertTo-SecureString expects the string to not be empty.
$password = ConvertTo-SecureString "" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ("DOMAIN\dev-user$", $password)
New-Service -Name Service -BinaryPathName C:\Service -StartupType Automatic -Credential $credential
Start-Service -Name "Service"
I then tried setting the string to just a to see if it even cared about the password since this is a gMSA account and I got this error.
New-Service : Service '(Service)' cannot be created due to the following error: The account name is invalid or does not exist, or the password is invalid for the account name specified
EDIT: I know there are other ways I could accomplish this like Wmi-Object or sc.exe but I wanted to see if there was a means to do this via New-Service just to see if I am missing something or doing something wrong.
I found an answer for how to make a new blank SecureString and this worked
$credential = New-Object System.Management.Automation.PSCredential("DOMAIN\dev-user$", (New-Object System.Security.SecureString))
New-Service -Name Service -BinaryPathName C:\Service -StartupType Automatic -Credential $credential
Start-Service -Name "Service"
This answer assisted me in figuring out how to do this.
EDIT: Wanted to add this did not working on 2012r2 but worked on Windows 10 and 2016
I have 2 servers (windows server 2012 R2) in the same domain.
I execute a command on server01:
Invoke-Command -ComputerName server02 -Credential Administrator -ScriptBlock {Get-Culture}
I give the password of my Administrator (of server2) and it works well.
But when I try:
Invoke-Command -ComputerName server02 -ScriptBlock {Get-Culture}
It also seems to work. Probably because the 2 servers are in the same domain. While I only want it to work when you can provide the right credentials. Can someone help me with it?
You are probably doing this by Domain Admin account or account that it's in Domain Admins group or so.
In any case this results because your account has privelegies on that computer.
With which user do you execute the script on server01? Does that user have permissions on server02 too? If your user has admin permission on server01 and server02 then no credentials are neccessary... (as far as I know)
To check if the provided credentials are valid have a look here:
https://gallery.technet.microsoft.com/scriptcenter/Test-Credential-dda902c6
Or something like this:
$cred = Get-Credential #Read credentials
$username = $cred.username
$password = $cred.GetNetworkCredential().password
# Get current domain using logged-on user's credentials
$CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName
$domain = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$UserName,$Password)
if ($domain.name -eq $null)
{
write-host "Authentication failed - please verify your username and password."
exit #terminate the script.
}
else
{
write-host "Successfully authenticated with domain $domain.name"
}
Which was found here (but I haven't tested it):
https://serverfault.com/questions/276098/check-if-user-password-input-is-valid-in-powershell-script
So basically I've been working forever on a PS remote self help script that originally was thought to be simple: Restart the spooler service, clear the queue, and print a test page on the default printer. Getting there however hasn't been so easy, due to security issues. After some hours, I was able to get my local user test account to accept the credentials of my domain administrator. I thought all was well, until I tried to replicate it on a local administrator's account, in which event access was denied. This is sort of important, because the majority of the accounts we will be deploying the script on are local admins. I suspect it may be a UAC issue, but I have no idea what I should do to work around the problem. Here's what I'm working with currently:
$v = [bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544")
If ($v = "False")
{
$password = "ElPassword" | ConvertTo-SecureString -asPlainText -Force
$username = "Domainname\Username"
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
invoke-command {Stop-Service spooler} -comp $env:ComputerName -cred $credential
Remove-Item C:\Windows\System32\spool\PRINTERS\* -Force
invoke-command {Start-Service spooler} -comp $env:ComputerName -cred $credential
$printer = Get-WmiObject -Query " SELECT * FROM Win32_Printer WHERE Default=$true"
$PrintTestPage = $printer.PrintTestPage() } Else
{ Stop-Service spooler
$printer = Get-WmiObject -Query " SELECT * FROM Win32_Printer WHERE Default=$true"
Start-Service spooler
$PrintTestPage = $printer.PrintTestPage() }
The first thing this does is check if the current PS session is being run as admin; seeing as the users don't actually see the PowerShell window or script, and we recently started using the RMM tool, I'm still trying to figure out under what conditions the tool runs PS elevated - the documentation says that it runs with the credentials of the logged in user, but that doesn't seem to be the case, as an hour with their support team told me that the reason the script wasn't doing it's job on any admin accounts was because it wasn't being elevated. Anyways, after the check, it either passes credentials for the commands or it doesn't. This script seems to handle every scenario but that of a local admin account running PS non elevated. In that event, it simply denies me access where the exact same creds give me access on a regular user account. I'm not sure how to even approach this problem, so any help is appreciated.
I have a client and a server. The client will call a script like:
#Predefine necessary information
$Username = "Niels"
$Password = "password"
$ComputerName = "192.168.1.51"
$Script = {powershell c:/build/jclbuild2.bat}
#Create credential object
$SecurePassWord = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $Username, $SecurePassWord
#Create session object with this
$Session = New-PSSession -ComputerName $ComputerName -credential $Cred
#Invoke-Command
$Job = Invoke-Command -Session $Session -Scriptblock $Script
echo $Job
#Close Session
Remove-PSSession -Session $Session
On the server the jclbuild2.bat will run and access a network drive like \\otherserver\something, it says access denied if I do this command:
cmd.exe /C copy "\\server\file1.pdf" "\\server2\file1.pdf"
How do I access a network drive from a powershell file on a remote server? The user I use with the $username and $password should have access to the network drive.
I think it's a double hop issue, which I don't know how to solve.
You can't do this using the default authentication mechanism. You need to use an authentication mechanism that allows you to flow credentials, not just identity. Kerberos is one of these. CredSSP is another that is built into Windows starting from Vista/Server 2008 onwards.
I have experience setting up CredSSP. Note that there is some security risk because the target machine will have access to the credentials as plain text.
To set it up you will need to run two commands (both from an elevated shell). One on the machine you are running the above script on (the client) and another on the target that you will be connecting to via remoting (the server).
Enable-WSManCredSSP -Role Client -DelegateComputer $ComputerName -Force
This enables delegation to $ComputerName from the client (note you may have to use the FQDN). For security reasons you should avoid using the wild card '*' although you might consider using '*.mydomain.int' to enable delegation to all machines on the domain.
On the target server
Enable-WSManCredSSP -Role Server
Then when you create the session use the -Authentication flag
$Session = New-PSSession -ComputerName $ComputerName -credential $Cred -Authentication Credssp
There are questions on ServerFault on setting up CredSSP. There is also a blog post here with additional explanation. This post has troubleshooting tips for some commonly encountered error messages.
Another option is to use a delegated session on your server.
Basically, you create a custom remote session that uses the -RunAs parameter to designate the credentials that the session will run under. You can also constrain what scripts and cmdlets can be run in the session and specify who can connect to the session.
In this case, the session would run as the Niels account, and everything done in the session would be under that account authority, regardless of who was connected to the session. From that session, you can now make one hop to another server without needing CredSSP.
This also eliminates the security risk involved in storing that account password in the script file on the client computer.
http://blogs.technet.com/b/heyscriptingguy/archive/2014/04/03/use-delegated-administration-and-proxy-functions.aspx
I want to start/stop apache and mysql services on remote machine by using powershell version 2.0 (Windows Server 2008). I found syntax for remote execution as follow:
(Get-WmiObject -Computer myCompName Win32_Service -Filter "Name='myServiceName'").InvokeMethod("Stop-Service",$null)
But I have to provide credentials (DOMAIN_NAME\USERNANE and PASSWORD) also for this exceution. I am new to powershell and need help for correct syntax (example will be easy to understand and implement).
Get-WMIObject accepts the -Credential parameter. You shouldn't be keeping your credentials in plain text in your script, so you'll want to prompt for them.
$creds = get-credential;
(Get-WmiObject -Computer myCompName Win32_Service -Filter "Name='myServiceName'" -credential $creds).InvokeMethod("Stop-Service",$null)
If you have PSRemoting enabled on the remote system, you can do this without WMI.
$creds = get-credential;
Invoke-Command -computername myCompName -credential $creds -scriptblock {(get-service -name myServiceName).Stop()};
Update based on comments
Since you're running this as a scheduled job, you should not be storing or prompting for credentials at all. Configured the scheduled job itself (via Scheduled Tasks) to run under the required user account, then either of the following should work:
# Your original code
(Get-WmiObject -Computer myCompName Win32_Service -Filter "Name='myServiceName'").InvokeMethod("Stop-Service",$null)
# If you have remoting enabled
Invoke-Command -computername myCompName -scriptblock {(get-service -name myServiceName).Stop()};