Handling Date in Power-Shell JSON Object - powershell

I am trying to get user info based on LastLogondate from AD. I am converting final output in JSON format.
Here the small script to handle dates. Actually I am passing $PasswordAge as LastLogonDate of the user.
$PasswordAge = (Get-Date).AddDays(-60) # This is actually LastLogonDate from user info
$CurrentDate = (Get-Date)
#Write-Host $PasswordAge
try
{
$LastLogonDays = (New-TimeSpan -Start $PasswordAge -End $CurrentDate).Days
#Write-Host $LastLogonDays
}
catch{
$Message = $_.Exception.Message
}
$JSONOutput = #{"result"=$LastLogonDays;"error"=$Message} | ConvertTo-Json #-Compress
Write-Output $JSONOutput
Receiving below error if I use ConvertTo-Json and print output. Day difference is correct but every-time getting message in error
{
"error": "Cannot bind parameter \u0027Start\u0027 to the target. Exception setting \"Start\": \"Cannot convert null to type \"System.DateTime\".\"",
"result": 60
}
Thank for your help in advance.

New-TimeSpan sucks. I almost always get the same error. It's reported that the issue stems from a mismatch of the culture (language) settings on your OS, the format your OS is configured to use for dates and times, and the format the DateTime object is actually in. I live in the US, use the English language, and use our standard format for dates and I still have this problems with getting New-TimeSpan to function. I've long-since given up on getting this cmdlet to work consistently.
Fortunately there's an easy workaround; just subtract two datetimes to get a System.TimeSpan instead. Works every time.
$LastLogonDays = ( $CurrentDate - $PasswordAge ).Days

Related

PowerShell CSV file conversion errors due PC with different DateTime format that the input file

I’m using PowerShell with a script to convert a .CSV raw data file into more manageable data format with separate columns, a cleaner view etc. And because the source file with the raw data is in US date and time format (e.g. 11/23/21, 1:00 PM), then if the PC is in that same US format the conversion process runs perfectly as should with 0 errors. BUT, if the PC is in a different country date and time format, then PowerShell shows errors in red in the process.
When the PC is in other DateTime format I see the main error is:
"Parse" with "1" argument(s): "String was not recognized as a valid DateTime."
And the problem is the PC where this will be used is not in US format (only changed to US format for testing), so could someone here please help me to add to the conversion process the syntax or sentence/s to simply specify directly in the code a fixed format that keeps a static output format independently about the PC clock date and time format, and if one of the inputs into the file is “11/23/21, 1:00 PM” then to specify in the code you want the output in the format “dd-MMM-yyyy hh:mm” to have a result like “23-Nov-2021 01:00 PM”
The code section in the script used for the conversion is:
…
$data = $csvData | ? {$_ -match "\(DTRE"}
dtreFileData = New-Object System.Collections.Generic.List[PSCustomObject]
foreach ($item in $data)
{
$null = $item.Strategy -match "\(DTRE\|(.*)\)"
$v = $Matches[1] -split '\|'
$resultvalue = $v[0] | Convert-CurrencyStringToDecimal
$expectedvalue = $v[1] | Convert-CurrencyStringToDecimal
$dtreData = [PSCustomObject]#{
'DateTime' = ([datetime]::Parse($item.'Date/Time'))
'ResultValue' = [decimal]$resultvalue
'ExpectedValue' = [decimal]$expectedvalue
}
$null = $dtreFileData.Add($dtreData)
$null = $dtreAllData.Add($dtreData)
}
$dtreFileData | Export-Csv -Path (Join-Path (Split-Path -Path $f -Parent) ($outFile + '.csv')) -Force -NoTypeInformation -Encoding ASCII
…
Example of raw source data (in the CVS file are dozens of lines like the next one):
...(DTRE|49.0|48.2);...;11/23/21, 12:58 PM...;
...(DTRE|52.1|52.0);...;11/23/21, 1:00 PM...;
...
...
And the Output looks like:
I tried with DateTime examples in other posts from here (stackoverflow.com) to adjust the code to work in a PC without US date and time format and to get the DateTime format result described above. Examples like:
'DateTime' = ([datetime]::Parse($item.'yyyy-MM-dd:HH:mm:ss'))
'DateTime' = ([datetime]::ParseExact($item.'yyyy-MM-dd:HH:mm:ss'))
…
$culture = [Globalization.CultureInfo]::InvariantCulture
…
'DateTime' = ([datetime]::ParseExact($item.'yyyy-MM-dd:HH:mm:ss', $culture))
…
But with these examples PowerShell shows the error “Cannot bind argument to parameter 'InputObject' because it is null”
Update after the answer from #Seth:
When trying next modification of the code, with the PC system date format in “24-Nov-21” and leaving the rest as above:
…
$resultvalue = $v[0] | Convert-CurrencyStringToDecimal
$expectedvalue = $v[1] | Convert-CurrencyStringToDecimal
$cultureInfo= New-Object System.Globalization.CultureInfo("es-ES")
$dtreData = [PSCustomObject]#{
'DateTime' = ([System.DateTime]::Parse($item.'Date/Time', $cultureInfo))
'ResultValue' = [decimal]$resultvalue
'ExpectedValue' = [decimal]$expectedvalue
…
then, PowerShell shows the next errors:
As it has been explained it's a good idea to fix the CSV to have a better dateformat. An example would be ISO 8601 which can be used with Get-Date -Format "o".
That said Get-Date relies on C# stuff in the background. So you can use C# code to read that in a particular culture. As you know the origin culture this should work. Fixing the timestamp is still a better idea.
$cultureInfo= New-Object System.Globalization.CultureInfo("en-US")
$dateString = "11/23/21, 12:58 PM";
$dateTime = [System.DateTime]::Parse($dateString, $cultureInfo);
Get-Date -Format "o" $dateTime
With this example code you'd assign $dateString the value of $item.' Date/Time' and the result you likely want would be the result of Get-Date. So you'd assign $dtreData.'DateTime' the result of that Get-Date call. Alternatively it is possible to use the .NET DateTime Object to directly convert to a particular culture. For instance by calling $dateTime.ToString((New-Object System.Globalization.CultureInfo("en-ES"))). Though not all that useful you could also pass the format identifier to this method. This might be relevant if you want to avoid creating additional objects. A somewhat unnecessary call would be $dateTime.ToString("o", (New-Object System.Globalization.CultureInfo("en-ES"))) (as format o is the same in every culture).

