Powershell | filter array with get-date - powershell

I have an array of staff data($employees) and in that array their is a field called start_date which is listed as a string. I am trying to filter out any staff member who's start_date is equal to the current month. Initially I thought I could had just wrote:
$employee | Where-Object {$_.start_date -ge Get-Date()}
Which didn't work.
I then tried to convert the Get-Date to a string with:
$date = Get-Date -f '01/MM/yyyy'
$dateString = $date.toString
$employee | Where-Object {$_.start_date -ge $dateString}
Which also did not give me the results I was after also.
My Colleague who is fairly business with his work states, I might be best writing a foreEach-Object but I have always struggled how to write this, and don't wish to pull him away from his work to assist me on this task.
Example Array:
preferred_name : John
name_suffixes :
gender : M
date_of_birth : 09/05/1596
surname : Doe
supervisor : PRNMA01
start_date : 28/11/2020
school_email : j.doe#email.com
teacher_code :
termination_date :
employee_photo : #{file_info=}
supervisor_2 :
title : Mr
driver_licence_no :
supplier_code :
given_names : John Jane
Task:
Get the list of new staff and email the list to the department who looks after new staff training.
Any help on this would be good. Please ensure you explain in detail as i am not very bright in this field.
Thanks in advance.

You can convert the start_date into datetime using parseexact and then compare the datetime objects directly.
$employee | Where-Object {[datetime]::parseexact($_.start_date, 'dd/MM/yyyy', $null) -ge (Get-Date)}

Related

Powershell - creating hashtables from large text files and searching

