powershell auto shutdown script using ScheduledJob - powershell

[Update]:
Finally figured it out thanks to Hey, Scripting Guy blog (https://blogs.technet.microsoft.com/heyscriptingguy/2014/05/16/troubleshooting-powershell-scheduled-jobs/), updated original code with working one. [/Update]
I would like to write a powershell script that would add a scheduled job for me on Windows 10 1706.
The job should:
Stop-Computer (localhost)
Run everyday except weekends # a specific time.
Wake PC if its asleep to run the script block.
Ignore if user is on "lock screen" and shutdown anyway.
I wrote a few lines using the built-in help in PS, that would accomplish some of this:
$T = New-JobTrigger -DaysOfWeek 1,2,3,4,5 -At 18:40 -Weekly
$O = New-ScheduledJobOption -RunElevated -WakeToRun -IdleDuration 00:00:00 -IdleTimeout 00:00:00
Register-ScheduledJob -Name "AutoShutdown" -ScriptBlock {Stop-Computer -Force} -Trigger $T -ScheduledJobOption $O -Credential Admin_username
Running these lines adds the job, but it simply doesn't do anything.
I'd love to make a script that later can be used in a domain, however currently I only need to use them on a few local machines to ease administration.
Thanks for the aid in advance!
*Update:
Finally figured it out thanks to Hey, Scripting Guy blog (https://blogs.technet.microsoft.com/heyscriptingguy/2014/05/16/troubleshooting-powershell-scheduled-jobs/), updated original code with working one.

Related

Powershell Loop Script. The process is taking too long. Can't we speed up the processing?

Below is my original question and I have already received answers but due to account suspension problem, I'm edditing the words.
https://github.com/oklokl/advfirewall-ip-list-firehol_level1.netset/blob/main/temp/out_1.csv
https://github.com/oklokl/advfirewall-ip-list-firehol_level1.netset/blob/main/temp/out_2.csv
https://github.com/oklokl/advfirewall-ip-list-firehol_level1.netset/blob/main/out.csv (final boss)
out.csv is the problematic one. I call it the last boss of a game.
csv file(or txt file) is consisted of pretty simple structures.
IP's are aligned and each file contains 1,000 lines.
My plan is to run these files with cmd process simultaneously, like multitasking, to enhance the progression speed; which I heard is called 'multi thread' process.
Here come my real questions : How do I 'multi thread' files and how should I create the batch file? I'd like to know any efficient ways.
1.ps1
Start-Process Powershell.exe -Argumentlist "-file test1.ps1"
Start-Process Powershell.exe -Argumentlist "-file test2.ps1"
.
test1.ps1
$ports=get-content out_1.csv
Foreach($port in $ports){
New-NetFirewallRule -DisplayName 'A HTTP-Inbound' -Profile any -Direction Inbound -Action Block -RemoteAddress $port | Out-Null
}
exit
I have tried multiple ways but none seemed to work properly.
The CPU rose up to 45% but it became so slow that it felt like as if the Windows was controling the overall speed. Are there any recommended solutions?
Below is the message I got for my failure
Below is the method I tried
Finally, thank you everyone who answered to my questions.
I have been suspended for 6months for my previous article where I used 'translator english'.
Google translator seems to have misinterpreted my intentions which led to miscommunications and I am sorry for everything.
Nonetheless, my questions were all answered thanks to everyone here and I am trying to rectify my former mistake by asking someone to translate my actual words.
Thank you
I edited them one by one.
To speed up loops in PowerShell 7. Use the Foreach-Object -parallel functionality.
So change your script to the below:
1.ps1
Start-Process Pwsh.exe -Argumentlist "-file test1.ps1"
Start-Process Pwsh.exe -Argumentlist "-file test2.ps1"
test1.ps1
get-content "out_1.csv" | Foreach-Object -parallel {
New-NetFirewallRule -DisplayName 'A HTTP-Inbound' -Profile any -Direction Inbound -Action Block -RemoteAddress $_ | Out-Null
}

Why don't the applications run by Powershell appear on remote desktop but appear in task manager? [duplicate]

I've created a pssession on a remote computer and entered that possession. From within that session I use start-process to start notepad. I can confirm that notepad is running with the get-process command, and also with taskmgr in the remote computer. However, the GUI side of the process isn't showing. This is the sequence I've been using:
$server = New-PSSession -ComputerName myserver -Credential mycreds
Enter-PSSession $server
[$server]: PS C:\>Start-Process notepad -Wait -WindowStyle Maximized
The process is running, but while RDP'd to the box, notepad does not open. If I open notepad from the server, a new notepad process begins. I also tried by using the verb parameter like this:
[$server]: PS C:\>Start-Process notepad -Wait -WindowStyle Maximized -Verb Open
Same result tho... Process starts, but no notepad shows. I've tried this while remoted into the box (but issued from my local host) as well as before remoting into the server.
That is because your powershell session on the remote machine does not go to any visible desktop, but to an invisible system desktop. The receiving end of your powershell remote session is a Windows service. The process is started, but nor you nor anyone else can ever see it.
And if you think about it, since multiple users could RDP to the same machine, there is really no reason to assume a remote powershell session would end up showing on any of the users desktops. Actually, in almost all cases you wouldn't want it anyway.
psexec with the -i parameter is able to do what you want, but you have to specify which of the sessions (users) you want it to show up in.
I know this is old, but I came across it looking for the solution myself so I wanted to update it for future poor souls.
A native workaround for this problem is to use a scheduled task. That will use the active session
function Start-Process-Active
{
param
(
[System.Management.Automation.Runspaces.PSSession]$Session,
[string]$Executable,
[string]$Argument,
[string]$WorkingDirectory,
[string]$UserID
)
if (($Session -eq $null) -or ($Session.Availability -ne [System.Management.Automation.Runspaces.RunspaceAvailability]::Available))
{
$Session.Availability
throw [System.Exception] "Session is not availabile"
}
Invoke-Command -Session $Session -ArgumentList $Executable,$Argument,$WorkingDirectory,$UserID -ScriptBlock {
param($Executable, $Argument, $WorkingDirectory, $UserID)
$action = New-ScheduledTaskAction -Execute $Executable -Argument $Argument -WorkingDirectory $WorkingDirectory
$principal = New-ScheduledTaskPrincipal -userid $UserID
$task = New-ScheduledTask -Action $action -Principal $principal
$taskname = "_StartProcessActiveTask"
try
{
$registeredTask = Get-ScheduledTask $taskname -ErrorAction SilentlyContinue
}
catch
{
$registeredTask = $null
}
if ($registeredTask)
{
Unregister-ScheduledTask -InputObject $registeredTask -Confirm:$false
}
$registeredTask = Register-ScheduledTask $taskname -InputObject $task
Start-ScheduledTask -InputObject $registeredTask
Unregister-ScheduledTask -InputObject $registeredTask -Confirm:$false
}
}
When you use New-PSSession and then RDP into that same computer, you're actually using two separate and distinct user login sessions. Therefore, the Notepad.exe process you started in the PSSession isn't visible to your RDP session (except as another running process via Task Manager or get-process).
Once you've RDP'd into the server (after doing what you wrote in your post), start another Notepad instance from there. Then drop to PowerShell & run this: get-process -name notepad |select name,processid
Note that there are two instances, each in a different session.
Now open up Task Manager and look at the user sessions. Your RDP session will probably be listed as session 1.
Now quit Notepad and run get-process again. You'll see one instance, but for session 0. That's the one you created in your remote PSSession.
There are only 2 workarounds that I know of that can make this happen.
Create a task schedule as the logged in user, with no trigger and trigger it manually.
Create a service that starts the process with a duplicated token of the logged in user.
For the task schedule way I will say that new-scheduledtask is only available in Windows 8+. For windows 7 you need to connect to the Schedule Service to create the task like this (this example also starts the task at logon);
$sched = new-object -ComObject("Schedule.Service")
$sched.connect()
$schedpath = $sched.getFolder("\")
$domain = "myDomain"
$user="myuser"
$domuser= "${domain}\${user}"
$task = $sched.newTask(0) # 0 - reserved for future use
$task.RegistrationInfo.Description = "Start My Application"
$task.Settings.DisallowStartIfOnBatteries=$false
$task.Settings.ExecutionTimeLimit="PT0S" # there's no limit
$task.settings.priority=0 # highest
$task.Settings.IdleSettings.StopOnIdleEnd=$false
$task.settings.StopIfGoingOnBatteries=$false
$trigger=$task.Triggers.create(9) # 9 - at logon
$trigger.userid="$domuser" # at logon
$action=$task.actions.create(0) # 0 - execute a command
$action.path="C:\windows\system32\cmd.exe"
$action.arguments='/c "c:\program files\vendor\product\executable.exe"'
$action.WorkingDirectory="c:\program files\vendor\product\"
$task.principal.Id="Author"
$task.principal.UserId="$domuser"
$task.principal.LogonType=3 # 3 - run only when logged on
$task.principal.runlevel=1 # with elevated privs
# 6 - TASK_CREATE_OR_UPDATE
$schedpath.RegisterTaskDefinition("MyApplication",$viztask,6,$null,$null,$null)
Creating a service is way more complicated, so I'll only outline the calls needed to make it happen. The easy way is to use the invoke-asservice script on powershell gallery: https://www.powershellgallery.com/packages/InvokeAsSystem/1.0.0.0/Content/Invoke-AsService.ps1
Use WTSOpenServer and WTSEnumerateSessions to get the list of sessions on the machine. You also need to use WTSQuerySessionInformation on each session to get additional information like username. Remember to free your resources using WTSFreeMemory and WTSCloseServer You'll end up with some data which looks like this (this is from the qwinsta command);
SESSIONNAME USERNAME ID STATE
services 0 Disc
>rdp-tcp#2 mheath 1 Active
console 2 Conn
rdp-tcp 65536 Listen
Here's an SO post about getting this data; How do you retrieve a list of logged-in/connected users in .NET?
This is where you implement your logic to determine which session to target, do you want to display it on the Active desktop regardless of how it's being presented, over RDP or on the local console? And also what will you do if there is no one logged on? (I've setup auto logon and call a lock desktop command at logon so that a logged in user is available.)
You need to find the process id of a process that is running on the desktop as that user. You could go for explorer, but your machine might be Server Core, which explorer isn't running by default. Also not a good idea to target winlogon because it's running as system, or dwm as it's running as an unprivileged user.
The following commands need to run in a service as they require privileges that only system services have. Use OpenProcess to get the process handle, use OpenProcessToken to get the security token of the process, duplicate the token using DuplicateTokenEx then call ``CreateProcessAsUser``` and finally Close your handles.
The second half of this code is implemented in invoke-asservice powershell script.
You can also use the sysinternals tool psexec, I didn't list it as a 3rd way because it just automates the process of creating a service.

How to run Powershell ScheduledJob (-AtLogOn) in user's session?

Using powershell I'm creating a ScheduledJob to run on user logon (using New-JobTrigger -AtLogOn).
However I noticed that this job runs in session 0 and not in the logged-in user's session.
Also none of the JobTrigger and ScheduledJobOption settings allow me to run in user's session.
Any ideas on how to get this ScheduledJob to run in user's session?
$trigger = New-JobTrigger -AtLogOn
Register-ScheduledJob -Name 'my job' -Trigger $trigger -ScriptBlock { xxx }
You need to use -User parameter:
-AtLogOn
Starts the scheduled job when the specified users log on to the computer. To specify a user, use the User parameter.
https://technet.microsoft.com/en-us/library/hh849759.aspx
Turns out that the only way to do this is to use a ScheduledTask (instead of a ScheduledJob).
Edit: Marking this as an 'answer' since it seemed to resolve my issue. However if someone has a better solution, please feel free to post here.

Powershell Using Start-Process in PSSession to Open Notepad

I've created a pssession on a remote computer and entered that possession. From within that session I use start-process to start notepad. I can confirm that notepad is running with the get-process command, and also with taskmgr in the remote computer. However, the GUI side of the process isn't showing. This is the sequence I've been using:
$server = New-PSSession -ComputerName myserver -Credential mycreds
Enter-PSSession $server
[$server]: PS C:\>Start-Process notepad -Wait -WindowStyle Maximized
The process is running, but while RDP'd to the box, notepad does not open. If I open notepad from the server, a new notepad process begins. I also tried by using the verb parameter like this:
[$server]: PS C:\>Start-Process notepad -Wait -WindowStyle Maximized -Verb Open
Same result tho... Process starts, but no notepad shows. I've tried this while remoted into the box (but issued from my local host) as well as before remoting into the server.
That is because your powershell session on the remote machine does not go to any visible desktop, but to an invisible system desktop. The receiving end of your powershell remote session is a Windows service. The process is started, but nor you nor anyone else can ever see it.
And if you think about it, since multiple users could RDP to the same machine, there is really no reason to assume a remote powershell session would end up showing on any of the users desktops. Actually, in almost all cases you wouldn't want it anyway.
psexec with the -i parameter is able to do what you want, but you have to specify which of the sessions (users) you want it to show up in.
I know this is old, but I came across it looking for the solution myself so I wanted to update it for future poor souls.
A native workaround for this problem is to use a scheduled task. That will use the active session
function Start-Process-Active
{
param
(
[System.Management.Automation.Runspaces.PSSession]$Session,
[string]$Executable,
[string]$Argument,
[string]$WorkingDirectory,
[string]$UserID
)
if (($Session -eq $null) -or ($Session.Availability -ne [System.Management.Automation.Runspaces.RunspaceAvailability]::Available))
{
$Session.Availability
throw [System.Exception] "Session is not availabile"
}
Invoke-Command -Session $Session -ArgumentList $Executable,$Argument,$WorkingDirectory,$UserID -ScriptBlock {
param($Executable, $Argument, $WorkingDirectory, $UserID)
$action = New-ScheduledTaskAction -Execute $Executable -Argument $Argument -WorkingDirectory $WorkingDirectory
$principal = New-ScheduledTaskPrincipal -userid $UserID
$task = New-ScheduledTask -Action $action -Principal $principal
$taskname = "_StartProcessActiveTask"
try
{
$registeredTask = Get-ScheduledTask $taskname -ErrorAction SilentlyContinue
}
catch
{
$registeredTask = $null
}
if ($registeredTask)
{
Unregister-ScheduledTask -InputObject $registeredTask -Confirm:$false
}
$registeredTask = Register-ScheduledTask $taskname -InputObject $task
Start-ScheduledTask -InputObject $registeredTask
Unregister-ScheduledTask -InputObject $registeredTask -Confirm:$false
}
}
When you use New-PSSession and then RDP into that same computer, you're actually using two separate and distinct user login sessions. Therefore, the Notepad.exe process you started in the PSSession isn't visible to your RDP session (except as another running process via Task Manager or get-process).
Once you've RDP'd into the server (after doing what you wrote in your post), start another Notepad instance from there. Then drop to PowerShell & run this: get-process -name notepad |select name,processid
Note that there are two instances, each in a different session.
Now open up Task Manager and look at the user sessions. Your RDP session will probably be listed as session 1.
Now quit Notepad and run get-process again. You'll see one instance, but for session 0. That's the one you created in your remote PSSession.
There are only 2 workarounds that I know of that can make this happen.
Create a task schedule as the logged in user, with no trigger and trigger it manually.
Create a service that starts the process with a duplicated token of the logged in user.
For the task schedule way I will say that new-scheduledtask is only available in Windows 8+. For windows 7 you need to connect to the Schedule Service to create the task like this (this example also starts the task at logon);
$sched = new-object -ComObject("Schedule.Service")
$sched.connect()
$schedpath = $sched.getFolder("\")
$domain = "myDomain"
$user="myuser"
$domuser= "${domain}\${user}"
$task = $sched.newTask(0) # 0 - reserved for future use
$task.RegistrationInfo.Description = "Start My Application"
$task.Settings.DisallowStartIfOnBatteries=$false
$task.Settings.ExecutionTimeLimit="PT0S" # there's no limit
$task.settings.priority=0 # highest
$task.Settings.IdleSettings.StopOnIdleEnd=$false
$task.settings.StopIfGoingOnBatteries=$false
$trigger=$task.Triggers.create(9) # 9 - at logon
$trigger.userid="$domuser" # at logon
$action=$task.actions.create(0) # 0 - execute a command
$action.path="C:\windows\system32\cmd.exe"
$action.arguments='/c "c:\program files\vendor\product\executable.exe"'
$action.WorkingDirectory="c:\program files\vendor\product\"
$task.principal.Id="Author"
$task.principal.UserId="$domuser"
$task.principal.LogonType=3 # 3 - run only when logged on
$task.principal.runlevel=1 # with elevated privs
# 6 - TASK_CREATE_OR_UPDATE
$schedpath.RegisterTaskDefinition("MyApplication",$viztask,6,$null,$null,$null)
Creating a service is way more complicated, so I'll only outline the calls needed to make it happen. The easy way is to use the invoke-asservice script on powershell gallery: https://www.powershellgallery.com/packages/InvokeAsSystem/1.0.0.0/Content/Invoke-AsService.ps1
Use WTSOpenServer and WTSEnumerateSessions to get the list of sessions on the machine. You also need to use WTSQuerySessionInformation on each session to get additional information like username. Remember to free your resources using WTSFreeMemory and WTSCloseServer You'll end up with some data which looks like this (this is from the qwinsta command);
SESSIONNAME USERNAME ID STATE
services 0 Disc
>rdp-tcp#2 mheath 1 Active
console 2 Conn
rdp-tcp 65536 Listen
Here's an SO post about getting this data; How do you retrieve a list of logged-in/connected users in .NET?
This is where you implement your logic to determine which session to target, do you want to display it on the Active desktop regardless of how it's being presented, over RDP or on the local console? And also what will you do if there is no one logged on? (I've setup auto logon and call a lock desktop command at logon so that a logged in user is available.)
You need to find the process id of a process that is running on the desktop as that user. You could go for explorer, but your machine might be Server Core, which explorer isn't running by default. Also not a good idea to target winlogon because it's running as system, or dwm as it's running as an unprivileged user.
The following commands need to run in a service as they require privileges that only system services have. Use OpenProcess to get the process handle, use OpenProcessToken to get the security token of the process, duplicate the token using DuplicateTokenEx then call ``CreateProcessAsUser``` and finally Close your handles.
The second half of this code is implemented in invoke-asservice powershell script.
You can also use the sysinternals tool psexec, I didn't list it as a 3rd way because it just automates the process of creating a service.

Powershell V3 new ScheduleJob and ScheduleTask cmdlets

I'm currently learning all about the new cmd-lets in powershell version 3 and attempting to use some of them.
I'm in need to schedule different things and I'm trying to learn the new new-scheduledtask and new-scheduledjob cmdlets but I'm having a little difficulty.
I was wondering if anyone can help me discover what would be the simplest way to schedule a service restart (apache in this case), via a script? the script would schedule a restart when a certain condition is met ( basically when I need to restart to add new apache vhosts).
Any ideas? Currently I'm leaning towards creating a scriptblock that will do it and then registering a job and then a job trigger for the time of the day I want to restart which would be that night that the script schedules the restart.
Thanks!
<#So Here's an exmaple i thought to bring up for you it does a daily restart of BITS Service at 3am,i guess you can modify the script with your apache service example#>
<#Here Im Creating a Trigger for the Job and im setting it to start at 3am daily#>
$trigger = New-JobTrigger -Daily -At 3am
<# Below Im Registering the ScheduledJob with a Name of and a trigger of $trigger which is at daily 3am, Also im placing the script to be executed inside of a script block#>
Register-ScheduledJob -Name BitsServiceRestart -Trigger $trigger -ScriptBlock {
<# Suppose Lets say that you want to schedule a restart of BITS Service#>
Restart-Service -ServiceName BITS
}
<# Once the Job is registered you can get the Job details using Get-ScheduledJob
You can also view task details using TaskScheduler --> Windows --> PowerShell #>
Get-ScheduledJob BitsServiceRestart | fl *
<# You can recieve the output of Scheduled Job using Recieve Job #>
Receive-Job -Name BitsServiceRestart