Getting New User Notifications for Users that Aren't New


I have a script I am using to identify when new users are added to a website we use. The below code pulls a list of users that are on the website. When I first started, I exported the current list of users from the website into a CVS (UserDatabase.csv). The CSV file is imported each time and turned into the ‘$UserDatabase’ variable. Any new users detected are later appended to the CSV so they are not detected again.

The issue is that, intermittently, it will notify me of a new user being added to the website, but the user in fact already exists in the reference CSV file. It should only be flagging up users which exist on the website, and not in the local CSV. We have multiple companies with their own, separate instances of the website, so the foreach loop is used to connect to each company’s instance, pull their user data, and compare it to the data in UserDatabase.csv. The reference file contains the data from every site instance. I don’t think that would cause an issue though. Compare-object should still be able to find the user in the reference CSV. I have checked previous versions of UserDatabase.csv and the users it is flagging definitely already exist in there.

## Script using OAuth2 ###

#Refresh Tokens to talk to API for the site (multiple companies on the same site, each have their own refresh token)
$RefreshTokens = "Example1","Example2","Example3"

$UserDatabasePath = "C:\UserDatabase\UserDatabase.csv"

$UserDatabase = Import-Csv -Path $UserDatabasePath | sort firstname

#URL to retrieve site API Access Token
$TokenRequestURL = 'https://example/token'

foreach ($RefreshToken in $RefreshTokens) {

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"

$Params = @{"client_id"="Example";

$SiteAccessToken = Invoke-webrequest -uri $TokenRequestURL -Method POST -body $Params -ContentType "application/x-www-form-urlencoded" -headers $headers -UseBasicParsing | convertfrom-json | select -ExpandProperty access_token

#Add client ID and access token to the HTTP headers
$headers.Add("X-ClientId", 'Example')
$headers.Add("Authorization", "Bearer $SiteAccessToken")

#Retrieve list of users through API
$NewUserUri = 'URL pointing to list of users'
$SiteUsers = Invoke-webrequest -uri $NewUserUri -Method GET -headers $headers -UseBasicParsing | convertfrom-json | select -expandproperty data | select firstname, lastname | sort firstname

#Compare local and remote list of users. Flag any users which exist on the website, but not locally.
$NewUsers = Compare-object -ReferenceObject $UserDatabase -DifferenceObject $SiteUsers -Property firstname, lastname | where-object SideIndicator -EQ "=>"

### Some code to send an email notification with details of any new users ####

#Append any new users to the database
$NewUsers | export-csv -append $UserDatabasePath 

Any help would be greatly appreciated. I’ve probably made an oversight in the compare-object bit, not sure what it is though as I’m still a semi-beginner.

Nothing is jumping out as wrong, per se. The real question is there anything else available on the website aside from First and Last name? How do you know there aren’t 2 users named Tim Smith? You really want something unique like a ID, a date or something that can be actually compared because First\Lastname isn’t really a good measure. So what are all of the properties returned from $SiteUsers query?

Another tip, if you use Invoke-RestMethod, it will automatically parse the JSON for you.

Hi Rob,

Thank you for your reply. Yes, in the version I have running the data is actually like the below. Just omitted ID and dateTimeCreated as I was being overly cautios, haha. Data in both variables ($UserDatabase and $SiteUsers) have the same columns, and the data in each column is in the same format. The users in Userdatabase.csv aren’t organised; when I built the file up initially, I just took the notification code out, and ran it. That way it would just append users from every site to the list. Wasn’t sure if Powershell would check through my CSV from top to bottom for the users, or search alphabetically. As I wasn’t sure, I made Powershell sort the list from A-Z while importing; didn’t fix it. Thanks for the tip!

<colgroup><col style="width: 48pt;" span="4" width="64" /> </colgroup>
id firstName lastName dateTimeCreated
123456 Adam Ainsworth 2019-05-07T14:01:41.443Z

I don’t think it is anything that’s happening with the users on the website side. As I’m using the code where-object SideIndicator -EQ “=>”, I believe that even if a user was somehow temporarily moved out of the ‘employee’ API URI which my API call points to, it wouldn’t populate the $NewUsers variable. As even though it exists in the CSV and not the website, the code shouldn’t care; only looks at what’s on the website that’s not in the CSV.

Weird ¯_(ツ)_/¯

If you are getting an ID, then you should be doing the Compare-Object on that property, not a First\Last Name. Another way to do this is to also use the date time stamp to identify new users. The date is a string, but you can easily parse it to a date object and use a comparison operator:

PS C:\Users\rasim> Get-Date -Date '2019-05-07T14:01:41.443Z'

Tuesday, May 7, 2019 10:01:41 AM

To implement, it would be something like:

$SiteUsers = Invoke-RestMethod -uri $NewUserUri -Method GET -headers $headers -UseBasicParsing | select -expandproperty data | Select Id, FirstName, LastName, dateTimeCreated, @{Name='dateTimeCreatedParsed';Expression={Get-Date -Date $_.dateTimeCreated}}

#Find all users created in the last 24 hours
$newUsers = $SiteUsers | Where{$_.dateTimeCreatedParsed -gt (Get-Date).AddDays(-1)}

Hi Rob,

That is great. Using dateTimeCreated will be much better, then I won’t have the CSV to look after and can get rid of the code for it. Thank you very much for your guidance!