I'm working with a hashtable which I've built using a list of 3.5 million IP addresses stored in CSV format, and I am trying to search through this table using wildcards.
The CSV is MaxMind's list of IPs, which I convert to Hashtable using the following code
[System.IO.File]::ReadLines("C:\temp\iptest.csv") | ForEach-Object { $data= $_.split(','); $ht = #{"geoname_id"="$($data[1])";"registered_country_geoname_id"="$($data[2])"}
$name = $($data[0])
$mainIPHhash.add($name, $ht)}
The code just pulls out the CIDR and it's corresponding City/Country code.
This works well, and builds the table in a little over two minutes, but the issue I am now facing is searching this hashtable for wild card entries.
If I search for a complete CIDR, the search happens in milliseconds
$mainIPHhash.item("1.0.0.0/24")
Measure command reports - TotalSeconds : 0.0001542
But if I need to do a wildcard search, it has to loop through the hashtable looking for my like values, which takes a long time!
$testingIP = "1.0.*"
$mainIPHhash.GetEnumerator() | Where-Object { $_.key -like $testingIP }
Measure command reports - TotalSeconds : 33.3016279
Is there a better way for searching wildcard entries in Hashtables?
Cheers
Edit:
Using a regex search, I can get it down to 19 seconds. But still woefully slow
$findsStr = "^$(($testingIP2).split('.')[0])" +"\."+ "$(($testingIP2).split('.')[1])" +"\."
$mainIPHhash.GetEnumerator() | foreach {if($_.Key -match $findsStr){#Dostuff }}
The above takes the first two octets of the IP address, and uses regex to find them in the hashtable.
Days : 0
Hours : 0
Minutes : 0
Seconds : 19
Milliseconds : 733
Ticks : 197339339
TotalDays : 0.000228402012731481
TotalHours : 0.00548164830555556
TotalMinutes : 0.328898898333333
TotalSeconds : 19.7339339
TotalMilliseconds : 19733.9339
You can take the list of IPs and do either -like or -match for a list. Either should be faster than a Where-Object clause
$mainIPhash.Values -like '1.0.*'
$mainIPhash.Values -match '^1\.0\.'
Other solution may be, use group-object :
$contentcsv=import-csv "C:\temp\iptest.csv" -Header Name, geoname_id, registered_country_geoname_id |Group Name
$contentcsv | where Name -like '1.0.*'

Removing samaccountname from group after N time - powershell

I am looking to find a way to remove a user from a group after a specific amount of time.
Via the below link I found that you can find users that were added with 10 days or more:
https://gallery.technet.microsoft.com/scriptcenter/Find-the-time-a-user-was-a0bfc0cf#content
As an output I get the example below:
ModifiedCount : 2
DomainController : DC3
LastModified : 5/4/2013 6:48:06 PM
Username : joesmith
State : ABSENT
Group : CN=Domain Admins,CN=Users,DC=Domain,DC=Com
I would like to return SamAccountName instead of Username.
I was trying to look at code and I know this is something to do with the variable $pattern But I am not that good in powershell to know at first sight.
Looking at that code, the Username property IS the SamAccountName.
However, if you want to change that label, you can either simply change it on line 106 from
Username = [regex]::Matches($rep.context.postcontext,"CN=(?<Username>.*?),.*") | ForEach {$_.Groups['Username'].Value}
into:
SamAccountName = [regex]::Matches($rep.context.postcontext,"CN=(?<Username>.*?),.*") | ForEach {$_.Groups['Username'].Value}
Or change the label in the objects returned afterwards with a calculated property:
$returnedObjects | Select-Object #{Name = 'SamAccountName'; Expression = {$_.Username}}, * -ExcludeProperty Username

Analyzing a CSV in Powershell

I am very new to Powershell(about 1 day to be more precise) and I am having what I assume are some syntax issues with some variables. I have a CSV file spreadsheet(converted from an Excel xlsx) with around 21 columns and 74,000 rows. The four columns of interest to me are columns having to do with an employees start date, a termination date, a department name, and a vice president they report to. I am trying to write a script that will return all employees whom have reached their start date, have not been terminated, work in a department that contains 'HR' in the name, and report to a specific VP. I will elaborate on my specific issues after the block of code.
$Lawson = Import-Csv .\Documents\Lawson_HR.csv
PS C:\Users\louiez> $startDate = $Lawson | where {$_.'LAW HIRE DATE' -le (Get-Date -format M-DD-YYYY)}
PS C:\Users\louiez> $endDate = $startDate | where {$_.'LAW TERM DATE' -eq ''}
PS C:\Users\louiez> $HR = $endDate | where {$_.'LAW DEPT NAME' -contains 'HR'}
PS C:\Users\louiez> $VP = $endDate | where {$_.'VICE PRESIDENT' -contains 'Croner'}
PS C:\Users\louiez> $startdate | Measure-Object
Count : 51641
Average :
Sum :
Maximum :
Minimum :
Property :
PS C:\Users\louiez> $enddate | Measure-Object
Count : 19428
Average :
Sum :
Maximum :
Minimum :
Property :
PS C:\Users\louiez> $HR | Measure-Object
Count : 0
Average :
Sum :
Maximum :
Minimum :
Property :
First, the startDate variable does not count the correct amount of items. I would like for it to count all rows in which the employee hire date is before today's date. the code in its current for returns about 51k items, it should be around 73k. (The endDate variable functions as it should.)
Second the HR variable returns 0 items, it should be several hundred. I would like for it to search the Dept Name field in each row for any instance on the letters 'HR'. Similarly I would like for the VP variable to return all items in which the Vice President column has a given name(in this case, Croner).
As I said, I am incredibly new to Powershell and have some very limited programming experience so I am not sure what in the syntax is causing these errors.
There are a couple of flaws in your design, the easy one:
$_.'LAW DEPT NAME' -contains 'HR'
$_.'VICE PRESIDENT' -contains 'Croner'
-contains is un-intuitive, it does not match text content, it matches items in a container of multiple items. Help about_Comparison_Operators for details. Use -match instead.
$_.'LAW DEPT NAME' -match 'HR'
$_.'VICE PRESIDENT' -match 'Croner'
The second is more complex:
$_.'LAW HIRE DATE' -le (Get-Date -format M-DD-YYYY)
$_.'LAW HIRE DATE' is probably going to return you a string of text, get-date with the -format parameter will return you a string of text, and -le will do alphabetical order sorting (with adjustments), which will be completely unreliable, saying the 1st Feb comes before 2nd Jan because it starts with 1.
Alphabetical order sorting is more workable on a date format like yyyy-MM-dd, but as wOxxOm comments, the right way is to process the date in the CSV into
a [datetime] object and then compare that with the current date as a [datetime] object. This will work more reliably for comparisons (give or take timezone and daylight savings time considerations).
[datetime]::ParseExact($_.'LAW HIRE DATE', 'dd-MM-yyyy', $null) -le (Get-Date)
Assuming that the LAW HIRE DATE is always and exactly in the format dd-MM-yyyy, otherwise this will fall over and you'll have to adjust to fit your data - or adjust your spreadsheet to fit your code.

PowerShell ForEach removes leading zeros

I am kind of new with PowerShell and programming in general, so I hope you have some patience while reading this. Before I explain my problem, I feel like I have to first tell you some background information:
I have all my transactions saved in $Transactions. Each transaction has Receiver, Date and Amount.
I have grouped the yearly transactions into $TransactionsPerYear the following way:
$TransactionsPerYear = $Transactions | Group-Object { [int]($_.date -replace '.*\.') }
(Btw. Could someone explain the regex in the end for me, what each character does?)
Next thing I am doing is grouping yearly income and expenses into separate variables. After this I am trying to extract the months from each year and save them into $Months. The date is in the following format dd.MM.yyyy
Question 1:
Here's how I can get all the dates, but how do I extract just the months?
$TransactionsPerYear | Select -ExpandProperty Group | Select -ExpandProperty date | Select -Unique
Question 2:
Because I don't know how to extract the months, I've tried it the following way:
[String[]]$Months = "01","02","03","04","05","06","07","08","09","10","11","12"
When I have each month in $Months I am trying to get monthly transactions and save them into new variables:
ForEach($Month in $Months){
New-Variable -Name "Transactions_$Month$Year" -Value ($Transactions | Where {$_.Date -like "*.$Month.$Year"} | Group-Object 'Receiver' | Select-Object Count, Name, #{L="Total";E={$_ | Select -ExpandProperty Group | Measure-Object Amount -Sum | Select -ExpandProperty Sum}} | Sort-Object {[double]$_.Total})
}
The problem that I am facing here is that ForEach removes the leading zero from each month, and when this happens, this part in ForEach doesn't match with anything, and the new variable is null:
Where {$_.Date -like "*.$Month.$Year"}
Let me know if you need more info. I'd be really thankful if anyone could help me.
The date looks like: 25.02.2016
From your post, it looks like you've jumped further down the rabbithole than necessary.
Instead of trying to do string manipulation every time you need to interact with the Date property, simply turn it into a DateTime object!
$Transactions = $Transactions |Select-Object *,#{Name='DateParsed';Expression={[datetime]::ParseExact($_.Date, 'dd.MM.yyyy', $null)}}
The DateTime.ParseExact() method allows us to specify the format (eg. dd.MM.yyyy), and parse a string representation of a date.
Now you can group on year simply by:
$TransactionsPerYear = $Transactions |Group-Object { $_.DateParsed.Year }
To group by both Year and then Month, I'd create a nested hashtable, like so:
# Create a hashtable, containing one key per year
$MonthlyTransactions = #{}
foreach($Year in $Transactions |Group {$_.DateParsed.Year})
{
# Create another hashtable, containing a key for each month in that year
$MonthlyTransactions[$Year.Name] = #{}
foreach($Month in $Year.Group |Group {$_.DateParsed.Month})
{
# Add the transactions to the Monthly hashtable
$MonthlyTransactions[$Year.Name][$Month.Name] = $Month.Group
}
}
Now you can calculate the transaction value for a specific month by doing:
$TotalValueMay2010 = ($MonthlyTransactions[2010][5] |Measure-Object Amount -Sum).Sum
(Btw. Could someone explain the regex in the end for me, what each character does?)
Sure:
. # match any character
* # zero of more times
\. # match a literal . (dot)
Taking your own example input string 25.02.2016, the first group (.*) will match on 25.02, and \. will match on the . right after, so the only thing left is 2016.
Do you mean this?
$dates = ([DateTime] "1/1/2016"),([DateTime] "1/2/2016"),
([DateTime] "2/1/2016"),([DateTime] "3/1/2016")
$uniqueMonths = $dates | ForEach-Object { $_.Month } | Sort-Object -Unique
# $uniqueMonths contains 1,2,3

PowerShell: ConvertFrom-Json to export multiple objects to csv

As you probably understand from the title, I'm new to PowerShell, having a hard time even trying to describe my question. So please forgive my terminology.
Scenario
I am using PowerShell to query the audit log of Office 365. The cmdlet Search-UnifiedAuditLog returns "multiple sets of objects"(?), one of which has an array of other objects(?). The output is JSON if I got this right.
Here is an example of what is returned (I will call it one "Set of Objects"):
RunspaceId : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
RecordType : AzureActiveDirectoryStsLogon
CreationDate : 21/02/2017 12:05:23
UserIds : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Operations : UserLoggedIn
AuditData : {"CreationTime":"2017-02-21T12:05:23","Id":"{"ID":"00000000000000000","Type":3}],"ActorContextId":"xxxxxxxxxxxxxxxxxxxxxxxxx","ActorIpAddress":"xxxxxxxxxxxxx","InterSystemsId":"xxxxxxxxxxxxxxxxx","IntraSystemId":"000000000000-000000-000","Target":[{"ID":"00-0000-0000-c000-000000000000","Type":0}],"TargetContextId":"xxxxxxxxxxxxxxxxxx","ApplicationId":"xxxxxxxxxxxxxxxxxxxxxxxxxx"}
ResultIndex : 1
ResultCount : 16
Identity : xxxxxxxxxxxxxxxxxxxxxxxxxxx
IsValid : True
ObjectState : Unchanged
Now, I want some of the content of the AuditData line exported to a csv (normally containing much more data than copied here). This works fine for one "set of objects" (like the one above). To do this I use:
$LogOutput = Search-UnifiedAuditLog -StartDate 2/20/2017 -EndDate 2/23/2017 -ResultSize 1
$ConvertedOutput = ConvertFrom-Json -InputObject $LogOutput.AuditData
$ConvertedOutput | Select-Object CreationTime,UserId,ClientIP | Export-Csv -Path "C:\users\some.user\desktop\users.csv
ResultSize returns 1 instead of multiple "sets of objects". The ConvertFrom-Json does not work if I remove ResultSize.
So the question is:
Can I loop through all the "set of objects" and convert from json and have this exported line-by-line on a csv? Resulting in something like this:
UserId,Activity,UserIP
this#user.com, loggedIn, 10.10.10.10
that#user.org, accessedFile, 11.11.11.11
A pedagogic answer would be very, very much appreciated. Many thanks!
Instead of -ResultSize, try using Search-UnifiedAuditLog <args> | Select-Object -ExpandProperty AuditData | ConvertFrom-Json
This will make only the AuditData property get forwarded into ConvertFrom-Json and ignore the rest of the object from Search-UnifiedAuditLog