1
1
# !/usr/bin/env pwsh
2
2
<#
3
3
. SYNOPSIS
4
- Find Azure DevOps Service Connections
4
+ List Azure DevOps Service Connections
5
5
. DESCRIPTION
6
- Use the Microsoft Graph API to find Azure DevOps Service Connections by organization & project, using Azure DevOps Service Connection naming convention
6
+ Use the Azure CLI to find Azure DevOps Service Connections by organization & project
7
7
#>
8
- # Requires -Version 7
8
+ # Requires -Version 7.2
9
+
10
+ [CmdletBinding (DefaultParameterSetName = ' name' )]
9
11
param (
10
- [parameter (Mandatory = $false , ParameterSetName = " Organization " , HelpMessage = " Name of the Azure DevOps Organization " )]
11
- [string ]
12
- $Organization = (( $ env: AZDO_ORG_SERVICE_URL ?? $ env: SYSTEM_COLLECTIONURI ) -split ' / ' | Select-Object - Index 3 ) ,
12
+ [parameter (Mandatory = $false , ParameterSetName = " app " )]
13
+ [guid [] ]
14
+ $AppId ,
13
15
14
- [parameter (Mandatory = $false , ParameterSetName = " Organization " , HelpMessage = " Name of the Azure DevOps Project " )]
16
+ [parameter (Mandatory = $false , ParameterSetName = " name " , HelpMessage = " Name of the Service Connection " )]
15
17
[string ]
16
- $Project ,
17
-
18
- [parameter (Mandatory = $false )]
19
- [switch ]
20
- $HasCertificates = $false ,
21
-
22
- [parameter (Mandatory = $false )]
23
- [switch ]
24
- $HasNoCertificates = $false ,
25
-
26
- [parameter (Mandatory = $false )]
27
- [switch ]
28
- $HasFederation = $false ,
29
-
30
- [parameter (Mandatory = $false )]
31
- [switch ]
32
- $HasNoFederation = $false ,
18
+ $ServiceConnectionName ,
33
19
34
- [parameter (Mandatory = $false )]
35
- [switch ]
36
- $HasSecrets = $false ,
37
-
38
- [parameter (Mandatory = $false )]
39
- [switch ]
40
- $HasNoSecrets = $false ,
20
+ [parameter (Mandatory = $false , HelpMessage = " Name of the Azure DevOps Project" )]
21
+ [string ]
22
+ [ValidateNotNullOrEmpty ()]
23
+ $Project = $env: SYSTEM_TEAMPROJECT ,
41
24
42
- [parameter (Mandatory = $false , HelpMessage = " Azure subscription id" )]
25
+ [parameter (Mandatory = $false , HelpMessage = " Url of the Azure DevOps Organization" )]
26
+ [uri ]
43
27
[ValidateNotNullOrEmpty ()]
44
- [guid ]
45
- $SubscriptionId ,
28
+ $OrganizationUrl = ($env: AZDO_ORG_SERVICE_URL ?? $env: SYSTEM_COLLECTIONURI ),
46
29
47
- [parameter (Mandatory = $false , HelpMessage = " Azure Active Directory tenant id" )]
48
- [guid ]
49
- $TenantId = ($env: ARM_TENANT_ID ?? $env: AZURE_TENANT_ID ?? [guid ]::Empty)
30
+ [parameter (Mandatory = $false )]
31
+ [ValidateSet (' List' , ' Table' )]
32
+ [string ]
33
+ $Format = ' List'
50
34
)
51
-
52
- Write-Debug $MyInvocation.line
35
+ Write-Verbose $MyInvocation.line
53
36
. (Join-Path $PSScriptRoot .. functions.ps1)
37
+ $apiVersion = " 7.1"
38
+ if ($AppId ) {
39
+ $AppId | Foreach-Object {$_.ToString ().ToLower()} | Set-Variable AppId
40
+ }
54
41
55
- # Login to Azure CLI
56
- Write-Verbose " Logging into Azure..."
57
- Login- Az - Tenant ([ref ]$TenantId )
58
-
59
- $message = " Identities of type 'Application' in Azure DevOps"
60
- if ($Organization ) {
61
- $federationPrefix += " sc://${Organization} /"
62
- $namePrefix = " ${Organization} -"
63
- $message += " organization '${Organization} '"
64
- } elseif (! $HasFederation ) {
65
- Write-Warning " Organization not specified, listing all Service Connections with federation instead"
66
- $HasFederation = $true
42
+ # -----------------------------------------------------------
43
+ # Log in to Azure
44
+ if (-not (Get-Command az - ErrorAction SilentlyContinue)) {
45
+ Write-Error " Azure CLI is not installed. You can get it here: http://aka.ms/azure-cli"
46
+ exit 1
67
47
}
68
- if ($Project ) {
69
- if (! $Organization ) {
70
- Write-Warning " Project '${Project} ' requires Organization to be specified"
71
- exit 1
72
- }
73
- $federationPrefix += " ${Project} /"
74
- $namePrefix += " ${Project} -"
75
- $message += " and project '${Project} '"
48
+ az account show - o json 2> $null | ConvertFrom-Json | Set-Variable account
49
+ if (! $account ) {
50
+ az login -- allow- no- subscriptions - o json | ConvertFrom-Json | Set-Variable account
51
+ }
52
+ # Log in to Azure & Azure DevOps
53
+ $OrganizationUrl = $OrganizationUrl.ToString ().Trim(' /' )
54
+ az account get-access - token -- resource 499b84ac- 1321 - 427f - aa17- 267ca6975798 `
55
+ -- query " accessToken" `
56
+ -- output tsv `
57
+ | Set-Variable accessToken
58
+ if (! $accessToken ) {
59
+ Write-Error " $ ( account.user.name) failed to get access token for Azure DevOps"
60
+ exit 1
61
+ }
62
+ if (! (az extension list -- query " [?name=='azure-devops'].version" - o tsv)) {
63
+ Write-Host " Adding Azure CLI extension 'azure-devops'..."
64
+ az extension add - n azure- devops - y - o none
65
+ }
66
+ $accessToken | az devops login -- organization $OrganizationUrl
67
+ if ($lastexitcode -ne 0 ) {
68
+ Write-Error " $ ( $account.user.name ) failed to log in to Azure DevOps organization '${OrganizationUrl} '"
69
+ exit $lastexitcode
76
70
}
77
- $federationPrefix ?? = " sc://"
78
71
79
- if ($HasFederation ) {
80
- $message += " using federation"
81
- Write-Host " Searching for ${message} ..."
82
- Find-ApplicationsByFederation - StartsWith $federationPrefix | Set-Variable msftGraphObjects
83
- } else {
84
- Write-Host " Searching for ${message} ..."
85
- Find-ApplicationsByName - StartsWith $namePrefix | Set-Variable msftGraphObjects
72
+ # -----------------------------------------------------------
73
+ # Check parameters
74
+ az devops project show -- project " ${Project} " -- organization $OrganizationUrl -- query id - o tsv | Set-Variable projectId
75
+ if (! $projectId ) {
76
+ Write-Error " Project '${Project} ' not found in organization '${OrganizationUrl} "
77
+ exit 1
86
78
}
87
79
88
- Write-Host " ${message} :"
89
- $msftGraphObjects | Where-Object {
90
- # We already check federation on organization/project, so we can ignore it here
91
- ! $HasFederation -or $_.name -match " ${Organization} -[^-]+-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
92
- } | Where-Object {
93
- ! $SubscriptionId -or $_.name -match $SubscriptionId
94
- } | Where-Object {
95
- $_.certCount -ge ($HasCertificates ? 1 : 0 )
96
- } | Where-Object {
97
- ! $HasNoCertificates -or $_.certCount -eq 0
98
- } | Where-Object {
99
- ! $HasFederation -or $_.federatedSubjects -match " sc://[^/]+/[^/]+/[^/]+"
100
- } | Where-Object {
101
- ! $HasNoFederation -or [string ]::IsNullOrEmpty($_.federatedSubjects )
102
- } | Where-Object {
103
- $_.secretCount -ge ($HasSecrets ? 1 : 0 )
80
+ # -----------------------------------------------------------
81
+ # Retrieve the service connection
82
+
83
+ $getApiUrl = " ${OrganizationUrl} /${Project} /_apis/serviceendpoint/endpoints?type=azurerm&includeFailed=true&includeDetails=true&api-version=${apiVersion} "
84
+ az rest -- resource 499b84ac- 1321 - 427f - aa17- 267ca6975798 - u " ${getApiUrl} " - m GET -- query " sort_by(value[?authorization.scheme=='ServicePrincipal' && data.creationMode=='Automatic' && !(isShared && serviceEndpointProjectReferences[0].projectReference.name!='${Project} ')],&name)" - o json `
85
+ | Tee-Object - Variable rawResponse `
86
+ | ConvertFrom-Json `
87
+ | Tee-Object - Variable serviceEndpoints `
88
+ | Format-List | Out-String | Write-Debug
89
+ if (! $serviceEndpoints -or ($serviceEndpoints.count - eq 0 )) {
90
+ Write-Warning " No convertible service connections found"
91
+ exit 1
92
+ }
93
+
94
+ $serviceEndpoints | ForEach-Object {
95
+ " https://portal.azure.com/{0}/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Overview/appId/{1}" -f $_.authorization.parameters.tenantId , $_.authorization.parameters.servicePrincipalId | Set-Variable applicationPortalLink
96
+ $_ | Add-Member - NotePropertyName applicationPortalLink - NotePropertyValue $applicationPortalLink
97
+ " {0}/{1}/_settings/adminservices?resourceId={2}" -f $OrganizationUrl , $_.serviceEndpointProjectReferences [0 ].projectReference.id, $_.id | Set-Variable serviceConnectionPortalLink
98
+ $_ | Add-Member - NotePropertyName serviceConnectionPortalLink - NotePropertyValue $serviceConnectionPortalLink
99
+ $_ | Add-Member - NotePropertyName authorizationScheme - NotePropertyValue $_.authorization.scheme
100
+ $_ | Add-Member - NotePropertyName appId - NotePropertyValue $_.authorization.parameters.servicePrincipalId.ToLower ()
101
+
102
+ $_
104
103
} | Where-Object {
105
- ! $HasNoSecrets -or $_.secretCount -eq 0
106
- } | Format-Table - AutoSize
104
+ # We already check federation on organization/project, so we can ignore it here
105
+ ! $AppId -or ($_.appId -in $AppId )
106
+ } | Set-Variable filteredServiceEndpoints
107
+
108
+ switch ($Format ) {
109
+ ' List' {
110
+ $filteredServiceEndpoints | Format-List
111
+ }
112
+ ' Table' {
113
+ $filteredServiceEndpoints | Format-Table - AutoSize - Property name, authorizationScheme, appId
114
+ }
115
+ }
116
+ $filteredServiceEndpoints | ForEach-Object {
117
+ $_.appId
118
+ } | Set-Variable matchedAppIds
119
+ Write-Host " Matched AppIds: $ ( $matchedAppIds -join ' , ' ) "
120
+ $AppId | Where-Object {
121
+ $_ -notin $matchedAppIds
122
+ }
123
+ | Set-Variable unmatchedAppIds
124
+ if ($unmatchedAppIds ) {
125
+ Write-Warning " Unmatched AppIds: $ ( $unmatchedAppIds -join ' , ' ) "
126
+ }
0 commit comments