Comparing dates of a CSV column in PowerShell - powershell

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
$startDate = $Lawson | where [datetime]::ParseExact($_.'LAW HIRE DATE', 'dd-MM-yyyy', $null) -le (Get-Date)
$endDate = $startDate | where {$_.'LAW TERM DATE' -eq ''}
$HR = $endDate | where {$_.'LAW DEPT NAME' -match 'HR'}
$VP = $endDate | where {$_.'VICE PRESIDENT' -match 'Croner'}
First, the $startDate variable does not work, I am unsure of the syntax needed to compare a given date (from the CSV) to today's date. (The $endDate variable functions as it should, but I was told that the method used is unreliable.)
Also, I would like to search the Dept Name column in each row for any instance of the letters 'HR' (note: dept names could be things like 'HR - Career Services' or 'HR - Diversity'. I want all rows that have 'HR' anywhere in the Dept Name field). I get the feeling the -match operator is not the way to do that, but I'm not certain.
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).

This line needs curly braces { } but looks otherwise OK to me:
$startDate = $Lawson | where { [datetime]::ParseExact($_.'LAW HIRE DATE', 'dd-MM-yyyy', $null) -le (Get-Date) }
To do a simple partial match you're better off using -Like and a wildcard character as -Match uses regex (although should work).
Also I just noticed you were piping the $enddate variable not $lawson:
$HR = $Lawson | where {$_.'LAW DEPT NAME' -like '*HR*'}
If you're trying to do all of these criteria together, just combine them with -and:
$Lawson | where { [datetime]::ParseExact($_.'LAW HIRE DATE', 'dd-MM-yyyy', $null) -le (Get-Date) -and $_.'LAW TERM DATE' -eq '' -and $_.'LAW DEPT NAME' -like '*HR*' -and $_.'VICE PRESIDENT' -match 'Croner'}

Related

How to bring in the original string after comparison

this is the first time I ask a question. I'm a foreigner and I'm not good at english. so it's a little bit hard to explain my question. Maybe my title is wrong too...
I am trying to compare all the names and info in Active Directory with the time, and then add them to the group,but the info has "WIFI" and numbers,So I a regex the info and compared it with time
$time = Get-Date -Format "yyyyMMdd"
$ccc = Get-ADUser -Filter {info -like "WIFI*" }-properties name,info | select name,info
$bbbs = $ccc -replace '\D+([0-9]*).*','$1'
Get-ADUser -Filter {info -like "WIFI*" }-properties name,info | select name,info | export-csv -path c:\test.csv -NoTypeInformation -Encoding "UTF8"
Import-CSV "C:\test.csv" | % {Add-ADGroupMember -Identity namegroup -Members $_.Name}
$bbbs -lt $time
The output of $ccc is here
name info
---- ----
mike WIFI-20210515 VPN-20210512
terry WIFI-20210519 VPN-20210519
hack WIFI-20210530 VPN-20210513
The output of $bbbs is here
20210515
20210519
20210530
But I don't know how to WIFI-"datatime" is less than the current date, remove it from the group.(
I joined the group using a CSV file)
I can't figure out how to do it.
Although it is still very unclear what (date) numbers from info you want to compare, but one way of doing what I think you want to achieve can be:
# get the reference date as DateTime object
$refDate = (Get-Date).Date # today at midnight
# better keep the returned ADUsers objects complete, with added 'Info' property
# if you want to add them to a Group. By default, Get-ADUser returnes these properties:
# DistinguishedName, Enabled, GivenName, Name, ObjectClass, ObjectGUID, SamAccountName, SID, Surname, UserPrincipalName
$users = Get-ADUser -Filter "Info -like 'WIFI*'" -Properties Info |
# for convenience, also add a custom property 'WifiDate' converted to DateTime object to compare against.
# if you need to use the other date (the one after "VPN-"), then change below `$1` to `$2`
Select-Object *, #{Name = 'WifiDate'; Expression = {[datetime]::ParseExact(($_.Info -replace '^WIFI-(\d{8}).*(\d{8})', '$1'), 'yyyyMMdd', $null)}}
# next filter to get all users that have a 'WifiDate' earlier than today
$usersToAddToGroup = $users | Where-Object { $_.WifiDate -lt $refDate }
Now you can add these to an AD group using something like:
Add-ADGroupMember -Identity 'TheNameOfTheGroup' -Members $usersToAddToGroup
Of course, if you want to remove those users from the group, replace Add-GroupMember with Remove-ADGroupMember
Regex details:
^ Assert position at the beginning of the string
WIFI- Match the characters “WIFI-” literally
( Match the regular expression below and capture its match into backreference number 1
\d Match a single digit 0..9
{8} Exactly 8 times
)
. Match any single character that is not a line break character
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
( Match the regular expression below and capture its match into backreference number 2
\d Match a single digit 0..9
{8} Exactly 8 times
)

