Skip to content

Instantly share code, notes, and snippets.

@jschlackman
Last active January 30, 2025 20:03
Show Gist options
  • Save jschlackman/d44ef32de2062506fe0838a01704091d to your computer and use it in GitHub Desktop.
Save jschlackman/d44ef32de2062506fe0838a01704091d to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Searches for and updates Jira users in preparation for SSO by changing their login name to match their email address.
.DESCRIPTION
Uses the Jira Server API (via the JiraPS module) to bulk update the usernames of selected Jira users so that their login name matches their email address, allowing them to be correctly logged in via SSO mechanisms such as SAML.
Author: James Schlackman
Last Modified: January 29 2025
.PARAMETER ServerUri
Jira server to connect to.
.PARAMETER UserFilter
User search filter. Uses the same syntax as searches within the user management section of Jira.
.PARAMETER ConfirmChanges
Confirm update to each individual user. When not specified, batch process all users selected for update without additional confirmation.
.PARAMETER SkipAuthenticatedUser
Set to false to include the user account used to authenticate to the API in any batch change.
When true (default), the user account used to authenticate to the API will be excluded from any change operations to prevent the login token from being expired.
.NOTES
Information on documentation for the JiraPS module can be found at https://atlassianps.org/docs/JiraPS/
#>
#Requires -Modules JiraPS
Param(
[Parameter(Mandatory)] [String] $ServerUri,
[Parameter(Mandatory)] [String] $UserFilter,
[Parameter()] [Switch] $ConfirmChanges,
[Parameter()] [Bool] $SkipAuthenticatedUser = $true
)
Import-Module JiraPS
Function Connect-JiraServer {
Param(
[Parameter(Mandatory=$true)] [string]$ServerUri
)
# Clean up server parameter
$ServerUri = $ServerUri.Trim().TrimEnd('/').ToLower()
# Default to https:// if protocol not specified
If ($ServerUri -notmatch '^http(s)?:\/\/') {
$ServerUri = 'https://{0}' -f $ServerUri
}
# Check for a current connection
$currentServer = Get-JiraServerInfo -ErrorAction SilentlyContinue
# If already connected to the correct server, return immediately
If ($currentServer.BaseURL -eq $ServerUri) {
Write-Host ($currentServer | Out-String)
Return $currentServer
} Else {
# Set the server Uri
Set-JiraConfigServer -Server $ServerUri
# Get new credentials
Do {
$newCred = $null
$newCred = Get-Credential
# Test provided credentials
If ($newCred) {
$serverInfo = Get-JiraServerInfo -Credential $newCred
} Else {
$serverInfo = $null
}
} Until (![bool]$newCred -or $serverInfo)
# If we got server info successfully, start a session with those credentials
If ($serverInfo) {
New-JiraSession -Credential $newCred
Write-Host "`nConnection established." -ForegroundColor Green
Write-Host ($serverInfo | Out-String)
Return $serverInfo
} Else {
Return $null
}
}
}
# Attempt to connect to the server and process users
If (Connect-JiraServer -ServerUri $ServerUri) {
If (!$SkipAuthenticatedUser) {
Write-Warning 'Authenticated user is NOT being skipped during updates. If credentials are modified during batch update, all subsequent API operations will fail.'
}
Write-Host 'Finding users that match ' -NoNewline
Write-Host $UserFilter -ForegroundColor Cyan -NoNewline
Write-Host '...'
$candidateUsers = @()
$startAt = 0
# Fetch user details in batches of 1000 until the number returned is less than the maximum (indicating no more users to fetch)
do {
$apiResult = Get-JiraUser -UserName $UserFilter -MaxResults 1000 -Skip $startAt
$candidateUsers += $apiResult
$startAt += 1000
} until (@($apiResult).Count -lt 1000)
# Now process any returned users
If ($candidateUsers) {
Write-Host ('Matching users: {0}' -f @($candidateUsers).Count)
Write-Host 'Review grid output and select users to update.'
# Show grid for selection
$changeUsers = $candidateUsers| Select Name,DisplayName,EmailAddress,Active,@{Name='Groups';Expression={($_.Groups -join ', ')}} | Out-GridView -Title 'Select Jira users to update' -OutputMode Multiple
If ($SkipAuthenticatedUser) {
$changeUsers = $changeUsers | Where-Object -Property Name -ne (Get-JiraSession).Username
}
Write-Host ('Users selected for update: {0}' -f @($changeUsers).Count)
$jobActivity = 'Updating users...'
# Process selected users
For ($changeIndex = 0; $changeIndex -lt @($changeUsers).Count; $changeIndex++ ) {
$changeUser = @($changeUsers)[$changeIndex]
# Post job progress
Write-Progress -Activity $jobActivity -CurrentOperation $changeUser.Name -PercentComplete ($changeIndex / @($changeUsers).Count * 100)
# Make sure the user confirms the change if required
$changeConfirmed = !$ConfirmChanges
# If the username and email already match, skip this user
If ($changeUser.Name -eq $changeUser.EmailAddress) {
Write-Verbose ('Skipping name update for user {0}, login name already matches email.' -f $changeUser.Name)
} Else {
# If the username does not match, update it
$changeDetail = @{name = $changeUser.EmailAddress}
# Write a summary of the changes to be submitted and ask for confirmation if required
If ($changeConfirmed) {
Write-Verbose ('Username for {0} changing from {1} to {2}' -f $changeUser.DisplayName, $changeUser.Name, $changeDetail.name)
} Else {
Write-Host ("`nUsername for {0} will change from " -f $changeUser.DisplayName) -NoNewline
Write-Host $changeUser.Name -ForegroundColor Cyan -NoNewline
Write-Host ' to ' -NoNewline
Write-Host $changeDetail.Name -ForegroundColor Cyan
$changeConfirmed = (Read-Host 'Proceed? [y/N]').Trim().ToUpper() -eq 'Y'
}
# Once the change is confirmed
If ($changeConfirmed)
{
# Submit the change to the server
$changeResult = Set-JiraUser -User $changeUser.Name -Property $changeDetail -PassThru
# Report result
If ($changeResult -and ($changeResult.Name -eq $changeResult.EmailAddress)) {
If ($ConfirmChanges) {
Write-Host 'User update succeeded.' -ForegroundColor Green
} Else {
Write-Verbose 'User update succeeded.'
}
Write-Debug $changeResult
} Else {
Write-Host ('{0} - ' -f $changeUser.Name) -NoNewline
Write-Host 'User update FAILED.' -ForegroundColor Red
}
}
}
}
# Mark job completed
Write-Progress -Activity $jobActivity -Completed
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment