Powershell remote application(.cmd) deployment - powershell

I am beginner with PowerShell and struggling to get this around with the help from different sites, My requirement and scenario is
I have a windows server 2008(rktdepy) with PowerShell installed and I have packaged application with a .cmd file. When I click this .cmd file the application will be deployed.
The server name is rktdepy and I want to create a PowerShell script which will connect to other servers in the network (the server names should be picked up from a txt files) and install the application accessing the file remotely from rktdepy server. The files are not supposed to be copied to any server and should not use psxec for security reason.
So far I have used invoke and mapping the network drive but still I have issues
$Comsession = Get-content c:\adminfiles\scripts\deploy.txt | new-pssession -throttlelimit 50
Invoke-command -computername RKTDEPLY54 -scriptblock { (new-object -comobject wscript.network).mapnetworkdrive("R:", "\\rktdepy\deploy", $true) }
Invoke-command -session $comsession -scriptblock {"CMD /C r:\QR_DEPLOY.CMD"}
The above script throws error,
I dont want to use any password in the script and it should fetch the current logged in user password from rktdepy server. I is ok if the scripts prompts for a user name and password which will have admin access to all servers.

It looks like you are dealing with a couple problems. One is that the session where you map the drive is gone when you run the next Invoke-Command that uses the mapped drive. You could move that into the same script block to fix a problem like that. The second one is a "second hop" issue. See a resource like Don Jones' Secrets of PowerShell Remoting free ebook on http://powershell.org/wp/books.
Steve

I have testing the following on my machine and it is working so far. There is also another method you can try out listed below.
Method1:
1. I have txt file with a list of computers named allcomputers.txt. It contains name of machines on each line.
Machine10
Machine20
Machine30
Machine40
The deployment script (mydeploytest.ps1) which accepts Computername, Username and Password as input and creates a new PSSession and then invokes command.
param(
[string]$ComputerName,
[string]$User,
[string]$pass
)
Get-PSSEssion | Remove-PSSession
$session = New-PSSession -ComputerName $ComputerName
Invoke-Command -Session $session -ScriptBlock {
param(
[string]$ComputerName,
[string]$Username,
[string]$Password
)
$net = new-object -ComObject WScript.Network
$net.MapNetworkDrive("U:", "\\RKTDEPY\deploy", $false, $Username, $Password)
Invoke-Expression "CMD /C U:\deploy.cmd"
$net.RemoveNetworkDrive("U:")
} -args $ComputerName,$User,$pass
Get-PSSEssion | Remove-PSSession
Powershell commandline oneline to accomplish deployment task.
PS C:> Get-Content C:\scripts\allcomputers.txt | Foreach { C:\scripts\mydeploytest.ps1 $_ "yourserviceaccount" "password"}
Method2:
The help method for Invoke-Command has an example on how to solve the doublehop issue stevals is mentioning in the answer.
PS C:\> Enable-WSManCredSSP -Delegate Server02
PS C:\>Connect-WSMan Server02
PS C:\>Set-Item WSMan:\Server02*\Service\Auth\CredSSP -Value $true
PS C:\>$s = New-PSSession Server02
PS C:\>Invoke-Command -Session $s -ScriptBlock {Get-Item \\Net03\Scripts\LogFiles.ps1} -Authentication CredSSP
-Credential Domain01\Admin01
I think with little modification to method 2 you can achieve what you want.

Related

PowerShell run Command as different User when Credential Parameter is missing