Where statement in foreach loop, comparing dates

Trying to import a list of users with Name, Email, Termination Date, Stop Date and first check if either stop date or termination date has passed.
Tried adding [datetime] and using get-date $user.'stop date' but without any luck.
It seems to work with the below code without the same issues though, or I get the same error, but it does check and write out that one of the values is greater:
$StopFolder = Get-ChildItem C:\test\123\*.csv |sort LastWriteTime -descending|select -first 1
$Stoplist = Import-Csv $StopFolder -delimiter ';'
$CurrentDate = Get-Date
foreach($Date in $Stoplist){
if($CurrentDate -eq (get-date $Date.'Stop Date')){write-host Equal}
if($CurrentDate -gt (get-date $Date.'Stop Date')){write-host Greater}
if($CurrentDate -lt (get-date $Date.'Stop Date')){write-host Less}}
But the same didn't seem to work for the below and can't really figure out why. I think I need to convert it to a date, but not sure why it's working in the above and not the below, nor how exactly to convert it if get-date doesn't work.
$StopFolder = Get-ChildItem C:\test\123\*.csv |sort LastWriteTime -descending|select -first 1
$Stoplist = Import-Csv $StopFolder -delimiter ';'
$CurrentDate = Get-Date
foreach($User in $Stoplist|where($_.'stop date' -lt $CurrentDate)){
try{
$Usermail = $User.'e-mail address'
$Username = get-aduser -Filter "EmailAddress -eq '$Usermail'" -properties Enabled
if($Username.enabled){
echo $Username 'still exists and is NOT disabled' >> C:\NotDisabled.txt
}
if($Username.enabled -eq $false){
echo $Username 'still exists and is disabled' >> C:\NotDeleted.txt
}
}
catch{continue}
}
Expected result is to have it only initiate the loop if the current users stop date is less than the current date. Currently nothing happens, removing the where part and the rest seems to run fine.
Any help is much appreciated.
Edit:
CSV dates are like this:
stop date
01-02-2023
21-09-2019
21-01-2019
01-01-2019
01-01-2019
Edit: the error was not just within the logic but a typo: | where needs curly braces > | where {} not parenthesis.
Create a date from 'stop date':
(get-date -date $_.'stop date')
in one line:
foreach($User in $Stoplist|where{(get-date -date $_.'stop date') -lt $CurrentDate}){...}
Here $Stoplist|where{(get-date -date $_.'stop date') -lt $CurrentDate} is one unit and could be encapsulated in brackets:
foreach($User in ($Stoplist|where{(get-date -date $_.'stop date') -lt $CurrentDate}) ){...}
But without brackets around $User in $Stoplist the pipe | only refers to the last object $Stoplist

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

Compare Date from CSV Import To Get-Date

I have a CSV that contains a number of columns but I want to import just the description column, a department column and a date column. I then want to create a new object with the description, department and date information but only for items that have a date 45 days or older. I know that the Import-Csv is bringing in the "Item Date" column as a string so that I need to use something like Get-Date or datetime to get it to a date format for comparison.
$data = import-csv .\items.csv | select "Description", "Department", "Item Date"
$CheckDate = (Get-Date).AddDays(-45)
$data2 | Foreach {get-date $_."Item Date"} |
select "Description", "Department", "Item Date"
$newdata = $data2 | where {$data."Item Date" -lt $CheckDate}
There may be an easier way to do this or there may be a way to get this to work but I am having trouble.
Definitely some room for simplification here.
$CheckDate = (Get-Date).AddDays(-45)
$data = Import-Csv .\items.csv |
Where-Object {
($_."Item Date" -as [DateTime]) -lt $CheckDate
}
Just cast the "Item Date" string as a [DateTime] with the -as operator and then compare that to your $CheckDate in the Where-Object call.
Depending on the date format used in the CSV and the computer's regional settings simply casting the string to a DateTime value may or may not work. If you find that it doesn't use the ParseExact() method instead. And perhaps a calculated property, since you're selecting columns anyway.
$fmt = 'dd\/mm\/yyyy'
$culture = [Globalization.CultureInfo]::InvariantCulture
$data = Import-Csv .\items.csv |
Select-Object Description, Department, #{n='Item Date';e={
[DateTime]::ParseExact($_.'Item Date', $fmt, $culture)
}} |
Where-Object { $_.'Item Date' -lt $CheckDate }
Note that forward slashes in the format string must be escaped if you need to match literal forward slashes, otherwise they will match whatever date separator character is configured in the computer's regional settings.