Powershell keep looping until condition is true then proceed

I have written a script that so far is able to check a file "latest.json" for the "created_at" object which shows the last date that a commit has occurred for software.
$websiteJson = Invoke-WebRequest "https://website/latest.json" | ConvertFrom-Json | select created_at
$todaysDate = Get-Date -Format "yyyy-MM-dd HH:mm"
if($websitejson.created_at | where {$_.created_at -eq $todaysDate}){
Write-Output "Today's date matches"
} else {
Write-Output "has not yet been updated"
}
How part of latest.json looks like
"created_at":"2020-03-23 17:32:48"
How do I change this to keep looping until the date pull from latest.json matches then proceed to next step (would download and install software). Also, since "created at" has "17:32:48" will this cause the date check to fail since the time does not match?
. I want it to keep checking if dates match.
Thank you!
Right now, I'm not going to bother converting dates to match to make sure they're the same format, but what you need for your specific questions is just a do until loop. I might update this to check the date formats if you supply an example layout of the returned JSON.
Do{
$websiteJson = Invoke-WebRequest "https://website/latest.json" | ConvertFrom-Json | select created_at
$todaysDate = Get-Date -Format "yyyy-MM-dd HH:mm"
if($websitejson.created_at | where {$_.created_at -eq $todaysDate}){
Write-Output "Today's date matches"
} else {
Write-Output "has not yet been updated"
}
start-sleep -s 60
}until($websiteJson -eq $todaysDate)
I believe this wont work right off the bat. You'll have to get the JSON date and $todaysDate to be the same format, then you can do this and it will work.
if you want to compare the date and/or time, use datetime objects instead of datetime strings. something like this ...
if you want to test for the actual time difference between two time objects ...
((Get-Date -Date '2020-03-23 18:11:22') - [datetime]'2020-03-23 17:32:48').TotalHours
# result = 0.642777777777778
you keep mentioning date as if you don't want the time, so this method would work for comparing the date parts of two timestamps ...
# the date at the time the code was run = 2020 April 03, Friday 4:30:34 PM
$Today = (Get-Date).Date
$Created_At = '2020-04-03 15:15:15'
$Today -eq ([datetime]$Created_At).Date
result = True