I need to run a common PowerShell command to trigger a Group Policy Update "gpupdate" on a remote computer out of a workflow.
The workflow runs in a system user context, which do not have the local admin permissions on the clients to force a remote "gpupdate".
For that reason, I import a PowerShell credential secure string with "Import-CliXml" to run that statement in scope of a user which is local admin on the clients.
But, the command I want to use, don't support the native credential parameter. And I need to use a parameter for the remote client.
Invoke-GPUpdate -Computer $client -RandomDelayInMinutes 0
I tried many approches from the internet, but it won't work for me:
Start-Process powershell.exe -Credential $credentials -ArgumentList $ProcessCommand -WorkingDirectory $env:windir -NoNewWindow -PassThru
Start-Process powershell.exe -wait -Credential $credentials -ArgumentList "-command &{Start-Process Powershell.exe -argumentlist '$($cmnd)' -verb runas -wait}"
If I test to send the remote gpupdate out of a PowerShell console started with a user which is local admin on the remote client, it works.
Did anyone has a solution for this problem?
Many thanks!
When I connect to remote computers using PowerShell to execute commands on those computers I normally run the following. I've left an example of my code for you to use to execute Invoke-GPUpdate
#Local Host Computer
#$RequestingServer = $env:COMPUTERNAME
#Server List From Text File
#$ServerList = Get-Content 'C:\temp\servicetest\servers.txt'
#Server List In Script
$ServerList = 'Computer1','Computer2','Computer3','Computer4'
#Domain Admin Account
[STRING]$DomainAccountName = (whoami)
[STRING]$DomainAccountName = $DomainAccountName.Split("\")[1]
[STRING]$DomainAccountPassword = "Password01" #Obviously Change Password
$DomainAccountSecurePassword = $DomainAccountPassword | ConvertTo-SecureString -AsPlainText -Force
$DomainCredentials = New-Object System.Management.Automation.PSCredential -ArgumentList $DomainAccountName, $DomainAccountSecurePassword
#Local Server Admin Account
[STRING] $LocalUser = "Administrator" #Obviously Change Account
[STRING] $LocalPassword = "Password01" #Obviously Change Password
$LocalSecurePassword = $LocalPassword | ConvertTo-SecureString -AsPlainText -Force
$LocalCredentials = New-Object System.Management.Automation.PSCredential -ArgumentList $LocalUser, $LocalSecurePassword
#If running on multiple computers / servers etc. - - See Lines 5 and 8
ForEach($ComputerName in $ServerList) {
#Update Windows Something Locally - See Line 2
#$DomainSession = New-PSSession -Computername $RequestingServer -Credential $DomainCredentials
#Update Windows Something Remotely - See Lines 5 and 8
$DomainSession = New-PSSession -Computername $ComputerName -Credential $DomainCredentials
Invoke-Command -Session $DomainSession -ScriptBlock {
#Some commands need the computername currently using localhost...
$GPUpdateServer = $Using:ComputerName
#$GPUpdateServer = $Using:RequestingServer
# enter code of what you plan to do...
Invoke-GPUpdate -Computer $GPUpdateServer -RandomDelayInMinutes 0
}
} End of ForEach Statement
#If running on multiple computers / servers etc. - - See Lines 5 and 8
ForEach($ComputerName in $ServerList) {
#Update Windows Something Locally - See Line 2
#$LocalSession = New-PSSession -Computername $RequestingServer -Credential $LocalCredentials
#Update Windows Something Remotely - See Lines 5 and 8
$LocalSession = New-PSSession -Computername $ComputerName -Credential $LocalCredentials
Invoke-Command -Session $LocalSession -ScriptBlock {
#Some commands need the computername currently using localhost...
$GPUpdateServer = $Using:ComputerName
#$GPUpdateServer = $Using:RequestingServer
# enter code of what you plan to do...
Invoke-GPUpdate -Computer $GPUpdateServer -RandomDelayInMinutes 0
}
} End of ForEach Statement
Facing this problem more in detail, I tested the approach above with the remote PowerShell session. This needs some more preparation in domain for deploying all necessary GPO settings to all clients to make WinRM work.
The remote PowerShell approach works, but I found out that the Invoke-GPUpdate command is only available on clients which have RSAT installed. So only works on a few in clients in IT department.
$Session = New-PSSession -Computername $clientname -Credential $domainAccountWithLocalAdminRights
Invoke-Command -Session $Session -ScriptBlock { Invoke-GPUpdate -Computer $env:ComputerName -RandomDelayInMinutes 0 }
$Session | Remove-PSSession
I switched over to a different approach which worked for me without using remote PS sessions. Completely silent on the client, you will find the triggered gpupdates only in Windows event viewer.
Invoke-Command -ComputerName $clientname -ScriptBlock { gpupdate } -Credential $domainAccountWithLocalAdminRights

Executing CMD or EXE file using PSSession (remote powershell) caused Error 1603 access denied

I have the following script powershell command but it returns access denied. I assume the Error 1603 is caused by remote accessing the server. However, the $username has admin rights in the computer01 server.
To recheck if my hunch was right, I tried to test with the following and I got access denied:
Start-Process cmd -Credential $Cred
Update
The error was due to the $Cred . Removing the -Credential argument works fine.
End of Update
The commands have no problems executing directly in the computer01 machine using the cmd.exe.
I want to use cmd /c in this case as I need to get the real exit code from the SETUP.EXE installer.
See full script below:
$script = {
#Param(
# [String]$username,
# [String]$password
#)
# $Cred = New-Object System.Management.Automation.PSCredential ($username, $password)
$respfile = "$env:TEMP\test.resp"
echo 'key=value' > $respfile
$username = "$env:USERDOMAIN\$env:USERNAME"
Write-Host Hello $username
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
Start-Process cmd -Credential $Creds
#This command cannot be run due to the error: Access is denied.
# + CategoryInfo : InvalidOperation: (:) [Start-Process], #InvalidOperationException
# + FullyQualifiedErrorId : #InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
# + PSComputerName : computer01
# cmd /c "$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
runas /user:$Username "SETUP.EXE" /INSTALL -s /RESPFILE:"$respfile"
echo $LASTEXITCODE
# Error 1603
}
#$username = 'domain/user'
#$password = 'password'
$server = 'computer01'
$Creds = New-Object System.Management.Automation.PSCredential
$session = New-PSSession -ComputerName $server
#Invoke-Command -Session $session -Scriptblock $script -Argumentlist $username, $password
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds #updated based on #postanote advise
Remove-PSSession -ComputerName $server
I have found the following similar link install-remotely but do not want to use the ENTER-PSSession command. I do not want to exit the current PSSession and remotely join again in server just to install then exit.
Any suggestions how to use only PSSession and successfully executing installers in the remote server?
As one mentioned in the comments, you don't need cmd.exe. You can use the call/invocation operator - & - to specify that the next token on the line is a command:
& "$path\SETUP.EXE" /INSTALL -s /RESPFILE:$respfile
Of course, for this to work, the parameters to SETUP.EXE need to be correct (I don't know whether that's the case or not).
Never pass plain text passwords in scripts. It exposes you to uneeded risks.
Use proper secured credentials models.
• Working with Passwords, Secure Strings and Credentials in Windows PowerShell
• quickly-and-securely-storing-your-credentials-powershell
PowerShell remoting requires the use of an implicit (New-PSSession) or explicit (Enter-PSSession) session.
• About Remote Requirements
There are only a handful of cmdlets you can use as non-Admin ir run without PSRemoting enabled.
• Tip: Work Remotely with Windows PowerShell without using Remoting or WinRM
As noted in the Powershell Help file | MS Docs link above, with PSRemoting, you must be using an account that is an admin on the remote host.
In Windows OS proper, to install software, you must be an admin and running that in an admin session.
PowerShell runs in the context of the user who started it.
If you are trying to run in another user context, that is a Windows Security boundary, and you cannot do that without PowerShell natively, you'd need other tools like MS Sysinternals PSExec. See also:
Find-Module -Name '*Invoke*' | Format-Table -AutoSize
# Results
<#
Version Name Repository Description
------- ---- ---------- -----------
...
3.1.6 Invoke-CommandAs PSGallery Invoke Command as System/User on Local/Remote computer using ScheduleTask.
...
#>
Try this refactored option...
$script = {
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
$respfile = 'whatever this is'
& "SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
Write-Output $LASTEXITCODE
}
$server = 'computer01'
$session = New-PSSession -ComputerName $server
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds
Remove-PSSession -ComputerName $server
Details
# Get specifics for a module, cmdlet, or function
(Get-Command -Name Invoke-Command).Parameters
(Get-Command -Name Invoke-Command).Parameters.Keys
Get-help -Name Invoke-Command -Examples
# Results
<#
Invoke-Command -ComputerName server01 -Credential domain01\user01 -ScriptBlock {Get-Culture}
$s = New-PSSession -ComputerName Server02 -Credential Domain01\User01
$LiveCred = Get-Credential
Invoke-Command -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.exchangelabs.com/PowerShell -Credential $LiveCred -Authentication Basic
Invoke-Command -Session $s -ScriptBlock { Get-HotFix } -SessionOption $so -Credential server01\user01
Enable-WSManCredSSP -Delegate Server02
Set-Item WSMan:\Server02*\Service\Auth\CredSSP -Value $True
Invoke-Command -Session $s -ScriptBlock {Get-Item \\Net03\Scripts\LogFiles.ps1} -Authentication CredSSP -Credential Domain01\Admin01
#>
Get-help -Name Invoke-Command -Full
Get-help -Name Invoke-Command -Online
So, I was able to solve my problem.
1603 is the error thrown by the setup.exe.
Just to be sure, I manually executed first the following directly in the server using CMD and it was working!
$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'
I did a lot of testings. Researched and as mentioned from comments above, I did different ways to execute programs using powershell. I even used ACL to change ownership of installer directory/ files, switching to different user accounts (with different priviledges) but still getting access denied (including the Admin account).
It took days before I realized the difference in output file size of manual run in machine and the remote. The cause was the $respfile. It really is worth checking every possible reason/ scenario why there's access denied. Plus I cannot extract the setup.exe and its contents to troubleshoot.
The $respfile was created via powershell. I noticed the size created by powershell is doubled compared to a CMD size that was needed. With that, I assumed that the setup.exe reads file in UTF-8 format. I only know that it's working when triggered via CMD and not via powershell.
I suddenly bumped on this links differrent Powershell and CMD sizes and convert file content to CMD readable file - utf8. After converting the $respfile to UTF-8 format, I was able to run the exe successfully.
Hopefully, this can help others too!

Run Get-ClusterGroup on remote server using Central server

I have a centralized server from which i can run the following PowerShell command to get the clustergroup of cluster servers.
Enter-pssession -computername (ip-address) -credential (domain user)
And it prompts me to enter password then i get the session and execute
get-clustergroup
Okay till this it is fine.
Now i wanted to make this fully automated by converting in to a PowerShell script
The following commands works well when i run it in Powershell ISE and gets me the output of get-clustergroup
$password = ConvertTo-SecureString "password" -AsPlainText -Force
$user = "domain\user"
$cred = New-Object System.Management.Automation.PSCredential ($user,$password)
Enter-PSSession -ComputerName IP.Add.RE.SS -Credential $cred
get-clustergroup
but when i save the about script and run with PowerShell i get the following error.
get-clustergroup: the cluster service is not running
I want to automate the process by writing script to get get-clustergroup output of four cluster servers.
i am new to PowerShell scripting. how can i save the output?
Instead of creating a session to the other server, you can run the following which will run the command on the remote computer and return the output to your console:
Invoke-Command -ComputerName <IPAddress> -ScriptBlock { Get-ClusterGroup } -Credential $cred
You can store that output into a variable if you wish for future retrieval.
Since -ComputerName can accept an array object, you can modify your command to include all four of your servers. Below shows how to use all of your computer names and store the output in the variable $Output:
$Output = Invoke-Command -ComputerName "Server1","Server2","Server3","Server4" `
-ScriptBlock {Get-ClusterGroup} -Credential $cred
$Output
Your computer names could also be stored in a variable as an array. Then that variable can be used in your -ComputerName parameter:
$Computers = "Server1","Server2","Server3","Server4"
Invoke-Command -ComputerName $Computers -ScriptBlock { Get-ClusterGroup } -Credential $cred
See Invoke-Command for more information.

Enter-PSSession is not working in my Powershell script

When I run the lines below from a script the file ends up being created on my local machine.
$cred = Get-Credential domain\DanTest
Enter-PSSession -computerName xsappb01 -credential $cred
New-Item -type file c:\temp\blahxsappk02.txt
exit-pssession
When I run each line individually from the powershell console the remote session is created correctly and the file is created on the remote machine. Any thoughts on why? Is it a timing issue is the script perhaps?
Not sure if it is a timing issue. I suspect it's more like Enter-PSSession is invoking something like a nested prompt and your subsequent commands are not executing within it. Anyway, I believe Enter/Exit-PSSession is meant for interactive use - not scripting use. For scripts use New-PSSession and pass that session instance into Invoke-Command e.g.:
$cred = Get-Credential domain\DanTest
$s = New-PSSession -computerName xsappb01 -credential $cred
Invoke-Command -Session $s -Scriptblock {New-Item -type file c:\temp\blah.txt}
Remove-PSSession $s

PowerShell Remote sessions and scope question: Commands appear to run locally

Here's a sample script that attempts to create a remote session on a server, then use WMI to get a list of the server's IIS application pools, and list their names:
function Test-Remoting
{
[CmdletBinding()]
param
(
)
begin
{
Enter-PSSession TestServer
$appPools = Get-WmiObject -namespace "root\MicrosoftIISv2" -class "IIsApplicationPool" -Authentication 6
$appPools | ForEach-Object {
$appPool = $_;
$appPool.Name
}
Exit-PSSession
}
}
This function is contained in a file called "Test-Remoting.ps1." I open up PowerShell, CD into the directory that contains this file, dot-source the file in, and call the function:
PS C:\Users\moskie> . .\Test-Remoting.ps1
PS C:\Users\moskie> Test-Remoting
But the result of this script is a list of the application pools on my local machine, and not TestServer.
Alternatively, if I run the following lines (identical to the ones in the function) manually at the PowerShell prompt, I do get the list of app pools on the remote server:
PS C:\Users\moskie> Enter-PSSession TestServer
[TestServer]: PS C:\> $appPools = Get-WmiObject -namespace "root\MicrosoftIISv2" -class "IIsApplicationPool" -Authentication 6
[TestServer]: PS C:\> $appPools | ForEach-Object { $appPool = $_; $appPools.Name }
<a list of the names of the application pools on TestServer>
[TestServer]: PS C:\>
I think there's a concept I'm oblivious to, regarding PowerShell remoting and scope. Can anyone help explain this behavior?
I believe Enter/Exit-PSSession is meant more interactive use. From the Enter-PSSession help:
SYNOPSIS
Starts an interactive session with a remote computer.
In a script, use New-PSSession and Invoke-Command like so:
$session = New-PSSession server01
Invoke-Command -Session $session {hostname}
Remove-PSSession -Session $session
Update: To execute a complete script remotely use the FilePath parameter on Invoke-Command:
icm server01 -FilePath C:\users\keith\myscript.ps1 -arg 1,2
This will copy the script to the remote computer server01 and execute it there with the supplied parameters.