Create monthly trigger for Scheduled Task in Powershell (With additional criteria) - windows

I'm currently working on a script that when run, creates some Scheduled tasks that makes the host machine do several things and then restart within a specified time span.
This script needs to be run on multiple domain controllers, and therefor i would like to "load balance" by using something like New-ScheduledTaskTrigger -RandomDelay in order for them to not reboot all at once, but kind of spread it out.
The goal is to be able to change some variables of when to restart, things like:
First Monday of the month between 18:00 and 23:59
Every Thursday between 01:00 and 06:00
Every day between 04:00 and ..... you see where I'm going
However there is no such thing as a "-Monthly" in New-ScheduledTaskTrigger
That's the first problem, this one i can probably solve with the help from other posts, but if i do it for example like this I'm not able to use the -RandomDelay which I think is a major feature for this to work.
Here is how I imagine it should look if the -Monthly did work (for a monthly trigger):
$rebootFrequency = MONTHLY # DAILY, WEEKLY, MONTHLY
$rebootWeek = FIRST # FIRST, SECOND, THIRD, FOURTH, LAST
$rebootDayOfWeek = MON # MON, TUE, WED, THU, FRI, SAT, SUN
$rebootTimeFrom = 10:00 # HH:MM[:SS]
$rebootTimeTo = 16:00 # HH:MM[:SS]
New-ScheduledTaskTrigger -"$rebootFrequency" -WeekOfMonth $rebootWeek;
-DayOfWeek $rebootDayOfWeek -At $rebootTimeFrom -RandomDelay $rebootTimeTo
Do you have any suggestions as to how I should solve this problem?
I could do the same thing with schtask.exe, however I would end up having to make some kind of script to do the "RandomDelay" function.
Feel free to ask further if you have any questions.
Thanks in advance.
Challenge 1
I've now got it to work, but I'm trying to make the script a bit more intuitive, but I can't figure out how i would do it...
What i want to do is to "convert" from using the numbers in days (for example: 16 for Thursday) to being able to write "THU" instead.
Right now it looks something like this:
$rebootDaysOfWeek = "16" # SUN=1, MON=2, TUE=4, WED=8, THU=16 etc.
$trigger.DaysOfWeek = $rebootDaysOfWeek
But I would find it alot cooler if it was something like this:
$rebootDaysOfWeek = "THU" # SUN, MON, TUE, WED, THU, FRI, SAT
$trigger.DaysOfWeek = $rebootDaysOfWeek
But I can't seem to find a way to "convert" $rebootDaysOfWeek to work with the bit mask.

Check out the Microsoft Docs:
https://learn.microsoft.com/en-us/windows/win32/taskschd/time-trigger-example--scripting-
The sample is in VB, but it looks like it's just a ComObject. I haven't had enough time to play around, but you can start like this:
$service = new-object -comobject Schedule.Service
$service.connect()
$taskdefinitiion = $service.NewTask(0)
There's lots of task definition stuff, but it get's down to the triggers and you'll do this:
$triggers = $taskDefinition.Triggers
$trigger = triggers.Create(5) # I had to try different numbers here, didn't dig through the docs
$trigger.DaysOfWeek = 16 #Thursday
$trigger.WeeksOfMonth = 1 # First week, 2 for second, 6 for third, 8 for forth
$trigger.MonthsOfYear = 4095 # all months
$trigger.RandomDelay = 'PT1H' # 1 hour random delay.
I'll let you take it from here. Links to some of the items above:
https://learn.microsoft.com/en-us/windows/win32/taskschd/monthlydowtrigger-daysofweek
https://learn.microsoft.com/en-us/windows/win32/taskschd/monthlydowtrigger-monthsofyear
https://learn.microsoft.com/en-us/windows/win32/taskschd/monthlydowtrigger-weeksofmonth
https://learn.microsoft.com/en-us/windows/win32/taskschd/monthlydowtrigger-randomdelay
UPDATE FOR CHALLENGE 1
In order to use "friendly" references to the bitwise decimal value you can either create a constants section or use hashtable, either way you are going to have to do the conversion yourself:
# Constants
$SUN = 1
$MON = 2
$TUE = 4
$WED = 8
$THU = 16
$FRI = 32
$SAT = 64
# Hashtable - because why not!
$DaysOfWeek = #{
SUN = 1
MON = 2
TUE = 4
WED = 8
THU = 16
FRI = 32
SAT = 64
}
Then you can use:
$trigger.DaysOfWeek = $THU
or
$trigger.DaysOfWeek = $DaysOfWeek["THU"]