Trying to put together a script for automatic timezone updates

I am working on a powershell script and it isn't reading my "If, elseIf" properly so I know I am doing something wrong. I need some help figuring this out.
I start with pulling the Default Gateway and storing it:
$Gateway = (Get-wmiObject Win32_networkAdapterConfiguration | ?{$_.IPEnabled}).DefaultIPGateway
Next I am trying to get it to sort that so that if it equals one of my designated Default Gateways it will update the timezone.
If ($Gateway = "10.100.4.1")
{
$TZ = "Central Standard Time"
}
ElseIf ($Gateway = "10.101.4.1")
{
$TZ = "Central Standard Time"
}
and I am finishing it up with a
Set-TimeZone $TZ
The purpose is that if I image a system at the home office, and I ship it to "remote location" I can't trust an end user to update their time zone, and I have POS that was poorly written so that it doesn't use UTC/GMT, and can cause issues with the BOH systems et cetera.
I will be placing this in as a startup item to execute whenever the system starts up to ensure that it is always up to date with the TZ.
Changing the Win 10 to use an automatic update for the TZ doesn't work because reasons (read: Networking Team and Security Team are out to get me and in this instance it isn't paranoia).
So, where can I find help to put this all together?
Edit: I had a typo which is why it wasn't working. So... nevermind. For those ofyou interested in the typo, I removed it already. it was in my $Gateway portion, adding a " after {$_.IPEnabled} and before the )
Your "if" statements are using the assignment operator = instead of the equality operator, -eq.
Switch it to If ($Gateway -eq "10.100.4.1") and it should work.
P.S. I missed the typo you mentioned, but the assignment operator is still an issue. When the assignment operator is used in an "if/elsif" statment it will always return $true which would be rather problematic.
I would suggest using a lookup Hashtable for the designated default gateways.
Because you want this to be run as startup script, you also need to create a centralized path where errors can be logged or write errors in the windows eventlog.
Create a Hashtable with your designated gateway IP addresses as key and the timezone ids as value.
Either directly in code:
$timeZones = #{
'10.100.4.1' = 'Central Standard Time'
'10.101.4.1' = 'Central Standard Time'
'10.102.4.1' = 'Eastern Standard Time'
# etc.
}
Or by reading a centralized CSV file with columns 'IPAddress' and 'TimeZone'
$defaultTz = Import-Csv -LiteralPath '\\Server\Share\Folder\Timezones.csv'
$timeZones = #{}
$defaultTz | ForEach-Object {
$timeZones[$_.IPAddress] = $_.TimeZone
}
Next, use these values something like this (demo uses a centralized error log file):
$errorlog = '\\Server\Share\Folder\TimezonesErrors.log'
$now = (Get-Date).ToString() # use default format or specify the date format yourself here
$currentTz = (Get-TimeZone).Id # get the current timezone Id
# get the default gateway IPv4 address for this computer
$gw = #((Get-wmiObject Win32_networkAdapterConfiguration | Where-Object {$_.IPEnabled -and $_.DefaultIPGateway-like '*.*.*.*'}).DefaultIPGateway)[0]
# check if the gateway IP address is present in the lookup Hashtable
if ($timeZones.ContainsKey($gw)) {
$tz = $timeZones[$gw]
# only set the wanted timezone if it differs from the current timezone
if ($tz -ne $currentTz) {
try {
Set-TimeZone -Id $tz -ErrorAction Stop
}
catch {
# add the exception to the error log
$msg = "$now - Error setting the timezone on computer $($env:COMPUTERNAME): $_.Exception.Message"
Add-Content -LiteralPath $errorlog -Value $msg
}
}
}
else {
# make it known that the IP address for this gateway was not found in the Hashtable
$msg = "$now - No timezone found for default gateway $gw on computer $($env:COMPUTERNAME)"
# write error to the central error.log file
Add-Content -LiteralPath $errorlog -Value $msg
}

PowerShell Comparing Date issue

