Hi!
I'm probably not the only one who has done this, so I'm sharing a ready-made solution with you until Fresh introduces automatic import. :)
I’ve split the solution into two separate PowerShell scripts. The first one takes a list of users to be added - it reads the "User" column, under which email addresses must be listed. Then it queries Fresh for the requestor ID of the person with that email. This is necessary because the import can only be done using the requestor ID, not the email address, etc.
Notes for the first script:
- Replace the paths where the file is located and where it should be saved after adding the requestor ID
- Replace the API Key with one that has the appropriate permissions
- Replace DOMAIN in the link with your domain
- The command `Start-Sleep -Seconds 1` - you can change the number of seconds if Fresh is unable to handle the volume of your requests and therefore fails to find users
- The final file will include an additional column with users who couldn't be found
- The script is unable to load agents - they have different IDs. In my case, I took their accounts from the export and added them manually
- The script removes duplicates
# File path
$inputPath = "C:\...\UserList.csv"
$outputPath = "C:\...\FinalExport.csv"
# API Key
$apiKey = "XYZ"
# Read email from column 'User'
$emails = Import-Csv -Path $inputPath | Select-Object -ExpandProperty User | Where-Object { $_ -and $_.Trim() -ne "" } | Select-Object -Unique
# Prepare list
$results = @()
foreach ($email in $emails) {
$url = "https://DOMAIN.freshservice.com/api/v2/requesters/?email=$email"
$curlCommand = "curl.exe -s -u $apiKey`:X -H `"Content-Type: application/json`" -X GET `"$url`""
try {
$json = Invoke-Expression $curlCommand | ConvertFrom-Json
if ($json.requesters.Count -gt 0) {
foreach ($requester in $json.requesters) {
$results += lPSCustomObject]@{
ID = $requester.id
Name = "$($requester.first_name) $($requester.last_name)"
Email = $requester.primary_email
NotFound = ""
}
}
} else {
$results += lPSCustomObject]@{
ID = ""
Name = ""
Email = ""
NotFound = $email
}
}
} catch {
$results += lPSCustomObject]@{
ID = ""
Name = ""
Email = ""
NotFound = $email
}
Write-Warning "Fail for e-mail: $email — $_"
}
Start-Sleep -Seconds 1
}
# Save to CSV
$results | Export-Csv -Path $outputPath -NoTypeInformation -Encoding UTF8
Write-Host "File FinalEport.csv saved in: $outputPath"
The second script takes the file generated by the first script, then reads the ID column and uses it to add users to the specified software via the API.
Notes for the second script:
- Replace the file path
- Replace XYZ with your API key
- Replace DOMAIN with your domain
- Replace SOFTWAREID with the ID of the software to which you want to add users (you can find it at the end of the page URL after opening the specific software)
- Just like in the first script, you can edit the Start-Sleep value
- The script will generate a file with a list of users that failed to be added
- However, I recommend comparing the number of rows in FinalExport to the user count in the software on Fresh - if the number of rows minus the header row matches, it means the import was successful
# CSV File path
$csvPath = "C:\...\FinalExport.csv"
# API
$username = "XYZ"
$password = "X"
$apiUrl = "https://DOMAIN.freshservice.com/api/v2/applications/SOFTWAREID/users"
# Prepare Basic Auth
$pair = "${username}:${password}"
$bytes = rSystem.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = Convert]::ToBase64String($bytes)
$headers = @{
"Content-Type" = "application/json"
"Authorization" = "Basic $base64"
}
# Failed users list
$failedUsers = @()
# Read CSV file and get ID column
$userList = Import-Csv -Path $csvPath
foreach ($entry in $userList) {
$userId = $entry.ID.Trim()
if (-not string]::IsNullOrWhiteSpace($userId)) {
$body = @{
application_users = @(
@{
user_id = long]$userId
source = "API"
}
)
} | ConvertTo-Json -Depth 3
try {
Invoke-RestMethod -Uri $apiUrl -Method Post -Headers $headers -Body $body
Write-Host "✅ User added ${userId}" -ForegroundColor Green
}
catch {
Write-Warning ("❌ Failed to add ${userId}: " + $_.Exception.Message)
$failedUsers += $userId
}
Start-Sleep -Seconds 1
}
}
# Summary
if ($failedUsers.Count -gt 0) {
Write-Host "`n❗ Users failed to add:" -ForegroundColor Yellow
$failedUsers | ForEach-Object { Write-Host " - $_" }
} else {
Write-Host "`n🎉 All users added" -ForegroundColor Cyan
}
If you have any issues, feel free to share them here or send me a DM – I’ll do my best to help when I have a moment.