Related

Procmail filtering by Date: field

I need to move away mails older than given time - let it be 24h = 86400s. I use old good procmail for multiple other purposes on that machine, so I wanted to use is as well for this purpose. It also behaves well under the load (~1 000 000 small automated messages per day).
It took me a while to get to this ugly solution (excerpt from bigger procmailrc file):
Grab Date: field using formail
Grab current date in UNIX format (seconds)
bash convert the mail date to unix format
compare values using bash
return result to procmail using exit code.
Together:
MAILDATE_RFC=`formail -zxDate:`
DATE_UNIX=`date "+%s"`
:0
* ? MAILDATE_UNIX=`date -d "$MAILDATE_RFC" "+%s"` ; if ( (( ($DATE_UNIX-$MAILDATE_UNIX) > 86400)) ) then exit 0; else exit 1; fi
! account_for_outdated_mails
In this case I need to use the "Date:" field, as this contains the local time at which the mail was generated (it can take multiple days to get to my machine). We are 100% sure that "Date:" field exists and contains RFC-style date (those are automated messages in separated mail network).
My solution looks pretty ugly:
Getting the comparison result from bash using exit codes looks pretty bad. Might be inefficient as well.
I would like to calculate the MAILDATE_RFC still in procmail but it seems I cannot use any variable as the argument to generate another variable:
MAILDATE_UNIX=`date -d "$MAILDATE_RFC" "+%s"`
does not work.
The only optimization I am aware of would be to push the whole process of getting MAILDATE_RFC, MAILDATE_UNIX and DATE_UNIX processed in bash script and doing it in one bash session instead of 3.
My question: Is there a better way to do it? Maybe more efficient?
What you say doesn't work actually does. Here's a quick demo.
testing.rc:
DEFAULT=/dev/null
SHELL=/bin/sh
VERBOSE=yes
MAILDATE_RFC=`formail -zxDate:`
MAILDATE_UNIX=`date -d "$MAILDATE_RFC" "+%s"`
NOW=`date +%s`
:0
* 86400^0 ^
* $ -$NOW^0 ^
* $ $MAILDATE_UNIX^0 ^
{ LOG="score: $=
" }
Test run, in a fresh Ubuntu 20.04 Docker image:
tripleee#bash$ procmail -m testing.rc <<\:
> Subject: demo message
> Date: Fri, 10 Jun 2022 06:20:36 +0000
>
> Try me
> :
procmail: [263] Fri Jun 10 06:21:23 2022
procmail: Executing "formail,-zxDate:"
procmail: [263] Fri Jun 10 06:21:23 2022
procmail: Assigning "MAILDATE_RFC=Fri, 10 Jun 2022 06:20:36 +0000"
procmail: Executing "date,-d,Fri, 10 Jun 2022 06:20:36 +0000,+%s"
procmail: Assigning "MAILDATE_UNIX=1654842036"
procmail: Executing "date,+%s"
procmail: Assigning "NOW=1654842083"
procmail: Score: 86400 86400 "^"
procmail: Score: -1654842083 -1654755683 "^"
procmail: Score: 1654842036 86353 "^"
procmail: Assigning "LOG=score: 86353
"
score: 86353
procmail: Assigning "LASTFOLDER=/dev/null"
procmail: Opening "/dev/null"
Folder: /dev/null 68
This also demonstrates how to use scoring to do the calculation. It's perhaps somewhat intimidating, but saves an external process, and so should be more efficient than doing the calculation in Bash.
In some more detail, 123^0 regex says to add 123 to the score just once if the message matches the regex regex (in the recipe above, we use the regex ^ which of course always matches; every message contains a beginning. You could change the 0 to e.g. 1 to say to add for every match, or etc - see the procmailsc man page for proper documentation). The $ modifier says to expand any variables in the recipe itself.
If you are not using GNU date, you don't have date -d; in that case, probably refer to your platform's man page for how to calculate a date stamp for an arbitrary date. How to convert date string to epoch timestamp with the OS X BSD `date` command? has a discussion for MacOS, which should also work for any other *BSD platform.
If you really wanted to make this more efficient, and can be sure that the Date: header really always uses the RFC-mandated format, you could even parse the date in Procmail. Something like
:0
* ^Date: [A-Z][a-z][a-z], \/[ 0-9][0-9] [A-Z][a-z][a-z] [0-9][0-9][0-9][0-9]
{
date=$MATCH
:0
* date ?? ^\/[ 0-9][0-9]
{ dd=$MATCH }
:0
* date ?? ^[ 0-9][0-9] \/[A-Z][a-z][a-z]
{ mon=$MATCH }
* date ?? [A-Z][a-z][a-z] \/[0-9][0-9][0-9][0-9]
{ yyyy=$MATCH }
:0
* mon ?? 1^0 ^Jan
* mon ?? 2^0 ^Feb
* mon ?? 3^0 ^Mar
* mon ?? 4^0 ^Apr
* mon ?? 5^0 ^May
* mon ?? 6^0 ^Jun
* mon ?? 7^0 ^Jul
* mon ?? 8^0 ^Aug
* mon ?? 9^0 ^Sep
* mon ?? 10^0 ^Oct
* mon ?? 11^0 ^Nov
* mon ?? 12^0 ^Dec
{ mm=$= }
}
The \/ token in a regex says to save the matched text after it into the special variable MATCH. We then copy that variable to date and perform additional matching to extract its parts.
Performing the necessary arithmetic to convert this into seconds since January 1, 1970 should be doable at this point, I hope. If you need complete per-day accuracy, you would also need to extract the time and the time zone and adjust to the correct day if it's not in your preferred time zone, or perhaps UTC (that would be +0000 at the very end); but this is just a sketch, anyway, because I think I have a better idea altogether.
Namely, save the messages to the correct folder as they arrive, then just forward or discard or archive older folders when you no longer need them.
MAILDATE_RFC=`formail -czxDate:`
MAILDATE=`date -d "$MAILDATE_RFC" +%F`
:0:
inbox-$MAILDATE
This will save to an mbox file named like inbox-2022-06-10 based on the extracted Date: header. (Again, you could avoid the external processes if you really wanted to squeeze out the last bit of performance, using the date parsing sketch above. And again, if you can't have a message from a different time zone land in the previous or next day's folder, you need to recalculate the date for your time zone.)

console output of the current calendar month in Ruby

I need to output to the console the calendar of the current month in Ruby. The result should be similar to ncal on UNIX-like systems. I found a solution for C ++ but can't adapt for Ruby. So far, I only realized that I need to use nested loops to output the height and width. Tell me in which direction to move?
require 'date'
days = %w[Mun Tue Wed Thu Fri Sat Sun]
puts " #{Date::MONTHNAMES[Date.today.month]} #{Date.today.year}"
i = 0
start_month = (Date.today - Date.today.mday + 1).strftime("%a")
while i < days.size
print days[i]
j = 1
while j <= 31
if days[i] == start_month
print " #{j}"
end
j += 7
end
i += 1
puts
end
I'll take your solution so far, and try to give some specific pointers for how to progress with it - but of course, there are many different ways to approach this problem in general, so this is by no means the only approach!
The first critical issue (as you're aware!) is that you're only printing things for the row starting on the 1st of the month, due to this line:
if days[i] == start_month
Sticking with the current overall design, we know we'll need to print something for every line, so clearly a conditional like this isn't going to work. Let's try removing it.
Firstly, it will be more convenient to know which day of the week the month started on as a number, not a string, so we can easily calculate offsets against another day. Let's do that with:
# e.g. for 1st July 2021 this was a Thursday, so we get `4`.
start_of_month_weekday = (Date.today - Date.today.mday + 1).cwday
Next (and this is the crucial step!), we can use the above information to find out "which day of the month is it, on this day of the week?"
Here a first version of that calculation, incorporated into your solution so far:
require 'date'
days = %w[Mon Tue Wed Thu Fri Sat Sun]
puts " #{Date::MONTHNAMES[Date.today.month]} #{Date.today.year}"
i = 0
# e.g. for 1st July 2021 this was a Thursday, so we get `4`.
start_of_month_weekday = (Date.today - Date.today.mday + 1).cwday
while i < days.size
print days[i]
day_of_month = i - start_of_month_weekday + 2 # !!!
while day_of_month <= 31
print " #{day_of_month}"
day_of_month += 7
end
i += 1
puts
end
This outputs:
July 2021
Mon -2 5 12 19 26
Tue -1 6 13 20 27
Wed 0 7 14 21 28
Thu 1 8 15 22 29
Fri 2 9 16 23 30
Sat 3 10 17 24 31
Sun 4 11 18 25
Not bad! Now we're getting somewhere!
I'll leave you to figure out the rest 😉 .... But here are some clues, for what I'd tackle next:
This code, print " #{day_of_month}", needs to print a "blank space" if the day number is less than 1. This could be done with a simple if statement.
Similarly, since you want this calendar to line up neatly in a grid, you need this code to always print a something two characters wide. sprintf is your friend here! Check out the "Examples of width", about halfway down the page.
You've hardcoded 31 for the number of days in the month. This should be fixed, of course. (Use the Date library!)
It's funny how you used strftime("%a") in one place, yet constructed the calendar title awkwardly in the line above! 😄 Take a look at the documentation for formatting dates; it's extremely flexible. I think you can use: Date.today.strftime("%B %Y").
If you'd like to add some colour (or background colour?) to the current day of the month, consider doing something like this, or use a library to assist.
Using while loops works OK, but is quite un-rubyish. In 99% of cases, ruby has even better tools for the job; it's a very expressive language - iterators are king! (I'm guessing you first learned another language, before ruby? Seeing while loops, and/or for loops, is a dead giveaway that you're more familiar with a different language.) Instead of the outer while loop (while i < days.size), you could use days.each_with_index. And instead of the inner while loop (while j < 31), you could use day_of_month.step(31, 7) (how cool is that!!).
This is one way:
Construct a one-dimensional array, beginning with the daynames (Mon Tue ...).
Figure out a way to determine with how many "blanks" the month starts (these are days from the previous month. wday might help). Attach that amount of empty strings to the array.
Determine how many days the month has (hint Date.new(2021,7,-1), and attach all these daynumbers to the array.
Attach empty strings to the array until the size of the array is divisible by 7 (or better, calculate). Skip this if you're skipping the last bullet.
Convert all elements of this array to right-adjusted strings of size 3 or some-such.
Use each_slice(7) to slice the array into weeks.
If desired, transpose this array of week-slices to mimic the ncal output.
Thank you for your help, literally 10 hours and I figured it out thanks to you. I apologize once again for the initially incorrectly posed question.
With the help of hints, I assembled such a solution.
require 'date'
days = %w[Mon Tue Wed Thu Fri Sat Sun]
p days
blanks = Date.new(2021,7,1).wday - 1
blanks.times do
days.push(' ')
end
days_in_month = Date.new(2021, 7, -1).day
days_in_month
day = 1
while day <= days_in_month
days.push(day)
day += 1
end
unless (days.size % 7) == 0
days.push(' ')
end
days.join(', ')
new_arr = days.each_slice(7).to_a
puts"Массив дней: #{new_arr}"
for i in 0...7
for j in 0...new_arr.size
print " #{new_arr[j][i]}"
end
puts
end
require 'date'
# init
DAYS_ORDER = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
today = Date.today
month = today.month
year = today.year
first_day = Date.new(year, month, 1)
last_day = Date.new(year, month, -1)
hash_days = {}
# get all current months days and add to hash_days
first_day.upto(last_day) { |day| hash_days[day.day] = day.strftime('%a') }
# group by wday
grouped_hash = hash_days.group_by { |day| day.pop }.transform_values { |days| days.flatten }
# sort by wday from DAYS_ORDER
sorted_arr = grouped_hash.sort_by { |k, v| DAYS_ORDER.index(k) }
# rendering current month's calendar with mark current day
## title
print "\x1b[4m#{today.strftime("%B %Y")}\x1b[0m\n"
## calendar
indent = true
sorted_arr.each do |wday, days|
print wday
if days[0] != 1 && indent == true
print " "
else
indent = false
end
days.each do |value|
spaces = " " * (value > 9 ? 1 : 2)
str_day = spaces + value.to_s
current_day = "\x1b[1;31m#{str_day}\x1b[0m"
print value == today.day ? current_day : str_day
end
puts
end
view

Graylog , Handle files before store it

I am trying to create log system using Gray log,
I have logs like this
Mon Aug 17 16:53:10 2020
NAS-IP-Address = 172.20.101.1
User-Name = "netconf"
Acct-Status-Type = Start
Acct-Session-Id = "ACCTID20200817112721000fe14d000000001040717"
Acct-Authentic = RADIUS
NAS-Identifier = "NR-DELHI-NRTCC-CR-01"
Framed-IP-Address = 172.20.16.6
NAS-Port-Type = Virtual
Event-Timestamp = "Aug 17 2020 16:57:21 IST"
Tmp-String-9 = "ai:"
Acct-Unique-Session-Id = "23be63c276bbda95385d118ff93ba298"
Timestamp = 1597663390
Mon Aug 17 16:54:15 2020
NAS-IP-Address = 172.20.101.1
User-Name = "netconf"
Acct-Status-Type = Start
Acct-Session-Id = "ACCTID20200817112825000fe14f000000001040719"
Acct-Authentic = RADIUS
NAS-Identifier = "NR-DELHI-NRTCC-CR-01"
Framed-IP-Address = 172.20.16.6
NAS-Port-Type = Virtual
Event-Timestamp = "Aug 17 2020 16:58:25 IST"
Tmp-String-9 = "ai:"
Acct-Unique-Session-Id = "49fcad388a523e7eebea6043529c323e"
Timestamp = 1597663455
actually, every block is one log record, but the problem is that Graylog considering every line as a separated log, so what I want is to tell Graylog to read every block as one record.
is there any way to do that, some configuration that I have to do in Gray log to achieve my goal.
I have an idea but I am not sure about it, I am thinking about creating a bash script which will read the file and merge lines which belong to one block in one line,
but I am not sure if this method will work or not, so I am wondering if there is any way to do it from Graylog itself.
any suggestion will be appreciate
Best Regards
if messages in the log file begin with a blank line, then you could use the readmode parameter of the imfile module: https://www.rsyslog.com/doc/v8-stable/configuration/modules/imfile.html#readmode

How to examine if a the time of a DateTime is between 9 PM and 17 PM (Ruby)?

I have this datetime:
time = 2013-08-21 11:54:49 +0200
I also have two integers that represents a time interval. For example if I wish it to be between 9 AM and 5 PM I will use:
start = 9
finish = 17
I now wish to examine if the datetime is within the interval of the two integers. In this example the result would be true, because the event at 11:54 is between 9 and 17...
How to do this?
If you don't care about timezones the code could look something like:
(start..finish).include? DateTime.now.strftime('%H').to_i
In your question the assignment is not correct. This would work:
require 'date'
event = DateTime.parse('2013-08-21 11:54:49 +0200')
If you have a DateTime instance you can simply use #hour:
event.hour # => 11
You can compare this integer with any other:
event.hour.between?(start, finish) # => true

Get last day of the month in Ruby

I made new object Date.new with args (year, month). After create ruby added 01 number of day to this object by default. Is there any way to add not first day, but last day of month that i passed as arg(e.g. 28 if it will be 02month or 31 if it will be 01month) ?
use Date.civil
With Date.civil(y, m, d) or its alias .new(y, m, d), you can create a new Date object. The values for day (d) and month (m) can be negative in which case they count backwards from the end of the year and the end of the month respectively.
=> Date.civil(2010, 02, -1)
=> Sun, 28 Feb 2010
>> Date.civil(2010, -1, -5)
=> Mon, 27 Dec 2010
To get the end of the month you can also use ActiveSupport's helper end_of_month.
# Require extensions explicitly if you are not in a Rails environment
require 'active_support/core_ext'
p Time.now.utc.end_of_month # => 2013-01-31 23:59:59 UTC
p Date.today.end_of_month # => Thu, 31 Jan 2013
You can find out more on end_of_month in the Rails API Docs.
So I was searching in Google for the same thing here...
I wasn't happy with above so my solution after reading documentation
in RUBY-DOC was:
Example to get 10/31/2014
Date.new(2014,10,1).next_month.prev_day
require "date"
def find_last_day_of_month(_date)
if(_date.instance_of? String)
#end_of_the_month = Date.parse(_date.next_month.strftime("%Y-%m-01")) - 1
else if(_date.instance_of? Date)
#end_of_the_month = _date.next_month.strftime("%Y-%m-01") - 1
end
return #end_of_the_month
end
find_last_day_of_month("2018-01-01")
This is another way to find
You can do something like that:
def last_day_of_month?
(Time.zone.now.month + 1.day) > Time.zone.now.month
end
Time.zone.now.day if last_day-of_month?
This is my Time based solution. I have a personal preference to it compared to Date although the Date solutions proposed above read somehow better.
reference_time ||= Time.now
return (Time.new(reference_time.year, (reference_time.month % 12) + 1) - 1).day
btw for December you can see that year is not flipped. But this is irrelevant for the question because december always has 31 day. And for February year does not need flipping. So if you have another use case that needs year to be correct, then make sure to also change year.
Here is taking the first and third answers to find the last day of the previous month.
today_c = Date.civil(Date.today.prev_month.year, -1, -1)
p today_c

Resources