Functions/Common.ps1
|
<# .SYNOPSIS Connects to an Azure DevOps organization for use with DevOpsTools.AzureDevOps.PSRule cmdlets. .DESCRIPTION The Connect-AzDevOps function establishes a connection to an Azure DevOps organization using one of several authentication methods: Personal Access Token (PAT), Service Principal, Managed Identity, or Bearer token. The connection details are stored in a script-level variable for use by other cmdlets in the DevOpsTools.AzureDevOps.PSRule module. .PARAMETER Organization The name of the Azure DevOps organization to connect to. .PARAMETER OrganizationId Optional organization ID (GUID). Stored on the connection for every authentication mode; the organization billing settings export requires it. .PARAMETER TokenType The permission profile of the credential: FullAccess (default), ReadOnly, or FineGrained. Collectors skip data the profile cannot read. .PARAMETER PAT A Personal Access Token (PAT) used to authenticate to Azure DevOps. Used with the 'Pat' parameter set. .PARAMETER TenantId The Microsoft Entra ID tenant ID for Service Principal authentication. Used with the 'ServicePrincipal' parameter set. .PARAMETER ClientId The client ID of the Service Principal. Used with the 'ServicePrincipal' parameter set. .PARAMETER ClientSecret The client secret of the Service Principal. Used with the 'ServicePrincipal' parameter set. .PARAMETER ManagedIdentity Specifies that a Managed Identity should be used for authentication. Used with the 'ManagedIdentity' parameter set. .PARAMETER AccessToken A Bearer token (e.g., OAuth 2.0 access token from Microsoft Entra ID) used to authenticate to Azure DevOps. Used with the 'Bearer' parameter set. .EXAMPLE Connect-AzDevOps -Organization "MyOrg" -PAT "abc123" Connects to the "MyOrg" organization using a Personal Access Token. .EXAMPLE Connect-AzDevOps -Organization "MyOrg" -AccessToken "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik..." Connects to the "MyOrg" organization using a Bearer token. .EXAMPLE Connect-AzDevOps -Organization "MyOrg" -TenantId "00000000-0000-0000-0000-000000000000" -ClientId "11111111-1111-1111-1111-111111111111" -ClientSecret "secret" Connects to the "MyOrg" organization using a Service Principal. .EXAMPLE Connect-AzDevOps -Organization "MyOrg" -ManagedIdentity Connects to the "MyOrg" organization using a Managed Identity. .NOTES - The Bearer token must have appropriate permissions for Azure DevOps APIs (e.g., read access to projects and settings). - Bearer tokens typically expire after 1 hour; re-run Connect-AzDevOps with a new token if expired. - The connection is stored in a script-level variable and persists for the session. .LINK https://docs.microsoft.com/en-us/rest/api/azure/devops/ #> function Connect-AzDevOps { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $Organization, [Parameter(ParameterSetName = 'Bearer')] [Parameter(ParameterSetName = 'Pat')] [Parameter(ParameterSetName = 'ServicePrincipal')] [Parameter(ParameterSetName = 'ManagedIdentity')] [string] $OrganizationId, [Parameter(ParameterSetName = 'Pat')] [Parameter(ParameterSetName = 'ServicePrincipal')] [Parameter(ParameterSetName = 'ManagedIdentity')] [Parameter(ParameterSetName = 'Bearer')] [ValidateSet('FullAccess', 'ReadOnly', 'FineGrained')] [string] $TokenType = 'FullAccess', [Parameter(Mandatory = $true, ParameterSetName = 'Pat')] [string] $PAT, [Parameter(Mandatory = $true, ParameterSetName = 'ServicePrincipal')] [string] $TenantId, [Parameter(Mandatory = $true, ParameterSetName = 'ServicePrincipal')] [string] $ClientId, [Parameter(Mandatory = $true, ParameterSetName = 'ServicePrincipal')] [string] $ClientSecret, [Parameter(Mandatory = $true, ParameterSetName = 'ManagedIdentity')] [switch] $ManagedIdentity, [Parameter(Mandatory = $true, ParameterSetName = 'Bearer')] [string] $AccessToken ) try { switch ($PSCmdlet.ParameterSetName) { 'Pat' { $candidate = [AzureDevOpsConnection]::new($Organization, $PAT, $TokenType) } 'ServicePrincipal' { $candidate = [AzureDevOpsConnection]::new($Organization, $ClientId, $ClientSecret, $TenantId, $TokenType) } 'ManagedIdentity' { $candidate = [AzureDevOpsConnection]::new($Organization, $TokenType) } 'Bearer' { $candidate = [AzureDevOpsConnection]::new($Organization, $OrganizationId, $AccessToken, $TokenType, $true) } } $candidate.OrganizationId = $OrganizationId # Verify connection with a simple API call $uri = "https://dev.azure.com/$Organization/_apis/projects?api-version=7.0" $verification = Invoke-RestMethod -Uri $uri -Method Get -Headers $candidate.GetHeader() if ($verification -is [string]) { throw 'Authentication failed.' } $script:connection = $candidate Write-Verbose "Successfully connected to Azure DevOps organization: $Organization" } catch { throw "Failed to connect to Azure DevOps: $($_.Exception.Message)" } } Export-ModuleMember -Function Connect-AzDevOps <# .SYNOPSIS Disconnect from Azure DevOps .DESCRIPTION Disconnect from Azure DevOps and remove the connection object .EXAMPLE Disconnect-AzDevOps #> Function Disconnect-AzDevOps { [CmdletBinding()] param () Clear-Variable connection -Scope Script -ErrorAction SilentlyContinue Remove-Variable connection -Scope Script -ErrorAction SilentlyContinue $script:connection = "" $script:connection = $null } # End of Function Disconnect-AzDevOps <# .SYNOPSIS Get all Azure DevOps projects for an organization .DESCRIPTION Get all Azure DevOps projects for an organization using Azure DevOps Rest API .EXAMPLE Get-AzDevOpsProject #> function Get-AzDevOpsProject { [CmdletBinding()] param ( [Parameter()] [string] $Project = "" ) if ($null -eq $script:connection) { throw "Not connected to Azure DevOps. Run Connect-AzDevOps first" } $header = $script:connection.GetHeader() $Organization = $script:connection.Organization Write-Verbose "Getting projects for organization $Organization" if([string]::IsNullOrEmpty($Project) -eq $false) { $uri = "https://dev.azure.com/$Organization/_apis/projects/$($Project)?api-version=7.2-preview.4" } else { $uri = "https://dev.azure.com/$Organization/_apis/projects?api-version=7.2-preview.4" } Write-Verbose "URI: $uri" try { $response = if ($Project) { Invoke-RestMethod -Uri $uri -Method Get -Headers $header } else { Invoke-AzDevOpsPagedRequest -Uri $uri -Headers $header } # If the response is not an object but a string, the authentication failed if ($response -is [string]) { throw "Authentication failed or organization not found" } } catch { throw "Failed to get projects from Azure DevOps" } if ($response.PSObject.Properties.Name -contains 'value') { return $response.value } else { if (-not $Project) { Set-AzDevOpsMissingCollectionData -Data $response -RequiredProperties 'value' if ($null -ne $script:collectionStatus) { return } } return $response } } # End of Function Get-AzDevOpsProject <# .SYNOPSIS Get all Azure DevOps Project Acls .DESCRIPTION Get all Azure DevOps Project Acls using Azure DevOps Rest API .PARAMETER ProjectId Project Id for Azure DevOps .EXAMPLE Get-AzDevOpsProjectAcls -ProjectId $ProjectId #> function Get-AzDevOpsProjectAcls { [CmdletBinding()] param ( [Parameter(Mandatory)] [string] $ProjectId ) if ($null -eq $script:connection) { throw "Not connected to Azure DevOps. Run Connect-AzDevOps first" } $Organization = $script:connection.Organization $header = $script:connection.GetHeader() $aclInfo = @{ 'Environments' = "https://dev.azure.com/$Organization/_apis/accesscontrollists/83d4c2e6-e57d-4d6e-892b-b87222b7ad20?includeExtendedInfo=true&api-version=7.2-preview.1&token=Environments/$ProjectId" 'Pipelines' = "https://dev.azure.com/$Organization/_apis/accesscontrollists/33344d9c-fc72-4d6f-aba5-fa317101a7e9?includeExtendedInfo=true&api-version=7.2-preview.1&token=$ProjectId" 'ReleaseDefinitions' = "https://dev.azure.com/$Organization/_apis/accesscontrollists/c788c23e-1b46-4162-8f5e-d7585343b5de?includeExtendedInfo=true&api-version=7.2-preview.1&token=$ProjectId" 'Repositories' = "https://dev.azure.com/$Organization/_apis/accesscontrollists/2e9eb7ed-3c0a-47d4-87c1-0ffdd275fd87?includeExtendedInfo=true&api-version=7.2-preview.1&token=repoV2/$ProjectId" 'ServiceConnections' = "https://dev.azure.com/$Organization/_apis/accesscontrollists/49b48001-ca20-4adc-8111-5b60c903a50c?includeExtendedInfo=true&api-version=7.2-preview.1&token=endpoints/$ProjectId" 'VariableGroups' = "https://dev.azure.com/$Organization/_apis/accesscontrollists/b7e84409-6553-448a-bbb2-af228e07cbeb?includeExtendedInfo=true&api-version=7.2-preview.1&token=Library/$ProjectId" } $acls = @{} try { # Walk through the aclInfo hashtable and get the acls for each resource foreach($key in $aclInfo.Keys) { $aclUri = $aclInfo[$key] Write-Verbose "Getting $key ACLs for project $ProjectId" $aclResponse = Invoke-RestMethod -Uri $aclUri -Method Get -Headers $header # If the response is not an object but a string, the authentication failed if ($aclResponse -is [string] -or $null -eq $aclResponse.value) { throw "Authentication failed or project not found" } $acls.Add($key,$aclResponse.value) } } catch { throw $_.Exception.Message } return $acls } <# .SYNOPSIS Export the Azure DevOps Project .DESCRIPTION Export the Azure DevOps Project using Azure DevOps Rest API to a JSON file .PARAMETER Project Project name for Azure DevOps .PARAMETER OutputPath Output path for JSON files .PARAMETER PassThru Return the exported project as objects to the pipeline instead of writing to a file .EXAMPLE Export-AzDevOpsProject -Project $Project -OutputPath $OutputPath .NOTES The output file will be named $Project.prj.ado.json #> function Export-AzDevOpsProject { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $Project, [Parameter(ParameterSetName = 'JsonFile')] [string] $OutputPath, [Parameter(ParameterSetName = 'PassThru')] [switch] $PassThru ) if ($null -eq $script:connection) { throw "Not connected to Azure DevOps. Run Connect-AzDevOps first" } $header = $script:connection.GetHeader() $Organization = $script:connection.Organization Write-Verbose "Getting project $Project for organization $Organization" try { $response = Get-AzDevOpsProject -Project $Project Set-AzDevOpsMissingCollectionData -Data $response -RequiredProperties @('id', 'name') $response | Add-Member -MemberType NoteProperty -Name ObjectType -Value "Azure.DevOps.Project" $response | Add-Member -MemberType NoteProperty -Name ObjectName -Value "$Organization.$Project" # Add the Project Acls to the response object $acls = Get-AzDevOpsProjectAcls -ProjectId $response.id $response | Add-Member -MemberType NoteProperty -Name ProjectAcls -Value $acls # Add a new id field to the response object $response.id = @{ originalId = $response.id; resourceName = $response.name; project = $Project; organization = $Organization } | ConvertTo-Json -Depth 100 } catch { throw $_.Exception.Message } if($PassThru) { Write-Output $response } else { Write-Verbose "Exporting project $Project as file $Project.prj.ado.json" $response | ConvertTo-Json -Depth 100 | Out-File -FilePath "$OutputPath/$Project.prj.ado.json" } } # End of Function Export-AzDevOpsProject |