From 764016edf64a99ac2a4117cd1d8a0952bb20fd71 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Wed, 10 Dec 2025 15:46:51 -0600 Subject: [PATCH 1/2] Sets default parameter set for cmdlet Configures the 'v1' parameter set as the default for the Read-VerkadaAccessUsers cmdlet. This simplifies usage by automatically using the latest Command v1 API endpoint unless a different parameter set is explicitly specified. --- verkadaModule/Public/Access/Read-VerkadaAccessUsers.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verkadaModule/Public/Access/Read-VerkadaAccessUsers.ps1 b/verkadaModule/Public/Access/Read-VerkadaAccessUsers.ps1 index b6f9940..5f80149 100644 --- a/verkadaModule/Public/Access/Read-VerkadaAccessUsers.ps1 +++ b/verkadaModule/Public/Access/Read-VerkadaAccessUsers.ps1 @@ -26,7 +26,7 @@ function Read-VerkadaAccessUsers{ Read-VerkadaAccessUsers -version v1 -refresh This will return all the active access users in an organization with the most recent data available from the Command v1 public API endpoint. The token will be populated from the cache created by Connect-Verkada. #> - [CmdletBinding(PositionalBinding = $true)] + [CmdletBinding(PositionalBinding = $true, DefaultParameterSetName = 'v1')] param ( #This is the graphql query to be submitted (do not use unless you know what you are doing) [Parameter(Position = 1, ParameterSetName = 'legacy')] From c10a149e2afac41b232dc660d2c78d427df5189d Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Wed, 10 Dec 2025 15:47:44 -0600 Subject: [PATCH 2/2] Adds merge duplicate users example Adds a new example that demonstrates how to merge duplicate Simplifies the Connect-Verkada examples by removing the org_id parameter, as it is no longer required. --- docs/examples/AC_Profile_picture_import.md | 6 +- docs/examples/Merge_duplicate_AC_users.md | 110 +++++++++++++++++++++ docs/examples/Using_secrets.md | 4 +- 3 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 docs/examples/Merge_duplicate_AC_users.md diff --git a/docs/examples/AC_Profile_picture_import.md b/docs/examples/AC_Profile_picture_import.md index c83e8ad..8fa8498 100644 --- a/docs/examples/AC_Profile_picture_import.md +++ b/docs/examples/AC_Profile_picture_import.md @@ -3,11 +3,11 @@ Say you want to bulk import AC profile pictures by dropping them in a folder and naming the image files using a unique identifier. The first the first thing you need to do is authenticate: ```powershell -Connect-Verkada -org_id [your_orgId] -x_api_key (Get-Secret -Name VrkdApiKey -AsPlainText) +Connect-Verkada -x_api_key (Get-Secret -Name VrkdApiKey -AsPlainText) #or for simplicity when not using secrets. -Connect-Verkada -org_id [your_orgId] -x_api_key [your_api_key] +Connect-Verkada -x_api_key [your_api_key] ``` >Then if you've named the image files using the user's **user_id**, like fc6c3648-aa4a-4999-b1a0-a64b81e2cb76.jpg, you can use something like this: @@ -26,7 +26,7 @@ or or ->If you've named the image files using the user's **email**, like some.user@contoso.com.jpg, we will need to find the user_id to set the picture with something like this: +>If you've named the image files using the user's **email**, like `some.user@contoso.com.jpg`, we will need to find the user_id to set the picture with something like this: > >```powershell >Get-ChildItem ~/Documents/AC_profile_pictures | ForEach-Object {$temp = $_; read-VerkadaAccessUsers -version v1 -refresh | Where-Object {$_.email -eq $temp.BaseName} | Set-VerkadaAccessUserProfilePicture -imagePath $temp.FullName; $temp = $null} diff --git a/docs/examples/Merge_duplicate_AC_users.md b/docs/examples/Merge_duplicate_AC_users.md new file mode 100644 index 0000000..e5f15d6 --- /dev/null +++ b/docs/examples/Merge_duplicate_AC_users.md @@ -0,0 +1,110 @@ +# Merge Duplicate AC Users + +Say you want to find duplicate AC users and merge their group membership, cards, and profile pictures. First thing to do is to get an export of the AC users from Command. Then, add a new field to the CSV, **duplicate_userId**. Then we will use that CSV for the import in the script. + +```powershell +Connect-Verkada -x_api_key (Get-Secret -Name VrkdApiKey -AsPlainText) + +#or for simplicity when not using secrets. + +Connect-Verkada -x_api_key [your_api_key] +``` + +After connecting, we will import the user CSV and create our logic for determining duplicate Ids and which user we want to live on. + +```powershell +# import users +$exportedUsers = Import-Csv ~/command-user-export.csv + +# find users with emails that have duplicate users with the same first and last names +# this logic can be changed to meet the needs to determine duplicates +foreach ($user in $exportedUsers){ + if (!([string]::IsNullOrEmpty($user.email))){ + $user.duplicate_userId = $exportedUsers | Where-Object {[string]::IsNullOrEmpty($_.email) -and $_.firstName -ieq $user.firstName -and $_.lastName -ieq $user.lastName} | Select-Object -ExpandProperty userId + } +} +$duplicateSCIMusers = $exportedUsers | Where-Object {!([string]::IsNullOrEmpty($_.duplicate_userId))} +``` + +Once we have the duplicate_userId field added to our CSV the logic below will describe what will be done in the output and perform it in Command. + +```powershell +Start-Transcript -Path ~/transcript_output.txt +$doWork = $false +foreach ($dupe in $duplicateSCIMusers){ + foreach ($dupeId in $dupe.duplicate_userId){ + # determine if duplicate is active + if ($dupe.status -ieq 'active'){ + if (($exportedUsers | Where-Object {$_.userId -eq $dupeId}).status -ieq 'active'){ + write-host "$($dupe.firstName) $($dupe.lastName) - $($dupe.userId) SCIM user is active and has the duplicate account $($dupeId) for us to work on" + $doWork = $true + } else { + write-host "Will do nothing, $($dupe.firstName) $($dupe.lastName) - $($dupe.userId) SCIM user is active but the duplicate account $($dupeId) is $(($exportedUsers | Where-Object {$_.userId -eq $dupeId}).status)." + $doWork = $false + } + } else { + if (($exportedUsers | Where-Object {$_.userId -eq $dupeId}).status -ieq 'active'){ + write-host "$($dupe.firstName) $($dupe.lastName) - $($dupe.userId) SCIM user is $($dupe.status) but the duplicate account $($dupeId) is active, what should we do?" + $doWork = $false + } else { + write-host "Will do nothing, Neither $($dupe.firstName) $($dupe.lastName) - $($dupe.userId) SCIM user or the duplicate account $($dupeId) is active." + $doWork = $false + } + } + + if($doWork){ + # gather all the groups the duplicate is a part of + try { + $groups = Get-VerkadaAccessUser -userId $dupeId -ErrorAction Stop | Select-Object -ExpandProperty access_groups + Write-host "$($dupeId) is part of the following AC Groups: $($groups.name -join ',')" + $scimUserGroups = Get-VerkadaAccessUser -userId $dupe.userId | Select-Object -ExpandProperty access_groups | Select-Object -ExpandProperty group_id + foreach ($group in $groups){ + # determine if the user is already apart of that group + If ($scimUserGroups -contains $group.group_id ){ + Write-Host "$($dupe.firstName) $($dupe.lastName) - $($dupe.userId) is already a part of $($group.name) and doesn't need to be added" + } else { + Write-Host "$($dupe.firstName) $($dupe.lastName) - $($dupe.userId) needs to be added to $($group.name)" + # add user to group if necessary + Set-VerkadaAccessUserGroup -userId $dupe.userId -groupId $group.group_id + } + } + $scimUserGroups = $null + } catch { + $groups = @() + Write-Host "$($dupeId) is not part of any AC groups" + } + + # gather all the cards the duplicate has + $cards = Get-VerkadaAccessUser -userId $dupeId | Select-Object -ExpandProperty cards | Where-Object {$_.active -eq $true} + if ([string]::IsNullOrEmpty($cards)){ + Write-Host "$($dupeId) has no active cards to move" + } else { + Write-host "$($dupeId) has the following cards to move to $($dupe.firstName) $($dupe.lastName) - $($dupe.userId): $($cards.card_number -join ',')" + # assign those cards to the SCIM user + foreach ($card in $cards){ + Add-VerkadaAccessUserCard -userId $dupe.userId -cardType $card.type -cardNumber $card.card_number -facilityCode $card.facility_code -active $true + } + } + + # determine if duplicate has a photo and the SCIM user doesn't + if (!(Get-VerkadaAccessUser -userId $dupe.userId | Select-Object -ExpandProperty has_profile_photo)){ + if (Get-VerkadaAccessUser -userId $dupeId | Select-Object -ExpandProperty has_profile_photo){ + Write-Host "$($dupeId) has a profile photo to copy to $($dupe.firstName) $($dupe.lastName) - $($dupe.userId)" + # get profile photo + Get-VerkadaAccessUserProfilePicture -userId $dupeId -original $true -outPath ~/Downloads/tempPics/ + + # add profile photo to SCIM user + Set-VerkadaAccessUserProfilePicture -userId $dupe.userId -imagePath "~/Downloads/tempPics/$dupeId.jpg" + } + } + # deactivtate the duplicate user + Set-VerkadaAccessUserEndDate -userId $dupeId -endDate (Get-Date) + } + $doWork = $false + + Write-Host "" + } + +} +Stop-Transcript +``` diff --git a/docs/examples/Using_secrets.md b/docs/examples/Using_secrets.md index 44fef27..3b2fd69 100644 --- a/docs/examples/Using_secrets.md +++ b/docs/examples/Using_secrets.md @@ -6,11 +6,11 @@ If you need to retrieve an API key and submit it as plain text. ```powershell $vrkdApiKey = Get-Secret -Name VrkdApiKey -AsPlainText -Connect-Verkada -org_id [your_orgId] -x_api_key $vrkdApiKey +Connect-Verkada -x_api_key $vrkdApiKey #or -Connect-Verkada -org_id [your_orgId] -x_api_key (Get-Secret -Name VrkdApiKey -AsPlainText) +Connect-Verkada -x_api_key (Get-Secret -Name VrkdApiKey -AsPlainText) ``` If you need to retrieve a user password and submit it as a SecureString.