So i'm trying to run CleanMgr via powershell on a remote computer. Since CleanMgr has a GUI Powershell cannot run it directly, as described here
If you do this then CleanMgr will hang forever waiting for user input. So this means we have to approach this differently.
I tried several approaches, see below. But non of them works like i would. It still hangs OR runs attached from powershell giving no feedback when done.
Simply put i want to run (remotely) CleanMgr on our Office PC's from my PC (Domain Admin).
Invoke-Command -ComputerName $ComputerOBJ -ScriptBlock {
# Create registry values
Write-Host "Setup keys..."
$volumeCaches = Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches"
foreach ($key in $volumeCaches) {
Set-ItemProperty -Path "$($key.PSPath)" -Name StateFlags0333 -Value 2
}
# Execute Disk Cleanup Tool (cleanmgr.exe)
#Write-Host 'Starting CleanMgr.exe...'
Attempt 1:
Start-Process -FilePath "CleanMgr.exe" -ArgumentList '/sagerun:333' -NoNewWindow
Attempt 2:
Start-Process -FilePath "CleanMgr.exe" -ArgumentList '/sagerun:333' -WindowStyle Hidden
Attempt 3:
Invoke-WmiMethod -Class Win32_Process -Name "Create" -ArgumentList 'CleanMgr.exe /sagerun:333'
Attempt 4:
C:\temp\PsExec.exe \\$ComputerOBJ CleanMgr.exe /sagerun:333
Attempt 5:
$A = New-ScheduledTaskAction -Execute "cleanmgr.exe" -Argument '/sagerun:333'
$T = New-ScheduledTaskTrigger -Once -At (get-date).AddSeconds(1); $t.EndBoundary = (get-date).AddSeconds(60).ToString('s')
$S = New-ScheduledTaskSettingsSet -StartWhenAvailable -DeleteExpiredTaskAfter 00:00:30
Register-ScheduledTask -Force -user SYSTEM -TaskName "Run CleanMgr" -Action $A -Trigger $T -Settings $S
#Wait until Clean is done.
Write-Host 'Waiting for CleanMgr and DismHost processes to complete...'
Get-Process -Name cleanmgr, dismhost -ErrorAction SilentlyContinue | Wait-Process
# Remove the previously created registry values
Write-Host "Cleanmgr completed, now deleting keys"
$volumeCaches = Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches"
foreach ($key in $volumeCaches) {
Remove-ItemProperty -Path "$($key.PSPath)" -Name StateFlags0333 -Force
}
}
Related
I am trying to upgrade Powershell on a bunch of Windows 7 boxes so I can do other remote installs and such. I am using Invoke-Expression but I swear this worked once before without it. There doesn't appear to be a Wait option for any of this. It does work when I run the Invoke-Expression locally. I also tried Start-Process. Is there a better way to get feedback on why it didn't run? The debugging is painfully slow because it has been a lot of just guessing, both due to lack of feedback and due to its hard to tell on the remote machine when its actually installing the background. The script is getting copied. I've tried without the Remove-item in case I was deleting it too fast. The $cred is admin. I'm not sure Execution Policy is necessary.
foreach ($comp in $computers) {
$comp.Name
if(test-connection -ComputerName $comp.Name -quiet ){
$Destination = "\\$($comp.Name)\c$\Temp\"
copy-item -path "\\10.1.32.161\New Client Setups\WMF_5.1_PowerShell\*" -Destination $Destination -recurse -force
"`t Copied"
$session = Enter-PSSession $comp.Name -Credential $cred
$results = Invoke-Command -ComputerName $comp.Name -ScriptBlock {
Set-ExecutionPolicy RemoteSigned
$ver = $PSVersionTable.PSVersion.Major
"`t Powershell Version : $ver"
if ($ver -lt "5"){
"`tNeeds upgrade"
$argumentList = #()
$argumentList += , "-AcceptEULA"
$argumentList += , "-AllowRestart"
#Invoke-Expression "& 'C:\Temp\Windows7_Server2008r2\Install-WMF5.1.ps1' + $argumentList"
Invoke-Expression 'C:\Temp\Windows7_Server2008r2\Install-WMF5.1.ps1 -AllowRestart -AcceptEULA'
}
}
$results
Remove-item -Path "$Destination*" -recurse
Exit-PSSession
Remove-PSSession -session $session
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'm trying to return exit code from a powershell script that is executed on a remote machine. But, when I check ExitCode it has some random number.
What I'm doing wrong? In addition, is it possible to return the whole text?
my script
$proc = Start-Process -Filepath "$PSExec" -ArgumentList "\\$server -h -u $user -p $pass -d PowerShell $command" -PassThru -Wait
$proc.ExitCode
remote script
New-Item "c:\temp\1.txt" -type file -force
exit 123
UPDATE
$secureString = ConvertTo-SecureString $password -Force -AsPlainText #$password includes password in clear text
$cred = New-Object System.Management.Automation.PSCredential($usrName, $secureString)
$sess = New-PSSession -ComputerName $serverName -Credential $cred
$command = "`"C:\temp\1.ps1`""
$result = Invoke-Command -Session $sess -ScriptBlock {
Start-Process -Filepath "$PSExec" -ArgumentList "\\$server -h -u $usrName -p $password -d PowerShell $command" -PassThru -Wait
}
Can you use Invoke-Command as an alternative?
Example:
$session = New-PSSesson -ComputerName $serverName -Credential (Get-Credential)
$result = Invoke-Command -Session $session -ScriptBlock {
Start-Process ...
}
As an alternative to Get-Credential you can created a credential object and pass it via the -Credential paramter to Invoke-Command. Example:
$secureString = ConvertTo-SecureString $password -Force -AsPlainText #$password includes password in clear text
$cred = [System.Management.Automation.PSCredential]::new($usrName, $secureString)
$sess = New-PSSession -ComputerName $ComputerName -Credential $cred
Invoke-Command -Session $sess -ScriptBlock { ... }
$result should also include the ExitCode property, since Powershell Remoting serializes the remote object. I always suggest Powershell Remoting compared to the cmdlet specific ComputerName implementations. It uses a more standardized way (WsMan -> HTTP(S)). See this link for further details.
Hope that helps.
For your first approach, your issue is that when running psexec with the -d (don't wait) flag it returns the pid of the command that launched it, rather than waiting and returning the exitcode.
Altogether your process also could be optimized. First if you wanted to use psexec.exe, I don't see a reason for Start-Process since you are waiting and passing through. Just & $psexec ... would suffice.
However Moerwald's suggestion for using Invoke-Command is a great one. In your updated code, you are still running Start-Process and Psexec which are unnecessary. When you are invoking the command, you are already remotely running code, so just run the code:
$secureString = ConvertTo-SecureString $password -Force -AsPlainText
$cred = New-Object System.Management.Automation.PSCredential($usrName, $secureString)
$result = Invoke-Command -ComputerName $serverName -Credential $cred -ScriptBlock {
New-Item "c:\temp\1.txt" -type file -force
exit 123
}
Also, since it doesn't look like you are reusing the session, I dropped the saving the session to a variable. And it would also be better to replace all of the credential setup with a Get-Credential rather than passing plaintext passwords around (avoid the password ending up in a saved transcript). That would look like this:
$result = Invoke-Command -ComputerName $serverName -Credential (Get-Credential) -ScriptBlock {
New-Item "c:\temp\1.txt" -type file -force
exit 123
}
I believe for Register-ScheduledTask you can specify -User "System"or do something like:
$principal = New-ScheduledTaskPrincipal -UserId SYSTEM -LogonType ServiceAccount -RunLevel Highest
How do I do this with Register-ScheduledJob?
This command will be running the context of the local admin so it will have access to do this. I just don't see this option in the cmdlet.
Here is an example of how to do this with the scheduled tasks cmdlet
edit: Does windows make this impossible by design? If I open an interactive PS session as the system (using psexec) and try to create a schedualed job I get an error:
PS C:\Windows\system32> Register-ScheduledJob -Name systemsssss -ScriptBlock {'s
dfsdfsdfsd'}
Register-ScheduledJob : An error occurred while registering scheduled job
definition systemsssss to the Windows Task Scheduler. The Task Scheduler
error is: (32,4):UserId:.
At line:1 char:1
+ Register-ScheduledJob -Name systemsssss -ScriptBlock {'sdfsdfsdfsd'}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Microsoft.Power...edJobDefini
tion:ScheduledJobDefinition) [Register-ScheduledJob], ScheduledJobExceptio
n
+ FullyQualifiedErrorId : CantRegisterScheduledJobDefinition,Microsoft.Pow
erShell.ScheduledJob.RegisterScheduledJobCommand
This same command works fine when run as the local administrator account
First use Register-ScheduledJob to create your PowerShell job.
Then use Set-ScheduledTask to change a startup account to the Local System or any other built-in accounts, i.e. SYSTEM, LOCAL SERVICE, NETWORK SERVICE, etc.
Use the following PS-script. Or download it from my GitHub Gist
The code is self-explanatory (I believe).
You can run it multiple times under an administrative account if you want to check how it works.
BTW, I prefer to use jobs (Register-ScheduledJob) over tasks because jobs allow me to embed PowerShell script blocks (strings) instead using of external script files. Look at -ScriptBlock below.
Also pay attention to -RunElevated. It is a must be.
$ErrorActionPreference = 'Stop'
Clear-Host
#### Start of Main Logic ###########################
$taskName = "my_PowerShell_job"
$accountId = "NT AUTHORITY\SYSTEM";
#$accountId = "NT AUTHORITY\LOCAL SERVICE";
$task = Get-ScheduledJob -Name $taskName -ErrorAction SilentlyContinue
if ($task -ne $null)
{
Unregister-ScheduledJob $task -Confirm:$false
Write-Host " # The old ""$taskName"" PowerShell job has been unregistered"; Write-Host;
}
# Uncomment the following exit command to only delete your job.
# exit;
# Shchedule your job. Using of -AtStartup as an example.
$trigger = New-JobTrigger -AtStartup;
$options = New-ScheduledJobOption -StartIfOnBattery -RunElevated;
Write-Host " # Registering of ""$taskName"" job";
Register-ScheduledJob -Name $taskName -Trigger $trigger -ScheduledJobOption $options `
-ScriptBlock {
# Put your code here.
Write-Host Your job has been launched!;
}
$principal = New-ScheduledTaskPrincipal -UserID $accountId `
-LogonType ServiceAccount -RunLevel Highest;
$psJobsPathInScheduler = "\Microsoft\Windows\PowerShell\ScheduledJobs";
$someResult = Set-ScheduledTask -TaskPath $psJobsPathInScheduler `
-TaskName $taskName -Principal $principal
#### End of Main Logic ###########################
Write-Host;
Write-Host " # Let's look at running account of ""$taskName"" PowerShell job"
$task = Get-ScheduledTask -TaskName $taskName
$task.Principal
Write-Host " # Let's start ""$taskName"" manually"
Start-Job -DefinitionName $taskName | Format-Table
Write-Host " # Let's proof that ""$taskName"" PowerShell job has been launched"; Write-Host;
Start-Sleep -Seconds 3
Receive-Job -Name $taskName
Write-Host;
Sadly you can't run schedule a job or task as the system account.
But you can create local administrator accounts as the system account.
And you can schedule jobs or tasks as a local administrator account.
So what I did to get around this problem is this:
$password = ConvertTo-SecureString (New-Guid).Guid -AsPlainText -Force
$user = New-LocalUser "service.scheduler" -Password $Password -Description "For scheduling in tasks from system account"
$credentials = New-Object System.Management.Automation.PSCredential($user.name, $password)
Register-ScheduledJob -Trigger $trigger -ScriptBlock $scriptblock -Name $taskName -ScheduledJobOption $options -credential $credentials
This does mean you are passing in credentials, but you don't have to store them as plain text or specify them.
Sorry, can't make comments with reputation under 50.
Can you use Group Policy to run it as a start up script? That will run as the Local System account. Doesn't look like this cmdlet has the -verb paramater to runas.
Looking at: https://technet.microsoft.com/en-us/library/hh849755.aspx under -ScheduledJobOption there is a setting in there RunElevated=$False, that is the defualt. If you set that to true does it run as admin?
I haven't tried it, it might work.
Hope this helps.
Thanks, Tim.