Get email addresses for Bitbucket Cloud users
For some convoluted logic, the admins of a paid space in Bitbucket Cloud cannot programmatically retrieve user email addresses. This is ridiculous because one adds new users via email address! If your organization also uses Jira Cloud, there is a way to mash the data together and retrieve some email addresses for Bitbucket users. The user uuids are not always the same across the products, but this will give your list a good head start.
# correlating Bitbucket accounts with email addresses
# References:
# https://community.atlassian.com/t5/Bitbucket-questions/How-to-get-user-email-address-using-BitBucket-API/qaq-p/123783#M54771
# Bitbucket creds
$org = "example"
$username = "bgstack15@example.com"
$password = "ASTERISKS"
$CloudHeaders = @{ Authorization = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$($username):$($password)")) }
# jira creds
$jiraCloudHeaders = @{ Authorization = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$($username):$($password)")) }
# Get jira users
$jira0 = Invoke-RestMethod -Headers $jiraCloudHeaders -Uri "https://example.atlassian.net/rest/api/3/users/search?startAt=0&maxResults=1000"
$jira1 = Invoke-RestMethod -Headers $jiraCloudHeaders -Uri "https://example.atlassian.net/rest/api/3/users/search?startAt=1000&maxResults=1000"
$jira2 = Invoke-RestMethod -Headers $jiraCloudHeaders -Uri "https://example.atlassian.net/rest/api/3/users/search?startAt=2000&maxResults=1000"
$jira3 = Invoke-RestMethod -Headers $jiraCloudHeaders -Uri "https://example.atlassian.net/rest/api/3/users/search?startAt=3000&maxResults=1000"
$jira = $jira0 + $jira1 +$jira2 + $jira3
$jiranew = $jira | Group-Object accountId -AsHashTable
# Get bitbucket users
$bitbucket = Iterate-Values-From-BB-API -StartUri "https://api.bitbucket.org/2.0/workspaces/${org}/members" -AuthHeaders $CloudHeaders -SkipCount $True
# Combine them.
$bitbucket_expanded = $bitbucket | select-object -ExpandProperty user | Select-Object display_name, nickname, account_id
$bitbucket_expanded | Select-Object display_name, @{n='email';e={$jiranew[$_.account_id][0].emailAddress }} | Export-Csv -NoTypeInformation "bitbucket-emails.csv"
So you probably noticed the Iterate-Value-From-BB-API function. I wrote this myself, because I use it in a soon-to-be-published Powershell library for interacting with Bitbucket Cloud and Server instances.
Function _Iterate-Values-From-BB-API {
[CmdletBinding()]
param(
[Parameter(Mandatory = $False)][Hashtable]$AuthHeaders = ${global:CloudHeaders},
[Parameter(Mandatory = $True )][string]$StartUri,
[Parameter(Mandatory = $False)][string]$Location = "cloud", # "cloud" or "local" which sets page defaults
[Parameter(Mandatory = $False)][string]$Descriptor = "item",
[Parameter(Mandatory = $False)][boolean]$SkipCount = $False
)
# StartUri could be any of these, for example:
# https://api.bitbucket.org/2.0/repositories/${org}
# https://api.bitbucket.org/2.0/workspaces/${org}/projects
# https://git.example.com/rest/api/1.0/repos
# https://git.example.com/rest/api/1.0/projects
# https://api.bitbucket.org/2.0/teams/${org}/permissions/repositories?q=permission%3D%22write%22
Begin {
$done = $False
$output = @()
$size = -1
$count = 0
$Baseuri = $StartUri
$keepcounting = $false
# All this just to get object count for the local Bitbucket, because its API does not list total object count on each return page.
if (!($SkipCount)) {
if ($Location -like "*local*" ) {
$pageSize = 250
$CountUri = "$($Baseuri)?limit=$($pageSize)"
$localcount = 0
While (!$done) {
Write-Progress -Activity "Counting objects" -Status "$($localcount)"
$interim = Invoke-RestMethod $CountUri -Method "GET" -Headers $AuthHeaders
if ( !($interim.isLastPage) ) {
$localcount += $pageSize
$CountUri = "$($Baseuri)?limit=$($pageSize)&start=$($localcount)"
} else {
$done = $True
$localcount += $interim.size
}
}
$done = $False
$size = $localcount
} Elseif ($Location -like "*cloud*" ) {
$CountUri = "$($Baseuri)"
$cloudcount = 0
While (!$done) {
Write-Progress -Activity "Counting $($Descriptor)s" -Status "$($cloudcount)"
$interim = Invoke-RestMethod $CountUri -Method "GET" -Headers $AuthHeaders
# short circuit if size is provided
If ($interim.size -ne $null) {
$cloudcount = $interim.size
$done = $True
} Elseif ($interim.next -eq $null) {
$cloudcount += $interim.pagelen
$done = $True
} Else {
$cloudcount += $interim.pagelen
$Counturi = $interim.next
}
}
$done = $False
$size = $cloudcount
}
} Else {
# skip the count!
$size = 10
}
}
Process {
Write-Verbose "Will look for $($size) $($descriptor)s"
$Uri = $StartUri
While (!$done) {
$interim = Invoke-RestMethod -Uri $Uri -Method "GET" -Headers $AuthHeaders
if (!($SkipCount) -And $size -eq -1) { # only run once because it will always be the same
if ($interim.size -ne $null) { $size = $interim.size }
Else { $keepcounting = $True; $size += $interim.values.count }
}
if ($keepcounting) { $size += $interim.values.count }
$interim.values | % {
$output += $_ ;
$count += 1 ;
[int]$percent = ($count/$size)*100
$percent = (@($percent, 100) | Measure -Minimum).Minimum
$percent = (@($percent, 0) | Measure -Maximum).Maximum
Write-Progress -Activity "Listing $($descriptor)" -Status "$count/$size" -PercentComplete $percent
}
# Bitbucket Cloud uses property "next" but on-prem server uses "nextPageStart"
If ( $interim.next -ne $Null ) {
$Uri = $interim.next
} Elseif ( $interim.nextPageStart -ne $Null -And (!($interim.isLastPage)) ) {
$Uri = "$($Baseuri)?start=$($interim.nextPageStart)"
} else { $done = $True }
}
}
End { $output }
}
References
Web links
Ripped off and enhanced from a user in the Atlassian community: How to get user email address using BitBucket API?
Comments