If any of you folks use KnowBe4...
The below script may be helpful...
I tapped into their API - Hoping to pull in an automated list of users who have not completed mandatory training -
This list of users was being manually harvested, via the web interface (not an easy, intuitive, or remotely contiguous process either).
Only to discover that such base level, rudimentary details are not actually available from a simple API call...
I even asked their support team... Nope!
To say the least, I was dismayed, and annoyed in learning this.
(I did promise KnowBe4 support that I would call them out on Reddit, hoping that some amount of public embarrassment, might get their dev team to fix " this shortsighted absurdity"... " And act like a company that offers IT related services" -
https://www.reddit.com/r/PowerShell/comments/oo9ir1/delinquent_knowbe4_training_list_api_seriously/
https://www.reddit.com/r/sysadmin/comments/oo9n51/delinquent_knowbe4_training_list_api_seriously/
I sent a link to those Reddit posts, in my reply to the KnowBe4 manager that replied to me, when I explained my feelings about this, in the support email)
~~~~~~~~~~~~~~~~~~~~~~~~~~
It is necessary to call three (3) different API directories, in order to get something useful.
Querying first ‘Campaigns’, and then using THAT result, and querying ‘Enrollments’, and THEN using THAT result, querying ‘users’.
The below code does a far bit of data massaging, and may be interspersed with some filtering that not everyone might need in place...
No doubt there are ways to streamline it - TBH - I don't really care.
Much of what I did, was done on-the-fly... And like I said - I was annoyed... I probably still am.
$EAP = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue" # Because possibly dealing with converting a null value to 'DateTime', below.
Function Token {
$AccessToken = "'Reporting' API key goes in here" # Get this from within the KnowBe4 portal
$authorizationHeader = "Authorization: Bearer $AccessToken"
}
$URL = 'https://us.api.knowbe4.com/v1/training/campaigns/'
Token
$campaigns = $null; $campaigns = (curl.exe --url $URL --header $authorizationHeader --silent | ConvertFrom-Json)
# $campaigns | select name, campaign_id, status, completion_percentage, start_date, end_date, relative_duration, duration_type | sort completion_percentage | ft
$campaigns | % {
If ($_.start_date -ne $null) { $_.start_date = [DateTime]"$($_.start_date)" }
If ($_.start_date -eq $null) { $_.start_date = [Nullable[DateTime]]$null }
If ($_.end_date -ne $null) { $_.end_date = [DateTime]"$($_.end_date)" }
If ($_.end_date -eq $null) { $_.end_date = [Nullable[DateTime]]$null }
}
$PastDue = @()
$campaigns | ? { $_.Status -ne "closed" -and ($_.end_date -eq $null -or (Get-Date) -lt $_.end_date) } | sort name | % {
Write-Host $_.campaign_id -Fore 14 -No
Write-Host " - "$_.name -Fore 11 -No
If ($_.relative_duration) {
$_relative_duration = $_.relative_duration
Write-Host " - Rel. Dur.:"$_.relative_duration -No
}
If (!$_.relative_duration) {
}
If ($_.end_date -ne $null) {
Write-Host " (End date: $($_.end_date))" -Fore 10
}
If ($_.end_date -eq $null) {
Write-Host " (No End date)"
}
#>
$URL = "https://us.api.knowbe4.com/v1/training/enrollments?campaign_id=$($_.campaign_id)?per_page=500"
Token
$enrollments = $null; $enrollments = (curl.exe --url $URL --header $authorizationHeader --silent | ConvertFrom-Json)
$enrollments = $enrollments | select campaign_name, completion_date, content_type, enrollment_date, enrollment_id, module_name, policy_acknowledged, start_date, status, user_status, user, id, first_name, last_name, email
$enrollments | % {
$_.id = $_.user.id
$_.first_name = $_.user.first_name
$_.last_name = $_.user.last_name
$_.email = $_.user.email
}
$PastDue += $enrollments | ? { $_.status -eq "past due" } | select campaign_name, enrollment_date, @{ Name='duration';Expression ={ $_relative_duration } }, days_late, module_name, status, user_status, id, first_name, last_name, email }
$ErrorActionPreference = $EAP
$PastDue | sort campaign_name, email | % {
$duration_period = (($_.duration).Trim()).Split(' ')
If ($duration_period[1] -match "weeks") { $_days = ((Get-Date).AddDays(-([int]$duration_period[0] *7)) - [Datetime]"$($_.enrollment_date)").Days }
If ($duration_period[1] -match "months") { $_days = ((Get-Date).AddMonths(-([int]$duration_period[0])) - [Datetime]"$($_.enrollment_date)").Days }
$_.days_late = " $_days "
$_.enrollment_date = [DateTime]"$($_.enrollment_date)"
}
# Use the user 'Active', or 'Archived'?
$PastDue | % { $StatusURL = "https://us.api.knowbe4.com/v1/users/$($_.id)"; Token; $_.user_status = (curl.exe --url $StatusURL --header $authorizationHeader --silent | ConvertFrom-Json).Status }
''; $PastDue | ? {$_.user_status -eq "Active" -and $_.campaign_name -NOTmatch "The.Inside.Man"} | sort campaign_name, email | select campaign_name,duration,days_late,status,user_status,id,first_name,last_name,email | ft