I created a small scrip that have do to a couple of thinks.
get string from description field from user in specific container
take part of this string (substring method) that holds date information
convert this string to date format
compare this formated string with a current date - 30 days and do sth
The problem is that comparing is not working correctly. I tried do recognise date that is older than 30 days and do something but i see that comparison not always work. sometimes it does not recognize that date is less then - 30 days from current day
Script below
$DateMaxTime = (Get-date).AddDays(-30)
$DateFormatMaxTime = Get-Date $DateMaxTime -Format dd/MM/yyyy
$getData = get-ADUser -Filter * -Properties * -SearchBase "OU=Disabled,OU=Control,OU=x,OU=x,DC=x,DC=x,DC=x" `
|where {$_.Description -like "LEFT*"} |select name,samaccountname,description
Foreach ($Data IN $getData){
$DataPart = $null
$DataPart=$Data.description
$DatePart= $DataPart.substring(5,10)
$FinalDate = [datetime]::ParseExact($DatePart,'dd/MM/yyyy',$null)
$FinalDateFormat = Get-Date $FinalDate -Format dd/MM/yyyy
If ($FinalDateFormat -lt $DateFormatMaxTime ){ Write-Host "$($Data.samaccountname), $($Data.description) moved to deleteMe" }
else{ Write-Host "$($Data.samaccountname), $($Data.description) still in disabled" }
}
Below output shows me the wrong results (as example i did it for one user - >
Based on this logic the value $FinalDateFormat that hold date -> 31-12-2018 is less then value $DateFormatMaxTime that hold this date -> 25-06-2019 but it still applies else statement ...
I am not sure why, i did something wrong with date conversion ?
I put the comments as the answer:
I would compare the datetime versions of the dates rather than the string versions.
If ($FinalDate -lt $DateMaxTime)
Running
get-date -format
makes them strings.
$finaldate.gettype(); $datemaxtime.gettype()
shows the types. They are [datetime], not [string] like the other two.

How to get retrieve relevant calendar events through Google Calendar API?

I have come this far with my script, which is simply supposed to retrieve the entries for a particular calendar for a specific time period (i.e. exactly what I see in the calendar).
#Powershell
ls (join-path $Script:scriptpath .\GDataCmdLet-master\Binaries\*.dll) | % {
[System.Reflection.Assembly]::LoadFile($_)
}
$service=new-object Google.GData.Calendar.CalendarService('Test')
$cred = New-Object Google.GData.Client.GDataCredentials('joe#gmail.com', '1234')
$service.credentials=$cred
$eventquery=new-object Google.GData.Calendar.EventQuery
$eventquery.uri='http://www.google.com/calendar/feeds/joe#gmail.com/private/full'
$eventquery.StartDate = (Get-Date -Date '2014-02-10')
$eventquery.EndDate = (get-date -date '2014-02-20')
$eventfeed=$service.query($eventquery)
The DLLs I got from https://github.com/robertj/GDataCmdLet
All events from the calendar have actually been removed (i.e. I don't see any in the webinterface), but I keep on getting these as a result.
Questions, an answer to either one would suffice:
Is there a flag in the results, which distinguishes deleted from non-deleted events?
Is there a way to retrieve only the non-deleted events in the first place? (the parameter ?showhidden=false on the query didn't help)
Thank you!
Sandro
Solved it! The following seems to work, not sure exactly why tough
ls (join-path $Script:scriptpath .\GDataCmdLet-master\Binaries\*.dll) | % {
[System.Reflection.Assembly]::LoadFile($_)
}
$service=new-object Google.GData.Calendar.CalendarService('Test')
$cred = New-Object Google.GData.Client.GDataCredentials('joe#gmail.com', '1234')
$service.credentials=$cred
$eventquery=new-object Google.GData.Calendar.EventQuery
$eventquery.uri='http://www.google.com/calendar/feeds/joe#gmail.com/private/basic'
#don't use StartDate and EndDate this way
#they just didn't have the expected effect
$eventquery.StartTime = (Get-Date -Date '2014-02-10')
$eventquery.EndTime = (get-date -date '2014-02-30')
#that does the trick to get the actual occurences, not just the events
#and it also removed the non-deleted ones
$eventquery.SingleEvents = $true
$eventfeed=$service.query($eventquery)
Now I don't know where to get the start and end times of the occurences from, but that is for another question to answer...