我正在尝试编写一个 PowerShell 脚本来列出在 Azure DevOps 中直接分配的所有用户,不包括某些技术帐户。该脚本还应从 Azure AD 检索其经理的电子邮件地址。但是,该脚本没有返回预期结果。
我想要实现的目标:
我当前的脚本:
# Variables
$organization = "org-name"
$azureDevOpsPat = "your-azure-devops-pat" # PAT with full access
$tenantId = "your-tenant-id"
$clientId = "your-client-id"
$excludedUsers = @(
"[email protected]",
"[email protected]",
"[email protected]"
)
# Scopes for Microsoft Graph
$scopes = @("User.Read.All", "Directory.Read.All")
# Connect to Microsoft Graph
Connect-MgGraph -ClientId $clientId -TenantId $tenantId -Scopes $scopes -DeviceCode
# Azure DevOps API Endpoint
$devOpsApiUrl = "https://vsaex.dev.azure.com/$organization/_apis/userentitlements?api-version=7.0"
# Prepare Azure DevOps API Headers
$devOpsHeaders = @{
Authorization = ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$azureDevOpsPat")))
"Content-Type" = "application/json"
}
# Fetch user entitlements from Azure DevOps
$devOpsResponse = Invoke-RestMethod -Uri $devOpsApiUrl -Headers $devOpsHeaders -Method Get
# Check if the response contains any users
if ($devOpsResponse.count -gt 0) {
# Include all users with direct assignments
$directUsers = $devOpsResponse.value | Where-Object {
$_.accessLevel.assignmentSource -eq "direct"
}
foreach ($user in $directUsers) {
$userEmail = $user.user.mailAddress
$userPrincipalName = $user.user.principalName
# Get user identifier for exclusion check (UPN or Email)
$userIdentifier = if ($userPrincipalName) { $userPrincipalName } else { $userEmail }
# Check if the user is in the exclusion list
if ($excludedUsers -contains $userIdentifier) {
continue
}
# Get user details from Microsoft Graph API
try {
$userGraphResponse = Get-MgUser -UserId $userIdentifier -ErrorAction Stop
$userEmail = $userGraphResponse.Mail
# Get manager's information
try {
$managerResponse = Get-MgUserManager -UserId $userIdentifier -ErrorAction Stop
$managerUpn = $managerResponse.UserPrincipalName
} catch {
$managerUpn = $null
}
} catch {
continue
}
# Collect email addresses
if ($userEmail) {
$userEmailAddresses += $userEmail
}
if ($managerUpn) {
$managerEmailAddresses += $managerUpn
}
}
# Output the lists
$userEmailsString = $userEmailAddresses -join "; "
$managerEmailsString = $managerEmailAddresses -join "; "
Write-Host "User Emails (To): $userEmailsString"
Write-Host "Manager Emails (CC): $managerEmailsString"
} else {
Write-Host "No user entitlements found in Azure DevOps."
}
# Disconnect from Microsoft Graph
Disconnect-MgGraph
问题:
当我运行该脚本时,它会返回“找到 0 个具有直接分配的用户”,即使我知道在 Azure DevOps 中存在具有直接分配的用户。 手动调用 Azure DevOps API 仅返回我自己的用户权限,并且 $directUsers.Count 为 0。
附加信息:
我在 macOS 上运行 PowerShell。 使用最新版本的 Microsoft.Graph 模块。 在 Azure AD(租户所有者)中拥有读取用户信息所需的权限。
您的脚本中有几个错误。
AssignmentSource
支持的值为groupRule
、none
和unknown
。要获取分配源为直接的用户,您应该使用 $_.accessLevel.assignmentSource -eq "unknown"
if ($devOpsResponse.count -gt 0)
更改为 if ($devOpsResponse.members.Count -gt 0)
,将 $directUsers = $devOpsResponse.value
更改为 $directUsers = $devOpsResponse.members
。{
"members": [
{
"user": {
...
},
UserId
是AAD中用户的对象ID,不能使用UPN或电子邮件作为输入。请改用 UPN 作为过滤器。例如,$userGraphResponse = Get-MgUser -Filter "userPrincipalName eq '$userIdentifier'" -ErrorAction Stop
。$userId = $userGraphResponse.Id
获取用户的id,稍后会用到。UserId
是对象id,使用$userId
作为输入来获取有关该用户的经理的信息。$managerResponse.UserPrincipalName
的响应中没有这样的参数Get-MgUserManager
。先运行 Get-MgUserManager
获取管理员的 id,然后运行 Get-MgUser
获取该管理员的邮件。以下是修改后的脚本供您参考。
# Variables
$organization = "{DevOps Org}"
$azureDevOpsPat = "{PAT}" # PAT with full access
$tenantId = "{tenant id}"
$clientId = "{client id}"
$clientSecret = "{client secret}"
$secureClientSecret = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ($clientId, $secureClientSecret)
$excludedUsers = @(
"[email protected]",
"[email protected]",
"[email protected]"
)
# Scopes for Microsoft Graph
$scopes = @("User.Read.All", "Directory.Read.All")
# Connect to Microsoft Graph
Connect-MgGraph -TenantId $tenantId -ClientSecretCredential $credential
# Azure DevOps API Endpoint
$devOpsApiUrl = "https://vsaex.dev.azure.com/$organization/_apis/userentitlements?top=2000&api-version=7.0"
# Prepare Azure DevOps API Headers
$devOpsHeaders = @{
Authorization = ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$azureDevOpsPat")))
"Content-Type" = "application/json"
}
# Fetch user entitlements from Azure DevOps
$devOpsResponse = Invoke-RestMethod -Uri $devOpsApiUrl -Headers $devOpsHeaders -Method Get
# Write-Host "Fetched user entitlements: $($devOpsResponse | ConvertTo-Json -Depth 3)"
# Check if the response contains any users
if ($devOpsResponse.members.Count -gt 0) {
# Include all users with direct assignments
$directUsers = $devOpsResponse.members | Where-Object {
$_.accessLevel.assignmentSource -eq "unknown"
}
foreach ($user in $directUsers) {
# Write-Host "user: $($user | ConvertTo-Json -Depth 3)"
$userEmail = $user.user.mailAddress
$userPrincipalName = $user.user.principalName
# Get user identifier for exclusion check (UPN or Email)
$userIdentifier = if ($userPrincipalName) { $userPrincipalName } else { $userEmail }
# Write-Host $userIdentifier
# Check if the user is in the exclusion list
if ($excludedUsers -contains $userIdentifier) {
continue
}
# Get user details from Microsoft Graph API
try {
$userGraphResponse = Get-MgUser -Filter "userPrincipalName eq '$userIdentifier'" -ErrorAction Stop
$userId = $userGraphResponse.Id
$userEmail = $userGraphResponse.Mail
# Get manager's information
try {
$managerResponse = Get-MgUserManager -UserId $userId -ErrorAction Stop
# Write-Host "managerResponse: $($managerResponse | ConvertTo-Json -Depth 3)"
$managerId= $managerResponse.Id
$manager = Get-MgUser -UserId $managerId
$managerMail = $manager.Mail
} catch {
$managerMail = $null
}
} catch {
continue
}
# Collect email addresses
if ($userEmail) {
$userEmailsString += $userEmail + ";"
}
if ($managerMail) {
$managerEmailsString += $managerMail + ";"
}
}
# Output the lists
Write-Host "User Emails (To): $userEmailsString"
Write-Host "Manager Emails (CC): $managerEmailsString"
} else {
Write-Host "No user entitlements found in Azure DevOps."
}
# Disconnect from Microsoft Graph
Disconnect-MgGraph
结果: