I'm very new to PowerShell (forgive my ignorance) and am trying to install a program remotely on multiple computers on my domain. Currently, I'm just trying to get it to work on one computer. The script below was found online and adapted for my needs. Yesterday it worked, but today it's complaining about the session parameter.
I don't fully understand "sessions", but I have ensured on the client machine that the winrm service is running and I have invoked Enable-PSRemoting -force.
Here's the script:
$computers = Get-Content "c:\tmpPS\computers.txt"
$rs = Get-PSSession
Get-PSSession | Get-Member
######
## Functions
################
foreach ($comp in $computers)
{
Write-Host "should work with $comp"
}
PushMSI
RemoteConnect
InstallMSI
Function PushMSI {
Write-Host "------------------------------------------------"
Write-Host "This will copy the MSI file from localhost c:\tmpPS\"
write-Host "------------------------------------------------"
Write-Host ""
Write-Host ""
foreach ($comp in $computers)
{
Copy-Item -path "c:\tmpPS\clientInstall.msi" -Destination \\$comp\c$\tmpPS
}
}
Function RemoteConnect
{
Write-Host "------------------------------------------------"
Write-Host "This will establish a PSSession with all computers in c:\temp\computers.txt"
write-Host "------------------------------------------------"
Write-Host ""
Write-Host ""
Get-Content C:\tmpPS\computers.txt | New-PSSession -ThrottleLimit 50
}
Function InstallMSI
{
Write-Host "------------------------------------------------"
Write-Host "This will Install UPS Update on all computers with an Established PSSession"
write-Host "------------------------------------------------"
Write-Host "After the Install PSSessions will be removed"
Write-Host ""
Invoke-Command -Session $rs -ScriptBlock {invoke-item "c:\tmpPS\ClientInstall.msi"}
}
Get-PSSession | Remove-PSSession
And here's the output:
PS C:\Users\Me> C:\tmpPS\remoteInstall.ps1
Get-Member : No object has been specified to the get-member cmdlet.
At C:\tmpPS\remoteInstall.ps1:3 char:17
+ Get-PSSession | Get-Member
+ ~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException
+ FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
should work with eSignWin81.informa.local
------------------------------------------------
This will copy the MSI file from localhost c:\tmpPS\
------------------------------------------------
------------------------------------------------
This will establish a PSSession with all computers in c:\temp\computers.txt
------------------------------------------------
Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
6 Session6 eSignWin81.i... Opened Microsoft.PowerShell Available
------------------------------------------------
This will Install UPS Update on all computers with an Established PSSession
------------------------------------------------
After the Install PSSessions will be removed
Invoke-Command : Cannot validate argument on parameter 'Session'. The argument is null or empty. Supply an argument that is not null or empty and then try
the command again.
At C:\tmpPS\remoteInstall.ps1:49 char:25
+ Invoke-Command -Session $rs -ScriptBlock {invoke-item "c:\tmpPS\ClientInstall.ms ...
+ ~~~
+ CategoryInfo : InvalidData: (:) [Invoke-Command], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeCommandCommand
Try not to use Write-Host as it brakes the pipeline and kills puppy's.
I've tried to improve your script a little bit, so you understand the logic behind the structure better. It hasn't been tested, but it should do the trick.
# First parameters
[CmdletBinding()]
Param(
[ValidateScript({Test-Path $_ -PathType leaf})]
$ComputerList = "c:\tmpPS\computers.txt",
[ValidateScript({Test-Path $_ -PathType leaf})]
$MSI = "c:\tmpPS\clientInstall.msi"
)
# Then functions
Begin {
Function Copy-MSI {
foreach ($Com in $Computers) {
Copy-Item -path $MSI -Destination "\\$Com\c$\tmpPS"
}
}
Function Install-MSI {
foreach ($Com in $Computers) {
Enter-PSSession -ComputerName $Com
invoke-item "c:\tmpPS\ClientInstall.msi"
Exit-PSSession
}
}
}
# Then the actions
Process {
$Computers = Get-Content $ComputerList
Copy-MSI
Install-MSI
}
If you run this, you'll find the information you're looking for:
Get-Help Enter-PSSession
Related
Even though the below script runs successfully to remove software, I
am still able to see the software in the control panel program list,
what I am doing wrong?
See the command below :
PS D:\Project> .\ScriptWithBelowCode.ps1 -ComputerName
"X-XXX50947" -AppGUID XXXXXX-A60B-4899-A369-XXXXXXX
Uninstallation command triggered successfully
[cmdletbinding()]
param (
[parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[string]$ComputerName = $env:computername,
[parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)]
[string]$AppGUID
)
try {
$returnval = ([WMICLASS]"\\$computerName\ROOT\CIMV2:win32_process").Create("msiexec `/x$AppGUID `/norestart `/qn")
} catch {
write-error "Failed to trigger the uninstallation. Review the error message"
$_
exit
}
switch ($($returnval.returnvalue)){
0 { "Uninstallation command triggered successfully" }
2 { "You don't have sufficient permissions to trigger the command on $Computer" }
3 { "You don't have sufficient permissions to trigger the command on $Computer" }
8 { "An unknown error has occurred" }
9 { "Path Not Found" }
9 { "Invalid Parameter"}
}
Command to execute .\ScriptFileWithAboveContent.ps1 -ComputerName "X-15XX150947" -AppGUID 30F836D2-A60B-4899-A369-XXXXX884EAF
Command to get GUID
Get-WmiObject -Class Win32_Product ` -Filter "Name like '%Microsoft%'" | Select-Object -ExpandProperty IdentifyingNumber
Second try
$app = Get-WmiObject -Class Win32_Product | Where-Object {$_.IdentifyingNumber -match "XXXXXX-A60B-4899-XXXX-B0FXXX84EAF" }
$app.Uninstall()
I am getting error showing below
PS D:\Project> .\ScriptFileWithAboveContent.ps1
Exception calling "Uninstall" : "Operation is not valid due to the current state of the object."
At D:\MicrosoftAIM\ScriptFileWithAboveContent.ps1:2 char:5
+ $app.Uninstall()
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : WMIMethodException
Third try
The below script ran successfully, But still, the software is still in the program list.
echo "Getting product code"
$ProductCode = Get-WmiObject win32_product -Filter "Name like '%somesoftware%'" | Select-Object -Expand IdentifyingNumber
echo "removing Product"
# Out-Null argument is just for keeping the power shell command window waiting for msiexec command to finish else it moves to execute the next echo command
& msiexec /x $ProductCode | Out-Null
echo "uninstallation finished"
PS D:\MicrosoftAIM> .\ScriptWithAboveContent.ps1
Getting product code
removing Product
uninstallation finished
PS D:\MicrosoftAIM> Get-WmiObject -Class Win32_Product ` -Filter "Name like '%Microsoft%azure%info%'"
The script below works and produces output file but gives errors. I've looked at other questions and answers that addresses the same problem in a similar way but I'm still not able to figure out how to fix this problem. I've done this with a splat before but not quite sure how to make it work in this script. All help would be appreciated. Error is first and script is below that.
Error below
Test-Connection : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command
again.
At C:\Temp\PowerShellScripts\PingComputers\colorfulPingMultipleComputers.ps1:6 char:37
+ if (Test-Connection -ComputerName $name -Count 1 -ErrorAction Silen ...
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand
Script below
$Output= #()
$names = Get-content C:\Temp\Computers.txt
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
$Output+= "$name, Pinging"
Write-Host -ForegroundColor Cyan "$Name, Pinging"
}
else{
$Output+= "$name, Not Pinging"
Write-Host Write-Host -ForegroundColor Red "$Name, Not Pinging"
}
}
$Output | Out-file "C:\Temp\PingComputers.csv"
The script produces this output, computer names changed for privacy. So it does what I want it to do, but it still gives the errors as indicated above for each machine
AAA-L-CWCXRQ2, Pinging
BBB-L-DW2FQQ2, Pinging
CCC-L-JN3RM72, Pinging
DDD-L-34SQRQ2, Pinging
EEE-L-84F3ZM2, Pinging
Write-Host FFF-L-1B1CM72, Not Pinging
GGG-L-94XXRQ2, Pinging
Write-Host FFF-L-D5KXRQ2, Not Pinging
Write-Host GGG-L-75SX4Q2, Not Pinging
My guess is you have a blank line at the end of your text file. See this example.
$tempfile = New-TemporaryFile
#'
google.com
bing.com
'# | Set-Content $tempfile -Encoding UTF8
Get-Content $tempfile | foreach {
Test-Connection -ComputerName $_ -Count 1 -ErrorAction SilentlyContinue
}
I'd recommend just changing this line
$names = Get-content C:\Temp\Computers.txt
to
$names = Get-content C:\Temp\Computers.txt | Where-Object {$_}
which will filter out blank lines
I'm trying to run a powershell script from rundeck(linux), If I run the script locally[Deletes some files from multiple terminal servers](Windows server) it is working as expected however if I call it from rundeck server(winrm configured) it seems that the script cant access the remote folders I'm trying to access.
I tried running the script using the same user but still shows different result.
Script bellow:
$userAD = "someuser"
$servers = Get-Content C:\TSList.csv
$Folder = "c$\Users\$userAD\"
$TSFolderShare = "\\sharepath"
Write-Output "#####Start of script#####"
Write-output `n
Write-output "Checking if $userAD user profile exist in Terminal servers..."
sleep -seconds 1
foreach ($server in $servers) {
Test-Path "\\$server\$Folder" -PathType Any
Get-ChildItem "\\$server\$Folder"
if (Test-Path "\\$server\$Folder" -PathType Any) {
Write-output "Resetting user profile in $server.."
Get-ChildItem "\\$server\$Folder" -Recurse -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
sleep -seconds 1
Write-output "Done."
if( (Get-ChildItem "\\$server\$Folder" | Measure-Object).Count -eq 0)
{
Write-output "Done."
}
}
else
{
Write-output "Resetting user profile in $server.."
sleep -seconds 1
Write-output "User profile does not exist in $server."
#Write-output "\\$server\$Folder does not exist in $server!" -ForegroundColor Red
}
}
EDIT: It seems my problem is when running my script from another script with RunAS.
Below I'm trying to access a folder from another server using ps script, but since I want to integrate this to Rundeck I need to call my ps script from my linux server using python. I did a test running the ps script directly and calling the test path script using another script with RunUs using the same user I used to run the script manually
Scenario 1
Running PS script via separate PS script with RunAS(my_account)
$username = "my_account"
$password = "my_password"
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
Invoke-Command -FilePath "C:\testpath.ps1" -Credential $cred -Computer localhost
(C:\testpath.ps1) Content below:
Test-Path "\\server\c$\Users\myaccount\"
result:
Access is denied
+ CategoryInfo : PermissionDenied: (\server\c$\Users\myaccount:String) [Test-Path], UnauthorizedAccessException
+ FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.TestPathCommand
+ PSComputerName : localhost
False
Scenario 2
Running C:\testpath.ps1 directly as my_account
Test-Path "\\server\c$\Users\myaccount\"
result:
True
I used session configuration in powershell to solve the issue. This way allows you to tie a credential to a PowerShell session configuration and reuse this configuration for all future connections.
https://4sysops.com/archives/solve-the-powershell-multi-hop-problem-without-using-credssp/
Thanks a lot!
You're facing a double-hop issue with Rundeck and Powershell, here the explanation. That's asked before, take a look a this, and here a good workaround. Also this to solve it.
What i'm trying to do ?
Create four files in local disk in the following order.
Note : In my local machine and not in any server remotely.
Three files to be created
Restart the system
On system startup create another file
Script i have used.
get-job | remove-job -Force
function create-file {
Param ([string] $a)
$p = "D:\" + $a
Write-Host $p
if (!(Test-Path $p))
{
New-Item -path D:\$a -type "file" -value "my new text"
Write-Host "Created new file and text content added"
}
else
{
Add-Content -path D:\$a -value "new text content"
Write-Host "File already exists and new text content added"
}
}
Workflow New-ServerSetup
{
create-file "one.txt"
create-file "two.txt"
create-file "three.txt"
Restart-Computer -ComputerName $env:COMPUTERNAME -Wait
Start-Sleep -Seconds 7
create-file "four.txt"
Unregister-ScheduledJob -Name NewServerSetupResume
}
$adm = "####"
$pwd = ConvertTo-SecureString -String "####" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($adm, $pwd)
$AtStartup = New-JobTrigger -AtStartup
Register-ScheduledJob -Name NewServerSetupResume -Credential $cred -Trigger $AtStartup -ScriptBlock {Import-Module PSWorkflow; Get-Job -Name NewSrvSetup -State Suspended | Resume-Job}
New-ServerSetup -JobName NewSrvSetup
Issues i'm facing
The execution returns Cannot wait for local computer to restart
i'm new to powershell things if any mistakes burden me.
Thanks in advance.
Schedule a job first, then reboot without waiting.
I recently updated PSWindowsUpdate from version 1.6.1.1 to the latest version (2.1.0.1) and when I try to run the script:
Write-Host " Centralized Update"
Write-Host "================================"
ipmo activedirectory
$computers = Get-ADComputer -Filter {enabled -eq $true} -properties * -SearchBase "OU=Workstations, DC=contoso, DC=com" | select name
$Script = {ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll -Install -Verbose}
foreach ($computer in $computers) {
Write-Host "Running update on:" $computer.name
Invoke-WUJob -ComputerName $computer.name -Script $Script -Confirm:$false -RunNow
}
Write-Host "================================"
pause
I get the following error:
Invoke-WUJob : The system cannot find the file specified. (Exception from HRESULT: 0x80070002)
At C:\Users\Administrator\Desktop\Update_Workstations.ps1:10
char:2
+ Invoke-WUJob -ComputerName $computer.name -Script $Script -Confir ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Invoke-WUJob], FileNotFoundException
+ FullyQualifiedErrorId : System.IO.FileNotFoundException,PSWindowsUpdate.InvokeWUJob
The only edit to the script was to change:
Invoke-WUInstall -ComputerName $computer.name -Script $Script -Confirm:$false
To:
Invoke-WUJob -ComputerName $computer.name -Script $Script -Confirm:$false -RunNow
More details:
The version of PowerShell is 5.1 (both client and server side)
The list of terminals is correctly extracted from the "Workstations" organizational unit
I can connect via "enter-pssession" to all the terminals without problems
Using "Invoke-Command" instead of "Invoke-WUJob" run but fails at the time of download with the error "UnauthorizedAccessException"
What's wrong with the code ? before updating to version 2.1.0.1 it works fine
I was also getting the same error. By scratching my head for a day finally a found one power shell command needs to run on the target server before proceeding invoke-wujob
the command is Enable-WURemoting