Wait for remote Powershell script to finish - powershell

I'm creating a powershell script to import Dynamics NAV Application Objects to my Dynamics NAV 2018 database.
Microsoft is providing a PS cmdlet - Import-NAVApplicationObjects - to do so, but I have trouble to wait for the command to finish.
I have a calling script which does the following
$PSSession = New-PSSession -ComputerName $TargetMachine -Credential $MyCredential
Invoke-Command -Session $PSSession -ArgumentList $Database_Name_user, $MyCredential -ScriptBlock {
$process = Start-Process powershell.exe -Verb RunAs -Wait -PassThru -ArgumentList "-File `"C:\Users\User\Desktop\Database\Import1.ps1`" $args[0]"
$process.WaitForExit()
if ($process.ExitCode -ne 0) {
throw $process.StandardError.ReadToEnd()
}
}
The script Import1.ps1 on my $TargetMachine looks like this
param(
[String]$Database_Name_user
)
try {
$AllFiles = "C:\Users\User\Documents\AllFiles.txt"
$Modules = "C:\GIT\Loading Components\NAVModuleImport.ps1"
$OutputBuffer = import-module $Modules
Import-NAVApplicationObject -Path $AllFiles -DatabaseServer "SQLSRV001" -DatabaseName $Database_Name_user -SynchronizeSchemaChanges "No" -ImportAction "Overwrite" -Confirm:$false | Out-Null
}
catch{
$_ | Out-File "C:\Users\User\Documents\Import1.txt"
}
The file AllFiles.txt has a size of 220 MB and contains more than 7700 Dynamics NAV Application Objects (tables, pages, codeunits and so on).
When I launch the script which executes Import-NAVApplicationObject directly from the remote computer stored in $TargetMachine everything works smootly and the process takes up to 10 to 15 minutes, as expected.
When calling the script as shown in the first code example the output stops for a minute and then says everything is done.
Any help is appreciated, thank you in advance.
Edit: Solution
I noticed that my scripts as shown are working, the Import-NAVApplicationObjects cmdlet just failed.
When I elevate the powershell process on the remote computer and run the import, the cmdlet tried to authenticate as NT-Authority\Anonymous to the database.
Then I passed the credentials of the user that opens the remote PSSession to Import1.ps1 and used the parameters -UserName and -Password of the import cmdlet.
Sadly this process failed again.
Now I tried some things with the user I want to use for authenticating, changing passwords etc and it worked! The password contained a semicolon ; and apparently the import cmdlet was not happy with that.
So my simple solution is: Removing the semicolon from the password.

Related

Install MSI on remote computer using Powershell

I am trying to write a Powershell script which will deploy software on a collection of WS2016 servers. I am a local administrator on all these servers. Here's what I have so far:
$Cred = Get-Credential
$Computer = 'myserver.contoso.com'
$SplunkMSI = '\\mylocalbox\C$\Splunk.msi'
$InstallDir = 'C:\Apps\Splunk\'
$sb = {
param($installer, $dir)
Start-Process -FilePath 'c:\windows\system32\msiexec.exe' -ArgumentList "$installer INSTALLDIR=$dir AGREETOLICENSE=Yes /qn /norestart /L*v C:\temp\splunkInstall.log" -Wait -NoNewWindow
}
Write-Host "Deploying Splunk to host $Computer"
Invoke-Command -Computer $Computer -Credential $Cred -ScriptBlock $sb -ArgumentList $SplunkMSI, $InstallDir -ErrorAction Stop
When I run this script, I get prompted for credentials, and then I see the output of the Write-Host, but then... nothing. I have to manually terminate the script.
I logged onto the remote host, but see no evidence that the MSI was executed or failed to execute.
Anyone see a smoking gun?
ya, it looks like it's looking for $installer and $dir in the script block but they're not specified

Power shell Invoke remote script is not working

I am trying to invoke a remote bat file from my local machine that should start and run the bat in a remote machine terminal. My script is able to connect and show me the output. However, it is invoking the remote bat file but waiting on my screen with bat file output. and my idea is the bat file should invoke and running in the remote machine rather than showing the output on my terminal local terminal. What should be the way here?
loop{
Invoke-Command -ComputerName $computer -credential $cred -ErrorAction Stop -ScriptBlock { Invoke-Expression -Command:"cmd.exe /c 'C:\apacheserver.bat'" }
}
From what I understand you want the .bat file to not show you the result. If so, you should do it with the Out-Null or by redirecting STDOUT and STDERR to NULL.
IE: Invoke-Expression -Command:"cmd.exe /c 'C:\apacheserver.bat'" | Out-Null
If I'm understanding correctly, you want to suppress the output from the .bat file inside your local console? If so, redirecting to some form of $null is the way to go. You also shouldn't need Invoke-Expression as Invoke-Command can run the file directly:
Invoke-Command -ComputerName $computer -Credential $cred -ErrorAction Stop -Scriptblock {
cmd.exe /c 'C:\apacheserver.bat' | Out-Null
}
You could also use > $null instead of Out-Null if you prefer.
If you want to redirect the output of the Invoke-Command call to the remote console instead, that kind of defeats the purpose of the cmdlet. You could try redirecting the console output to a file on the remote computer if you want a local record:
$remoteFile = '\\server\C$\Path\To\Output.txt'
Invoke-Command -ComputerName $computer -Credential $cred -ErrorAction Stop -Scriptblock {
cmd.exe /c 'C:\apacheserver.bat' > $remoteFile
}
If I understand you, you want to run the script in the background and not wait for it:
Invoke-Command $computer -Credential $cred { start-process C:\apacheserver.bat }

Unable to install program in self extracting cabinet using Invoke-Command

I'm writing a script to set up a test SharePoint server for trusted (AD FS) authentication on a stamped test environment that consists of a SharePoint server (server 2016) and a domain controller (server 2008R2). I'm writing the script to run on the SharePoint server and use a remote session to configure the DC because the DC only has PowerShell 2.0 which is missing some convenient functionality.
I have a specific segment of the script that runs a script block on the DC which downloads the AD FS 2.0 installer, a self extracting cabinet, and tries to install it. Every line of the block executes except for the actual installation. If I log onto the machine and run those same lines it works perfectly.
Invoke-Command -Session $domainControllerSession -ScriptBlock {
$installerUrl = "https://download.microsoft.com/download/F/3/D/F3D66A7E-C974-4A60-B7A5-382A61EB7BC6/RTW/W2K8R2/amd64/AdfsSetup.exe"
$filename = "$($PWD.Path)\AdfsSetup.exe"
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($installerUrl, $filename)
Start-Process -FilePath $filename -ArgumentList "/quiet" -Wait
}
I tried manually extracting the contents (using /x:) and then executing the setup file but there was no change in result (Note: The files are extracted but the extractor process never exits, this doesn't seem pertinent to the problem). I also moved to the DC and created a session to localhost and got the same exact behavior.
PS C:\Users\Administrator> $session = New-PSSession -ComputerName Localhost
PS C:\Users\Administrator> Invoke-Command -Session $session -ScriptBlock {
>> $filename = "$($PWD.Path)\AdfsSetup.exe"
>> write-host $filename
>> Test-Path -Path $filename
>> Start-Process -FilePath $filename -ArgumentList "/quiet" -Wait
>> Test-Path -Path 'C:\Program Files\Active Directory Federation Services 2.0'
>> }
>>
C:\Users\Administrator\Documents\AdfsSetup.exe
True
False
PS C:\Users\Administrator>
Update 1
I ran the process with the /Logfile parameter and confirmed that the installation is failing due to an access denied error. I've also confirmed that, as expected, the remote session is running under the same administrator account that I am using to initiate the session. I am assuming that the missing ingredient here is that the remote session is not running in an elevated shell. However, I can't seem to get that working either.
Invoke-Command -Session $session -ScriptBlock {
Start-Process PowerShell -Verb RunAs -ArgumentList "& C:\Users\Administrator\Documents\AdfsSetup.exe /quiet /Logfile C:\Users\Administrator\Documents\AdfsSetup.log" -Wait -PassThru
}
The error is the same.

Trouble calling powershell script from within powershell with arguments

I have spent the last 4 hours on this issue and would greatly appreciate any input you might have.
I need to call a powershell script with different credentials and pass arguments onto that script.
Following the installation of a program wrapped in WISEScript this script kicks off to gather AD accounts for the machine and remove them from specific AD Security Groups. Unfortunately as the script runs locally I cannot use ActiveDirectory modules in powershell as not all machines in our environment have RSAT.
The initial script is run from an elevated account on the machine:
$creds = New-Object System.Management.Automation.PsCredential("DOMAIN\USER", (ConvertTo-SecureString "Password" -AsPlainText -Force))
$ProfileGUIDS = Get-ChildItem 'hklm:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileGuid'
$Groups = [ADSI]"LDAP://CN=Group4d_test,OU=GroupMigrationTesting,OU=TestOU,OU=US,DC=DOMAIN",[ADSI]"LDAP://CN=Group3d_test,OU=GroupMigrationTesting,OU=TestOU,OU=US,DC=DOMAIN"
Function Get-DistinguishedName ($strUserName)
{
$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]'')
$searcher.Filter = "(&(objectClass=User)(samAccountName=$strUserName))"
$result = $searcher.FindOne()
if ($result)
{
Return $result.GetDirectoryEntry().DistinguishedName
}
}
forEach ($GUIDkey in $ProfileGUIDS)
{
$GUID = Out-String -InputObject $GUIDKey
$index = $GUID.IndexOf("S-1")
$GUID = $GUID.Substring($index)
$GUID = $GUID.Substring(0,128)
$index = $GUID.IndexOf(" ")
$GUID = $GUID.Substring(0,$index)
$Profile = "hklm:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$GUID"
$ProfileItems = Get-ItemProperty $Profile
$SAM = $ProfileItems.ProfileImagePath
$index = $SAM.LastIndexOf("\")
$index ++
$SAM = $SAM.substring($index)
$UserDN = Get-DistinguishedName $SAM
$User = [ADSI]"LDAP://$UserDN"
if($User -ne $null)
{
forEach($group in $groups)
{
Right here is where I need to call the 2nd script with different credentials.
This is RemoveUsers.ps1, the script I need to run with different credentials:
param
(
[string]$group = "MyDefaultSAM",
[string]$user = "MyDefaultUser"
)
$Group.remove($User.ADsPath)
I have tried:
start-process powershell.exe -Credential $creds -NoNewWindow -ArgumentList "Start-Process $PSSCriptRoot\RemoveUsers.ps1 -Verb
This will run the script however I cannot specify any arguments
powershell.exe -file "$PSScriptRoot\RemoveUsers.ps1" -user $user -group $group
This calls the script with arguments but does not allow for the -Credentials switch
I have also tried:
$job = Start-Job -ScriptBlock {
powershell.exe -file "$PSScriptRoot\RemoveUsers.ps1" -user $user -group $group
} -Credential $creds
This runs but does not appear to work properly as the users remain in the AD groups.
Any help is appreciated.
Thanks - Jeff
**** UPDATE ****
Thanks for the information. When I add the changes you suggest I receive an error
Invoke-Command : Parameter set cannot be resolved using the specified named parameters
It appears, as I have found online, the -Credential switch cannot be used without the -Computer switch. If I specify $env:COMPUTERNAME or localhost for the computer I receive the error
\RemoveUsers.ps1 is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again
I can avoid this issue if I remove the -Credential switch and open the AD group to everyone. At this point I don't need to elevate a new powershell script and can add the command in the same. If I cannot resolve the issue with Invoke-Command this is likely what I will do.
**** UPDATE ****
What I ultimately had to do was use -Authentication Credssp in the argument list as there is an issue with using the AD Module via Invoke-Command. In addition I had to start the Win-RM service, Enable WSMacCredSSP (-role client on each machine and add a DelegateComputer entry and -role server on the server connecting to). Only after the service was started and an entry was made for WSManCredSSP was I able to use the Invoke-Command switch and have the AD Module work correctly.
This of course makes things more complicated and I decided just installing the AD Module on each PC (after finding a way to do it without RSAT) and forgetting about running the command remotely all together. Thanks for your help with the matter.
Thanks
You don't need to run PowerShell scripts with powershell.exe when calling them from another PowerShell script. Simply use the call operator (&). Also, I'd use Invoke-Command for running something inline with different credentials.
Beware that the scriptblock doesn't automatically know about the variables in the rest of your script. You need to pass them into the scriptblock via the -ArgumentList parameter. That is most likely the reason why removal didn't work when you ran RemoveUsers.ps1 as a job.
Something like this should work:
Invoke-Command -ScriptBlock {
& "$PSScriptRoot\RemoveUsers.ps1" -user $args[0] -group $args[1]
} -ArgumentList $user, $group -Credential $creds -Computer $env:COMPUTERNAME
This requires PSRemoting, though (run Enable-PSRemoting as an administrator).

Powershell remote application(.cmd) deployment

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.