From de3bb1db64aa88f105d3aa4ef7695b2ca67b8c2a Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Date: Sun, 11 May 2025 23:22:20 +0200 Subject: [PATCH 01/22] adding azdo test suit and preview module --- tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 | 164 + tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 | 4439 ++++++ tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml | 11788 ++++++++++++++++ .../Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml | 700 + tests/Azdo/AdopsMaesterTests.psd1 | 158 + tests/Azdo/AdopsMaesterTests.psm1 | 1014 ++ tests/Azdo/README.md | 12 + tests/Azdo/Test-Azdo.Tests.ps1 | 163 + .../Azdo/Test-AzdoAllowRequestAccessToken.md | 18 + ...doAllowTeamAdminsInvitationsAccessToken.md | 19 + ...ArtifactsExternalPackageProtectionToken.md | 22 + tests/Azdo/Test-AzdoAuditStreams.md | 24 + .../Test-AzdoEnforceAADConditionalAccess.md | 20 + tests/Azdo/Test-AzdoExternalGuestAccess.md | 13 + tests/Azdo/Test-AzdoFeedbackCollection.md | 14 + tests/Azdo/Test-AzdoLogAuditEvents.md | 19 + ...icEnrollmentAdvancedSecurityNewProjects.md | 19 + .../Test-AzdoOrganizationBadgesArePrivate.md | 17 + ...ganizationCreationClassicBuildPipelines.md | 23 + ...nizationCreationClassicReleasePipelines.md | 23 + ...obAuthorizationScopeNonReleasePipelines.md | 14 + ...itJobAuthorizationScopeReleasePipelines.md | 14 + ...doOrganizationLimitVariablesAtQueueTime.md | 17 + tests/Azdo/Test-AzdoOrganizationOwner.md | 16 + ...OrganizationProtectAccessToRepositories.md | 17 + ...ositorySettingsDisableCreationTFVCRepos.md | 17 + ...izationRepositorySettingsGravatarImages.md | 12 + .../Azdo/Test-AzdoOrganizationStageChooser.md | 14 + .../Azdo/Test-AzdoOrganizationStorageUsage.md | 15 + ...TaskRestrictionsDisableMarketplaceTasks.md | 17 + ...zationTaskRestrictionsDisableNode6Tasks.md | 16 + ...RestrictionsShellTaskArgumentValidation.md | 23 + ...ionTriggerPullRequestGitHubRepositories.md | 14 + ...est-AzdoProjectCollectionAdministrators.md | 15 + tests/Azdo/Test-AzdoPublicProjects.md | 18 + tests/Azdo/Test-AzdoResourceUsageProjects.md | 8 + .../Test-AzdoResourceUsageWorkItemTags.md | 5 + tests/Azdo/Test-AzdoSSHAuthentication.md | 17 + .../Azdo/Test-AzdoThirdPartyAccessViaOauth.md | 18 + 39 files changed, 18956 insertions(+) create mode 100644 tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 create mode 100644 tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 create mode 100644 tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml create mode 100644 tests/Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml create mode 100644 tests/Azdo/AdopsMaesterTests.psd1 create mode 100644 tests/Azdo/AdopsMaesterTests.psm1 create mode 100644 tests/Azdo/README.md create mode 100644 tests/Azdo/Test-Azdo.Tests.ps1 create mode 100644 tests/Azdo/Test-AzdoAllowRequestAccessToken.md create mode 100644 tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md create mode 100644 tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md create mode 100644 tests/Azdo/Test-AzdoAuditStreams.md create mode 100644 tests/Azdo/Test-AzdoEnforceAADConditionalAccess.md create mode 100644 tests/Azdo/Test-AzdoExternalGuestAccess.md create mode 100644 tests/Azdo/Test-AzdoFeedbackCollection.md create mode 100644 tests/Azdo/Test-AzdoLogAuditEvents.md create mode 100644 tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md create mode 100644 tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.md create mode 100644 tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md create mode 100644 tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md create mode 100644 tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md create mode 100644 tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md create mode 100644 tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md create mode 100644 tests/Azdo/Test-AzdoOrganizationOwner.md create mode 100644 tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.md create mode 100644 tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md create mode 100644 tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md create mode 100644 tests/Azdo/Test-AzdoOrganizationStageChooser.md create mode 100644 tests/Azdo/Test-AzdoOrganizationStorageUsage.md create mode 100644 tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md create mode 100644 tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md create mode 100644 tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md create mode 100644 tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md create mode 100644 tests/Azdo/Test-AzdoProjectCollectionAdministrators.md create mode 100644 tests/Azdo/Test-AzdoPublicProjects.md create mode 100644 tests/Azdo/Test-AzdoResourceUsageProjects.md create mode 100644 tests/Azdo/Test-AzdoResourceUsageWorkItemTags.md create mode 100644 tests/Azdo/Test-AzdoSSHAuthentication.md create mode 100644 tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.md diff --git a/tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 b/tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 new file mode 100644 index 000000000..2c8934daa --- /dev/null +++ b/tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 @@ -0,0 +1,164 @@ +# +# Module manifest for module 'ADOPS' +# +# Generated by: the PwrOps collective +# +# Generated on: 2025-04-11 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = '.\ADOPS.psm1' + +# Version number of this module. +ModuleVersion = '1.1.2' + +# Supported PSEditions +CompatiblePSEditions = 'Core' + +# ID used to uniquely identify this module +GUID = '2fe11b9f-f7ec-4dca-84d8-2eda6ec6c86f' + +# Author of this module +Author = 'the PwrOps collective' + +# Company or vendor of this module +CompanyName = '' + +# Copyright statement for this module +Copyright = '(c) the PwrOps collective. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'Manage Azure DevOps using PowerShell' + +# Minimum version of the PowerShell engine required by this module +PowerShellVersion = '7.2' + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +RequiredModules = @(@{ModuleName = 'AzAuth'; GUID = '6efad2e7-3439-46fb-862d-eea4ebd67bc4'; ModuleVersion = '2.2.2'; }) + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Connect-ADOPS', 'Disconnect-ADOPS', 'Get-ADOPSAgentQueue', + 'Get-ADOPSArtifactFeed', 'Get-ADOPSAuditActions', + 'Get-ADOPSAuditStreams', 'Get-ADOPSBuildDefinition', + 'Get-ADOPSConnection', 'Get-ADOPSElasticPool', 'Get-ADOPSFileContent', + 'Get-ADOPSGroup', 'Get-ADOPSNode', + 'Get-ADOPSOrganizationAdminOverview', + 'Get-ADOPSOrganizationAdvancedSecurity', + 'Get-ADOPSOrganizationCommerceMeterUsage', + 'Get-ADOPSOrganizationPipelineSettings', + 'Get-ADOPSOrganizationPolicy', + 'Get-ADOPSOrganizationRepositorySettings', 'Get-ADOPSPipeline', + 'Get-ADOPSPipelineRetentionSettings', 'Get-ADOPSPipelineSettings', + 'Get-ADOPSPipelineTask', 'Get-ADOPSPool', 'Get-ADOPSProject', + 'Get-ADOPSRepository', 'Get-ADOPSResourceUsage', + 'Get-ADOPSServiceConnection', 'Get-ADOPSUsageData', 'Get-ADOPSUser', + 'Get-ADOPSVariableGroup', 'Get-ADOPSWiki', + 'Grant-ADOPSPipelinePermission', 'Import-ADOPSRepository', + 'Initialize-ADOPSRepository', 'Invoke-ADOPSRestMethod', + 'New-ADOPSArtifactFeed', 'New-ADOPSAuditStream', + 'New-ADOPSBuildPolicy', 'New-ADOPSElasticpool', + 'New-ADOPSElasticPoolObject', 'New-ADOPSEnvironment', + 'New-ADOPSGitBranch', 'New-ADOPSGitFile', 'New-ADOPSGroupEntitlement', + 'New-ADOPSMergePolicy', 'New-ADOPSPipeline', 'New-ADOPSProject', + 'New-ADOPSRepository', 'New-ADOPSServiceConnection', + 'New-ADOPSUserStory', 'New-ADOPSVariableGroup', 'New-ADOPSWiki', + 'Remove-ADOPSRepository', 'Remove-ADOPSVariableGroup', + 'Revoke-ADOPSPipelinePermission', 'Save-ADOPSPipelineTask', + 'Set-ADOPSArtifactFeed', 'Set-ADOPSBuildDefinition', + 'Set-ADOPSElasticPool', 'Set-ADOPSGitPermission', + 'Set-ADOPSPipelineRetentionSettings', 'Set-ADOPSPipelineSettings', + 'Set-ADOPSProject', 'Set-ADOPSRepository', + 'Set-ADOPSServiceConnection', 'Start-ADOPSPipeline', + 'Test-ADOPSConnection', 'Test-ADOPSYamlFile', 'Get-ADOPSMembership' + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @() + +# Variables to export from this module +# VariablesToExport = @() + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = 'Azure','DevOps','AZDO','AzureDevOps','ADO' + + # A URL to the license for this module. + LicenseUri = 'https://github.com/AZDOPS/AZDOPS/blob/main/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/AZDOPS/AZDOPS/' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + + } # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + diff --git a/tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 b/tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 new file mode 100644 index 000000000..c02a5cf56 --- /dev/null +++ b/tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 @@ -0,0 +1,4439 @@ +#region PreCode _PreModule_Requires + +#Requires -Modules @{ ModuleName="AzAuth"; ModuleVersion="2.2.2" } + +$script:AzTokenCache = 'adops.cache' + +$script:loginMethod = 'Default' +#endregion PreCode _PreModule_Requires + +#region GitAccessLevels + +[Flags()] enum AccessLevels { + Administer = 1 + GenericRead = 2 + GenericContribute = 4 + ForcePush = 8 + CreateBranch = 16 + CreateTag = 32 + ManageNote = 64 + PolicyExempt = 128 + CreateRepository = 256 + DeleteRepository = 512 + RenameRepository = 1024 + EditPolicies = 2048 + RemoveOthersLocks = 4096 + ManagePermissions = 8192 + PullRequestContribute = 16384 + PullRequestBypassPolicy = 32768 +} +#endregion GitAccessLevels + +#region ResourceType + +enum ResourceType { + VariableGroup + Queue + SecureFile + Environment +} +#endregion ResourceType + +#region SkipTest + +class SkipTest : Attribute { + [string[]]$TestNames + + SkipTest([string[]]$Name) { + $this.TestNames = $Name + } +} +#endregion SkipTest + +#region ConvertRetentionSettingsGetToPatch + +<# +.SYNOPSIS +Convert to responses from GET _apis/build/retention & PATCH _apis/build/retention +into the PATCH _apis/build/retention body property names. + +.DESCRIPTION +Below are the GET & PATCH _apis/build/retention request/respones. + +Notice the GET and PATCH responses are the same, while the PATCH body uses different property names. +This function will convert the GET/PATCH responses to match the PATCH request body properties. + +GET _apis/build/retention +-- +{ + "purgeArtifacts": { "min": 1, "max": 60, "value": 51 }, + "purgeRuns": { "min": 1, "max": 60, "value": 51 }, + "purgePullRequestRuns": { "min": 1, "max": 60, "value": 51 }, + "retainRunsPerProtectedBranch": null +} + +POST _apis/build/retention +Request Body: +{ + "artifactsRetention": { "min": 1, "max": 60, "value": 51 }, + "runRetention": { "min": 1, "max": 60, "value": 51 }, + "pullRequestRunRetention": { "min": 1, "max": 60, "value": 51 }, + "retainRunsPerProtectedBranch": { "min": 1, "max": 60, "value": 51 }, +} + +Response Body: +{ + "purgeArtifacts": { "min": 1, "max": 60, "value": 51 }, + "purgeRuns": { "min": 1, "max": 60, "value": 51 }, + "purgePullRequestRuns": { "min": 1, "max": 60, "value": 51 }, + "retainRunsPerProtectedBranch": null +} + +.NOTES +Research notes aligning UX, GET, PATCH fields: + +@{ + # UX Label: Days to keep artifacts, symbols and attachments + # UX Field: PurgeArtifacts + # Get Field: purgeArtifacts + # Patch Field: artifactsRetention + purgeArtifacts = @{ + min = 1 + max = 60 + value = 51 + } + + # UX Label: Days to keep runs + # UX Field: PurgeRuns + # Get Field: purgeRuns + # Patch Field: runRetention + purgeRuns = @{ + min = 30 + max = 731 + value = 37 + } + + # UX Label: Days to keep pull request runs + # UX Field: PurgePullRequestRuns + # Get Field: purgePullRequestRuns + # Patch Field: pullRequestRunRetention + purgePullRequestRuns = @{ + min = 1 + max = 30 + value = 4 + } + + # UX Label: Number of recent runs to retain per pipeline + # UX Help Label: This many runs will also be retained per protected branch and default pipeline branch. (Azure Repos only) + # UX Field: runsToRetainPerProtectedBranch + # Get Field: retainRunsPerProtectedBranch + # Patch Field: retainRunsPerProtectedBranch + # BUG: Always null on return + retainRunsPerProtectedBranch = @{ + min = 0 + max = 50 + value = 0 + } +} +#> +function ConvertRetentionSettingsGetToPatch { + [CmdletBinding()] + [SkipTest('HasOrganizationParameter')] + param ( + [Parameter(Mandatory)] + $Response + ) + + $Settings = @{} + + $FieldMap = @{ + 'purgeArtifacts' = 'artifactsRetention' + 'purgeRuns' = 'runRetention' + 'purgePullRequestRuns' = 'pullRequestRunRetention' + + # Note: This field is bugged, it's always NULL on GET/PATCH response, I think its meant to be runsToRetainPerProtectedBranch + 'retainRunsPerProtectedBranch' = 'retainRunsPerProtectedBranch' + } + $Fields = $Response.psobject.Properties | Where-object Name -in $FieldMap.Keys + + foreach ($Field in $Fields) { + $Settings.$($FieldMap[$Field.Name]) = $Field.Value.value + } + + [pscustomobject]$Settings +} +#endregion ConvertRetentionSettingsGetToPatch + +#region ConvertRetentionSettingsToPatchBody + +<# +.SYNOPSIS +Converts Retention Settings Dictionary into RetentionSetting objects + +.DESCRIPTION +Converts Retention Settings Dictionary into RetentionSetting objects. + +.PARAMETER Values +Keyed dictionary of integers + +Example: +@{ + artifactsRetention = 51 + runRetention = 51, + ... +} + +.OUTPUTS +@{ + artifactsRetention = @{ + min = 0, + max = 0, + value = 51 + }, + runRetention = @{ + min = 0, + max = 0, + value = 51 + }, + ... +} +#> +function ConvertRetentionSettingsToPatchBody { + [CmdletBinding()] + [SkipTest('HasOrganizationParameter')] + param ( + [Parameter(Mandatory)] + $Values + ) + + $Settings = @{} + if ($Values -is [pscustomobject]) { + foreach ($ValueProperty in $Values.psobject.Properties) { + $Settings[$ValueProperty.Name] = @{ + value = $ValueProperty.Value + min = $null + max = $null + } + } + } + else { + foreach ($Value in $Values.GetEnumerator()) { + $Settings[$Value.key] = @{ + value = $Value.value + min = $null + max = $null + } + } + } + + [pscustomobject]$Settings +} +#endregion ConvertRetentionSettingsToPatchBody + +#region GetADOPSConfigFile + +function GetADOPSConfigFile { + param ( + [Parameter()] + [string]$ConfigPath = '~/.ADOPS/Config.json' + ) + + # Create config if not exists + if (-not (Test-Path $ConfigPath)) { + NewADOPSConfigFile + } + + Get-Content $ConfigPath | ConvertFrom-Json -AsHashtable +} +#endregion GetADOPSConfigFile + +#region GetADOPSDefaultOrganization + +function GetADOPSDefaultOrganization { + [CmdletBinding()] + [SkipTest('HasOrganizationParameter')] + param () + + $ADOPSConfig = GetADOPSConfigFile + + if ([string]::IsNullOrWhiteSpace($ADOPSConfig['Default']['Organization'])) { + throw 'No default organization found! Use Connect-ADOPS or set Organization parameter.' + } + else { + Write-Output $ADOPSConfig['Default']['Organization'] + } +} +#endregion GetADOPSDefaultOrganization + +#region GetADOPSOrganizationAccess + +function GetADOPSOrganizationAccess { + [CmdletBinding()] + [SkipTest('HasOrganizationParameter')] + param ( + [Parameter(Mandatory)] + [string]$AccountId, + + [Parameter()] + [string]$Token + ) + + (InvokeADOPSRestMethod -Method GET -Token $Token -Uri "https://app.vssps.visualstudio.com/_apis/accounts?memberId=$AccountId&api-version=7.1-preview.1").value.accountName +} +#endregion GetADOPSOrganizationAccess + +#region InvokeADOPSRestMethod + +function InvokeADOPSRestMethod { + [SkipTest('HasOrganizationParameter')] + param ( + [Parameter(Mandatory)] + [URI]$Uri, + + [Parameter()] + [Microsoft.PowerShell.Commands.WebRequestMethod]$Method, + + [Parameter()] + [string]$Body, + + [Parameter()] + [string]$ContentType = 'application/json', + + [Parameter()] + [switch]$FullResponse, + + [Parameter()] + [string]$OutFile, + + [Parameter()] + [string]$Token + ) + + if (-not $PSBoundParameters.ContainsKey('Token')) { + $Token = (NewAzToken).Token + } + + $InvokeSplat = @{ + 'Uri' = $Uri + 'Method' = $Method + 'Headers' = @{ + 'Authorization' = "Bearer $Token" + } + 'ContentType' = $ContentType + } + + if (-not [string]::IsNullOrEmpty($Body)) { + $InvokeSplat.Add('Body', $Body) + } + + if ($FullResponse) { + $InvokeSplat.Add('ResponseHeadersVariable', 'ResponseHeaders') + $InvokeSplat.Add('StatusCodeVariable', 'ResponseStatusCode') + } + + if ($OutFile) { + Write-Debug "$Method $Uri" + Invoke-RestMethod @InvokeSplat -OutFile $OutFile + } + else { + Write-Debug "$Method $Uri" + $Result = Invoke-RestMethod @InvokeSplat + + if ($Result -like "*Azure DevOps Services | Sign In*") { + throw 'Failed to call Azure DevOps API. Please login using Connect-ADOPS before running commands.' + } + elseif ($FullResponse) { + @{ Content = $Result; Headers = $ResponseHeaders; StatusCode = $ResponseStatusCode } + } + else { + $Result + } + } +} +#endregion InvokeADOPSRestMethod + +#region NewADOPSConfigFile + +function NewADOPSConfigFile { + param ( + [Parameter()] + [string]$ConfigPath = '~/.ADOPS/Config.json' + ) + + @{ + 'Default' = @{} + } | SetADOPSConfigFile -ConfigPath $ConfigPath +} +#endregion NewADOPSConfigFile + +#region NewAzToken + +function NewAzToken { + [CmdletBinding()] + [SkipTest('HasOrganizationParameter')] + param () + + $TokenSplat = @{ + Resource = '499b84ac-1321-427f-aa17-267ca6975798' + } + switch ($script:LoginMethod) { + 'Default' { + try { + $UserContext = GetADOPSConfigFile + + $TokenSplat['Username'] = $Usercontext['Default']['Identity'] + $TokenSplat['TenantId'] = $Usercontext['Default']['TenantId'] + Get-AzToken @TokenSplat -TokenCache $script:AzTokenCache + } + catch { + # Make sure we present the inner exception to users but with a nicer error message + if ($_.Exception.GetType().FullName -eq 'Azure.Identity.CredentialUnavailableException') { + $Exception = New-Object System.InvalidOperationException "Could not find existing token, please run the command Connect-ADOPS!", $_.Exception + $ErrorRecord = New-Object Management.Automation.ErrorRecord $Exception, 'ADOPSGetTokenError', ([System.Management.Automation.ErrorCategory]::InvalidOperation), $null + throw $ErrorRecord + } + else { + throw $_ + } + } + } + 'ManagedIdentity' { + Get-AzToken @TokenSplat -ManagedIdentity + } + 'OAuthToken' { + return $Script:ScriptToken + } + Default { + throw 'No login method was set, module file may have been corrupted!' + } + } +} +#endregion NewAzToken + +#region SetADOPSConfigFile + +function SetADOPSConfigFile { + [CmdletBinding()] + param ( + [Parameter()] + [string]$ConfigPath = '~/.ADOPS/Config.json', + + [Parameter(ValueFromPipeline)] + [object]$ConfigObject + ) + + $null = New-Item -Path '~/.ADOPS/' -ItemType Directory -ErrorAction SilentlyContinue + Set-Content -Path $ConfigPath -Value ($ConfigObject | ConvertTo-Json -Compress) -Force +} +#endregion SetADOPSConfigFile + +#region SetADOPSPipelinePermission + +function SetADOPSPipelinePermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [string]$Project, + + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [switch]$AllPipelines, + + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [int]$PipelineId, + + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [ResourceType]$ResourceType, + + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [string]$ResourceId, + + [Parameter(ParameterSetName = 'AllPipelines')] + [Parameter(ParameterSetName = 'SinglePipeline')] + [bool]$Authorized = $true, + + [Parameter(ParameterSetName = 'AllPipelines')] + [Parameter(ParameterSetName = 'SinglePipeline')] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $URI = "https://dev.azure.com/${Organization}/${Project}/_apis/pipelines/pipelinepermissions/${ResourceType}/${ResourceId}?api-version=7.1-preview.1" + $method = 'PATCH' + + $Body = switch ($PSCmdlet.ParameterSetName) { + 'AllPipelines' { + @{ + allPipelines = @{ + authorized = $Authorized + } + } + } + 'SinglePipeline' { + @{ + pipelines = @( + [ordered]@{ + id = $PipelineId + authorized = $Authorized + } + ) + } + } + 'Default' { + throw 'Invalid parameter set, this should not happen' + } + } + $Body = $Body | ConvertTo-Json -Depth 10 -Compress + + InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body +} +#endregion SetADOPSPipelinePermission + +#region Connect-ADOPS + +function Connect-ADOPS { + [CmdletBinding(DefaultParameterSetName = 'Interactive')] + param ( + [Parameter(Mandatory, ParameterSetName = 'Interactive')] + [Parameter(Mandatory, ParameterSetName = 'ManagedIdentity')] + [Parameter(Mandatory, ParameterSetName = 'OAuthToken')] + [string]$Organization, + + [Parameter(ParameterSetName = 'Interactive')] + [Parameter(ParameterSetName = 'ManagedIdentity')] + [Parameter(ParameterSetName = 'OAuthToken')] + [string]$TenantId, + + [Parameter(ParameterSetName = 'Interactive')] + [Parameter(ParameterSetName = 'ManagedIdentity')] + [Parameter(ParameterSetName = 'OAuthToken')] + [switch]$SkipVerification, + + [Parameter(ParameterSetName = 'Interactive')] + [switch]$Interactive, + + [Parameter(Mandatory, ParameterSetName = 'ManagedIdentity')] + [switch]$ManagedIdentity, + + [Parameter(Mandatory, ParameterSetName = 'OAuthToken')] + [String]$OAuthToken + ) + + $TokenSplat = @{ + Resource = '499b84ac-1321-427f-aa17-267ca6975798' + Scope = '.default' + } + + # Add TenantId if provided + if ($PSBoundParameters.ContainsKey('TenantId')) { + $TokenSplat.Add('TenantId', $TenantId) + } + + switch ($PSCmdlet.ParameterSetName) { + 'OAuthToken' { + $script:LoginMethod = 'OAuthToken' + $script:ScriptToken = @{ + Token = $OAuthToken + } + $Token = $OAuthToken + $TokenTenantId = 'NotSpecified' + $TokenIdentity = $null + } + 'ManagedIdentity' { + $TokenSplat.Add('ManagedIdentity', $true) + $script:LoginMethod = 'ManagedIdentity' + + $Token = Get-AzToken @TokenSplat + $TokenTenantId = $Token.TenantId + $TokenIdentity = $Token.Identity + } + 'Interactive' { + $TokenSplat.Add('TokenCache', $script:AzTokenCache) + $TokenSplat.Add('Interactive', $true) + + $Token = Get-AzToken @TokenSplat + $TokenTenantId = $Token.TenantId + $TokenIdentity = $Token.Identity + } + } + + if ($Organization -like "https://dev.azure.com/*") { + $Organization = ($Organization -split "/")[3] + } + + if (-not $PSBoundParameters.ContainsKey('SkipVerification')) { + # Get User context + $Me = InvokeADOPSRestMethod -Method GET -Token $Token -Uri 'https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1-preview.3' + + # Get available orgs + $Orgs = GetADOPSOrganizationAccess -AccountId $Me.publicAlias -Token $Token + + if ($Organization -notin $Orgs) { + throw "The connected account does not have access to the organization '$Organization'. Organizations available: $($Orgs -join ",")`nAre you connected to the correct tennant? $TokenTenantId" + } + } + else { + Write-Verbose 'Skipping organization access verification.' + $Me = @{ id = 'unverified' } + } + + # If user provided a token, we have not parsed the JWT for the email/id + if ($null -eq $TokenIdentity) { + # Instead take info from the DevOps response + if (-not [string]::IsNullOrWhiteSpace($Me.emailAddress)) { + $TokenIdentity = $Me.emailAddress + } + else { + $TokenIdentity = $Me.id + } + } + + $ADOPSConfig = GetADOPSConfigFile + $ADOPSConfig['Default'] = @{ + 'Identity' = $TokenIdentity + 'TenantId' = $TokenTenantId + 'Organization' = $Organization + } + + SetADOPSConfigFile -ConfigObject $ADOPSConfig + + Write-Output $ADOPSConfig['Default'] +} +#endregion Connect-ADOPS + +#region Disconnect-ADOPS + +function Disconnect-ADOPS { + [CmdletBinding()] + [SkipTest('HasOrganizationParameter')] + param () + + # Reset context + NewADOPSConfigFile + + Clear-AzTokenCache -TokenCache $script:AzTokenCache +} +#endregion Disconnect-ADOPS + +#region Get-ADOPSAgentQueue + +function Get-ADOPSAgentQueue { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter()] + [string]$QueueName + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + + if ($PSBoundParameters.ContainsKey('QueueName')) { + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/queues?queueName=${QueueName}&api-version=7.1" + } + else { + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/queues?api-version=7.1" + } + + $Method = 'GET' + $Queue = InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body + + if ($Queue.psobject.properties.name -contains 'value') { + Write-Output $Queue.value + } + else { + Write-Output $Queue + } +} +#endregion Get-ADOPSAgentQueue + +#region Get-ADOPSArtifactFeed + +function Get-ADOPSArtifactFeed { + [CmdletBinding(DefaultParameterSetName = 'All')] + param ( + [Parameter(ParameterSetName = 'All')] + [Parameter(ParameterSetName = 'FeedId', Mandatory)] + [string]$Project, + + [Parameter(ParameterSetName = 'All')] + [Parameter(ParameterSetName = 'FeedId')] + [string]$Organization, + + [Parameter(ParameterSetName = 'FeedId', Mandatory)] + [Alias('Name')] + [string]$FeedId + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://feeds.dev.azure.com/${Organization}" + if (-not ([string]::IsNullOrEmpty($Project))) { + $Uri = "${Uri}/${Project}" + } + $Uri = "${Uri}/_apis/packaging/feeds" + if (-not ([string]::IsNullOrEmpty($FeedId))) { + $Uri = "${Uri}/${FeedId}" + } + $Uri = "${Uri}?api-version=7.2-preview.1" + + $Method = 'Get' + + $InvokeSplat = @{ + Uri = $Uri + Method = $Method + } + + $res = InvokeADOPSRestMethod @InvokeSplat + if ( + (($res | Get-Member -MemberType NoteProperty).Name -contains 'count') -and + (($res | Get-Member -MemberType NoteProperty).Name -contains 'value') + ) { + Write-Output $res.value -NoEnumerate + } + else { + Write-Output $res + } +} +#endregion Get-ADOPSArtifactFeed + +#region Get-ADOPSAuditActions + +function Get-ADOPSAuditActions { + param ( + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + (InvokeADOPSRestMethod -Uri "https://auditservice.dev.azure.com/$Organization/_apis/audit/actions" -Method Get).value +} +#endregion Get-ADOPSAuditActions + +#region Get-ADOPSAuditStreams + +function Get-ADOPSAuditStreams { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + (InvokeADOPSRestMethod -Uri "https://auditservice.dev.azure.com/$Organization/_apis/audit/streams" -Method Get).value +} +#endregion Get-ADOPSAuditStreams + +#region Get-ADOPSBuildDefinition + +function Get-ADOPSBuildDefinition { + [CmdletBinding()] + Param( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [int]$Id + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if ($PSBoundParameters.ContainsKey('Id')) { + [int[]]$idList = $id + } + else { + [int[]]$idList = (InvokeADOPSRestMethod -Method GET -Uri "https://dev.azure.com/${Organization}/${Project}/_apis/build/definitions?api-version=7.2-preview.7").value.id + } + + [array]$res = @() + foreach ($definition in $idList) { + [array]$res += InvokeADOPSRestMethod -Method GET -Uri "https://dev.azure.com/${Organization}/${Project}/_apis/build/definitions/${definition}?api-version=7.2-preview.7" + } + + Write-Output $res -NoEnumerate +} +#endregion Get-ADOPSBuildDefinition + +#region Get-ADOPSConnection + +function Get-ADOPSConnection { + [SkipTest('HasOrganizationParameter')] + param () + + $res = GetADOPSConfigFile + $res['Default'] +} +#endregion Get-ADOPSConnection + +#region Get-ADOPSElasticPool + +function Get-ADOPSElasticPool { + [CmdletBinding()] + param ( + [Parameter()] + [int32]$PoolId, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if ($PSBoundParameters.ContainsKey('PoolId')) { + $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools/$PoolId`?api-version=7.1-preview.1" + } + else { + $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools?api-version=7.1-preview.1" + } + + $Method = 'GET' + $ElasticPoolInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body + if ($ElasticPoolInfo.psobject.properties.name -contains 'value') { + Write-Output $ElasticPoolInfo.value + } + else { + Write-Output $ElasticPoolInfo + } +} +#endregion Get-ADOPSElasticPool + +#region Get-ADOPSFileContent + +function Get-ADOPSFileContent { + param ( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter(Mandatory)] + [string]$RepositoryId, + + [Parameter(Mandatory)] + [string]$FilePath + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if (-Not $FilePath.StartsWith('/')) { + $FilePath = $FilePath.Insert(0, '/') + } + + $UrlEncodedFilePath = [System.Web.HttpUtility]::UrlEncode($FilePath) + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$RepositoryId/items?path=$UrlEncodedFilePath&api-version=7.1-preview.1" + + InvokeADOPSRestMethod -Uri $Uri -Method Get +} +#endregion Get-ADOPSFileContent + +#region Get-ADOPSGroup + +function Get-ADOPSGroup { + param ([Parameter()] + [string]$Organization, + + [Parameter()] + [string] + $Descriptor, + + [Parameter(DontShow)] + [string]$ContinuationToken + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if ($PSBoundParameters.ContainsKey('Descriptor')) { + $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/groups/$Descriptor`?api-version=7.2-preview.1" + + $Response = InvokeADOPSRestMethod -Uri $Uri -Method 'GET' + + return $Response + } + else { + if (-not [string]::IsNullOrEmpty($ContinuationToken)) { + $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/groups?continuationToken=$ContinuationToken&api-version=7.1-preview.1" + } + else { + $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/groups?api-version=7.1-preview.1" + } + } + + $Method = 'GET' + + $Response = InvokeADOPSRestMethod -FullResponse -Uri $Uri -Method $Method + + $Groups = $Response.Content.value + Write-Verbose "Found $($Response.Content.count) groups" + + if ($Response.Headers.ContainsKey('X-MS-ContinuationToken')) { + Write-Verbose "Found continuationToken. Will fetch more groups." + $parameters = [hashtable]$PSBoundParameters + $parameters.Add('ContinuationToken', $Response.Headers['X-MS-ContinuationToken']?[0]) + $Groups += Get-ADOPSGroup @parameters + } + + Write-Output $Groups +} + +#endregion Get-ADOPSGroup + +#region Get-ADOPSNode + +function Get-ADOPSNode { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [int32]$PoolId, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools/$PoolId/nodes?api-version=7.1-preview.1" + + $Method = 'GET' + $NodeInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method + + if ($NodeInfo.psobject.properties.name -contains 'value') { + Write-Output $NodeInfo.value + } + else { + Write-Output $NodeInfo + } +} +#endregion Get-ADOPSNode + +#region Get-ADOPSOrganizationAdminOverview + +function Get-ADOPSOrganizationAdminOverview { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization, + + [Parameter()] + [string[]] + $ContributionIds = @("ms.vss-admin-web.organization-admin-overview-delay-load-data-provider") + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Body = @{ + 'contributionIds' = $ContributionIds + } | ConvertTo-Json -Depth 100 + + $Uri = "https://dev.azure.com/$Organization/_apis/Contribution/HierarchyQuery?api-version=7.2-preview" + + $Response = InvokeADOPSRestMethod -Uri $Uri -Method Post -Body $Body + + if ($Response.dataProviderExceptions) { + $Response.dataProviderExceptions + } + else { + $Response.dataProviders + } + +} +#endregion Get-ADOPSOrganizationAdminOverview + +#region Get-ADOPSOrganizationAdvancedSecurity + +function Get-ADOPSOrganizationAdvancedSecurity { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://advsec.dev.azure.com/$Organization/_apis/Management/enablement" + + (InvokeADOPSRestMethod -Uri $Uri -Method Get) +} +#endregion Get-ADOPSOrganizationAdvancedSecurity + +#region Get-ADOPSOrganizationCommerceMeterUsage + +function Get-ADOPSOrganizationCommerceMeterUsage { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization, + + [Parameter()] + [string]$MeterId + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + + $AccountId = (InvokeADOPSRestMethod -Method GET -Uri 'https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1-preview.3').publicAlias + + # GetADOPSOrganizationAccess could have been used instead. + # However it requires token and only returns accountName + + # Get available orgs + $Orgs = (InvokeADOPSRestMethod -Method GET -Uri "https://app.vssps.visualstudio.com/_apis/accounts?memberId=$AccountId&api-version=7.1-preview.1").value + + $OrganizationId = ($Orgs | Where-object accountName -eq $Organization).AccountId + + if ($PSBoundParameters.ContainsKey('MeterId')) { + InvokeADOPSRestMethod -Uri "https://azdevopscommerce.dev.azure.com/$OrganizationId/_apis/AzComm/MeterUsage2/$MeterId" -Method Get + } + else { + (InvokeADOPSRestMethod -Uri "https://azdevopscommerce.dev.azure.com/$OrganizationId/_apis/AzComm/MeterUsage2" -Method Get).value + } + +} +#endregion Get-ADOPSOrganizationCommerceMeterUsage + +#region Get-ADOPSOrganizationPipelineSettings + +function Get-ADOPSOrganizationPipelineSettings { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Body = '{ + "contributionIds": [ + "ms.vss-build-web.pipelines-org-settings-data-provider" + ] + }' + + $Uri = "https://dev.azure.com/$Organization/_apis/Contribution/HierarchyQuery?api-version=7.1-preview" + + (InvokeADOPSRestMethod -Uri $Uri -Method Post -Body $Body).dataProviders.'ms.vss-build-web.pipelines-org-settings-data-provider' + +} +#endregion Get-ADOPSOrganizationPipelineSettings + +#region Get-ADOPSOrganizationPolicy + +function Get-ADOPSOrganizationPolicy { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization, + + [Parameter()] + [ValidateSet( + 'Security', + 'Privacy', + 'ApplicationConnection', + 'User' + )] + [string] + $PolicyCategory + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/_settings/organizationPolicy?__rt=fps&__ver=2" + $Data = InvokeADOPSRestMethod -Uri $Uri -Method Get + if ($PSBoundParameters.ContainsKey('PolicyCategory')) { + switch ($PolicyCategory) { + 'Security' { + $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.security + } + 'Privacy' { + $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.privacy + } + 'ApplicationConnection' { + $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.applicationConnection + } + 'User' { + $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.user + } + } + } + else { + $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.psobject.Properties.name | ForEach-Object { + $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.$_.policy + } + } + + Write-Output $Policies +} +#endregion Get-ADOPSOrganizationPolicy + +#region Get-ADOPSOrganizationRepositorySettings + +function Get-ADOPSOrganizationRepositorySettings { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Body = '{ + "contributionIds": [ + "ms.vss-build-web.pipelines-org-settings-data-provider" + ] + }' + + $Uri = "https://dev.azure.com/$Organization/_api/_versioncontrol/AllGitRepositoriesOptions?__v=5" + + (InvokeADOPSRestMethod -Uri $Uri -Method Get).__wrappedArray + +} +#endregion Get-ADOPSOrganizationRepositorySettings + +#region Get-ADOPSPipeline + +function Get-ADOPSPipeline { + [CmdletBinding()] + param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [Parameter()] + [int]$Revision, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/pipelines?api-version=7.1-preview.1" + + $InvokeSplat = @{ + Method = 'Get' + Uri = $URI + } + + $AllPipelines = (InvokeADOPSRestMethod @InvokeSplat).value + + if ($PSBoundParameters.ContainsKey('Name')) { + $Pipelines = $AllPipelines | Where-Object { $_.name -eq $Name } + if (-not $Pipelines) { + throw "The specified PipelineName $Name was not found amongst pipelines: $($AllPipelines.name -join ', ')!" + } + } + else { + $Pipelines = $AllPipelines + } + + $return = @() + + foreach ($Pipeline in $Pipelines) { + + $pipelineRevision = [Uri]::EscapeDataString($PSBoundParameters.ContainsKey('Revision') ? $Revision : $Pipeline.revision) + $pipelineUrl = "https://dev.azure.com/$Organization/$Project/_apis/pipelines/$($Pipeline.id)?api-version=7.1-preview.1&pipelineVersion=$pipelineRevision" + + $InvokeSplat = @{ + Method = 'Get' + Uri = $pipelineUrl + } + + $result = InvokeADOPSRestMethod @InvokeSplat + + $return += $result + } + + return $return +} + +#endregion Get-ADOPSPipeline + +#region Get-ADOPSPipelineRetentionSettings + +function Get-ADOPSPipelineRetentionSettings { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/build/retention?api-version=7.2-preview.1" + $Response = InvokeADOPSRestMethod -Uri $Uri -Method Get + + $Settings = ConvertRetentionSettingsGetToPatch -Response $Response + + Write-Output $Settings +} +#endregion Get-ADOPSPipelineRetentionSettings + +#region Get-ADOPSPipelineSettings + +function Get-ADOPSPipelineSettings { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/build/generalsettings?api-version=7.1-preview.1" + $Settings = InvokeADOPSRestMethod -Uri $Uri -Method Get + + Write-Output $Settings +} +#endregion Get-ADOPSPipelineSettings + +#region Get-ADOPSPipelineTask + +function Get-ADOPSPipelineTask { + param ( + [Parameter()] + [string]$Name, + + [Parameter()] + [string]$Organization, + + [Parameter()] + [int]$Version + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/tasks?api-version=7.1-preview.1" + + $result = InvokeADOPSRestMethod -Uri $Uri -Method Get + + $ReturnValue = $result | ConvertFrom-Json -AsHashtable | Select-Object -ExpandProperty value + + if (-Not [string]::IsNullOrEmpty($Name)) { + $ReturnValue = $ReturnValue | Where-Object -Property name -EQ $Name + } + if ($Version) { + $ReturnValue = $ReturnValue | Where-Object -FilterScript { $_.version.major -eq $Version } + } + + $ReturnValue +} +#endregion Get-ADOPSPipelineTask + +#region Get-ADOPSPool + +function Get-ADOPSPool { + [CmdletBinding(DefaultParameterSetName = 'All')] + param ( + [Parameter(Mandatory, ParameterSetName = 'PoolId')] + [int32]$PoolId, + + [Parameter(Mandatory, ParameterSetName = 'PoolName')] + [string]$PoolName, + + # Include legacy pools + [Parameter(ParameterSetName = 'All')] + [switch]$IncludeLegacy, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + switch ($PSCmdlet.ParameterSetName) { + 'PoolId' { $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/pools/$PoolId`?api-version=7.1-preview.1" } + 'PoolName' { $uri = "https://dev.azure.com/$Organization/_apis/distributedtask/pools?poolName=$PoolName`&api-version=7.1-preview.1" } + 'All' { $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/pools?api-version=7.1-preview.1" } + } + + $Method = 'GET' + $PoolInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method + + if ($PoolInfo.psobject.properties.name -contains 'value') { + $PoolInfo = $PoolInfo.value + } + if ((-not ($IncludeLegacy.IsPresent)) -and $PSCmdlet.ParameterSetName -eq 'All') { + $PoolInfo = $PoolInfo | Where-Object { $_.IsLegacy -eq $false } + } + Write-Output $PoolInfo +} +#endregion Get-ADOPSPool + +#region Get-ADOPSProject + +function Get-ADOPSProject { + [CmdletBinding(DefaultParameterSetName = 'All')] + param ( + [Parameter(ParameterSetName = 'All')] + [Parameter(ParameterSetName = 'ByName')] + [Parameter(ParameterSetName = 'ById')] + [string]$Organization, + + [Parameter(ParameterSetName = 'ByName')] + [Alias('Project')] + [string]$Name, + + [Parameter(ParameterSetName = 'ById')] + [string]$Id + + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/_apis/projects?api-version=7.1-preview.4" + + $Method = 'GET' + $ProjectInfo = (InvokeADOPSRestMethod -Uri $Uri -Method $Method).value + + if ($PSCmdlet.ParameterSetName -eq 'ByName') { + $ProjectInfo = $ProjectInfo | Where-Object -Property Name -eq $Name + } + elseif ($PSCmdlet.ParameterSetName -eq 'ById') { + $ProjectInfo = $ProjectInfo | Where-Object -Property Id -eq $Id + } + + Write-Output $ProjectInfo +} +#endregion Get-ADOPSProject + +#region Get-ADOPSRepository + +function Get-ADOPSRepository { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [string]$Repository, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if ($PSBoundParameters.ContainsKey('Repository')) { + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$Repository`?api-version=7.1-preview.1" + } + else { + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories?api-version=7.1-preview.1" + } + + try { + $result = InvokeADOPSRestMethod -Uri $Uri -Method Get + } + catch [Microsoft.PowerShell.Commands.HttpResponseException] { + $ErrorMessage = $_.ErrorDetails.Message | ConvertFrom-Json + if ($ErrorMessage.message -like "TF401019:*") { + Write-Verbose "The Git repository with name or identifier $Repository does not exist or you do not have permissions for the operation you are attempting." + $result = $null + } + elseif ($ErrorMessage.message -like "TF200016:*") { + Write-Verbose "The following project does not exist: $Project. Verify that the name of the project is correct and that the project exists on the specified Azure DevOps Server." + $result = $null + } + else { + Throw $_ + } + } + + if ($result.psobject.properties.name -contains 'value') { + Write-Output -InputObject $result.value + } + else { + Write-Output -InputObject $result + } +} +#endregion Get-ADOPSRepository + +#region Get-ADOPSResourceUsage + +function Get-ADOPSResourceUsage { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + (InvokeADOPSRestMethod -Uri "https://dev.azure.com/$Organization/_apis/ResourceUsage" -Method Get).value +} +#endregion Get-ADOPSResourceUsage + +#region Get-ADOPSServiceConnection + +function Get-ADOPSServiceConnection { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization, + + [Parameter()] + [switch] + $IncludeFailed + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/serviceendpoint/endpoints?includeFailed=$IncludeFailed&api-version=7.1-preview.4" + + $InvokeSplat = @{ + Method = 'Get' + Uri = $URI + } + + $ServiceConnections = (InvokeADOPSRestMethod @InvokeSplat).value + + if ($PSBoundParameters.ContainsKey('Name')) { + $ServiceConnection = $ServiceConnections | Where-Object { $_.name -eq $Name } + if (-not $ServiceConnection) { + throw "The specified ServiceConnectionName $Name was not found amongst Connections: $($ServiceConnections.name -join ', ')!" + } + return $ServiceConnection + } + else { + return $ServiceConnections + } + +} +#endregion Get-ADOPSServiceConnection + +#region Get-ADOPSUsageData + +function Get-ADOPSUsageData { + param( + [Parameter()] + [ValidateSet('Private', 'Public')] + [string]$ProjectVisibility = 'Public', + + [Parameter()] + [Switch]$SelfHosted, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if ($SelfHosted.IsPresent) { + $Hosted = $false + } + else { + $Hosted = $true + } + + $URI = "https://dev.azure.com/$Organization/_apis/distributedtask/resourceusage?parallelismTag=${ProjectVisibility}&poolIsHosted=${Hosted}&includeRunningRequests=true" + $Method = 'Get' + + $InvokeSplat = @{ + Method = $Method + Uri = $URI + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion Get-ADOPSUsageData + +#region Get-ADOPSUser + +function Get-ADOPSUser { + [CmdletBinding(DefaultParameterSetName = 'Default')] + param ( + [Parameter(Mandatory, ParameterSetName = 'Name', Position = 0)] + [string]$Name, + + [Parameter(Mandatory, ParameterSetName = 'Descriptor', Position = 0)] + [string]$Descriptor, + + [Parameter()] + [string]$Organization, + + [Parameter(ParameterSetName = 'Default', DontShow)] + [string]$ContinuationToken + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if ($PSCmdlet.ParameterSetName -eq 'Default') { + $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/users?api-version=7.1-preview.1" + $Method = 'GET' + if (-not [string]::IsNullOrEmpty($ContinuationToken)) { + $Uri += "&continuationToken=$ContinuationToken" + } + $Response = (InvokeADOPSRestMethod -FullResponse -Uri $Uri -Method $Method) + $Users = $Response.Content.value + Write-Verbose "Found $($Response.Content.count) users" + + if ($Response.Headers.ContainsKey('X-MS-ContinuationToken')) { + Write-Verbose "Found continuationToken. Will fetch more users." + $parameters = [hashtable]$PSBoundParameters + $parameters.Add('ContinuationToken', $Response.Headers['X-MS-ContinuationToken']?[0]) + $Users += Get-ADOPSUser @parameters + } + Write-Output $Users + } + elseif ($PSCmdlet.ParameterSetName -eq 'Name') { + $Uri = "https://vsaex.dev.azure.com/$Organization/_apis/UserEntitlements?`$filter=name eq '$Name'&`$orderBy=name Ascending&api-version=7.1-preview.3" + $Method = 'GET' + $Users = (InvokeADOPSRestMethod -Uri $Uri -Method $Method).members.user + if ($null -eq $Users) { + Get-ADOPSUser | Where-Object -Property displayName -eq $Name + } + Write-Output $Users + } + elseif ($PSCmdlet.ParameterSetName -eq 'Descriptor') { + $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/users/$Descriptor`?api-version=7.1-preview.1" + $Method = 'GET' + $User = (InvokeADOPSRestMethod -Uri $Uri -Method $Method) + Write-Output $User + } +} +#endregion Get-ADOPSUser + +#region Get-ADOPSVariableGroup + +function Get-ADOPSVariableGroup { + [CmdletBinding(DefaultParameterSetName = 'All')] + param ( + [Parameter(ParameterSetName = 'All')] + [Parameter(ParameterSetName = 'Name')] + [Parameter(ParameterSetName = 'Id')] + [string]$Organization, + + [Parameter(Mandatory, ParameterSetName = 'All')] + [Parameter(Mandatory, ParameterSetName = 'Name')] + [Parameter(Mandatory, ParameterSetName = 'Id')] + [string]$Project, + + [Parameter(ParameterSetName = 'Name')] + [string]$Name, + + [Parameter(ParameterSetName = 'Id')] + [int]$Id + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Method = 'Get' + + if ($PSCmdlet.ParameterSetName -eq 'Name') { + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/variablegroups?groupName=$Name&api-version=7.2-preview.2" + } + else { + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/variablegroups?api-version=7.2-preview.2" + } + + $InvokeSplat = @{ + Uri = $Uri + Method = $Method + } + + $result = (InvokeADOPSRestMethod @InvokeSplat).value + + if ($PSCmdlet.ParameterSetName -eq 'Id') { + $result = $result.Where({ $_.Id -eq $Id }) + } + + Write-Output $result +} +#endregion Get-ADOPSVariableGroup + +#region Get-ADOPSWiki + +function Get-ADOPSWiki { + param ( + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [string]$WikiId, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $BaseUri = "https://dev.azure.com/$Organization/$Project/_apis/wiki/wikis" + + if ($WikiId) { + $Uri = "${BaseUri}/${WikiId}?api-version=7.1-preview.2" + } + else { + $Uri = "${BaseUri}?api-version=7.1-preview.2" + } + + $Method = 'Get' + + $res = InvokeADOPSRestMethod -Uri $URI -Method $Method + + if ($res.psobject.properties.name -contains 'value') { + Write-Output -InputObject $res.value + } + else { + Write-Output -InputObject $res + } +} +#endregion Get-ADOPSWiki + +#region Grant-ADOPSPipelinePermission + +function Grant-ADOPSPipelinePermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [string]$Project, + + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [switch]$AllPipelines, + + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [int]$PipelineId, + + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [ResourceType]$ResourceType, + + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [string]$ResourceId, + + [Parameter(ParameterSetName = 'AllPipelines')] + [Parameter(ParameterSetName = 'SinglePipeline')] + [string]$Organization + ) + + SetADOPSPipelinePermission @PSBoundParameters -Authorized $true +} +#endregion Grant-ADOPSPipelinePermission + +#region Import-ADOPSRepository + +function Import-ADOPSRepository { + [CmdLetBinding(DefaultParameterSetName = 'RepositoryName')] + param ( + [Parameter(Mandatory)] + [string]$GitSource, + + [Parameter(Mandatory, ParameterSetName = 'RepositoryId')] + [string]$RepositoryId, + + [Parameter(Mandatory, ParameterSetName = 'RepositoryName')] + [string]$RepositoryName, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [string]$Organization, + + [Parameter()] + [switch]$Wait + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + switch ($PSCmdlet.ParameterSetName) { + 'RepositoryName' { $RepoIdentifier = $RepositoryName } + 'RepositoryId' { $RepoIdentifier = $RepositoryId } + Default {} + } + $InvokeSplat = @{ + URI = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$RepoIdentifier/importRequests?api-version=7.1-preview.1" + Method = 'Post' + Body = "{""parameters"":{""gitSource"":{""url"":""$GitSource""}}}" + } + + $repoImport = InvokeADOPSRestMethod @InvokeSplat + + if ($PSBoundParameters.ContainsKey('Wait')) { + # There appears to be a bug in this API where sometimes you don't get the correct status Uri back. Fix it by constructing a correct one instead. + $verifyUri = "https://dev.azure.com/$Organization/$Project/_apis$($repoImport.url.Split('_apis')[1])" + while ($repoImport.status -ne 'completed') { + $repoImport = InvokeADOPSRestMethod -Uri $verifyUri -Method Get + Start-Sleep -Seconds 1 + } + } + + $repoImport +} +#endregion Import-ADOPSRepository + +#region Initialize-ADOPSRepository + +function Initialize-ADOPSRepository { + [CmdletBinding()] + [SkipTest('HasOrganizationParameter')] + param ( + [Parameter()] + [string]$Message = 'Repo initialized using ADOPS module.', + + [Parameter()] + [string]$Branch = 'Main', + + [Parameter(Mandatory)] + [string]$RepositoryId, + + [Parameter()] + [ValidateSet('Actionscript.gitignore', 'Ada.gitignore', 'Agda.gitignore', 'Android.gitignore', 'AppceleratorTitanium.gitignore', 'AppEngine.gitignore', 'ArchLinuxPackages.gitignore', 'Autotools.gitignore', 'C++.gitignore', 'C.gitignore', 'CakePHP.gitignore', 'CFWheels.gitignore', 'ChefCookbook.gitignore', 'Clojure.gitignore', 'CMake.gitignore', 'CodeIgniter.gitignore', 'CommonLisp.gitignore', 'Composer.gitignore', 'Concrete5.gitignore', 'Coq.gitignore', 'CraftCMS.gitignore', 'CUDA.gitignore', 'D.gitignore', 'Dart.gitignore', 'Delphi.gitignore', 'DM.gitignore', 'Drupal.gitignore', 'Eagle.gitignore', 'Elisp.gitignore', 'Elixir.gitignore', 'Elm.gitignore', 'EPiServer.gitignore', 'Erlang.gitignore', 'ExpressionEngine.gitignore', 'ExtJs.gitignore', 'Fancy.gitignore', 'Finale.gitignore', 'ForceDotCom.gitignore', 'Fortran.gitignore', 'FuelPHP.gitignore', 'gcov.gitignore', 'GitBook.gitignore', 'Go.gitignore', 'Godot.gitignore', 'Gradle.gitignore', 'Grails.gitignore', 'GWT.gitignore', 'Haskell.gitignore', 'Idris.gitignore', 'IGORPro.gitignore', 'Java.gitignore', 'Jboss.gitignore', 'Jekyll.gitignore', 'JENKINS_HOME.gitignore', 'Joomla.gitignore', 'Julia.gitignore', 'KiCAD.gitignore', 'Kohana.gitignore', 'Kotlin.gitignore', 'LabVIEW.gitignore', 'Laravel.gitignore', 'Leiningen.gitignore', 'LemonStand.gitignore', 'Lilypond.gitignore', 'Lithium.gitignore', 'Lua.gitignore', 'Magento.gitignore', 'Maven.gitignore', 'Mercury.gitignore', 'MetaProgrammingSystem.gitignore', 'nanoc.gitignore', 'Nim.gitignore', 'Node.gitignore', 'Objective-C.gitignore', 'OCaml.gitignore', 'Opa.gitignore', 'opencart.gitignore', 'OracleForms.gitignore', 'Packer.gitignore', 'Perl.gitignore', 'Phalcon.gitignore', 'PlayFramework.gitignore', 'Plone.gitignore', 'Prestashop.gitignore', 'Processing.gitignore', 'PureScript.gitignore', 'Python.gitignore', 'Qooxdoo.gitignore', 'Qt.gitignore', 'R.gitignore', 'Rails.gitignore', 'Raku.gitignore', 'RhodesRhomobile.gitignore', 'ROS.gitignore', 'Ruby.gitignore', 'Rust.gitignore', 'Sass.gitignore', 'Scala.gitignore', 'Scheme.gitignore', 'SCons.gitignore', 'Scrivener.gitignore', 'Sdcc.gitignore', 'SeamGen.gitignore', 'SketchUp.gitignore', 'Smalltalk.gitignore', 'stella.gitignore', 'SugarCRM.gitignore', 'Swift.gitignore', 'Symfony.gitignore', 'SymphonyCMS.gitignore', 'Terraform.gitignore', 'TeX.gitignore', 'Textpattern.gitignore', 'TurboGears2.gitignore', 'Typo3.gitignore', 'Umbraco.gitignore', 'Unity.gitignore', 'UnrealEngine.gitignore', 'VisualStudio.gitignore', 'VVVV.gitignore', 'Waf.gitignore', 'WordPress.gitignore', 'Xojo.gitignore', 'Yeoman.gitignore', 'Yii.gitignore', 'ZendFramework.gitignore', 'Zephir.gitignore')] + [string[]]$NewContentTemplate, + + [Parameter()] + [switch]$Readme, + + [Parameter()] + [string]$Path, + + [Parameter()] + [string]$Content = 'Repo initialized using ADOPS module.' + ) + + $Organization = GetADOPSDefaultOrganization + + $Uri = "https://dev.azure.com/$Organization/_apis/git/repositories/$RepositoryId/pushes?api-version=7.2-preview.3" + + if ($Branch -notmatch '^refs/.*') { + $Branch = 'refs/heads/' + $Branch + } + + $changes = @() + + if ($Readme -or ( [String]::IsNullOrEmpty($Path) -and ($newContentTemplate.Count -eq 0) )) { + $changes += @{ + changeType = 1 + item = @{path = "/README.md" } + newContentTemplate = @{ + name = "README.md" + type = "readme" + } + } + } + + if (-not ([string]::IsNullOrEmpty($Path))) { + $changes += @{ + changeType = "add" + item = @{ + path = $Path + } + newContent = @{ + content = $Content + contentType = "rawtext" + } + } + } + + if ($newContentTemplate.Count -eq 1) { + $changes += @{ + changeType = 1 + item = @{path = "/.gitignore" } + newContentTemplate = @{ + name = $newContentTemplate[0] + type = 'gitignore' + } + } + } + + if ($newContentTemplate.Count -gt 1) { + foreach ($t in $newContentTemplate) { + $changes += @{ + changeType = 1 + item = @{path = "/$t" } + newContentTemplate = @{ + name = $t + type = 'gitignore' + } + } + } + } + + $Body = @{ + commits = @( + @{ + comment = $Message + changes = $changes + } + ) + refUpdates = @( + @{ + name = $Branch.ToLower() + oldObjectId = "0000000000000000000000000000000000000000" + } + ) + } + + + + + $InvokeSplat = @{ + Uri = $Uri + Method = 'Post' + Body = $Body | ConvertTo-Json -Compress -Depth 100 + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion Initialize-ADOPSRepository + +#region Invoke-ADOPSRestMethod + +function Invoke-ADOPSRestMethod { + [SkipTest('HasOrganizationParameter')] + param ( + [Parameter(Mandatory)] + [string]$Uri, + + [Parameter()] + [Microsoft.PowerShell.Commands.WebRequestMethod]$Method = 'Get', + + [Parameter()] + [string]$Body + ) + + If ( ($Uri -NotLike "*dev.azure.com*") -and ($Uri -NotLike "*visualstudio.com*")) { + $Organization = GetADOPSDefaultOrganization + $Uri = "https://dev.azure.com/$Organization/$Uri" + } + + $InvokeSplat = @{ + Uri = $Uri + Method = $Method + } + + if (-Not [String]::IsNullOrEmpty($Body)) { + $InvokeSplat.Add('Body', $Body) + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion Invoke-ADOPSRestMethod + +#region New-ADOPSArtifactFeed + +function New-ADOPSArtifactFeed { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [Parameter()] + [string]$Description, + + [Parameter()] + [Alias('UpstreamEnabled')] + [switch]$IncludeUpstream + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://feeds.dev.azure.com/$Organization/$Project/_apis/packaging/feeds?api-version=7.2-preview.1" + + $body = [ordered]@{ + name = $name + upstreamEnabled = $IncludeUpstream.IsPresent + hideDeletedPackageVersions = $true + project = @{ + visibility = 'Private' + } + } + + if (-not [string]::IsNullOrEmpty($Description)) { + $body.Add('description', $Description) + } + + if ($IncludeUpstream.IsPresent) { + $upstreamSources = @( + @{ + name = "npmjs" + protocol = "npm" + location = "https://registry.npmjs.org/" + displayLocation = "https://registry.npmjs.org/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "NuGet Gallery" + protocol = "nuget" + location = "https://api.nuget.org/v3/index.json" + displayLocation = "https://api.nuget.org/v3/index.json" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "PowerShell Gallery" + protocol = "nuget" + location = "https://www.powershellgallery.com/api/v2/" + displayLocation = "https://www.powershellgallery.com/api/v2/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "PyPI" + protocol = "pypi" + location = "https://pypi.org/" + displayLocation = "https://pypi.org/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "Maven Central" + protocol = "Maven" + location = "https://repo.maven.apache.org/maven2/" + displayLocation = "https://repo.maven.apache.org/maven2/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "Google Maven Repository" + protocol = "Maven" + location = "https://dl.google.com/android/maven2/" + displayLocation = "https://dl.google.com/android/maven2/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "JitPack" + protocol = "Maven" + location = "https://jitpack.io/" + displayLocation = "https://jitpack.io/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "Gradle Plugins" + protocol = "Maven" + location = "https://plugins.gradle.org/m2/" + displayLocation = "https://plugins.gradle.org/m2/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "crates.io" + protocol = "Cargo" + location = "https://index.crates.io/" + displayLocation = "https://index.crates.io/" + upstreamSourceType = "public" + status = "ok" + } + ) + $body.Add('upstreamSources', $upstreamSources) + } + + $users = Get-ADOPSUser + $buildService = $users.Where({ $_.displayName -eq "$Project build service ($Organization)" }) + if ($buildService.Count -eq 0) { + Write-Verbose "Failed to find build service account. Not adding it as contributor." + } + else { + $buildServiceDescriptorObject = invokeADOPSRestMethod -Uri "https://vssps.dev.azure.com/$Organization/_apis/Identities?identityIds=$($buildService.originId)" -Method Get + $permissions = @( + @{ + identityDescriptor = "$($buildServiceDescriptorObject.Descriptor.IdentityType);$($buildServiceDescriptorObject.Descriptor.Identifier)" + role = 'contributor' + identityId = $buildServiceDescriptorObject.Id + } + ) + $body.Add('permissions', $permissions) + } + + $InvokeSplat = @{ + Uri = $Uri + Method = 'Post' + Body = $body | ConvertTo-Json -Compress + } + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSArtifactFeed + +#region New-ADOPSAuditStream + +function New-ADOPSAuditStream { + [CmdletBinding(DefaultParameterSetName = 'AzureMonitorLogs')] + param ( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory, ParameterSetName = 'AzureMonitorLogs')] + [ValidatePattern('^[a-fA-F0-9]{8}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{12}$', ErrorMessage = 'WorkspaceId should be in GUID format.')] + [string]$WorkspaceId, + + [Parameter(Mandatory, ParameterSetName = 'AzureMonitorLogs')] + [string]$SharedKey, + + [Parameter(Mandatory, ParameterSetName = 'Splunk')] + [ValidatePattern('^(http|HTTP)[sS]?:\/\/', ErrorMessage = 'SplunkUrl must start with http:// or https://')] + [string]$SplunkUrl, + + [Parameter(Mandatory, ParameterSetName = 'Splunk')] + [ValidatePattern('^[a-fA-F0-9]{8}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{12}$', ErrorMessage = 'SplunkEventCollectorToken should be in GUID format.')] + [string]$SplunkEventCollectorToken, + + [Parameter(Mandatory, ParameterSetName = 'AzureEventGrid')] + [ValidatePattern('^(http|HTTP)[sS]?:\/\/', ErrorMessage = 'EventGridTopicHostname must start with http:// or https://')] + [string]$EventGridTopicHostname, + + [Parameter(Mandatory, ParameterSetName = 'AzureEventGrid')] + [ValidatePattern('^[A-Za-z0-9+\/]*={0,2}$', ErrorMessage = 'EventGridTopicAccessKey should be Base64 encoded')] + [string]$EventGridTopicAccessKey + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Body = switch ($PSCmdlet.ParameterSetName) { + 'AzureMonitorLogs' { + [ordered]@{ + consumerType = 'AzureMonitorLogs' + consumerInputs = [Ordered]@{ + WorkspaceId = $WorkspaceId + SharedKey = $SharedKey + } + } | ConvertTo-Json -Compress + } + 'Splunk' { + [ordered]@{ + consumerType = 'Splunk' + consumerInputs = [Ordered]@{ + SplunkUrl = $SplunkUrl + SplunkEventCollectorToken = $SplunkEventCollectorToken + } + } | ConvertTo-Json -Compress + } + 'AzureEventGrid' { + [ordered]@{ + consumerType = 'AzureEventGrid' + consumerInputs = [ordered]@{ + EventGridTopicHostname = $EventGridTopicHostname + EventGridTopicAccessKey = $EventGridTopicAccessKey + } + } | ConvertTo-Json -Compress + } + } + $InvokeSplat = @{ + Uri = "https://auditservice.dev.azure.com/$Organization/_apis/audit/streams?api-version=7.1-preview.1" + Method = 'Post' + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSAuditStream + +#region New-ADOPSBuildPolicy + +function New-ADOPSBuildPolicy { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$RepositoryId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Branch, + + [Parameter(Mandatory)] + [int]$PipelineId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Displayname, + + [Parameter()] + [string[]]$FilenamePatterns + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if (-Not ($Branch -match '^\w+/\w+/\w+$')) { + $Branch = "refs/heads/$Branch" + } + $GitBranchRef = $Branch + + $settings = [ordered]@{ + scope = @( + [ordered]@{ + repositoryId = $RepositoryId + refName = $GitBranchRef + matchKind = "exact" + } + ) + buildDefinitionId = $PipelineId.ToString() + queueOnSourceUpdateOnly = $false + manualQueueOnly = $false + displayName = $Displayname + validDuration = "0" + } + + if ($FilenamePatterns.Count -gt 0) { + $settings.Add('filenamePatterns', $FilenamePatterns) + } + + $Body = [ordered]@{ + type = [ordered]@{ + id = "0609b952-1397-4640-95ec-e00a01b2c241" + } + isBlocking = $true + isEnabled = $true + settings = $settings + } + + $Body = $Body | ConvertTo-Json -Depth 10 -Compress + + $InvokeSplat = @{ + Uri = "https://dev.azure.com/$Organization/$Project/_apis/policy/configurations?api-version=7.1-preview.1" + Method = 'POST' + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSBuildPolicy + +#region New-ADOPSElasticpool + +function New-ADOPSElasticPool { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$PoolName, + + [Parameter(Mandatory)] + $ElasticPoolObject, + + [Parameter()] + [string]$ProjectId, + + [Parameter()] + [switch]$AuthorizeAllPipelines, + + [Parameter()] + [switch]$AutoProvisionProjectPools, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if ($PSBoundParameters.ContainsKey('ProjectId')) { + $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools?poolName=$PoolName`&authorizeAllPipelines=$AuthorizeAllPipelines`&autoProvisionProjectPools=$AutoProvisionProjectPools`&projectId=$ProjectId`&api-version=7.1-preview.1" + } + else { + $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools?poolName=$PoolName`&authorizeAllPipelines=$AuthorizeAllPipelines`&autoProvisionProjectPools=$AutoProvisionProjectPools`&api-version=7.1-preview.1" + } + + if ($ElasticPoolObject.gettype().name -eq 'String') { + $Body = $ElasticPoolObject + } + else { + try { + $Body = $ElasticPoolObject | ConvertTo-Json -Depth 100 + } + catch { + throw "Unable to convert the content of the ElasticPoolObject to json." + } + } + + $Method = 'POST' + $ElasticPoolInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body + Write-Output $ElasticPoolInfo +} +#endregion New-ADOPSElasticpool + +#region New-ADOPSElasticPoolObject + +function New-ADOPSElasticPoolObject { + [SkipTest('HasOrganizationParameter')] + [CmdletBinding()] + param ( + # Service Endpoint Id + [Parameter(Mandatory)] + [guid] + $ServiceEndpointId, + + # Service Endpoint Scope + [Parameter(Mandatory)] + [guid] + $ServiceEndpointScope, + + # Azure Id + [Parameter(Mandatory)] + [string] + $AzureId, + + # Operating System Type + [Parameter()] + [ValidateSet('linux', 'windows')] + [string] + $OsType = 'linux', + + # MaxCapacity + [Parameter()] + [int] + $MaxCapacity = 1, + + # DesiredIdle + [Parameter()] + [int] + $DesiredIdle = 0, + + # Recycle VM after each use + [Parameter()] + [boolean] + $RecycleAfterEachUse = $false, + + # Desired Size of pool + [Parameter()] + [int] + $DesiredSize = 0, + + # Agent Interactive UI + [Parameter()] + [boolean] + $AgentInteractiveUI = $false, + + # Time before scaling down + [Parameter()] + [Alias('TimeToLiveMinues')] + [int] + $TimeToLiveMinutes = 15, + + # maxSavedNodeCount + [Parameter()] + [int] + $MaxSavedNodeCount = 0, + + # Output Type + [Parameter()] + [ValidateSet('json', 'pscustomobject')] + [string] + $OutputType = 'pscustomobject' + ) + + if ($DesiredIdle -gt $MaxCapacity) { + throw "The desired idle count cannot be larger than the max capacity." + } + + $ElasticPoolObject = [PSCustomObject]@{ + serviceEndpointId = $ServiceEndpointId + serviceEndpointScope = $ServiceEndpointScope + azureId = $AzureId + maxCapacity = $MaxCapacity + desiredIdle = $DesiredIdle + recycleAfterEachUse = $RecycleAfterEachUse + maxSavedNodeCount = $MaxSavedNodeCount + osType = $OsType + desiredSize = $DesiredSize + agentInteractiveUI = $AgentInteractiveUI + timeToLiveMinutes = $TimeToLiveMinutes + } + + if ($OutputType -eq 'json') { + $ElasticPoolObject = $ElasticPoolObject | ConvertTo-Json -Depth 100 + } + + Write-Output $ElasticPoolObject +} +#endregion New-ADOPSElasticPoolObject + +#region New-ADOPSEnvironment + +function New-ADOPSEnvironment { + param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [Parameter()] + [string]$Description, + + [Parameter()] + [string]$AdminGroup, + + [Parameter()] + [switch]$SkipAdmin + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$organization/$project/_apis/distributedtask/environments?api-version=7.1-preview.1" + + $Body = [Ordered]@{ + name = $Name + description = $Description + } | ConvertTo-Json -Compress + + $InvokeSplat = @{ + Uri = $Uri + Method = 'Post' + Body = $Body + } + + Write-Verbose "Setting up environment" + $Environment = InvokeADOPSRestMethod @InvokeSplat + + if ($PSBoundParameters.ContainsKey('SkipAdmin')) { + Write-Verbose 'Skipped admin group' + } + else { + $secUri = "https://dev.azure.com/$organization/_apis/securityroles/scopes/distributedtask.environmentreferencerole/roleassignments/resources/$($Environment.project.id)_$($Environment.id)?api-version=7.1-preview.1" + + if ([string]::IsNullOrEmpty($AdminGroup)) { + $AdmGroupPN = "[$project]\Project Administrators" + } + else { + $AdmGroupPN = $AdminGroup + } + $ProjAdm = (Get-ADOPSGroup | Where-Object { $_.principalName -eq $AdmGroupPN }).originId + + $SecInvokeSplat = @{ + Uri = $secUri + Method = 'Put' + Body = "[{`"userId`":`"$ProjAdm`",`"roleName`":`"Administrator`"}]" + } + + try { + $SecResult = InvokeADOPSRestMethod @SecInvokeSplat + } + catch { + Write-Error 'Failed to update environment security. The environment may still have been created.' + } + } + + Write-Output $Environment +} +#endregion New-ADOPSEnvironment + +#region New-ADOPSGitBranch + +function New-ADOPSGitBranch { + param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidatePattern('^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}$', ErrorMessage = 'RepositoryId must be in GUID format (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)')] + [string]$RepositoryId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$BranchName, + + [Parameter(Mandatory)] + [ValidateLength(40, 40)] + [string]$CommitId + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Body = @( + [ordered]@{ + name = "refs/heads/$BranchName" + oldObjectId = '0000000000000000000000000000000000000000' + newObjectId = $CommitId + } + ) + $Body = ConvertTo-Json -InputObject $Body -Compress + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$RepositoryId/refs?api-version=7.1-preview.1" + $InvokeSplat = @{ + Uri = $Uri + Method = 'Post' + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSGitBranch + +#region New-ADOPSGitFile + +function New-ADOPSGitFile { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter(Mandatory)] + [string]$Repository, + + [Parameter(Mandatory)] + [string]$File, + + [Parameter()] + [string]$FileName, + + [Parameter()] + [string]$Path, + + [Parameter()] + [string]$CommitMessage = 'File added using the ADOPS PowerShell module' + ) + + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if ([string]::IsNullOrEmpty($Path)) { + $Path = '/' + } + + if ([string]::IsNullOrEmpty($FileName)) { + $FileName = (Get-Item -Path $File).Name + } + + $newFilePath = "/${Path}/$FileName" -replace '/{2,}', '/' # Make sure there are never two or more slashes in a row. + + $repoDetails = Get-ADOPSRepository -Project $Project -Repository $Repository + + $refIduri = "$($repoDetails.url)/refs?filter=$($repoDetails.defaultBranch -replace '^refs/','')&includeStatuses=true&latestStatusesOnly=true&api-version=7.2-preview.2" + $refId = InvokeADOPSRestMethod -Uri $refIduri -Method Get | Select-Object -ExpandProperty value + + $body = [ordered]@{ + refUpdates = @( + [ordered]@{ + name = $repoDetails.defaultBranch + oldObjectId = $refId.objectId + } + ) + commits = @( + [ordered]@{ + comment = $CommitMessage + changes = @( + [ordered]@{ + changeType = "add" + item = [ordered]@{ + path = $newFilePath + } + newContent = [ordered]@{ + content = $(Get-Content $File -Raw) + contentType = "rawtext" + } + } + ) + } + ) + } | ConvertTo-Json -Depth 100 -Compress + + $Uri = "$($repoDetails.url)/pushes?api-version=7.2-preview.3" + $InvokeSplat = @{ + Method = 'Post' + Uri = $Uri + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSGitFile + +#region New-ADOPSGroupEntitlement + +function New-ADOPSGroupEntitlement { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$GroupOriginId, + + [Parameter(Mandatory)] + [ValidateSet('Express', 'Advanced', 'Stakeholder', 'Professional', 'EarlyAdopter')] + [string]$AccountLicenseType, + + [Parameter(Mandatory)] + [ValidateSet('projectReader', 'projectContributor', 'projectAdministrator', 'projectStakeholder')] + [string]$ProjectGroupType, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ProjectId, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization, + + [Parameter()] + [switch]$Wait + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + # Group entitlements endpoint + $URI = "https://vsaex.dev.azure.com/$Organization/_apis/GroupEntitlements?api-version=7.1" + + # Initialize the request body + $Body = @{ + extensionRules = @( + @{ + id = 'ms.feed' + } + ) + group = @{ + origin = 'aad' + originId = $GroupOriginId + subjectKind = 'group' + } + licenseRule = @{ + licensingSource = 'account' + accountLicenseType = $AccountLicenseType + } + projectEntitlements = @( + @{ + group = @{ + groupType = $ProjectGroupType + } + projectRef = @{ + id = $ProjectId + } + } + ) + } + + $InvokeSplat = @{ + Method = 'Post' + Uri = $URI + Body = ($Body | ConvertTo-Json -Compress -Depth 10) + } + + $Out = InvokeADOPSRestMethod @InvokeSplat + + if ($PSBoundParameters.ContainsKey('Wait')) { + while ($Out.operationResult.status -eq 'inProgress') { + Start-Sleep -Seconds 1 + $Out = Invoke-ADOPSRestMethod -Uri $Out.operationResult.statusUrl -Method Get + } + } + + $Out +} +#endregion New-ADOPSGroupEntitlement + +#region New-ADOPSMergePolicy + +function New-ADOPSMergePolicy { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$RepositoryId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Branch, + + [Parameter()] + [Switch]$AllowNoFastForward, + + [Parameter()] + [Switch]$AllowSquash, + + [Parameter()] + [Switch]$AllowRebase, + + [Parameter()] + [Switch]$AllowRebaseMerge + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if (-Not ($Branch -match '^\w+/\w+/\w+$')) { + $Branch = "refs/heads/$Branch" + } + $GitBranchRef = $Branch + + $settings = [ordered]@{ + scope = @( + [ordered]@{ + repositoryId = $RepositoryId + refName = $GitBranchRef + matchKind = "exact" + } + ) + allowNoFastForward = $AllowNoFastForward.IsPresent + allowSquash = $AllowSquash.IsPresent + allowRebase = $AllowRebase.IsPresent + allowRebaseMerge = $AllowRebaseMerge.IsPresent + } + + + $Body = [ordered]@{ + type = [ordered]@{ + id = "fa4e907d-c16b-4a4c-9dfa-4916e5d171ab" + } + isBlocking = $true + isEnabled = $true + settings = $settings + } + + $Body = $Body | ConvertTo-Json -Depth 10 -Compress + + $InvokeSplat = @{ + Uri = "https://dev.azure.com/$Organization/$Project/_apis/policy/configurations?api-version=7.1-preview.1" + Method = 'POST' + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSMergePolicy + +#region New-ADOPSPipeline + +function New-ADOPSPipeline { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter(Mandatory)] + [ValidateScript( { + $_ -like '*.yaml' -or + $_ -like '*.yml' + }, + ErrorMessage = "Path must be to a yaml file in your repository like: folder/file.yaml or folder/file.yml")] + [string]$YamlPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Repository, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$FolderPath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/pipelines?api-version=7.1-preview.1" + + try { + $RepositoryID = (Get-ADOPSRepository -Organization $Organization -Project $Project -Repository $Repository -ErrorAction Stop).id + } + catch { + throw "The specified Repository $Repository was not found." + } + + if ($null -eq $RepositoryID) { + throw "The specified Repository $Repository was not found." + } + + $Body = [ordered]@{ + "name" = $Name + "folder" = "\$FolderPath" + "configuration" = [ordered]@{ + "type" = "yaml" + "path" = $YamlPath + "repository" = [ordered]@{ + "id" = $RepositoryID + "type" = "azureReposGit" + } + } + } + $Body = $Body | ConvertTo-Json -Compress + + $InvokeSplat = @{ + Method = 'Post' + Uri = $URI + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSPipeline + +#region New-ADOPSProject + +function New-ADOPSProject { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [Alias('Project')] + [string]$Name, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Description, + + [Parameter(Mandatory)] + [ValidateSet('Private', 'Public')] + [string]$Visibility, + + [Parameter()] + [ValidateSet('Git', 'Tfvc')] + [string]$SourceControlType = 'Git', + + # The process type for the project, such as Basic, Agile, Scrum or CMMI + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$ProcessTypeName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization, + + [Parameter()] + [switch]$Wait + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + # Get organization process templates + $URI = "https://dev.azure.com/$Organization/_apis/process/processes?api-version=7.1-preview.1" + + $InvokeSplat = @{ + Method = 'Get' + Uri = $URI + } + + $ProcessTemplates = (InvokeADOPSRestMethod @InvokeSplat).value + + if ([string]::IsNullOrWhiteSpace($ProcessTypeName)) { + $ProcessTemplateTypeId = $ProcessTemplates | Where-Object isDefault -eq $true | Select-Object -ExpandProperty id + } + else { + $ProcessTemplateTypeId = $ProcessTemplates | Where-Object name -eq $ProcessTypeName | Select-Object -ExpandProperty id + if ([string]::IsNullOrWhiteSpace($ProcessTemplateTypeId)) { + throw "The specified ProcessTypeName was not found amongst options: $($ProcessTemplates.name -join ', ')!" + } + } + + # Create project endpoint + $URI = "https://dev.azure.com/$Organization/_apis/projects?api-version=7.1-preview.4" + + $Body = [ordered]@{ + 'name' = $Name + 'visibility' = $Visibility + 'capabilities' = [ordered]@{ + 'versioncontrol' = [ordered]@{ + 'sourceControlType' = $SourceControlType + } + 'processTemplate' = [ordered]@{ + 'templateTypeId' = $ProcessTemplateTypeId + } + } + } + if (-not [string]::IsNullOrEmpty($Description)) { + $Body.Add('description', $Description) + } + $Body = $Body | ConvertTo-Json -Compress + + $InvokeSplat = @{ + Method = 'Post' + Uri = $URI + Body = $Body + } + + $Out = InvokeADOPSRestMethod @InvokeSplat + + if ($PSBoundParameters.ContainsKey('Wait')) { + $projectCreated = $Out.status + while ($projectCreated -ne 'succeeded') { + $projectCreated = (Invoke-ADOPSRestMethod -Uri $Out.url -Method Get).status + Start-Sleep -Seconds 1 + } + $Out = Get-ADOPSProject -Project $Name + } + + $Out +} +#endregion New-ADOPSProject + +#region New-ADOPSRepository + +function New-ADOPSRepository { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $ProjectID = (Get-ADOPSProject -Name $Project -Organization $Organization).id + + $URI = "https://dev.azure.com/$Organization/_apis/git/repositories?api-version=7.1-preview.1" + $Body = "{""name"":""$Name"",""project"":{""id"":""$ProjectID""}}" + + $InvokeSplat = @{ + Uri = $URI + Method = 'Post' + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSRepository + +#region New-ADOPSServiceConnection + +function New-ADOPSServiceConnection { + [cmdletbinding(DefaultParameterSetName = 'ServicePrincipal')] + param( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [string]$TenantId, + + [Parameter(Mandatory)] + [string]$SubscriptionName, + + [Parameter(Mandatory)] + [string]$SubscriptionId, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [string]$ConnectionName, + + [Parameter()] + [string]$Description, + + [Parameter(Mandatory, ParameterSetName = 'ServicePrincipal')] + [Parameter(Mandatory, ParameterSetName = 'ManagedServiceIdentity')] + [pscredential]$ServicePrincipal, + + [Parameter(Mandatory, ParameterSetName = 'ManagedServiceIdentity')] + [switch]$ManagedIdentity, + + [Parameter(Mandatory, ParameterSetName = 'WorkloadIdentityFederation')] + [switch]$WorkloadIdentityFederation, + + [Parameter(ParameterSetName = 'WorkloadIdentityFederation')] + [string]$AzureScope, + + [Parameter(ParameterSetName = 'WorkloadIdentityFederation')] + [ValidateSet('Manual', 'Automatic')] + [string] + $CreationMode = 'Automatic' + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + # Get ProjectId + $ProjectInfo = Get-ADOPSProject -Organization $Organization -Project $Project + + # Set connection name if not set by parameter + if (-not $ConnectionName) { + $ConnectionName = $SubscriptionName -replace ' ' + } + + switch ($PSCmdlet.ParameterSetName) { + + 'ServicePrincipal' { + $authorization = [ordered]@{ + parameters = [ordered]@{ + tenantid = $TenantId + serviceprincipalid = $ServicePrincipal.UserName + authenticationType = 'spnKey' + serviceprincipalkey = $ServicePrincipal.GetNetworkCredential().Password + } + scheme = 'ServicePrincipal' + } + + $data = [ordered]@{ + subscriptionId = $SubscriptionId + subscriptionName = $SubscriptionName + environment = 'AzureCloud' + scopeLevel = 'Subscription' + creationMode = 'Manual' + } + } + + 'ManagedServiceIdentity' { + $authorization = [ordered]@{ + parameters = [ordered]@{ + tenantid = $TenantId + serviceprincipalid = $ServicePrincipal.UserName + serviceprincipalkey = $ServicePrincipal.GetNetworkCredential().Password + } + scheme = 'ManagedServiceIdentity' + } + + $data = [ordered]@{ + subscriptionId = $SubscriptionId + subscriptionName = $SubscriptionName + environment = 'AzureCloud' + scopeLevel = 'Subscription' + } + } + + 'WorkloadIdentityFederation' { + if ($PSBoundParameters.ContainsKey('AzureScope')) { + $AuthParams = [ordered]@{ + tenantid = $TenantId + scope = $AzureScope + } + } + else { + $AuthParams = [ordered]@{ + tenantid = $TenantId + } + } + + $authorization = [ordered]@{ + parameters = $AuthParams + scheme = 'WorkloadIdentityFederation' + } + + $data = [ordered]@{ + subscriptionId = $SubscriptionId + subscriptionName = $SubscriptionName + environment = 'AzureCloud' + scopeLevel = 'Subscription' + creationMode = $CreationMode + isDraft = ($CreationMode = 'Manual') ? $True : $False + } + } + } + + # Create body for the API call + $Body = [ordered]@{ + data = $data + name = $ConnectionName + description = $Description + type = 'AzureRM' + url = 'https://management.azure.com/' + authorization = $authorization + isShared = $false + isReady = $true + serviceEndpointProjectReferences = @( + [ordered]@{ + projectReference = [ordered]@{ + id = $ProjectInfo.Id + name = $Project + } + name = $ConnectionName + } + ) + } | ConvertTo-Json -Depth 10 + + # Run function + $URI = "https://dev.azure.com/$Organization/$Project/_apis/serviceendpoint/endpoints?api-version=7.2-preview.4" + $InvokeSplat = @{ + Uri = $URI + Method = 'POST' + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSServiceConnection + +#region New-ADOPSUserStory + +function New-ADOPSUserStory { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Title, + + [Parameter(Mandatory)] + [string]$ProjectName, + + [Parameter()] + [string]$Description, + + [Parameter()] + [string]$Tags, + + [Parameter()] + [string]$Priority, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $URI = "https://dev.azure.com/$Organization/$ProjectName/_apis/wit/workitems/`$User Story?api-version=7.1-preview.3" + $Method = 'POST' + + $desc = $Description.Replace('"', "'") + $Body = "[ + { + `"op`": `"add`", + `"path`": `"/fields/System.Title`", + `"value`": `"$($Title)`" + }, + { + `"op`": `"add`", + `"path`": `"/fields/System.Description`", + `"value`": `"$($desc)`" + }, + { + `"op`": `"add`", + `"path`": `"/fields/System.Tags`", + `"value`": `"$($Tags)`" + }, + { + `"op`": `"add`", + `"path`": `"/fields/Microsoft.VSTS.Common.Priority`", + `"value`": `"$($Priority)`" + }, + ]" + + $ContentType = 'application/json-patch+json' + + $InvokeSplat = @{ + Uri = $URI + ContentType = $ContentType + Method = $Method + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSUserStory + +#region New-ADOPSVariableGroup + +function New-ADOPSVariableGroup { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$VariableGroupName, + + [Parameter(Mandatory, ParameterSetName = 'VariableSingle')] + [string]$VariableName, + + [Parameter(Mandatory, ParameterSetName = 'VariableSingle')] + [string]$VariableValue, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter(ParameterSetName = 'VariableSingle')] + [switch]$IsSecret, + + [Parameter(Mandatory, ParameterSetName = 'VariableHashtable')] + [ValidateScript( + { + $_ | ForEach-Object { $_.Keys -Contains 'Name' -and $_.Keys -Contains 'IsSecret' -and $_.Keys -Contains 'Value' -and $_.Keys.count -eq 3 } + }, + ErrorMessage = 'The hashtable must contain the following keys: Name, IsSecret, Value')] + [hashtable[]]$VariableHashtable, + + [Parameter()] + [string]$Description, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $ProjectInfo = Get-ADOPSProject -Organization $Organization -Project $Project + + $URI = "https://dev.azure.com/${Organization}/_apis/distributedtask/variablegroups?api-version=7.1-preview.2" + $Method = 'POST' + + if ($VariableName) { + $Body = @{ + Name = $VariableGroupName + Description = $Description + Type = 'Vsts' + variableGroupProjectReferences = @(@{ + Name = $VariableGroupName + Description = $Description + projectReference = @{ + Id = $ProjectInfo.Id + } + }) + variables = @{ + $VariableName = @{ + isSecret = $IsSecret.IsPresent + value = $VariableValue + } + } + } | ConvertTo-Json -Depth 10 + } + else { + + $Variables = @{} + foreach ($Hashtable in $VariableHashtable) { + $Variables.Add( + $Hashtable.Name, @{ + isSecret = $Hashtable.IsSecret + value = $Hashtable.Value + } + ) + } + + $Body = @{ + Name = $VariableGroupName + Description = $Description + Type = 'Vsts' + variableGroupProjectReferences = @(@{ + Name = $VariableGroupName + Description = $Description + projectReference = @{ + Id = $($ProjectInfo.Id) + } + }) + variables = $Variables + } | ConvertTo-Json -Depth 10 + } + + InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body +} +#endregion New-ADOPSVariableGroup + +#region New-ADOPSWiki + +function New-ADOPSWiki { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$WikiName, + + [Parameter(Mandatory)] + [string]$WikiRepository, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [string]$WikiRepositoryPath = '/', + + [Parameter()] + [string]$GitBranch = 'main', + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + try { + $ProjectId = (Get-ADOPSProject -Project $Project).id + } + catch { + throw "The specified Project $Project was not found." + } + if ($null -eq $ProjectId) { + throw "The specified Project $Project was not found." + } + + try { + $RepositoryId = (Get-ADOPSRepository -Project $Project -Repository $WikiRepository).id + } + catch { + throw "The specified Repository $WikiRepository was not found." + } + + if ($null -eq $RepositoryID) { + throw "The specified Repository $WikiRepository was not found." + } + + $URI = "https://dev.azure.com/$Organization/_apis/wiki/wikis?api-version=7.1-preview.2" + + $Method = 'Post' + $Body = [ordered]@{ + 'type' = 'codeWiki' + 'name' = $WikiName + 'projectId' = $ProjectId + 'repositoryId' = $RepositoryId + 'mappedPath' = $WikiRepositoryPath + 'version' = @{'version' = $GitBranch } + } + + $InvokeSplat = @{ + Uri = $URI + Method = $Method + Body = $Body | ConvertTo-Json -Compress + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion New-ADOPSWiki + +#region Remove-ADOPSRepository + +function Remove-ADOPSRepository { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$RepositoryID, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$RepositoryID`?api-version=7.1-preview.1" + + $result = InvokeADOPSRestMethod -Uri $Uri -Method Delete + + if ($result.psobject.properties.name -contains 'value') { + Write-Output -InputObject $result.value + } + else { + Write-Output -InputObject $result + } +} +#endregion Remove-ADOPSRepository + +#region Remove-ADOPSVariableGroup + +function Remove-ADOPSVariableGroup { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$VariableGroupName, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/variablegroups?api-version=7.1-preview.2" + $VariableGroups = (InvokeADOPSRestMethod -Uri $Uri -Method 'Get').value + + $GroupToRemove = $VariableGroups | Where-Object name -eq $VariableGroupName + if ($null -eq $GroupToRemove) { + throw "Could not find group $VariableGroupName! Groups found: $($VariableGroups.name -join ', ')." + } + + $ProjectId = (Get-ADOPSProject -Organization $Organization -Project $Project).id + + $URI = "https://dev.azure.com/$Organization/_apis/distributedtask/variablegroups/$($GroupToRemove.id)?projectIds=$ProjectId&api-version=7.1-preview.2" + $null = InvokeADOPSRestMethod -Uri $Uri -Method 'Delete' +} +#endregion Remove-ADOPSVariableGroup + +#region Revoke-ADOPSPipelinePermission + +function Revoke-ADOPSPipelinePermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [string]$Project, + + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [switch]$AllPipelines, + + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [int]$PipelineId, + + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [ResourceType]$ResourceType, + + [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] + [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] + [string]$ResourceId, + + [Parameter(ParameterSetName = 'AllPipelines')] + [Parameter(ParameterSetName = 'SinglePipeline')] + [string]$Organization + ) + + SetADOPSPipelinePermission @PSBoundParameters -Authorized $false +} +#endregion Revoke-ADOPSPipelinePermission + +#region Save-ADOPSPipelineTask + +function Save-ADOPSPipelineTask { + [CmdletBinding(DefaultParameterSetName = 'InputData')] + param ( + [Parameter(ParameterSetName = 'InputData')] + [Parameter(ParameterSetName = 'InputObject')] + [string]$Organization, + + [Parameter(ParameterSetName = 'InputData')] + [Parameter(ParameterSetName = 'InputObject')] + [string]$Path = '.', + + [Parameter(Mandatory, ParameterSetName = 'InputData')] + [string]$TaskId, + + [Parameter(Mandatory, ParameterSetName = 'InputData')] + [version]$TaskVersion, + + [Parameter(ParameterSetName = 'InputData')] + [string]$FileName, + + [Parameter(Mandatory, ParameterSetName = 'InputObject', ValueFromPipeline, Position = 0)] + [psobject[]]$InputObject + ) + begin { + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + } + process { + switch ($PSCmdlet.ParameterSetName) { + 'InputData' { + if ([string]::IsNullOrEmpty($FileName)) { + $FileName = "$TaskId.$($TaskVersion.ToString(3)).zip" + } + if (-Not $FileName -match '.zip$' ) { + $FileName = "$FileName.zip" + } + + [array]$FilesToDownload = @{ + TaskId = $TaskId + TaskVersionString = $TaskVersion.ToString(3) + OutputFile = Join-Path -Path $Path -ChildPath $FileName + } + } + 'InputObject' { + [array]$FilesToDownload = foreach ($o in $InputObject) { + @{ + TaskId = $o.id + TaskVersionString = "$($o.version.major).$($o.version.minor).$($o.version.patch)" + OutputFile = Join-Path -Path $Path -ChildPath "$($o.name)-$($o.id)-$($o.version.major).$($o.version.minor).$($o.version.patch).zip" + } + } + } + } + + foreach ($File in $FilesToDownload) { + $Url = "https://dev.azure.com/$Organization/_apis/distributedtask/tasks/$($File.TaskId)/$($File.TaskversionString)" + InvokeADOPSRestMethod -Uri $Url -Method Get -OutFile $File.OutputFile + } + } + end {} +} +#endregion Save-ADOPSPipelineTask + +#region Set-ADOPSArtifactFeed + +function Set-ADOPSArtifactFeed { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [Alias('Name')] + [string]$FeedId, + + [Parameter()] + [string]$Description, + + [Parameter()] + [Alias('IncludeUpstream')] + [bool]$UpstreamEnabled + ) + + if ( + -not ($PSBoundParameters.ContainsKey('Description')) -and + -not ($PSBoundParameters.ContainsKey('UpstreamEnabled')) + ) { + Write-Verbose 'Nothing to do. Exiting early' + } + else { + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://feeds.dev.azure.com/${Organization}/${Project}/_apis/packaging/feeds/${FeedId}?api-version=7.2-preview.1" + $Method = 'Patch' + + $Body = [ordered]@{} + if ($PSBoundParameters.ContainsKey('Description')) { + $Body['description'] = $Description + } + if ($PSBoundParameters.ContainsKey('UpstreamEnabled')) { + $Body['upstreamEnabled'] = $UpstreamEnabled + if ($UpstreamEnabled -eq $true) { + $upstreamSources = @( + @{ + name = "npmjs" + protocol = "npm" + location = "https://registry.npmjs.org/" + displayLocation = "https://registry.npmjs.org/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "NuGet Gallery" + protocol = "nuget" + location = "https://api.nuget.org/v3/index.json" + displayLocation = "https://api.nuget.org/v3/index.json" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "PowerShell Gallery" + protocol = "nuget" + location = "https://www.powershellgallery.com/api/v2/" + displayLocation = "https://www.powershellgallery.com/api/v2/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "PyPI" + protocol = "pypi" + location = "https://pypi.org/" + displayLocation = "https://pypi.org/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "Maven Central" + protocol = "Maven" + location = "https://repo.maven.apache.org/maven2/" + displayLocation = "https://repo.maven.apache.org/maven2/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "Google Maven Repository" + protocol = "Maven" + location = "https://dl.google.com/android/maven2/" + displayLocation = "https://dl.google.com/android/maven2/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "JitPack" + protocol = "Maven" + location = "https://jitpack.io/" + displayLocation = "https://jitpack.io/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "Gradle Plugins" + protocol = "Maven" + location = "https://plugins.gradle.org/m2/" + displayLocation = "https://plugins.gradle.org/m2/" + upstreamSourceType = "public" + status = "ok" + } + @{ + name = "crates.io" + protocol = "Cargo" + location = "https://index.crates.io/" + displayLocation = "https://index.crates.io/" + upstreamSourceType = "public" + status = "ok" + } + ) + $body.Add('upstreamSources', $upstreamSources) + } + } + + $Body = $Body | ConvertTo-Json -Compress + + $InvokeSplat = @{ + Uri = $Uri + Method = $Method + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat + } +} +#endregion Set-ADOPSArtifactFeed + +#region Set-ADOPSBuildDefinition + +function Set-ADOPSBuildDefinition { + [CmdletBinding()] + Param( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [Alias('Definition')] + [Object]$DefinitionObject + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $project = $DefinitionObject.project.id + $id = $DefinitionObject.id + + $Uri = "https://dev.azure.com/${Organization}/${project}/_apis/build/definitions/${id}?api-version=7.2-preview.7" + $Method = 'Put' + + if (-Not ($DefinitionObject -is [string])) { + $DefinitionObject = $DefinitionObject | ConvertTo-Json -Compress -Depth 100 + } + + $InvokeSplat = @{ + Uri = $Uri + Method = $Method + Body = $DefinitionObject + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion Set-ADOPSBuildDefinition + +#region Set-ADOPSElasticPool + +function Set-ADOPSElasticPool { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [int]$PoolId, + + [Parameter(Mandatory)] + $ElasticPoolObject, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools/$PoolId`?api-version=7.1-preview.1" + + if ($ElasticPoolObject.GetType().Name -eq 'String') { + $Body = $ElasticPoolObject + } + else { + try { + $Body = $ElasticPoolObject | ConvertTo-Json -Depth 100 + } + catch { + throw 'Unable to convert the content of the ElasticPoolObject to json.' + } + } + + $Method = 'PATCH' + $ElasticPoolInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body + Write-Output $ElasticPoolInfo +} +#endregion Set-ADOPSElasticPool + +#region Set-ADOPSGitPermission + +function Set-ADOPSGitPermission { + [CmdletBinding()] + param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization, + + [Parameter(Mandatory)] + [Alias('ProjectId')] + [string]$Project, + + [Parameter(Mandatory)] + [Alias('RepositoryId')] + [string]$Repository, + + [Parameter(Mandatory)] + [ValidatePattern('^[a-z]{3,5}\.[a-zA-Z0-9]{40,}$', ErrorMessage = 'Descriptor must be in the descriptor format')] + [string]$Descriptor, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [AccessLevels[]]$Allow, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [AccessLevels[]]$Deny + ) + + # Allow input of names instead of IDs + if ($Project -notmatch '^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}$') { + $Project = Get-ADOPSProject -Name $Project | Select-Object -ExpandProperty id + if ($null -eq $Project) { + throw "No project named $Project found." + } + } + if ($Repository -notmatch '^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}$') { + $Repository = Get-ADOPSRepository -Repository $Repository -Project $Project + if ($null -eq $Repository) { + throw "No repository named $Repository in project $Project found." + } + } + + + if (-not $Allow -and -not $Deny) { + Write-Verbose 'No allow or deny rules set' + } + else { + if ($null -eq $Allow) { + $allowRules = 0 + } + else { + $allowRules = ([accesslevels]$Allow).value__ + } + if ($null -eq $Deny) { + $denyRules = 0 + } + else { + $denyRules = ([accesslevels]$Deny).value__ + } + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $SubjectDescriptor = (InvokeADOPSRestMethod -Uri "https://vssps.dev.azure.com/$Organization/_apis/identities?subjectDescriptors=$Descriptor&queryMembership=None&api-version=7.1-preview.1" -Method Get).value.descriptor + + $Body = [ordered]@{ + token = "repov2/$Project/$Repository" + merge = $true + accessControlEntries = @( + [ordered]@{ + allow = $allowRules + deny = $denyRules + descriptor = $SubjectDescriptor + } + ) + } | ConvertTo-Json -Compress -Depth 10 + + $Uri = "https://dev.azure.com/$Organization/_apis/accesscontrolentries/2e9eb7ed-3c0a-47d4-87c1-0ffdd275fd87?api-version=7.1-preview.1" + + $InvokeSplat = @{ + Uri = $Uri + Method = 'Post' + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat + } +} +#endregion Set-ADOPSGitPermission + +#region Set-ADOPSPipelineRetentionSettings + +function Set-ADOPSPipelineRetentionSettings { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + $Values + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/build/retention?api-version=7.1-preview.1" + + $Body = ConvertRetentionSettingsToPatchBody -Values $Values | ConvertTo-Json + Write-Debug $Body + + $Response = InvokeADOPSRestMethod -Uri $Uri -Method Patch -Body $Body + Write-Debug $Response + + $Settings = ConvertRetentionSettingsGetToPatch -Response $Response + + Write-Output $Settings +} +#endregion Set-ADOPSPipelineRetentionSettings + +#region Set-ADOPSPipelineSettings + +function Set-ADOPSPipelineSettings { + [CmdletBinding()] + param ( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter(Mandatory)] + $Values + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/build/generalsettings?api-version=7.1-preview.1" + + $Body = $Values | ConvertTo-Json -Compress + $Settings = InvokeADOPSRestMethod -Uri $Uri -Method 'PATCH' -Body $Body + + Write-Output $Settings +} +#endregion Set-ADOPSPipelineSettings + +#region Set-ADOPSProject + +function Set-ADOPSProject { + [CmdletBinding(DefaultParameterSetName = 'ProjectName')] + param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization, + + [Parameter(Mandatory, ParameterSetName = 'ProjectId')] + [ValidateScript({ + [guid]::Parse($_) + }, ErrorMessage = 'ProjectID format is wrong.')] + [string]$ProjectId, + + [Parameter(Mandatory, ParameterSetName = 'ProjectName')] + [ValidateNotNullOrEmpty()] + [Alias('Project', 'Name')] + [string]$ProjectName, + + [Parameter()] + [string]$Description, + + [Parameter()] + [ValidateSet('Private', 'Public')] + [string]$Visibility, + + [Parameter()] + [switch]$Wait + ) + + if (-Not ($PSBoundParameters.ContainsKey('Description')) -and -Not ($PSBoundParameters.ContainsKey('Visibility'))) { + Write-Verbose 'Nothing to update. Exiting.' + } + else { + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + if ($PSCmdlet.ParameterSetName -eq 'ProjectName') { + $ProjectId = Get-ADOPSProject -Name $ProjectName | Select-Object -ExpandProperty id + } + + $uri = "https://dev.azure.com/${Organization}/_apis/projects/${ProjectId}?api-version=7.2-preview.4" + + + $body = [ordered]@{} + + if ($PSBoundParameters.ContainsKey('Description')) { + $body.Add('description', $Description) + } + + if (-not [string]::IsNullOrEmpty($Visibility)) { + $body.Add('visibility', $Visibility.ToLower()) + } + + $body = $body | ConvertTo-Json -Compress + + $InvokeSplat = [ordered]@{ + 'Uri' = $uri + 'Method' = 'Patch' + 'Body' = $body + } + + $res = InvokeADOPSRestMethod @InvokeSplat + + if ($PSBoundParameters.ContainsKey('Wait')) { + $i = 0 + while (($res.status -notin @('cancelled', 'failed', 'succeeded')) -and $i -le 20) { + $res = InvokeADOPSRestMethod -Uri $res.url -Method Get + $i++ + Start-Sleep -Seconds 1 + } + if ($i -ge 20) { + Write-Verbose 'Status still not complete, failed, or canceled. Please verify project update.' + } + } + + $res + } +} +#endregion Set-ADOPSProject + +#region Set-ADOPSRepository + +function Set-ADOPSRepository { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$RepositoryId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Project, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Organization, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$DefaultBranch, + + [Parameter()] + [bool]$IsDisabled, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$NewName + ) + + if ( ([string]::IsNullOrEmpty($DefaultBranch)) -and ([string]::IsNullOrEmpty($NewName)) -and (-Not $PSBoundParameters.ContainsKey('IsDisabled')) ) { + # Nothing to do, exit early + } + else { + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $URI = "https://dev.azure.com/${Organization}/${Project}/_apis/git/repositories/${RepositoryId}?api-version=7.1-preview.1" + + $InvokeSplat = @{ + URI = $Uri + Method = 'Patch' + } + + if ($PSBoundParameters.ContainsKey('IsDisabled') -and ($false -eq $IsDisabled)) { + # Enabling a repo needs to be done in a separate call before changing any other settings. + $Body = [ordered]@{ + 'isDisabled' = $IsDisabled + } + $InvokeSplat.Body = $Body | ConvertTo-Json -Compress + try { + InvokeADOPSRestMethod @InvokeSplat + } + catch { + if (($_.ErrorDetails.Message | ConvertFrom-Json).message -eq 'The repository change is not supported.') { + Write-Warning 'Failed to enable the repo. This is most likely because it is already enabled.' + } + else { + throw $_ + } + } + } + + $Body = [ordered]@{} + + if (-Not [string]::IsNullOrEmpty($NewName)) { + $Body.Add('name', $NewName) + } + + if (-Not [string]::IsNullOrEmpty($DefaultBranch)) { + if (-Not ($DefaultBranch -match '^\w+/\w+/\w+$')) { + $DefaultBranch = "refs/heads/$DefaultBranch" + } + $Body.Add('defaultBranch', $DefaultBranch) + } + + if ($body.Keys.Count -gt 0) { + $InvokeSplat.Body = $Body | ConvertTo-Json -Compress + try { + InvokeADOPSRestMethod @InvokeSplat + } + catch { + if (($_.ErrorDetails.Message | ConvertFrom-Json).message -like "TF401019*") { + Write-Warning 'Failed to update the repo. This may happen if the repo is disabled. Make sure it is enabled, or add -IsDisabled:$false' + } + else { + throw $_ + } + } + } + + if ($PSBoundParameters.ContainsKey('IsDisabled') -and ($true -eq $IsDisabled)) { + # Disabling a repo needs to be done in a separate call and after any other changes. + $Body = [ordered]@{ + 'isDisabled' = $IsDisabled + } + $InvokeSplat.Body = $Body | ConvertTo-Json -Compress + try { + InvokeADOPSRestMethod @InvokeSplat + } + catch { + if (($_.ErrorDetails.Message | ConvertFrom-Json).message -eq 'The repository change is not supported.') { + Write-Warning 'Failed to disable the repo. This is most likely because it is already disabled.' + } + else { + throw $_ + } + } + } + } +} +#endregion Set-ADOPSRepository + +#region Set-ADOPSServiceConnection + +function Set-ADOPSServiceConnection { + [CmdletBinding(DefaultParameterSetName = 'ServicePrincipal')] + param ( + [Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [string]$TenantId, + + [Parameter(Mandatory)] + [string]$SubscriptionName, + + [Parameter(Mandatory)] + [string]$SubscriptionId, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter(Mandatory)] + [guid]$ServiceEndpointId, + + [Parameter()] + [string]$ConnectionName, + + [Parameter()] + [string]$Description, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$EndpointOperation, + + [Parameter(Mandatory, ParameterSetName = 'ServicePrincipal')] + [pscredential]$ServicePrincipal, + + [Parameter(Mandatory, ParameterSetName = 'ManagedServiceIdentity')] + [switch]$ManagedIdentity, + + [Parameter(Mandatory, ParameterSetName = 'WorkloadIdentityFederation')] + [string]$ServicePrincipalId, + + [Parameter(Mandatory, ParameterSetName = 'WorkloadIdentityFederation')] + [string]$WorkloadIdentityFederationIssuer, + + [Parameter(Mandatory, ParameterSetName = 'WorkloadIdentityFederation')] + [string]$WorkloadIdentityFederationSubject + + ) + + process { + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + # Get ProjectId + $ProjectInfo = Get-ADOPSProject -Organization $Organization -Project $Project + + # Set connection name if not set by parameter + if (-not $ConnectionName) { + $ConnectionName = $SubscriptionName -replace " " + } + + switch ($PSCmdlet.ParameterSetName) { + + 'ServicePrincipal' { + $authorization = [ordered]@{ + parameters = [ordered]@{ + tenantid = $TenantId + serviceprincipalid = $ServicePrincipal.UserName + authenticationType = "spnKey" + serviceprincipalkey = $ServicePrincipal.GetNetworkCredential().Password + } + scheme = "ServicePrincipal" + } + + $data = [ordered]@{ + subscriptionId = $SubscriptionId + subscriptionName = $SubscriptionName + environment = "AzureCloud" + scopeLevel = "Subscription" + creationMode = "Manual" + } + } + + 'ManagedServiceIdentity' { + $authorization = [ordered]@{ + parameters = [ordered]@{ + tenantid = $TenantId + } + scheme = "ManagedServiceIdentity" + } + } + + 'WorkloadIdentityFederation' { + $authorization = [ordered]@{ + parameters = [ordered]@{ + tenantid = $TenantId + serviceprincipalid = $ServicePrincipalId + workloadIdentityFederationIssuer = $WorkloadIdentityFederationIssuer + workloadIdentityFederationSubject = $WorkloadIdentityFederationSubject + } + scheme = "WorkloadIdentityFederation" + } + + $data = [ordered]@{ + subscriptionId = $SubscriptionId + subscriptionName = $SubscriptionName + environment = "AzureCloud" + scopeLevel = "Subscription" + creationMode = "Manual" + } + } + } + + # Create body for the API call + $Body = [ordered]@{ + authorization = $authorization + data = $data + description = $Description + id = $ServiceEndpointId + isReady = $true + isShared = $false + name = $ConnectionName + serviceEndpointProjectReferences = @( + [ordered]@{ + projectReference = [ordered]@{ + id = $ProjectInfo.Id + name = $Project + } + name = $ConnectionName + } + ) + type = "AzureRM" + url = "https://management.azure.com/" + } | ConvertTo-Json -Depth 10 + + if ($PSBoundParameters.ContainsKey('EndpointOperation')) { + $URI = "https://dev.azure.com/$Organization/_apis/serviceendpoint/endpoints/$ServiceEndpointId`?operation=$EndpointOperation`&api-version=7.1-preview.4" + } + else { + $URI = "https://dev.azure.com/$Organization/_apis/serviceendpoint/endpoints/$ServiceEndpointId`?api-version=7.1-preview.4" + } + + $InvokeSplat = @{ + Uri = $URI + Method = "PUT" + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat + } +} +#endregion Set-ADOPSServiceConnection + +#region Start-ADOPSPipeline + +function Start-ADOPSPipeline { + param ( + [Parameter(Mandatory)] + [string]$Name, + + [Parameter(Mandatory)] + [string]$Project, + + [Parameter()] + [string]$Branch = 'main', + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $AllPipelinesURI = "https://dev.azure.com/$Organization/$Project/_apis/pipelines?api-version=7.1-preview.1" + $AllPipelines = InvokeADOPSRestMethod -Method Get -Uri $AllPipelinesURI + $PipelineID = ($AllPipelines.value | Where-Object -Property Name -EQ $Name).id + + if ([string]::IsNullOrEmpty($PipelineID)) { + throw "No pipeline with name $Name found." + } + + if ($Branch -notmatch '^refs/.*') { + $Branch = 'refs/heads/' + $Branch + } + $URI = "https://dev.azure.com/$Organization/$Project/_apis/pipelines/$PipelineID/runs?api-version=7.1-preview.1" + $Body = '{"stagesToSkip":[],"resources":{"repositories":{"self":{"refName":"' + $Branch + '"}}},"variables":{}}' + + $InvokeSplat = @{ + Method = 'Post' + Uri = $URI + Body = $Body + } + + InvokeADOPSRestMethod @InvokeSplat +} +#endregion Start-ADOPSPipeline + +#region Test-ADOPSYamlFile + +function Test-ADOPSYamlFile { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$Project, + + [Parameter(Mandatory)] + [ValidateScript({ + $_ -match '.*\.y[aA]{0,1}ml$' + }, ErrorMessage = 'Fileextension must be ".yaml" or ".yml"')] + [string]$File, + + [Parameter(Mandatory)] + [int]$PipelineId, + + [Parameter()] + [string]$Organization + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://dev.azure.com/$Organization/$Project/_apis/pipelines/$PipelineId/runs?api-version=7.1-preview.1" + + $FileData = Get-Content $File -Raw + + $Body = @{ + previewRun = $true + templateParameters = @{} + resources = @{} + yamlOverride = $FileData + } | ConvertTo-Json -Depth 10 -Compress + + $InvokeSplat = @{ + Uri = $URI + Method = 'Post' + Body = $Body + } + + try { + $Result = InvokeADOPSRestMethod @InvokeSplat + Write-Output "$file validation success." + } + catch [Microsoft.PowerShell.Commands.HttpResponseException] { + if ($_.ErrorDetails.Message) { + $r = $_.ErrorDetails.Message | ConvertFrom-Json + if ($r.typeName -like '*PipelineValidationException*') { + Write-Warning "Validation failed:`n$($r.message)" + } + else { + throw $_ + } + } + } +} +#endregion Test-ADOPSYamlFile + +function Get-ADOPSMembership { + param ([Parameter()] + [string]$Organization, + + [Parameter(Mandatory)] + [string] + $Descriptor, + + # The default value for direction is 'up' meaning return all memberships where the subject is a member (e.g. all groups the subject is a member of). Alternatively, passing the direction as 'down' will return all memberships where the subject is a container (e.g. all members of the subject group). + [Parameter()] + [string] + [ValidateSet('up', 'down')] + $Direction = 'up' + ) + + # If user didn't specify org, get it from saved context + if ([string]::IsNullOrEmpty($Organization)) { + $Organization = GetADOPSDefaultOrganization + } + + $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/Memberships/$Descriptor`?direction=$Direction&depth=1&api-version=7.2-preview.1" + $Response = InvokeADOPSRestMethod -Uri $Uri -Method 'GET' + + switch ($Direction) { + 'up' { + if ($Response.value.count -ne 0) { + $Membership = $Response.value.containerDescriptor +} else { +$Membership = $Response.value +} + } + 'down' { + if ($Response.value.count -ne 0) { +$Membership = $Response.value.memberDescriptor +} else { +$Membership = $Response.value +} + + } + } + + $Membership | ForEach-Object -begin { + $Members = New-object System.Collections.ArrayList + } -process { + $MemberType = ($_).split('.')[0] + $Identity = $_ + switch ($MemberType) { + 'aadgp' { + Get-ADOPSGroup -Descriptor $Identity | ForEach-Object { $Members.Add($_) | Out-Null } + } + 'vssgp' { + Get-ADOPSGroup -Descriptor $Identity | ForEach-Object { $Members.Add($_) | Out-Null } + } + 'aad' { + Get-ADOPSUser -Descriptor $Identity | ForEach-Object { $Members.Add($_) | Out-Null } + } + } + } + + Write-Output $Members +} \ No newline at end of file diff --git a/tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml b/tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml new file mode 100644 index 000000000..ab9a7ec77 --- /dev/null +++ b/tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml @@ -0,0 +1,11788 @@ + + + + + Connect-ADOPS + Connect + ADOPS + + Establish a connection to Azure DevOps. + + + + Establish a OAuth connection to one or more Azure DevOps organizations. If the logged on user does not have access to the given organization you may need to specify TenantID. + + + + Connect-ADOPS + + Interactive + + Connect to an Azure DevOps organization using an interactive browser login. + + + SwitchParameter + + + False + + + Organization + + Name of the Azure DevOps organization or url of the organization like `https://dev.azure.com/MyOrg/` + + String + + String + + + None + + + SkipVerification + + This switch is necessary to set when using System.AccessToken as OAuth token in Azure DevOps. This will bypass the access verification. If this flag is set we will not check if the provided account can access the selected organization, and there may be access errors when API calls are made. + + + SwitchParameter + + + False + + + TenantId + + Specify a tenant to connect to, by id. + + String + + String + + + None + + + + Connect-ADOPS + + ManagedIdentity + + Connect to an Azure DevOps organization using a managed identity. + + + SwitchParameter + + + False + + + Organization + + Name of the Azure DevOps organization or url of the organization like `https://dev.azure.com/MyOrg/` + + String + + String + + + None + + + SkipVerification + + This switch is necessary to set when using System.AccessToken as OAuth token in Azure DevOps. This will bypass the access verification. If this flag is set we will not check if the provided account can access the selected organization, and there may be access errors when API calls are made. + + + SwitchParameter + + + False + + + TenantId + + Specify a tenant to connect to, by id. + + String + + String + + + None + + + + Connect-ADOPS + + OAuthToken + + Connect to an Azure DevOps organization using an existing access token. + + String + + String + + + None + + + Organization + + Name of the Azure DevOps organization or url of the organization like `https://dev.azure.com/MyOrg/` + + String + + String + + + None + + + SkipVerification + + This switch is necessary to set when using System.AccessToken as OAuth token in Azure DevOps. This will bypass the access verification. If this flag is set we will not check if the provided account can access the selected organization, and there may be access errors when API calls are made. + + + SwitchParameter + + + False + + + TenantId + + Specify a tenant to connect to, by id. + + String + + String + + + None + + + + + + Interactive + + Connect to an Azure DevOps organization using an interactive browser login. + + SwitchParameter + + SwitchParameter + + + False + + + ManagedIdentity + + Connect to an Azure DevOps organization using a managed identity. + + SwitchParameter + + SwitchParameter + + + False + + + OAuthToken + + Connect to an Azure DevOps organization using an existing access token. + + String + + String + + + None + + + Organization + + Name of the Azure DevOps organization or url of the organization like `https://dev.azure.com/MyOrg/` + + String + + String + + + None + + + SkipVerification + + This switch is necessary to set when using System.AccessToken as OAuth token in Azure DevOps. This will bypass the access verification. If this flag is set we will not check if the provided account can access the selected organization, and there may be access errors when API calls are made. + + SwitchParameter + + SwitchParameter + + + False + + + TenantId + + Specify a tenant to connect to, by id. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + Connect-ADOPS -Organization 'ADOPS' + + Connect to an Azure DevOps organization interactively. + + + + -------------------------- Example 2 -------------------------- + Connect-ADOPS -ManagedIdentity -Organization 'ADOPS' -TenantId $TenantId + + Connect to an Azure DevOps organization using a managed identity, specifying the tenant. + + + + -------------------------- Example 3 -------------------------- + Connect-ADOPS -OAuthToken $AccessToken -Organization 'ADOPS' -SkipVerification + + Connect to an Azure DevOps organization using an existing access token. It will not verify that the token has access to the selected organization, and may result in failed API calls. + + + + + + + + Disconnect-ADOPS + Disconnect + ADOPS + + Remove an established connection to Azure DevOps. + + + + Remove an established connection to Azure DevOps. + + + + Disconnect-ADOPS + + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Disconnect-ADOPS -Organization 'ADOPS' + + Removes the established connection to the organization "ADOPS". + + + + -------------------------- Example 2 -------------------------- + PS C:\> Disconnect-ADOPS + + Removes the established connection without specifying organization, if there is only one. + + + + + + + + Get-ADOPSAgentQueue + Get + ADOPSAgentQueue + + Gets one or more agent queues (pools) connected to a project + + + + This command will get all or one named agent queue. Agent queues is what the Azure DevOps GUI refers to ass pools, connected to a project, as in the `Project Settings` -> `Pipelines` -> `Agent Pools` page. + + + + Get-ADOPSAgentQueue + + Organization + + The organization to get queues from. + + String + + String + + + None + + + Project + + The project to get queues from. + + String + + String + + + None + + + QueueName + + Specifies one specific queue to get. + + String + + String + + + None + + + + + + Organization + + The organization to get queues from. + + String + + String + + + None + + + Project + + The project to get queues from. + + String + + String + + + None + + + QueueName + + Specifies one specific queue to get. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSAgentQueue -Project 'MyProject' + + This command lists all agent queues in the `MyProject` project + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSAgentQueue -Project 'MyProject' -QueueName 'MyQueue' + + This command gets the `MyQueue` agent queue in the `MyProject` project. If no queue named `MyQueue` exists it will return nothing. + + + + + + + + Get-ADOPSArtifactFeed + Get + ADOPSArtifactFeed + + Gets one or more artifact feeds + + + + This command gets one or more artifact feeds. If a project is given returns project scoped feeds. If no project is given it returns organization and project scoped feeds. + + + + Get-ADOPSArtifactFeed + + FeedId + + Id of feed to get. GUID format. + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + Project + + Project to get feed(s) from + + String + + String + + + None + + + + Get-ADOPSArtifactFeed + + Organization + + Name of the organization to use. + + String + + String + + + None + + + Project + + Project to get feed(s) from + + String + + String + + + None + + + + + + FeedId + + Id of feed to get. GUID format. + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + Project + + Project to get feed(s) from + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSArtifactFeed + + This command returns the all feeds in the organization. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSArtifactFeed -Project 'MyProject' -FeedId '222cc2c2-2cc2-222c-c2c2-c22cc222222c' + + This command returns the feed with feed id '222cc2c2-2cc2-222c-c2c2-c22cc222222c' + + + + + + + + Get-ADOPSAuditActions + Get + ADOPSAuditActions + + Gets all actions that Azure DevOps can log. + + + + This commands lists all actions that Azure DevOps gets in the audit log. https://learn.microsoft.com/en-us/azure/devops/organizations/audit/azure-devops-auditing?view=azure-devops&tabs=preview-page#review-audit-log + + + + Get-ADOPSAuditActions + + Organization + + The organization to get log data from. + + String + + String + + + None + + + + + + Organization + + The organization to get log data from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSAuditActions + + This commands lists all actions that Azure DevOps gets in the audit log. + + + + + + + + Get-ADOPSBuildDefinition + Get + ADOPSBuildDefinition + + This command gets one or more build definitions. + + + + This command gets one or more build definitions. If will always return the full definition object in order for it to be used to update a definition. If no Id is given it will return all build definitions from the selected project. + + + + Get-ADOPSBuildDefinition + + Organization + + The organization to get definitions from. + + String + + String + + + None + + + Project + + The project to get definition from. + + String + + String + + + None + + + Id + + build definition id to get. + + Int32 + + Int32 + + + None + + + + + + Id + + build definition id to get. + + Int32 + + Int32 + + + None + + + Organization + + The organization to get definitions from. + + String + + String + + + None + + + Project + + The project to get definition from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSBuildDefinition -Project myProject -Id 123 + + This command will return the build definition with id 123. If no definition id 123 is found it will throw an error. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSBuildDefinition -Project myProject + + This command will return all build definitions in project myProject. If no definitions are found it will return an empty array. + + + + -------------------------- Example 3 -------------------------- + PS C:\> $Pipeline = Get-ADOPSPipeline -Project myProject -Name myPipeline +PS C:\> Get-ADOPSBuildDefinition -Project myProject -Id $Pipeline.id + + This command will return the build definition of the pipeline named myPipeline. + + + + + + + + Get-ADOPSConnection + Get + ADOPSConnection + + This command gets the active ADOPS connection. + + + + This command gets the active ADOPS connection. If no connection is made it will return $null. + + + + Get-ADOPSConnection + + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSConnection + +Name Value +---- ----- +TenantId 97547e13-918b-47eb-a923-6e58efef2e6a +Identity my.mail@address +Organization MYConnectedOrg + + This command will output any current ADOPS connection + + + + + + + + Get-ADOPSElasticPool + Get + ADOPSElasticPool + + Gets one or more Azure DevOps Elastic Pools. + + + + Gets one or more Azure DevOps Elastic Pools. + + + + Get-ADOPSElasticPool + + PoolId + + The Id of the Azure DevOps pool. + + Int32 + + Int32 + + + None + + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + + + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + PoolId + + The Id of the Azure DevOps pool. + + Int32 + + Int32 + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + Get-ADOPSElasticPool -Organization ADOPS -PoolId 10 + + Get the Azure DevOps Elastic Pool with the Id of 10 in the ADOPS organization. + + + + -------------------------- Example 2 -------------------------- + Get-ADOPSElasticPool -Organization ADOPS + + Get all the Azure DevOps Elastic Pool in the ADOPS organization. + + + + + + + + Get-ADOPSFileContent + Get + ADOPSFileContent + + Gets the content of a single file in an Azure DevOps repository. + + + + This command gets the contents of a file in an Azure DevOps repository It will try to get the contents no matter the file type, and may fail if a binary or otherwise unreadable file is given. + + + + Get-ADOPSFileContent + + Organization + + The organization where the repository is located + + String + + String + + + None + + + Project + + The project where the repository is located + + String + + String + + + None + + + RepositoryId + + The id (GUID) of the repository. This can be found using the command Get-ADOPSRepository -Project ProjectName -Repository RepositoryName | Select-Object -ExpandProperty id + + String + + String + + + None + + + FilePath + + Full path to the file which contents are read. + + String + + String + + + None + + + + + + FilePath + + Full path to the file which contents are read. + + String + + String + + + None + + + Organization + + The organization where the repository is located + + String + + String + + + None + + + Project + + The project where the repository is located + + String + + String + + + None + + + RepositoryId + + The id (GUID) of the repository. This can be found using the command Get-ADOPSRepository -Project ProjectName -Repository RepositoryName | Select-Object -ExpandProperty id + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSFileContent -Project MyProject -RepositoryId a1c93080-9d68-4ab9-95cf-1a5f68740628 -FilePath /MyFile.ps1 + + This command will return the contents of the file "MyFile.ps1" located in the root of the repository "a1c93080-9d68-4ab9-95cf-1a5f68740628" in the "MyProject" project of your default logged in organization. + + + + + + + + Get-ADOPSGroup + Get + ADOPSGroup + + Gets all Azure DevOps groups + + + + This command returns all Azure DevOps groups in your organization. It will not filter groups. + + + + Get-ADOPSGroup + + Organization + + The organization to get groups from. + + String + + String + + + None + + + ContinuationToken + + Internal parameter. Used to get paged results + + String + + String + + + None + + + + + + ContinuationToken + + Internal parameter. Used to get paged results + + String + + String + + + None + + + Organization + + The organization to get groups from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSGroup + + Gets all Azure DevOps groups + + + + + + + + Get-ADOPSNode + Get + ADOPSNode + + Get one or more Elastic nodes currently in the Elastic pool. + + + + Get one or more Elastic nodes currently in the Elastic pool. + + + + Get-ADOPSNode + + PoolId + + The unique Id of the Elastic pool. + + Int32 + + Int32 + + + None + + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + + + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + PoolId + + The unique Id of the Elastic pool. + + Int32 + + Int32 + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + Get-ADOPSNode -Organization 'ADOPS' -PoolId 10 + + Get the elastic nodes in the elastic pool with id 10. + + + + + + + + Get-ADOPSPipeline + Get + ADOPSPipeline + + Get a specific pipeline or all pipelines in a specific DevOps project. + + + + Lists a specific Pipeline or all Pipelines in a Specific Project. + + + + Get-ADOPSPipeline + + Name + + Name of the pipeline. + + String + + String + + + None + + + Revision + + Revision of the pipeline. Omit to get latest. + + int + + int + + + None + + + Project + + The project to get pipelines from. + + String + + String + + + None + + + Organization + + The organization to get pipelines from. + + String + + String + + + None + + + + + + Name + + Name of the pipeline. + + String + + String + + + None + + + Revision + + Revision of the pipeline. Omit to get latest. + + int + + int + + + None + + + Organization + + The organization to get pipelines from. + + String + + String + + + None + + + Project + + The project to get pipelines from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSPipeline -Organization $OrganizationName -Project $Project -Name $Name + + Get pipeline with name $Name from $Project. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSPipeline -Organization $OrganizationName -Project $Project -Name $Name -Revision $Revision + + Get pipeline with name $Name and specific $Revision from $Project + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSPipeline -Organization $OrganizationName -Project $Project + + List all Pipelines in $Project + + + + -------------------------- Example 3 -------------------------- + PS C:\> Get-ADOPSPipeline -Project $Project + + List all Pipelines in $Project and uses Default Organization. + + + + + + + + Get-ADOPSPipelineRetentionSettings + Get + ADOPSPipelineRetentionSettings + + Returns all pipeline retention settings. + + + + This command will return all pipeline retention settings for a Azure DevOps Project. + + + + Get-ADOPSPipelineRetentionSettings + + Organization + + The organization to get pipeline retention settings from. + + String + + String + + + None + + + Project + + The project to get pipeline retention settings from. + + String + + String + + + None + + + + + + Organization + + The organization to get pipeline retention settings from. + + String + + String + + + None + + + Project + + The project to get pipeline retention settings from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + A Key/value custom object of all the pipeline retention values. + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSPipelineRetentionSettings -Project 'MyProject' + + This command will get all the pipeline retention settings values for a project. + + + + + + + + Get-ADOPSPipelineSettings + Get + ADOPSPipelineSettings + + Returns all pipeline settings for a project. + + + + This command will return all pipeline settings for a project. + + + + Get-ADOPSPipelineSettings + + Organization + + The organization to get pipeline settings from. + + String + + String + + + None + + + Project + + The project to get pipeline settings from. + + String + + String + + + None + + + + + + Organization + + The organization to get pipeline settings from. + + String + + String + + + None + + + Project + + The project to get pipeline settings from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + A key/value custom object of all pipeline settings is returned. + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSPipelineSettings -Project 'MyProject' + +enforceReferencedRepoScopedToken : True +disableClassicPipelineCreation : True +disableClassicBuildPipelineCreation : True +disableClassicReleasePipelineCreation : True +forkProtectionEnabled : True +buildsEnabledForForks : True +enforceJobAuthScopeForForks : True +enforceNoAccessToSecretsFromForks : True +isCommentRequiredForPullRequest : True +requireCommentsForNonTeamMembersOnly : False +requireCommentsForNonTeamMemberAndNonContributors : False +enableShellTasksArgsSanitizing : True +enableShellTasksArgsSanitizingAudit : False +disableImpliedYAMLCiTrigger : True +statusBadgesArePrivate : True +enforceSettableVar : True +enforceJobAuthScope : True +enforceJobAuthScopeForReleases : True +publishPipelineMetadata : True + + This command will return a value for specific setting with the name `statusBadgesArePrivate` + + + + + + + + Get-ADOPSPipelineTask + Get + ADOPSPipelineTask + + Returns one or more pipeline tasks. + + + + This command will return one or more pipeline tasks available in your organization. If no parameters is given it will return all tasks installed in your Azure DevOps environment. + + + + Get-ADOPSPipelineTask + + Name + + Name of the task to search for. Exact matches, no wildcards. + + String + + String + + + None + + + Organization + + The organization to search for tasks in. + + String + + String + + + None + + + Version + + Major version of task(s) to return + + Int32 + + Int32 + + + None + + + + + + Name + + Name of the task to search for. Exact matches, no wildcards. + + String + + String + + + None + + + Organization + + The organization to search for tasks in. + + String + + String + + + None + + + Version + + Major version of task(s) to return + + Int32 + + Int32 + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSPipelineTask + + This command will return all pipeline tasks and all versions available in your organization. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSPipelineTask -Name 'PowerShell' + + This command will return pipeline tasks with the exact name "PowerShell", All versions available in your organization. + + + + -------------------------- Example 3 -------------------------- + PS C:\> Get-ADOPSPipelineTask -Version 1 + + This command will return all pipeline tasks with major version 1 available in your organization. + + + + -------------------------- Example 4 -------------------------- + PS C:\> Get-ADOPSPipelineTask -Name 'PowerShell' -Version 2 + + This command will return the PowerShell pipeline task version 2. + + + + + + + + Get-ADOPSPool + Get + ADOPSPool + + Get one or more Azure DevOps Agent pools. + + + + Get one or more Azure DevOps Agent pools. + + + + Get-ADOPSPool + + IncludeLegacy + + Includes all legacy agent pools. + + + SwitchParameter + + + False + + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + + Get-ADOPSPool + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + PoolId + + The Agent Pool id. + + Int32 + + Int32 + + + None + + + + Get-ADOPSPool + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + PoolName + + The name of the Agent pool. + + String + + String + + + None + + + + + + IncludeLegacy + + Includes all legacy agent pools. + + SwitchParameter + + SwitchParameter + + + False + + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + PoolId + + The Agent Pool id. + + Int32 + + Int32 + + + None + + + PoolName + + The name of the Agent pool. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + Get-ADOPSPool -Organization 'adops' -IncludeLegacy + + Get all the Azure DevOps Agent Pools in the Organization of adops and includes Legacy agent pools. + + + + -------------------------- Example 2 -------------------------- + Get-ADOPSPool -Organization 'adops' -PoolName 'agentpool1' + + Get the Azure DevOps Agent Pools with the name of agentpool1 in the Organization of adops. + + + + -------------------------- Example 3 -------------------------- + Get-ADOPSPool -Organization 'adops' -PoolId 10 + + Get the Azure DevOps Agent Pools with the pool id of 10 in the Organization of adops. + + + + + + get-agent-pools + https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/pools/get-agent-pools?view=azure-devops-rest-7.1 + + + + + + Get-ADOPSProject + Get + ADOPSProject + + Gets one or several projects in an Azure DevOps organization. + + + + Gets one or several projects in an Azure DevOps organization. + + + + Get-ADOPSProject + + Id + + The id (GUID) of the project to find. + + String + + String + + + None + + + Organization + + The name of the organization of the project. + + String + + String + + + None + + + + Get-ADOPSProject + + Name + + The name of the project to find. + + String + + String + + + None + + + Organization + + The name of the organization of the project. + + String + + String + + + None + + + + + + Id + + The id (GUID) of the project to find. + + String + + String + + + None + + + Name + + The name of the project to find. + + String + + String + + + None + + + Organization + + The name of the organization of the project. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSProject -Organization 'ADOPS' -Name 'ADOPSproj' + + Gets the project called "ADOPSproj" from the organization "ADOPS". + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSProject -Name 'ADOPSproj' + + Gets the project called "ADOPSproj" from the default organization. + + + + + + + + Get-ADOPSRepository + Get + ADOPSRepository + + Gets one or more repositories from a project in Azure DevOps. + + + + Gets one or more repositories from a project in Azure DevOps. + + + + Get-ADOPSRepository + + Project + + The project to get repositories from. + + String + + String + + + None + + + Repository + + The repository to get. + + String + + String + + + None + + + Organization + + The organization to get repositories from. + + String + + String + + + None + + + + + + Organization + + The organization to get repositories from. + + String + + String + + + None + + + Project + + The project to get repositories from. + + String + + String + + + None + + + Repository + + The repository to get. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSRepository -Organization 'ADOPS' -Project 'ADOPSproj' + + Gets all repositories from the project "ADOPSproj" in the organization "ADOPS". + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSRepository -Project 'ADOPSproj' -Repository 'ADOPSrepo' + + Gets the repository "ADOPSrepo" from the project "ADOPSproj", from the organization set as default amongst the established Azure DevOps connections. + + + + + + + + Get-ADOPSServiceConnection + Get + ADOPSServiceConnection + + Get a specific ServiceConnection or all ServiceConnections in a DevOps project. + + + + Lists a specific ServiceConnection or all ServiceConnections in a Project. + + + + Get-ADOPSServiceConnection + + Name + + Specify the name of a service connection to get. + + String + + String + + + None + + + Project + + The project to get ServiceConnection/s from. + + String + + String + + + None + + + Organization + + The organization to get ServiceConnection/s from. + + String + + String + + + None + + + IncludeFailed + + Includes Service Connections in failed & draft state. + + + SwitchParameter + + + False + + + + + + Name + + Specify the name of a service connection to get. + + String + + String + + + None + + + Organization + + The organization to get ServiceConnection/s from. + + String + + String + + + None + + + Project + + The project to get ServiceConnection/s from. + + String + + String + + + None + + + IncludeFailed + + Includes Service Connections in failed & draft state. + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSServiceConnection -Organization $OrganizationName -Project $Project -Name $Name + + Get ServiceConnection with name $Name from $Project. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSServiceConnection -Organization $OrganizationName -Project $Project + + List all ServiceConnection in $Project + + + + -------------------------- Example 3 -------------------------- + PS C:\> Get-ADOPSServiceConnection -Project $Project + + List all ServiceConnection in $Project and uses Default Organization. + + + + + + + + Get-ADOPSUsageData + Get + ADOPSUsageData + + Gets the usage data of your organization. + + + + This command gets usage data for you organization, such as + - Currently running requests + - Limits to your resource usage + - Parallel sessions + - Used minutes and hosts + + Note: This API is not public, and user data is dependent on current status, and is due to change with no notice. + + + + Get-ADOPSUsageData + + ProjectVisibility + + List usage data for private or public projects + + + Private + Public + + String + + String + + + None + + + Organization + + The organization to get usage data from. + + String + + String + + + None + + + SelfHosted + + If set, returns usage data for self hosted agents. If not set, returns usage data for Microsoft hosted agents. + + + SwitchParameter + + + False + + + + + + Organization + + The organization to get usage data from. + + String + + String + + + None + + + ProjectVisibility + + List usage data for private or public projects + + String + + String + + + None + + + SelfHosted + + If set, returns usage data for self hosted agents. If not set, returns usage data for Microsoft hosted agents. + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSUsageData + + This command will return usage statistics and data for your public projects (default), and Microsoft hosted agents (default). + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSUsageData -ProjectVisibility Private -SelfHosted + + This command will return usage statistics and data for your private projects, and self hosted agents. + + + + -------------------------- Example 3 -------------------------- + PS C:\> Get-ADOPSUsageData -ProjectVisibility Public + + This command will return usage statistics and data for your public projects (default), and Microsoft hosted agents (default). + + + + + + + + Get-ADOPSUser + Get + ADOPSUser + + Fetch one or more users + + + + Fetch user(s) by name or descriptor + + + + Get-ADOPSUser + + ContinuationToken + + Used to fetch the new page from the API. + + String + + String + + + None + + + Organization + + The organization to get users from. + + String + + String + + + None + + + + Get-ADOPSUser + + Descriptor + + A unique descriptor ID for the user. + + String + + String + + + None + + + Organization + + The organization to get users from. + + String + + String + + + None + + + + Get-ADOPSUser + + Name + + The query that will match name, display name, email fields. + + String + + String + + + None + + + Organization + + The organization to get users from. + + String + + String + + + None + + + + + + ContinuationToken + + Used to fetch the new page from the API. + + String + + String + + + None + + + Descriptor + + A unique descriptor ID for the user. + + String + + String + + + None + + + Name + + The query that will match name, display name, email fields. + + String + + String + + + None + + + Organization + + The organization to get users from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSUser + + Fetch all users. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSUser -Name 'john doe' + + Search for anyone that has `john doe` in it's name, display name or email. + + + + -------------------------- Example 3 -------------------------- + PS C:\> Get-ADOPSUser -Descriptor 'aad.am9obiBkb2Vqb2huIGRvZWpvaG4gZG9lam9obiBkb2U' + + Search for a single user that has the corresponding descriptor. + + + + + + user-entitlements/search-user-entitlements + https://docs.microsoft.com/en-us/rest/api/azure/devops/memberentitlementmanagement/user-entitlements/search-user-entitlements?view=azure-devops-rest-6.0 + + + users/get + https://docs.microsoft.com/en-us/rest/api/azure/devops/graph/users/get?view=azure-devops-rest-6.0 + + + users/list + https://docs.microsoft.com/en-us/rest/api/azure/devops/graph/users/list?view=azure-devops-rest-6.0 + + + + + + Get-ADOPSVariableGroup + Get + ADOPSVariableGroup + + This command gets one or more variable groups from Azure DevOps. + + + + This command gets one or more variable groups from Azure DevOps. If no matching variable group is found it outputs nothing. Searches can be done by Name or Id. + + + + Get-ADOPSVariableGroup + + Id + + Variable group id to search for. + + Int32 + + Int32 + + + None + + + Organization + + Organization to search in. + + String + + String + + + None + + + Project + + Project to search in. + + String + + String + + + None + + + + Get-ADOPSVariableGroup + + Name + + Variable group name to search for. + + String + + String + + + None + + + Organization + + Organization to search in. + + String + + String + + + None + + + Project + + Project to search in. + + String + + String + + + None + + + + + + Id + + Variable group id to search for. + + Int32 + + Int32 + + + None + + + Name + + Variable group name to search for. + + String + + String + + + None + + + Organization + + Organization to search in. + + String + + String + + + None + + + Project + + Project to search in. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSVariableGroup -Project MyAzureDevOpsProject + + This command will return all variable groups in the MyAzureDevOpsProject project. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSVariableGroup -Project MyAzureDevOpsProject -Name MyVariableGroup + + This command will return the "MyVariableGroup" variable group in the MyAzureDevOpsProject project. + + + + -------------------------- Example 3 -------------------------- + PS C:\> Get-ADOPSVariableGroup -Project MyAzureDevOpsProject -Id 4 + + This command will return the variable group with id 4 in the MyAzureDevOpsProject project. + + + + + + + + Get-ADOPSWiki + Get + ADOPSWiki + + Gets one or more wikis from a Azure DevOps project + + + + This command returns Azure DevOPs wikis from a project. If a wiki name is given it will return any wikis with that exact name. Wildcards are not supported. + + + + Get-ADOPSWiki + + Project + + The project to get wiki(s) from + + String + + String + + + None + + + WikiId + + Name or ID of a specific wiki to return. + + String + + String + + + None + + + Organization + + The organization to get wikis from. + + String + + String + + + None + + + + + + Organization + + The organization to get wikis from. + + String + + String + + + None + + + Project + + The project to get wiki(s) from + + String + + String + + + None + + + WikiId + + Name or ID of a specific wiki to return. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSWiki -Project MyProject -WikiId MyProject.wiki + + This command will return the MyProject.wiki wiki from the MyProject project. If no MyProjet.wiki exists it will return nothing. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSWiki -Project MyProject + + This command will return all wikis from the MyProject project. + + + + + + + + Grant-ADOPSPipelinePermission + Grant + ADOPSPipelinePermission + + Grants pipeline permission to a resource of type VariableGroup, Queue, SecureFile, or Environment. + + + + Grants pipeline permissions to a resource of type VariableGroup, Queue, SecureFile, or Environment. Specify a PipelineId to grant permissions to a single pipeline, or use the AllPipelines parameter to grant permissions to all pipelines. + + + + Grant-ADOPSPipelinePermission + + AllPipelines + + Grant access for all pipelines. This is equivalent to disable the restrictions on the resource. + + + SwitchParameter + + + False + + + Organization + + Name of the Azure DevOps organization. Defaults to the current organization. + + String + + String + + + None + + + Project + + Name of project containing the resource. + + String + + String + + + None + + + ResourceId + + Id of the resource to grant permissions to. + + String + + String + + + None + + + ResourceType + + Resource type to grant permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. + + + VariableGroup + Queue + SecureFile + Environment + + ResourceType + + ResourceType + + + None + + + + Grant-ADOPSPipelinePermission + + Organization + + Name of the Azure DevOps organization. Defaults to the current organization. + + String + + String + + + None + + + PipelineId + + Id of the pipeline to grant permissions for. + + Int32 + + Int32 + + + None + + + Project + + Name of project containing the resource. + + String + + String + + + None + + + ResourceId + + Id of the resource to grant permissions to. + + String + + String + + + None + + + ResourceType + + Resource type to grant permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. + + + VariableGroup + Queue + SecureFile + Environment + + ResourceType + + ResourceType + + + None + + + + + + AllPipelines + + Grant access for all pipelines. This is equivalent to disable the restrictions on the resource. + + SwitchParameter + + SwitchParameter + + + False + + + Organization + + Name of the Azure DevOps organization. Defaults to the current organization. + + String + + String + + + None + + + PipelineId + + Id of the pipeline to grant permissions for. + + Int32 + + Int32 + + + None + + + Project + + Name of project containing the resource. + + String + + String + + + None + + + ResourceId + + Id of the resource to grant permissions to. + + String + + String + + + None + + + ResourceType + + Resource type to grant permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. + + ResourceType + + ResourceType + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Grant-ADOPSPipelinePermission -Pipeline 42 -ResourceType SecureFile -ResourceId '88d047be-7e59-4eda-b1fe-1614c7a69e16' -Project 'MyProject' + + Grant pipeline 42 permission to the secure file with id 88d047be-7e59-4eda-b1fe-1614c7a69e16 in project MyProject. + + + + + + + + Import-ADOPSRepository + Import + ADOPSRepository + + Import an external public git repo to Azure DevOps repos. + + + + This command will import any public git repo using http or https git link. It requires a precreated empty git repo target. You van assign the target using repository name or repository Id + + + + Import-ADOPSRepository + + GitSource + + The source repo to fetch. Must be a public accessable http or https link. + + String + + String + + + None + + + Organization + + Your Azure DevOps organization. + + String + + String + + + None + + + Project + + The Azure DevOps project where your local repo exists. + + String + + String + + + None + + + RepositoryId + + Repository id where to import the GitSource. This must be an empty Azure DevOps repository. + + String + + String + + + None + + + Wait + + If defined this command will wait for the import to complete. Default will return the process started. + + + SwitchParameter + + + False + + + + Import-ADOPSRepository + + GitSource + + The source repo to fetch. Must be a public accessable http or https link. + + String + + String + + + None + + + Organization + + Your Azure DevOps organization. + + String + + String + + + None + + + Project + + The Azure DevOps project where your local repo exists. + + String + + String + + + None + + + RepositoryName + + Repository name where to import the GitSource. This must be an empty Azure DevOps repository. + + String + + String + + + None + + + Wait + + If defined this command will wait for the import to complete. Default will return the process started. + + + SwitchParameter + + + False + + + + + + GitSource + + The source repo to fetch. Must be a public accessable http or https link. + + String + + String + + + None + + + Organization + + Your Azure DevOps organization. + + String + + String + + + None + + + Project + + The Azure DevOps project where your local repo exists. + + String + + String + + + None + + + RepositoryId + + Repository id where to import the GitSource. This must be an empty Azure DevOps repository. + + String + + String + + + None + + + RepositoryName + + Repository name where to import the GitSource. This must be an empty Azure DevOps repository. + + String + + String + + + None + + + Wait + + If defined this command will wait for the import to complete. Default will return the process started. + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Import-ADOPSRepository -Project MyProject -GitSource https://github.com/AZDOPS/AZDOPS.git -RepositoryName MyLocalRepo + + This command will import all data from the `https://github.com/AZDOPS/AZDOPS.git` repo to the local Azure DevOps repo `MyLocalRepo` + + + + + + + + Initialize-ADOPSRepository + Initialize + ADOPSRepository + + This command will initialize a newly created Azure DevOps repository. + + + + This command will initialize a newly created Azure DevOps repository. It can do so by adding a default README.md template, Any of the existing .gitignore templates, or a custom file. + If only one .gitignore template is selected it will be named ".gitignore". If more than one is selected they will be named as the templates, and they will not work right away. Combine your chosen templates in to one and update it manually. + If a repository is already initiated this command will fail with any of a number of different messages depending on what files or templates are selected. + + + + Initialize-ADOPSRepository + + Message + + Commit message to use when initializing repository + + String + + String + + + None + + + Branch + + Which branch to initialize. Defaults to 'refs/heads/main' + + String + + String + + + None + + + RepositoryId + + Id (GUID) of the repository to initialize. + + String + + String + + + None + + + NewContentTemplate + + Initialize the repo with one or more of the existing .gitignore templates. If one template is selected it will be given the name '.gitignore' If 2 or more templates are given they will be named as the template name. + + + Actionscript.gitignore + Ada.gitignore + Agda.gitignore + Android.gitignore + AppceleratorTitanium.gitignore + AppEngine.gitignore + ArchLinuxPackages.gitignore + Autotools.gitignore + C++.gitignore + C.gitignore + CakePHP.gitignore + CFWheels.gitignore + ChefCookbook.gitignore + Clojure.gitignore + CMake.gitignore + CodeIgniter.gitignore + CommonLisp.gitignore + Composer.gitignore + Concrete5.gitignore + Coq.gitignore + CraftCMS.gitignore + CUDA.gitignore + D.gitignore + Dart.gitignore + Delphi.gitignore + DM.gitignore + Drupal.gitignore + Eagle.gitignore + Elisp.gitignore + Elixir.gitignore + Elm.gitignore + EPiServer.gitignore + Erlang.gitignore + ExpressionEngine.gitignore + ExtJs.gitignore + Fancy.gitignore + Finale.gitignore + ForceDotCom.gitignore + Fortran.gitignore + FuelPHP.gitignore + gcov.gitignore + GitBook.gitignore + Go.gitignore + Godot.gitignore + Gradle.gitignore + Grails.gitignore + GWT.gitignore + Haskell.gitignore + Idris.gitignore + IGORPro.gitignore + Java.gitignore + Jboss.gitignore + Jekyll.gitignore + JENKINS_HOME.gitignore + Joomla.gitignore + Julia.gitignore + KiCAD.gitignore + Kohana.gitignore + Kotlin.gitignore + LabVIEW.gitignore + Laravel.gitignore + Leiningen.gitignore + LemonStand.gitignore + Lilypond.gitignore + Lithium.gitignore + Lua.gitignore + Magento.gitignore + Maven.gitignore + Mercury.gitignore + MetaProgrammingSystem.gitignore + nanoc.gitignore + Nim.gitignore + Node.gitignore + Objective-C.gitignore + OCaml.gitignore + Opa.gitignore + opencart.gitignore + OracleForms.gitignore + Packer.gitignore + Perl.gitignore + Phalcon.gitignore + PlayFramework.gitignore + Plone.gitignore + Prestashop.gitignore + Processing.gitignore + PureScript.gitignore + Python.gitignore + Qooxdoo.gitignore + Qt.gitignore + R.gitignore + Rails.gitignore + Raku.gitignore + RhodesRhomobile.gitignore + ROS.gitignore + Ruby.gitignore + Rust.gitignore + Sass.gitignore + Scala.gitignore + Scheme.gitignore + SCons.gitignore + Scrivener.gitignore + Sdcc.gitignore + SeamGen.gitignore + SketchUp.gitignore + Smalltalk.gitignore + stella.gitignore + SugarCRM.gitignore + Swift.gitignore + Symfony.gitignore + SymphonyCMS.gitignore + Terraform.gitignore + TeX.gitignore + Textpattern.gitignore + TurboGears2.gitignore + Typo3.gitignore + Umbraco.gitignore + Unity.gitignore + UnrealEngine.gitignore + VisualStudio.gitignore + VVVV.gitignore + Waf.gitignore + WordPress.gitignore + Xojo.gitignore + Yeoman.gitignore + Yii.gitignore + ZendFramework.gitignore + Zephir.gitignore + + String[] + + String[] + + + None + + + Path + + If combined with the `-Content` parameter this will create a custom file with this content. If a path is given without `-Content` the default message 'Repo initialized using ADOPS module.' will be set in this file. + + String + + String + + + None + + + Content + + If combined with the `-Path` parameter this will create a custom file with this content. + + String + + String + + + None + + + Readme + + Initialize the repo with the default Azure DevOps README template + + + SwitchParameter + + + False + + + + + + Branch + + Which branch to initialize. Defaults to 'refs/heads/main' + + String + + String + + + None + + + Content + + If combined with the `-Path` parameter this will create a custom file with this content. + + String + + String + + + None + + + Message + + Commit message to use when initializing repository + + String + + String + + + None + + + Path + + If combined with the `-Content` parameter this will create a custom file with this content. If a path is given without `-Content` the default message 'Repo initialized using ADOPS module.' will be set in this file. + + String + + String + + + None + + + Readme + + Initialize the repo with the default Azure DevOps README template + + SwitchParameter + + SwitchParameter + + + False + + + RepositoryId + + Id (GUID) of the repository to initialize. + + String + + String + + + None + + + NewContentTemplate + + Initialize the repo with one or more of the existing .gitignore templates. If one template is selected it will be given the name '.gitignore' If 2 or more templates are given they will be named as the template name. + + String[] + + String[] + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> $r = New-ADOPSRepository -Name "myNewRepo" -Project 'myProject' -Organization 'myOrg' +PS C:\> Initialize-ADOPSRepository + + This command will initialize the repo with only the default "README.md" template. + + + + -------------------------- Example 2 -------------------------- + PS C:\> $r = New-ADOPSRepository -Name "myNewRepo" -Project 'myProject' -Organization 'myOrg' +PS C:\> Initialize-ADOPSRepository -RepositoryId $r.id -Message 'my initial commit message' -NewContentTemplate Actionscript.gitignore, Ada.gitignore, Android.gitignore -Path '/newCustomFile.txt' -Readme + + This command will initialize a repository containing the files - Actionscript.gitignore + - Ada.gitignore + - Android.gitignore + - newCustomFile.txt + - README.md + + newCustomFile.txt will contain the default message 'Repo initialized using ADOPS module.' + README.md is the default readme template from Azure DevOps + + + + -------------------------- Example 3 -------------------------- + PS C:\> $r = New-ADOPSRepository -Name "myNewRepo" -Project 'myProject' -Organization 'myOrg' +PS C:\> $content = @' +This is a multiline file. +It contains multiple lines, +Since, by definition, if it didn't it wouldn't be a multiline file +'@ +PS C:\> Initialize-ADOPSRepository -RepositoryId $r.id -Path '/newCustomFile.txt' -Content $content + + This command will initialize the repo by creating the file 'newCustomFile.txt' in the root folder, and setting contents of the file. + + + + + + + + Invoke-ADOPSRestMethod + Invoke + ADOPSRestMethod + + Invoke a ADOPS rest API call. + + + + This command invokes a API call to Azure DevOps using the standard ADOPS logged in account. If you do not give the full Uri to call, for example 'https://dev.azure.com/myOrganization/_api/GetStuff', it will append 'https://dev.azure.com/$Organization', where $Organization is the one you are connected to, to your URI. + + + + Invoke-ADOPSRestMethod + + Uri + + Full Uri to call, including "https://dev.azure.com/organization" + + String + + String + + + None + + + Method + + API method to use + + + Default + Get + Head + Post + Put + Delete + Trace + Options + Merge + Patch + + WebRequestMethod + + WebRequestMethod + + + None + + + Body + + Body to include in the API call + + String + + String + + + None + + + + + + Body + + Body to include in the API call + + String + + String + + + None + + + Method + + API method to use + + WebRequestMethod + + WebRequestMethod + + + None + + + Uri + + Full Uri to call, including "https://dev.azure.com/organization" + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Invoke-ADOPSRestMethod -Uri "https://dev.azure.com/$Organization/_apis/distributedtask/tasks" + + This command will return whatever this API endpoint returns. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Invoke-ADOPSRestMethod -Uri "/_apis/distributedtask/tasks" + + This command will append 'https://dev.azure.com/$Organization' to URI and return whatever the full API endpoint 'https://dev.azure.com/$Organization/_apis/distributedtask/tasks' returns. + $Organization is taken from the current ADOPS connection. + + + + -------------------------- Example 3 -------------------------- + PS C:\> Invoke-ADOPSRestMethod -Uri "https://dev.azure.com/$Organization/_apis/distributedtask/tasks" -Method Post -Body $BodyObject + + This command will perform a Post call to the API endpoint. It will perform the API call using the logged in ADOPS credentials. It will include the body object in the call. It will not format or validate any of the input values. + + + + + + + + New-ADOPSArtifactFeed + New + ADOPSArtifactFeed + + This command creates a new artifact feed + + + + This command creates a new artifact feed. It will try to add the Project build account as contributor to the feed in order for builds to be able to automatically publish packages. If -IncludeUpstream is set it will add all currently available upstreams. + + + + New-ADOPSArtifactFeed + + Project + + Project in which the feed will be created. + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + Name + + Feed name + + String + + String + + + None + + + Description + + Add a description to the feed description field. + + String + + String + + + None + + + IncludeUpstream + + Enable upstreams and add all currently available upstreams + + + SwitchParameter + + + False + + + + + + Description + + Add a description to the feed description field. + + String + + String + + + None + + + IncludeUpstream + + Enable upstreams and add all currently available upstreams + + SwitchParameter + + SwitchParameter + + + False + + + Name + + Feed name + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + Project + + Project in which the feed will be created. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSArtifactFeed -Project 'MyProject' -Name 'MyNewFeed' + + This command will add a new feed named `MyNewFeed` to the `MyProject` project. It will try to add the account `MyProject build service (ConnectedOrg)` as contributor. It will not enable upstreams. + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSArtifactFeed -Project 'MyProject' -Name 'MyNewFeed' -IncludeUpstreams + + This command will add a new feed named `MyNewFeed` to the `MyProject` project. It will try to add the account `MyProject build service (ConnectedOrg)` as contributor. It will enable upstreams and add all currently available sources. + + + + + + + + New-ADOPSAuditStream + New + ADOPSAuditStream + + Sets up an audit stream from Azure DevOps to supported external logging services. + + + + This command sets up a log stream from Azure DevOps to an external supported service. It only sets up log forwarding. You need to enable logging separately from this command. (Organization settings -> Policies -> Log audit events) + Supported services are - Azure Monitor + - Splunk + - Azure Event grid + + + + New-ADOPSAuditStream + + EventGridTopicAccessKey + + Access key to Azure Eventgrid + + String + + String + + + None + + + EventGridTopicHostname + + Azure Eventgrid host to send logs to. Must start with 'http://' or 'https://' + + String + + String + + + None + + + Organization + + The organization to set up forwarding from. + + String + + String + + + None + + + + New-ADOPSAuditStream + + Organization + + The organization to set up forwarding from. + + String + + String + + + None + + + SharedKey + + Azure Monitor shared access key + + String + + String + + + None + + + WorkspaceId + + Azure monitor workspace ID. Must be in GUID format. + + String + + String + + + None + + + + New-ADOPSAuditStream + + Organization + + The organization to set up forwarding from. + + String + + String + + + None + + + SplunkEventCollectorToken + + Splunk event collector token. Must be in GUID format. + + String + + String + + + None + + + SplunkUrl + + URL to Splunk log host. Must start with 'http://' or 'https://' + + String + + String + + + None + + + + + + EventGridTopicAccessKey + + Access key to Azure Eventgrid + + String + + String + + + None + + + EventGridTopicHostname + + Azure Eventgrid host to send logs to. Must start with 'http://' or 'https://' + + String + + String + + + None + + + Organization + + The organization to set up forwarding from. + + String + + String + + + None + + + SharedKey + + Azure Monitor shared access key + + String + + String + + + None + + + SplunkEventCollectorToken + + Splunk event collector token. Must be in GUID format. + + String + + String + + + None + + + SplunkUrl + + URL to Splunk log host. Must start with 'http://' or 'https://' + + String + + String + + + None + + + WorkspaceId + + Azure monitor workspace ID. Must be in GUID format. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSAuditStream -WorkspaceId "11111111-1111-1111-1111-111111111111" -SharedKey "123456" + + This command sets up log forwarding to Azure Monitor using the workspace id '11111111-1111-1111-1111-111111111111' and the shared key '123456' + + + + -------------------------- Example 2 -------------------------- + PS C:\> New-ADOPSAuditStream -SplunkUrl 'http://Splunkurl' -SplunkEventCollectorToken '11111111-1111-1111-1111-111111111111' + + This command sets up log forwarding to splunk using the token '11111111-1111-1111-1111-111111111111' and the splunk url 'http://Splunkurl' + + + + -------------------------- Example 3 -------------------------- + PS C:\> $Bytes = [System.Text.Encoding]::Unicode.GetBytes('EventGridTopicAccessKey') +PS C:\> $Base64 =[Convert]::ToBase64String($Bytes) +PS C:\> New-ADOPSAuditStream -EventGridTopicHostname 'http://eventgridUri' -EventGridTopicAccessKey $Base64 + + This command sets up log forwarding to Azure eventgrid using the hostname 'http://eventgridUri' and the access key 'EventGridTopicAccessKey' Access key must be base64 encoded. + + + + + + + + New-ADOPSBuildPolicy + New + ADOPSBuildPolicy + + Creates a build validation policy for a repository + + + + This command creates a build validation policy for a repository. It only creates the policy. You have to create the validation pipeline to be run manually. + + + + New-ADOPSBuildPolicy + + Project + + Project where the pipeline and repository is located + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + RepositoryId + + Id of the repository in which to create the policy. Can be found using the `Get-ADOPSRepository` command + + String + + String + + + None + + + Branch + + git branch to trigger on. Accepts both branch name ('main') or full git path ('refs/heads/main') If only path name is given it will append "refs/heads/" + + String + + String + + + None + + + PipelineId + + PipelineId, or build definition id. Can be found using the `Get-ADOPSPipeline` command + + Int32 + + Int32 + + + None + + + Displayname + + Display name of the policy setting + + String + + String + + + None + + + filenamePatterns + + Filter validation trigger on repository path(s) + + String[] + + String[] + + + None + + + + + + Branch + + git branch to trigger on. Accepts both branch name ('main') or full git path ('refs/heads/main') If only path name is given it will append "refs/heads/" + + String + + String + + + None + + + Displayname + + Display name of the policy setting + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + PipelineId + + PipelineId, or build definition id. Can be found using the `Get-ADOPSPipeline` command + + Int32 + + Int32 + + + None + + + Project + + Project where the pipeline and repository is located + + String + + String + + + None + + + RepositoryId + + Id of the repository in which to create the policy. Can be found using the `Get-ADOPSRepository` command + + String + + String + + + None + + + filenamePatterns + + Filter validation trigger on repository path(s) + + String[] + + String[] + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSBuildPolicy -Project 'MyProject' -RepositoryId 62a7c071-1a60-44a9-a151-d17cd4d367da -Branch 'main' -PipelineId 1 -Displayname 'MyVerificationPolicy' -filenamePatterns '/root/*','/side/*' + + This command will create a policy named `MyVerificationPolicy` on the repository with id `62a7c071-1a60-44a9-a151-d17cd4d367da` in the `MyProject` project, that triggers on branch `main`. It will filter the validation to run only on changes in folder `/root/` or `/side/`. + + + + -------------------------- Example 2 -------------------------- + PS C:\> $ADOPSProject = 'MyProject' +PS C:\> $RepoId = Get-ADOPSRepository -Project $ADOPSProject -Repository 'MyRepo' | Select-Object -ExpandProperty Id +PS C:\> $PipelineId = Get-ADOPSPipeline -Project $ADOPSProject -Name 'MyPipeline' | Select-Object -ExpandProperty id + +PS C:\> New-ADOPSBuildPolicy -Project $ADOPSProject -RepositoryId $RepoId -Branch 'refs/heads/main' -PipelineId $PipelineId -Displayname 'MyVerificationPolicy' + + This command will create a policy named `MyVerificationPolicy` running the `MyPipeline` pipeline on the repository `MyRepo` in the `MyProject` project, that triggers on branch `main`. + + + + + + + + New-ADOPSElasticPool + New + ADOPSElasticPool + + Create an Azure DevOps Elastic pool. + + + + Create an Azure DevOps Elastic pool. + + + + New-ADOPSElasticPool + + PoolName + + Name of the Elastic Pool. + + String + + String + + + None + + + ElasticPoolObject + + The full request body in json or as a pscustom object. The Help function New-ElasticPoolObject can help generate a powershell object/json string. + | Name | Type | Description | |----------------------|---------------------|-------------------------------------------------------------------------------| | agentInteractiveUI | boolean | Set whether agents should be configured to run with interactive UI | | azureId | string | Azure string representing to location of the resource | | desiredIdle | integer | Number of agents to have ready waiting for jobs | | desiredSize | integer | The desired size of the pool | | maxCapacity | integer | Maximum number of nodes that will exist in the elastic pool | | maxSavedNodeCount | integer | Keep nodes in the pool on failure for investigation | | offlineSince | string | Timestamp the pool was first detected to be offline | | osType | OperatingSystemType | Operating system type of the nodes in the pool | | poolId | integer | Id of the associated TaskAgentPool | | recycleAfterEachUse | boolean | Discard node after each job completes | | serviceEndpointId | string | Id of the Service Endpoint used to connect to Azure | | serviceEndpointScope | string | Scope the Service Endpoint belongs to | | sizingAttempts | integer | The number of sizing attempts executed while trying to achieve a desired size | | state | ElasticPoolState | State of the pool | | timeToLiveMinutes | integer | The minimum time in minutes to keep idle agents alive | + + Object + + Object + + + None + + + ProjectId + + If provided, a new TaskAgentQueue will be created in the specified project. + + String + + String + + + None + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + AuthorizeAllPipelines + + Setting to determine if all pipelines are authorized to use this TaskAgentPool by default. + + + SwitchParameter + + + False + + + AutoProvisionProjectPools + + Setting to automatically provision TaskAgentQueues in every project for the new pool. + + + SwitchParameter + + + False + + + + + + AuthorizeAllPipelines + + Setting to determine if all pipelines are authorized to use this TaskAgentPool by default. + + SwitchParameter + + SwitchParameter + + + False + + + AutoProvisionProjectPools + + Setting to automatically provision TaskAgentQueues in every project for the new pool. + + SwitchParameter + + SwitchParameter + + + False + + + ElasticPoolObject + + The full request body in json or as a pscustom object. The Help function New-ElasticPoolObject can help generate a powershell object/json string. + | Name | Type | Description | |----------------------|---------------------|-------------------------------------------------------------------------------| | agentInteractiveUI | boolean | Set whether agents should be configured to run with interactive UI | | azureId | string | Azure string representing to location of the resource | | desiredIdle | integer | Number of agents to have ready waiting for jobs | | desiredSize | integer | The desired size of the pool | | maxCapacity | integer | Maximum number of nodes that will exist in the elastic pool | | maxSavedNodeCount | integer | Keep nodes in the pool on failure for investigation | | offlineSince | string | Timestamp the pool was first detected to be offline | | osType | OperatingSystemType | Operating system type of the nodes in the pool | | poolId | integer | Id of the associated TaskAgentPool | | recycleAfterEachUse | boolean | Discard node after each job completes | | serviceEndpointId | string | Id of the Service Endpoint used to connect to Azure | | serviceEndpointScope | string | Scope the Service Endpoint belongs to | | sizingAttempts | integer | The number of sizing attempts executed while trying to achieve a desired size | | state | ElasticPoolState | State of the pool | | timeToLiveMinutes | integer | The minimum time in minutes to keep idle agents alive | + + Object + + Object + + + None + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + PoolName + + Name of the Elastic Pool. + + String + + String + + + None + + + ProjectId + + If provided, a new TaskAgentQueue will be created in the specified project. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + $Params = @{ + AuthorizeAllPipelines = $true + AutoProvisionProjectPools = $true + PoolName = ManagedPool1 +} +New-ADOPSElasticPool @Params -ElasticPoolObject @" +{ + "serviceEndpointId": "44868479-e856-42bf-9a2b-74bb500d8e36", + "serviceEndpointScope": "421eb3c8-1ca4-4a53-b93c-58997b9eb5e1", + "azureId": "/subscriptions/d83a7278-278c-4671-9a3e-a4cd81cd1194/resourceGroups/RG-TEST/providers/Microsoft.Compute/virtualMachineScaleSets/vmss-test", + "maxCapacity": 1, + "desiredIdle": 0, + "recycleAfterEachUse": true, + "maxSavedNodeCount": 0, + "osType": "linux", + "state": "online", + "offlineSince": null, + "desiredSize": 0, + "sizingAttempts": 0, + "agentInteractiveUI": false, + "timeToLiveMinutes": 15 +} +"@ + + To find your serviceEndpointScope, use Get-ADOPSProject as the scope is the project where the Service connection is bound. Create a Azure DevOps Elastic pool that Auto provisions in project and Authorizes the pool to be consumed by all pipelines. It also attaches to a Virtual Machine Scale Set using the azureId. Full description of the request body can be found at: https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/elasticpools/create?view=azure-devops-rest-7.1 + + + + -------------------------- Example 2 -------------------------- + $Params = @{ + AuthorizeAllPipelines = $true + AutoProvisionProjectPools = $true + PoolName = ManagedPool1 +} + +$ElasticPoolObject = New-ADOPSElasticPoolObject -ServiceEndpointId '44868479-e856-42bf-9a2b-74bb500d8e36' -ServiceEndpointScope '421eb3c8-1ca4-4a53-b93c-58997b9eb5e1' -AzureId '/subscriptions/8961f1f1-0bd1-4be0-b73c-6a8f3b304cf6/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/virtualMachineScaleSets/vmss-test' + +New-ADOPSElasticPool @Params -ElasticPoolObject $ElasticPoolObject + + To find your serviceEndpointScope, use Get-ADOPSProject as the scope is the project where the Service connection is bound. Create a Azure DevOps Elastic pool that Auto provisions in project and Authorizes the pool to be consumed by all pipelines. It also attaches to a Virtual Machine Scale Set using the azureId. Full description of the request body can be found at: https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/elasticpools/create?view=azure-devops-rest-7.1 + + + + + + elasticpools-create + https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/elasticpools/create?view=azure-devops-rest-7.1 + + + + + + New-ADOPSElasticPoolObject + New + ADOPSElasticPoolObject + + Creates an Elastic pool object that can be used. + + + + Creates an Elastic pool object that can be used. + + + + New-ADOPSElasticPoolObject + + ServiceEndpointId + + Id of the Service connection used to connect to Azure. (Get-ADOPSServiceConnection -Project MyAzureDevOpsProject -Name MyServiceConnection).id + + Guid + + Guid + + + None + + + ServiceEndpointScope + + Scope the Service Endpoint belongs to. Azure DevOps project ID (Get-ADOPSProject -Project MyAzureDevOpsProject).id + + Guid + + Guid + + + None + + + MaxSavedNodeCount + + Keep nodes in the pool on failure for investigation + + Int32 + + Int32 + + + 0 + + + OutputType + + In what format should the output be in. + + + json + pscustomobject + + String + + String + + + pscustomobject + + + AzureId + + The full resource id for the Virtual Machine Scale Set in Azure. /subscriptions/subscriptionID/resourceGroups/resourceGroup/providers/Microsoft.Compute/virtualMachineScaleSets/vmssName + + String + + String + + + None + + + OsType + + Operating system type of the nodes in the pool + + + linux + windows + + String + + String + + + linux + + + MaxCapacity + + Maximum number of nodes that will exist in the elastic pool + + Int32 + + Int32 + + + 1 + + + DesiredIdle + + Number of agents to have ready waiting for jobs + + Int32 + + Int32 + + + 0 + + + RecycleAfterEachUse + + Discard node after each job completes + + Boolean + + Boolean + + + False + + + DesiredSize + + The desired size of the pool + + Int32 + + Int32 + + + 0 + + + AgentInteractiveUI + + Set whether agents should be configured to run with interactive UI + + Boolean + + Boolean + + + False + + + TimeToLiveMinutes + + The minimum time in minutes to keep idle agents alive + + Int32 + + Int32 + + + 15 + + + + + + AgentInteractiveUI + + Set whether agents should be configured to run with interactive UI + + Boolean + + Boolean + + + False + + + AzureId + + The full resource id for the Virtual Machine Scale Set in Azure. /subscriptions/subscriptionID/resourceGroups/resourceGroup/providers/Microsoft.Compute/virtualMachineScaleSets/vmssName + + String + + String + + + None + + + DesiredIdle + + Number of agents to have ready waiting for jobs + + Int32 + + Int32 + + + 0 + + + DesiredSize + + The desired size of the pool + + Int32 + + Int32 + + + 0 + + + MaxCapacity + + Maximum number of nodes that will exist in the elastic pool + + Int32 + + Int32 + + + 1 + + + MaxSavedNodeCount + + Keep nodes in the pool on failure for investigation + + Int32 + + Int32 + + + 0 + + + OsType + + Operating system type of the nodes in the pool + + String + + String + + + linux + + + OutputType + + In what format should the output be in. + + String + + String + + + pscustomobject + + + RecycleAfterEachUse + + Discard node after each job completes + + Boolean + + Boolean + + + False + + + ServiceEndpointId + + Id of the Service connection used to connect to Azure. (Get-ADOPSServiceConnection -Project MyAzureDevOpsProject -Name MyServiceConnection).id + + Guid + + Guid + + + None + + + ServiceEndpointScope + + Scope the Service Endpoint belongs to. Azure DevOps project ID (Get-ADOPSProject -Project MyAzureDevOpsProject).id + + Guid + + Guid + + + None + + + TimeToLiveMinutes + + The minimum time in minutes to keep idle agents alive + + Int32 + + Int32 + + + 15 + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + $AzureDevOpsProject = Get-ADOPSProject -Project 'Azure' +$AzureVMSS = Get-AzVmss -VMScaleSetName 'vmss-test' +New-ADOPSElasticPoolObject -ServiceEndpointId '44868479-e856-42bf-9a2b-74bb500d8e36' -ServiceEndpointScope $AzureDevOpsProject.Id -AzureId $AzureVMSS.id + + Gets the Azure DevOps project where the Service endpoint is provisioned. Gets the Azure Virtual Machine Scale Set named vmss-test. Creates a new Azure DevOps Elastic Pool object with the correct references to the Azure path to the VMSS and DevOps project scope. + + + + -------------------------- Example 2 -------------------------- + New-ADOPSElasticPoolObject -ServiceEndpointId '44868479-e856-42bf-9a2b-74bb500d8e36' -ServiceEndpointScope '421eb3c8-1ca4-4a53-b93c-58997b9eb5e1' -AzureId '/subscriptions/8961f1f1-0bd1-4be0-b73c-6a8f3b304cf6/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/virtualMachineScaleSets/vmss-test' + + Creates a new Azure DevOps Elastic Pool by using the VMSS 'vmss-test' in the '8961f1f1-0bd1-4be0-b73c-6a8f3b304cf6' subscription. + + + + + + elasticpools-create + https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/elasticpools/create?view=azure-devops-rest-7.1 + + + + + + New-ADOPSEnvironment + New + ADOPSEnvironment + + Creates a new environment and sets up a better default access rule to it. + + + + This command creates a new environment in Azure DevOps. It also adds an admin to it that is not the creator, in order to fix this issue (https://developercommunity.visualstudio.com/t/Pipeline-environment-security-enhancemen/1236351?space=21&q=environment+permission&sort=votes&page=4)It does not support adding resources to the created environment. + + + + New-ADOPSEnvironment + + Organization + + Organization to connect to + + String + + String + + + None + + + Project + + Project to create the environment in + + String + + String + + + None + + + Name + + Name of the environment + + String + + String + + + None + + + Description + + Description of the environment + + String + + String + + + None + + + AdminGroup + + Custom group principalName to add as administrator in the environment. Can be found using the `Get-ADOPSGroup` command. + + String + + String + + + None + + + SkipAdmin + + Skip adding an administrative group. This will lead to only the creator of the environment having administrative rights on the environment. + + + SwitchParameter + + + False + + + + + + AdminGroup + + Custom group principalName to add as administrator in the environment. Can be found using the `Get-ADOPSGroup` command. + + String + + String + + + None + + + Description + + Description of the environment + + String + + String + + + None + + + Name + + Name of the environment + + String + + String + + + None + + + Organization + + Organization to connect to + + String + + String + + + None + + + Project + + Project to create the environment in + + String + + String + + + None + + + SkipAdmin + + Skip adding an administrative group. This will lead to only the creator of the environment having administrative rights on the environment. + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSEnvironment -Project 'myProject' -Name 'MyEnv' + + This command will create an environment named 'MyEnv' in the 'MyProject' project. It will add the '[myProject]\Project Administrators' group as administrator to the environment. + + + + -------------------------- Example 2 -------------------------- + PS C:\> New-ADOPSEnvironment -Project 'myProject' -Name 'MyEnv' -SkipAdmin + + This command will create an environment named 'MyEnv' in the 'MyProject' project. It will not add any group as administrator to the environment resulting in only the user running the command having admin access to the environment (default behaviour) + + + + -------------------------- Example 3 -------------------------- + PS C:\> New-ADOPSEnvironment -Project 'myProject' -Name 'MyEnv' -Description 'MyEnvironment description' -AdminGroup '[myProject]\myProject Team' + + This command will create an environment named 'MyEnv' with the description 'MyEnvironment description' in the 'MyProject' project. It will also add the '[myProject]\myProject Team' group as administrator to the environment. + + + + + + + + New-ADOPSGitBranch + New + ADOPSGitBranch + + Create a new branch in a Azure DevOps git repo. + + + + This command creates a new branch in an Azure DevOps git repo without the need to clone the repo or have any git tools installed. It creates the branch from the submitted commit id. + + + + New-ADOPSGitBranch + + Organization + + The organization to connect to + + String + + String + + + None + + + RepositoryId + + The ID of the repository in GUID format. This can be found using the `Get-ADOPSRepository` command + + String + + String + + + None + + + Project + + The project where the repository is located + + String + + String + + + None + + + BranchName + + Name of the new branch to create + + String + + String + + + None + + + CommitId + + Full hash commit id to use as base of the new branch. This can be found using `Invoke-ADOPSRestMethod` (see example), or by running the command `git log -1 --format=format:"%H"` inside a git repository. + + String + + String + + + None + + + + + + BranchName + + Name of the new branch to create + + String + + String + + + None + + + CommitId + + Full hash commit id to use as base of the new branch. This can be found using `Invoke-ADOPSRestMethod` (see example), or by running the command `git log -1 --format=format:"%H"` inside a git repository. + + String + + String + + + None + + + Organization + + The organization to connect to + + String + + String + + + None + + + Project + + The project where the repository is located + + String + + String + + + None + + + RepositoryId + + The ID of the repository in GUID format. This can be found using the `Get-ADOPSRepository` command + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSGitBranch -RepositoryId 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' -Project 'MyProj' -BranchName 'NewBranch' -CommitId '12345678910111213141516171819202122232425' + + This will create a new branch in the repository with id 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' from the commit with id '12345678910111213141516171819202122232425' with name 'NewBranch' + + + + -------------------------- Example 2 -------------------------- + PS C:\> $Project = 'myProject' +PS C:\> $RepoName = 'myRepo' +PS C:\> $BranchName = 'NewBranch' +PS C:\> $repo = Get-ADOPSRepository -Project $Project -Repository $RepoName +PS C:\> $AllCommits = Invoke-ADOPSRestMethod -Uri $repo._links.commits.href -Method Get | Select-Object -ExpandProperty value +PS C:\> $LatestCommit = ($AllCommits | Sort-Object -Property @{Expression = {$_.author.date}} -Descending | Select-Object -First 1).commitId +PS C:\> New-ADOPSGitBranch -RepositoryId $repo.id -Project $Project -BranchName $BranchName -CommitId $CommitId + + This command shows how to get all git history directly from a repository using the `Invoke-ADOPSRestMethod` command and use this to create a new branch from the latest commit. + + + + + + + + New-ADOPSGitFile + New + ADOPSGitFile + + This command uploads a file to an Azure DevOps repository using the API. + + + + This command uploads a file to an Azure DevOps repository using the API. If no other than required parameters are given it will be named as the source file and located in the root ("/") folder of the repository, with a default commit messadge of "File added using the ADOPS PowerShell module". You may override target filename, path, and commit message. + + + + New-ADOPSGitFile + + Organization + + The organization to connect to + + String + + String + + + None + + + Project + + The project where the repository is located + + String + + String + + + None + + + Repository + + Repository name where the file will be uploaded. + + String + + String + + + None + + + File + + Path to the local file to upload to the Azure DevOps repository. + + String + + String + + + None + + + FileName + + If set this will give the file a new filename in the Azure DevOps repository. + + String + + String + + + None + + + Path + + Sets the path to the file, relative to the root in Azure DevOps. Must use slash ('/') as path separator. + + String + + String + + + None + + + CommitMessage + + Override the default commit message + + String + + String + + + None + + + + + + CommitMessage + + Override the default commit message + + String + + String + + + None + + + File + + Path to the local file to upload to the Azure DevOps repository. + + String + + String + + + None + + + FileName + + If set this will give the file a new filename in the Azure DevOps repository. + + String + + String + + + None + + + Organization + + The organization to connect to + + String + + String + + + None + + + Path + + Sets the path to the file, relative to the root in Azure DevOps. Must use slash ('/') as path separator. + + String + + String + + + None + + + Project + + The project where the repository is located + + String + + String + + + None + + + Repository + + Repository name where the file will be uploaded. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSGitFile -Project 'myProject' -Repository 'myRepo' -File .\myFileToUplad.txt + + This will add the file 'myFileToUplad.txt' in the root ('/') folder of the 'myRepo'. It will contain the same data as the local file. It will be commited using the default commit message 'File added using the ADOPS PowerShell module' + + + + -------------------------- Example 2 -------------------------- + PS C:\> New-ADOPSGitFile -Project 'myProject' -Repository 'myRepo' -File .\myFileToUplad.txt -FileName 'newFileName.txt' -Path 'root/folder' + + This will add the file 'newFileName.txt' in the relative folder '/root/folder' folder of the 'myRepo'. It will contain the same data as the loca file '.\myFileToUpload.txt'. It will be commited using the default commit message 'File added using the ADOPS PowerShell module' Please note the path uses slash ('/') as path separator and is always relative to the root. + + + + -------------------------- Example 3 -------------------------- + PS C:\> New-ADOPSGitFile -Project 'myProject' -Repository 'myRepo' -File .\myFileToUplad.txt -CommitMessage 'Added this file' + + This will add the file 'myFileToUplad.txt' in the root ('/') folder of the 'myRepo'. It will contain the same data as the local file. It will be commited using the commit message 'Added this file' + + + + + + + + New-ADOPSGroupEntitlement + New + ADOPSGroupEntitlement + + Adds a group entitlement in Azure DevOps. + + + + Adds a group entitlement in Azure DevOps, allowing you to manage licensing and access levels for Entra ID groups. + + + + New-ADOPSGroupEntitlement + + GroupOriginId + + The ID of the Entra ID group to add entitlements for. + + String + + String + + + None + + + AccountLicenseType + + The type of license to assign to the group. + + + Express + Advanced + Stakeholder + Professional + EarlyAdopter + + String + + String + + + None + + + ProjectGroupType + + The type of access to grant in the project. + + + projectReader + projectContributor + projectAdministrator + projectStakeholder + + String + + String + + + None + + + ProjectId + + The ID of the project to grant access to. + + String + + String + + + None + + + Organization + + The Azure DevOps organization. + + String + + String + + + None + + + Wait + + If specified, waits for the operation to complete before returning. + + + SwitchParameter + + + False + + + + + + GroupOriginId + + The ID of the Entra ID group to add entitlements for. + + String + + String + + + None + + + AccountLicenseType + + The type of license to assign to the group. + + String + + String + + + None + + + ProjectGroupType + + The type of access to grant in the project. + + String + + String + + + None + + + ProjectId + + The ID of the project to grant access to. + + String + + String + + + None + + + Organization + + The Azure DevOps organization. + + String + + String + + + None + + + Wait + + If specified, waits for the operation to complete before returning. + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSGroupEntitlement -GroupOriginId "01d0472d-9949-421e-81d8-fcb5668a394d" -AccountLicenseType "Express" -ProjectGroupType "projectContributor" -ProjectId "8130f18e-f65b-431d-a777-5d4a6f3468ba" + + Adds a group entitlement for the specified Entra ID group with Express license and Contributor access to the specified project. + + + + -------------------------- Example 2 -------------------------- + PS C:\> New-ADOPSGroupEntitlement -GroupOriginId "01d0472d-9949-421e-81d8-fcb5668a394d" -AccountLicenseType "Stakeholder" -ProjectGroupType "projectReader" -ProjectId "8130f18e-f65b-431d-a777-5d4a6f3468ba" -Organization "MyOrg" -Wait + + Adds a group entitlement with Stakeholder license and Reader access to the specified project in the "MyOrg" organization, waiting for the operation to complete. + + + + + + + + New-ADOPSMergePolicy + New + ADOPSMergePolicy + + This command sets the Limit merge types setting of a repository git branch. + + + + This setting creates and enables a merge restriction policy on a git branch on a Azure DevOps repository. It will allow only those merge types activated. + WARNING: It is possible to allow no merge types at all, effectively preventing any code from being merged to a repository. Make sure to activate at least one policy. + + + + New-ADOPSMergePolicy + + Project + + Project where the pipeline and repository is located + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + RepositoryId + + Id of the repository in which to create the policy. Can be found using the `Get-ADOPSRepository` command + + String + + String + + + None + + + Branch + + git branch to trigger on. Accepts both branch name ('main') or full git path ('refs/heads/main') If only path name is given it will append "refs/heads/" + + String + + String + + + None + + + allowNoFastForward + + Enables basic merge (No fast forward) + + + SwitchParameter + + + False + + + allowRebase + + Enables Rebase and fast-forward + + + SwitchParameter + + + False + + + allowRebaseMerge + + Enables Rebase with merge commit + + + SwitchParameter + + + False + + + allowSquash + + Enables Squash merge + + + SwitchParameter + + + False + + + + + + Branch + + git branch to trigger on. Accepts both branch name ('main') or full git path ('refs/heads/main') If only path name is given it will append "refs/heads/" + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + Project + + Project where the pipeline and repository is located + + String + + String + + + None + + + RepositoryId + + Id of the repository in which to create the policy. Can be found using the `Get-ADOPSRepository` command + + String + + String + + + None + + + allowNoFastForward + + Enables basic merge (No fast forward) + + SwitchParameter + + SwitchParameter + + + False + + + allowRebase + + Enables Rebase and fast-forward + + SwitchParameter + + SwitchParameter + + + False + + + allowRebaseMerge + + Enables Rebase with merge commit + + SwitchParameter + + SwitchParameter + + + False + + + allowSquash + + Enables Squash merge + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSMergePolicy -Project 'MyProject' -RepositoryId 62a7c071-1a60-44a9-a151-d17cd4d367da -Branch 'main'-allowSquash + + This command will create a merge policy only allowing squash merges to the main branch of the repository with id 62a7c071-1a60-44a9-a151-d17cd4d367da + + + + -------------------------- Example 2 -------------------------- + PS C:\> $ADOPSProject = 'MyProject' +PS C:\> $RepoId = Get-ADOPSRepository -Project $ADOPSProject -Repository 'MyRepo' | Select-Object -ExpandProperty Id + +PS C:\> New-ADOPSMergePolicy -Project $ADOPSProject -RepositoryId $RepoId -Branch 'refs/heads/main' -allowNoFastForward -allowSquash -allowRebase -allowRebaseMerge + + This command will create a merge policy allowing any type of merges to the main branch of the repository MyRepo + + + + + + + + New-ADOPSPipeline + New + ADOPSPipeline + + Create a new pipeline from a existing yaml file in your repository. + + + + Create a new pipeline from a existing yaml file in your repository. + + + + New-ADOPSPipeline + + Name + + Name of the pipeline. + + String + + String + + + None + + + Project + + The project to add the pipeline to. + + String + + String + + + None + + + YamlPath + + The path to the Yaml file in your repository. Like: File.yaml or Folder/Folder/File.yaml + Only allows extensions .yaml or .yml + + String + + String + + + None + + + Repository + + The Repository Name where the Pipeline Yaml file is located. + + String + + String + + + None + + + FolderPath + + Folderpath to add your pipelines to under Devops Pipelines. Like: PipelineFolder or PipelineFolder1\PipelineSubFolder2 + + String + + String + + + None + + + Organization + + The organization to add pipeline to. + + String + + String + + + None + + + + + + FolderPath + + Folderpath to add your pipelines to under Devops Pipelines. Like: PipelineFolder or PipelineFolder1\PipelineSubFolder2 + + String + + String + + + None + + + Name + + Name of the pipeline. + + String + + String + + + None + + + Organization + + The organization to add pipeline to. + + String + + String + + + None + + + Project + + The project to add the pipeline to. + + String + + String + + + None + + + Repository + + The Repository Name where the Pipeline Yaml file is located. + + String + + String + + + None + + + YamlPath + + The path to the Yaml file in your repository. Like: File.yaml or Folder/Folder/File.yaml + Only allows extensions .yaml or .yml + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSPipeline -Name $PipelineName -Project $ProjectName -YamlPath 'pipelines/pipeline1.yaml' -Repository $RepositoryName -Organization $OrganizationName + + Create pipeline in $ProjectName + + + + -------------------------- Example 2 -------------------------- + PS C:\> New-ADOPSPipeline -Name $PipelineName -Project $ProjectName -YamlPath 'pipelines/pipeline1.yaml' -Repository $RepositoryName -FolderPath 'folder1\folder2' + + Create pipeline in $ProjectName and use Default Organization and put the pipeline in subfolder folder2 under DevOps Pipelines. + + + + + + + + New-ADOPSProject + New + ADOPSProject + + Creates a new project in Azure DevOps. + + + + Creates a new project in Azure DevOps. + + + + New-ADOPSProject + + Name + + The name of the new project. + + String + + String + + + None + + + Description + + The description of the new project. + + String + + String + + + None + + + Visibility + + The visibility of the project. + + + Private + Public + + String + + String + + + None + + + SourceControlType + + The source control type to use in the project. The default is git. + + + Git + Tfvc + + String + + String + + + Git + + + ProcessTypeName + + The name of a process to use in the project. + This can be the name of an existing process such as Basic, Agile, Scrum or CMMI. It can also be the name of a custom process created in the organization. + + String + + String + + + None + + + Organization + + The organization to create the project in. + + String + + String + + + None + + + Wait + + If defined this command will wait for the project to be created and return the project object. Default will return the process started. + + + SwitchParameter + + + False + + + + + + Description + + The description of the new project. + + String + + String + + + None + + + Name + + The name of the new project. + + String + + String + + + None + + + Organization + + The organization to create the project in. + + String + + String + + + None + + + ProcessTypeName + + The name of a process to use in the project. + This can be the name of an existing process such as Basic, Agile, Scrum or CMMI. It can also be the name of a custom process created in the organization. + + String + + String + + + None + + + SourceControlType + + The source control type to use in the project. The default is git. + + String + + String + + + Git + + + Visibility + + The visibility of the project. + + String + + String + + + None + + + Wait + + If defined this command will wait for the project to be created and return the project object. Default will return the process started. + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSProject -Name 'ADOPSproj' -Description 'an example project' -Visibility Public -Organization 'ADOPS' + + Creates a new, public project called "ADOPSproj" with a description in the organization "ADOPS". + + + + -------------------------- Example 2 -------------------------- + PS C:\> New-ADOPSProject -Name 'ADOPSproj' -Visibility Private -ProcessTypeName 'Agile' + + Creates a new, private project called "ADOPSproj" with the process type "Agile" in the default organization. + + + + -------------------------- Example 3 -------------------------- + PS C:\> New-ADOPSProject -Name 'ADOPSproj' -Visibility Private -ProcessTypeName 'MyOwnProcess' -SourceControlType 'Tfvc' + + Creates a new, private project called "ADOPSproj" with an existing, custom process type "MyOwnProcess" in the default organization, with the source control type set to Team Foundation Version Control. + + + + + + + + New-ADOPSRepository + New + ADOPSRepository + + Create a new Azure DevOps Git repository + + + + This command will create a new Git repository in your Azure DevOps project. + + + + New-ADOPSRepository + + Name + + The name of the new Git repo. + + String + + String + + + None + + + Project + + The project in which the repo will be created. + + String + + String + + + None + + + Organization + + The organization in which the repo will be created. + + String + + String + + + None + + + + + + Name + + The name of the new Git repo. + + String + + String + + + None + + + Organization + + The organization in which the repo will be created. + + String + + String + + + None + + + Project + + The project in which the repo will be created. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSRepository -Project 'MyProject' -Name 'MyNewRepo' + + This command will add a new empty repo named 'MyNewRepo' in the MyProject project + + + + + + + + New-ADOPSServiceConnection + New + ADOPSServiceConnection + + Create an Azure DevOps Service Connection to Azure. + + + + Creates an Azure DevOps Service Connection to Azure subscription using an existing Service Principal. Assign the necessary permissions in Azure for the service principal. + + + + New-ADOPSServiceConnection + + AzureScope + + resource group ID where the app registration will be granted contributor access. /subscriptions/4ba9b4a1-dc0d-4ec8-adaf-061771ccd1da/resourceGroups/MyAzureRG + + String + + String + + + None + + + ConnectionName + + Name of Service Connection in Azure DevOps. + + String + + String + + + None + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + Project + + Name of Azure DevOps project. + + String + + String + + + None + + + SubscriptionId + + The subscription id that the service connection will be connected to. + + String + + String + + + None + + + SubscriptionName + + The subscription name that the service connection will be connected to. + + String + + String + + + None + + + TenantId + + The tenant id connected to your Azure AD and subscriptions. + + String + + String + + + None + + + WorkloadIdentityFederation + + If Workload identity federation will be used for the connection. + + + SwitchParameter + + + False + + + CreationMode + + Sets the creation mode. + + String + + String + + + None + + + Description + + Description for the Service Connection. + + String + + String + + + None + + + + New-ADOPSServiceConnection + + ConnectionName + + Name of Service Connection in Azure DevOps. + + String + + String + + + None + + + ManagedIdentity + + If a managed identity will be used for the connection. + + + SwitchParameter + + + False + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + Project + + Name of Azure DevOps project. + + String + + String + + + None + + + ServicePrincipal + + Azure AD Service principal, Application (Client) ID and valid secret. + + PSCredential + + PSCredential + + + None + + + SubscriptionId + + The subscription id that the service connection will be connected to. + + String + + String + + + None + + + SubscriptionName + + The subscription name that the service connection will be connected to. + + String + + String + + + None + + + TenantId + + The tenant id connected to your Azure AD and subscriptions. + + String + + String + + + None + + + Description + + Description for the Service Connection. + + String + + String + + + None + + + + New-ADOPSServiceConnection + + ConnectionName + + Name of Service Connection in Azure DevOps. + + String + + String + + + None + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + Project + + Name of Azure DevOps project. + + String + + String + + + None + + + ServicePrincipal + + Azure AD Service principal, Application (Client) ID and valid secret. + + PSCredential + + PSCredential + + + None + + + SubscriptionId + + The subscription id that the service connection will be connected to. + + String + + String + + + None + + + SubscriptionName + + The subscription name that the service connection will be connected to. + + String + + String + + + None + + + TenantId + + The tenant id connected to your Azure AD and subscriptions. + + String + + String + + + None + + + Description + + Description for the Service Connection. + + String + + String + + + None + + + + + + AzureScope + + resource group ID where the app registration will be granted contributor access. /subscriptions/4ba9b4a1-dc0d-4ec8-adaf-061771ccd1da/resourceGroups/MyAzureRG + + String + + String + + + None + + + ConnectionName + + Name of Service Connection in Azure DevOps. + + String + + String + + + None + + + ManagedIdentity + + If a managed identity will be used for the connection. + + SwitchParameter + + SwitchParameter + + + False + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + Project + + Name of Azure DevOps project. + + String + + String + + + None + + + ServicePrincipal + + Azure AD Service principal, Application (Client) ID and valid secret. + + PSCredential + + PSCredential + + + None + + + SubscriptionId + + The subscription id that the service connection will be connected to. + + String + + String + + + None + + + SubscriptionName + + The subscription name that the service connection will be connected to. + + String + + String + + + None + + + TenantId + + The tenant id connected to your Azure AD and subscriptions. + + String + + String + + + None + + + WorkloadIdentityFederation + + If Workload identity federation will be used for the connection. + + SwitchParameter + + SwitchParameter + + + False + + + CreationMode + + Sets the creation mode. + + String + + String + + + None + + + Description + + Description for the Service Connection. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + $Params = @{ + TenantId = "32238a3e-4aae-4a9d-a3be-5c2912088b9b" + SubscriptionName = "My Subscription" + SubscriptionId = "34dacce0-5332-4b27-a804-4352202aca27" + ServicePrincipal = Get-Credential + Project = "My DevOps Project" + ConnectionName = "My Service Connection Name" +} +New-ADOPSServiceConnection @Params + + Creates an Azure DevOps Service Connection to an Azure subscription using an existing Service Principal object in Azure AD. + + + + -------------------------- Example 2 -------------------------- + $Params = @{ + TenantId = "32238a3e-4aae-4a9d-a3be-5c2912088b9b" + SubscriptionName = "My Subscription" + SubscriptionId = "34dacce0-5332-4b27-a804-4352202aca27" + ServicePrincipal = Get-Credential + Project = "My DevOps Project" + ConnectionName = "My Service Connection Name" + ManagedIdentity = $true +} +New-ADOPSServiceConnection @Params + + Creates an Azure DevOps Service Connection to an Azure subscription using a managed service principal in Azure AD. + + + + -------------------------- Example 3 -------------------------- + $Params = @{ + TenantId = "32238a3e-4aae-4a9d-a3be-5c2912088b9b" + SubscriptionName = "My Subscription" + SubscriptionId = "34dacce0-5332-4b27-a804-4352202aca27" + Project = "My DevOps Project" + ConnectionName = "My Service Connection Name" + WorkloadIdentityFederation = $true + AzureScope = (Get-AzResourceGroup -Name MyAzureRG).ResourceId +} +New-ADOPSServiceConnection @Params + + Creates an Azure DevOps Service Connection to an Azure resource group using workload identity federation (OAuth) and automatically creating an app registration in Azure AD. + + + + + + serviceconnection-create + https://docs.microsoft.com/en-us/rest/api/azure/devops/serviceendpoint/endpoints/create?view=azure-devops-rest-6.0 + + + + + + New-ADOPSUserStory + New + ADOPSUserStory + + Create User Story in DevOps Project + + + + Creates a User Story (Work Item) in an Azure DevOps Project. + + + + New-ADOPSUserStory + + Title + + Title of the User Story. + + String + + String + + + None + + + ProjectName + + Name of the DevOps project to use. + + String + + String + + + None + + + Description + + Description text for the User Story work item. + + String + + String + + + None + + + Tags + + Tags to use for the user story. Multiple tags can be added and separated by comma. + + String + + String + + + None + + + Priority + + User Story priority. + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + + + + Description + + Description text for the User Story work item. + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + Priority + + User Story priority. + + String + + String + + + None + + + ProjectName + + Name of the DevOps project to use. + + String + + String + + + None + + + Tags + + Tags to use for the user story. Multiple tags can be added and separated by comma. + + String + + String + + + None + + + Title + + Title of the User Story. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + New-ADOPSUserStory -Organization 'myOrganization' -ProjectName 'myProject' -Title 'My user story' -Description 'User story description' -Tags 'ADOPS' -Priority 1 + + Creates a user story with the title 'My user story' in the DevOps Project 'myProject'. + + + + -------------------------- Example 2 -------------------------- + New-ADOPSUserStory -Organization 'myOrganization' -ProjectName 'myProject' -Title 'My user story' -Description 'User story description' -Tags 'ADOPS,Important' -Priority 1 + + Creates a user story with multiple tags. + + + + + + + + New-ADOPSVariableGroup + New + ADOPSVariableGroup + + Creates a new variable group in an Azure DevOps project. + + + + Creates a new variable group in an Azure DevOps project. + The command only supports a single variable being created together with the variable group. + + + + New-ADOPSVariableGroup + + Description + + The description of the variable group. + + String + + String + + + None + + + IsSecret + + Whether or not the variable created should be considered a secret. + + + SwitchParameter + + + False + + + Organization + + The organization of the project to create the variable group in. + + String + + String + + + None + + + Project + + The name of the project to create the variable group in. + + String + + String + + + None + + + VariableGroupName + + The name of the new variable group. + + String + + String + + + None + + + VariableName + + The name of the variable to be created with the variable group. + + String + + String + + + None + + + VariableValue + + The value of the variable to be created with the variable group. + + String + + String + + + None + + + + New-ADOPSVariableGroup + + Description + + The description of the variable group. + + String + + String + + + None + + + Organization + + The organization of the project to create the variable group in. + + String + + String + + + None + + + Project + + The name of the project to create the variable group in. + + String + + String + + + None + + + VariableGroupName + + The name of the new variable group. + + String + + String + + + None + + + VariableHashtable + + A hashtable containing the variable to be created with the variable group. + + + @( @{ Name = "Key1" value = "Key1Value" IsSecret = $true }, @{ Name = "Key2" value = "Key2Value" IsSecret = $false } ) + + Hashtable[] + + Hashtable[] + + + None + + + + + + Description + + The description of the variable group. + + String + + String + + + None + + + IsSecret + + Whether or not the variable created should be considered a secret. + + SwitchParameter + + SwitchParameter + + + False + + + Organization + + The organization of the project to create the variable group in. + + String + + String + + + None + + + Project + + The name of the project to create the variable group in. + + String + + String + + + None + + + VariableGroupName + + The name of the new variable group. + + String + + String + + + None + + + VariableHashtable + + A hashtable containing the variable to be created with the variable group. + + + @( @{ Name = "Key1" value = "Key1Value" IsSecret = $true }, @{ Name = "Key2" value = "Key2Value" IsSecret = $false } ) + + Hashtable[] + + Hashtable[] + + + None + + + VariableName + + The name of the variable to be created with the variable group. + + String + + String + + + None + + + VariableValue + + The value of the variable to be created with the variable group. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> $Hashtable = @( + @{ + Name = "Key1" + value = "Key1Value" + IsSecret = $true + }, + @{ + Name = "Key2" + value = "Key2Value" + IsSecret = $false + } + ) + +PS C:\> New-ADOPSVariableGroup -Project 'ADOPSproj' -Organization 'ADOPS' -VariableGroupName 'ADOPSvars' -Description 'vargroup example' -VariableHashtable $Hashtable + + Creates a new variable group called "ADOPSvars" in the project "ADOPSproj" in the organization "ADOPS" with a description, providing a hashtable to create one non-secret variable called "Key2" with the value "Key2Value" and one secret variable called "Key1" with the value "Key1Value". + + + + -------------------------- Example 2 -------------------------- + PS C:\> New-ADOPSVariableGroup -Project 'ADOPSproj' -VariableGroupName 'ADOPSvars' -VariableName 'var-name' -VariableValue 'var-value' + + Creates a new variable group called "ADOPSvars" in the project "ADOPSproj" in the default organization, creating a variable called "var-name" with the value "var-value". + + + + + + + + New-ADOPSWiki + New + ADOPSWiki + + Creates a new Azure DevOps code wiki. + + + + This command lets you create a new Azure DevOps code wiki. This wiki is editable by changing markdown files directly in a repository in your Azure DevOps project. + + + + New-ADOPSWiki + + WikiName + + Name of the wiki. + + String + + String + + + None + + + WikiRepository + + The Git repository that will host the markdown wiki files. + + String + + String + + + None + + + Project + + The project in which to create the wiki. + + String + + String + + + None + + + WikiRepositoryPath + + Path relative to the root where the wiki home will be. + + String + + String + + + '/' + + + GitBranch + + The git branch used for wiki markdown pages. + + String + + String + + + Main + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + + + + GitBranch + + The git branch used for wiki markdown pages. + + String + + String + + + Main + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + Project + + The project in which to create the wiki. + + String + + String + + + None + + + WikiName + + Name of the wiki. + + String + + String + + + None + + + WikiRepository + + The Git repository that will host the markdown wiki files. + + String + + String + + + None + + + WikiRepositoryPath + + Path relative to the root where the wiki home will be. + + String + + String + + + '/' + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> New-ADOPSWiki -Project 'myproject' -WikiName 'MyWikiName' -WikiRepository 'MyWikiRepo' + + This command will create a new wiki named "MyWikiName", in the "MyProject" project in your current default logged in Azure DevOps environment. It will store the markdown files in the "MyWikiRepo" repository, using the root folder in the main branch. + + + + -------------------------- Example 2 -------------------------- + PS C:\> New-ADOPSWiki -Project 'myproject' -WikiName 'MyWikiName' -WikiRepository 'MyWikiRepo' -WikiRepositoryPath '/MyWikiFiles' -GitBranch 'PublicWiki' + + This command will create a new wiki named "MyWikiName", in the "MyProject" project in your current default logged in Azure DevOps environment. It will store the markdown files in the "MyWikiRepo" repository. The Markdown files will be stored in the "/MyWikiFiles" subfolder of the repository. The wiki will be published from the "PublicWiki" branch of the "MyWikiRepo" repository. + + + + + + + + Remove-ADOPSRepository + Remove + ADOPSRepository + + Deletes a repository from an Azure DevOps organization. + + + + This command deletes a repository from an Azure DevOps organization. + + + + Remove-ADOPSRepository + + RepositoryID + + The repositoru ID, GUID, to delet. + + String + + String + + + None + + + Project + + The project in which the repository exists. + + String + + String + + + None + + + Organization + + The organization to connect to. + + String + + String + + + None + + + + + + Organization + + The organization to connect to. + + String + + String + + + None + + + Project + + The project in which the repository exists. + + String + + String + + + None + + + RepositoryID + + The repositoru ID, GUID, to delet. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Remove-ADOPSRepository -Project MyProject -RepositoryID 1bb0b904-27b7-414d-811e-f51085e7ad02 + + This command will delete the repository with id '1bb0b904-27b7-414d-811e-f51085e7ad02' from the 'MyProject' project. + + + + + + + + Remove-ADOPSVariableGroup + Remove + ADOPSVariableGroup + + Removes a variable group from Azure DevOps. + + + + Removes a variable group from Azure DevOps. + + + + Remove-ADOPSVariableGroup + + VariableGroupName + + Name of the variable group to remove. + + String + + String + + + None + + + Project + + Name of the Azure DevOps project. + + String + + String + + + None + + + Organization + + Name of the Azure DevOps organization. + + String + + String + + + None + + + + + + Organization + + Name of the Azure DevOps organization. + + String + + String + + + None + + + Project + + Name of the Azure DevOps project. + + String + + String + + + None + + + VariableGroupName + + Name of the variable group to remove. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + Remove-ADOPSVariableGroup -Organization 'ADOPS' -Project 'ADOPSproj' -VariableGroupName 'ExampleVarGroup' + + Removes the variable group called "ExampleVarGroup" from the project "ADOPSproj" in the organization "ADOPS". + + + + -------------------------- Example 2 -------------------------- + Remove-ADOPSVariableGroup -Project 'ADOPSproj' -VariableGroupName 'ExampleVarGroup' + + Removes the variable group called "ExampleVarGroup" from the project "ADOPSproj" in the default organization. + + + + + + + + Revoke-ADOPSPipelinePermission + Revoke + ADOPSPipelinePermission + + Revokes pipeline permission to a resource of type VariableGroup, Queue, SecureFile, or Environment. + + + + Revokes pipeline permissions to a resource of type VariableGroup, Queue, SecureFile, or Environment. Specify a PipelineId to grant permissions to a single pipeline, or use the AllPipelines parameter to grant permissions to all pipelines. + + + + Revoke-ADOPSPipelinePermission + + AllPipelines + + Revoke access for all pipelines. This is equivalent to enable the restrictions on the resource. + + + SwitchParameter + + + False + + + Organization + + Name of the Azure DevOps organization. Defaults to the current organization. + + String + + String + + + None + + + Project + + Name of project containing the resource. + + String + + String + + + None + + + ResourceId + + Id of the resource to revoke permissions to. + + String + + String + + + None + + + ResourceType + + Resource type to revoke permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. + + + VariableGroup + Queue + SecureFile + Environment + + ResourceType + + ResourceType + + + None + + + + Revoke-ADOPSPipelinePermission + + Organization + + Name of the Azure DevOps organization. Defaults to the current organization. + + String + + String + + + None + + + PipelineId + + Id of the pipeline to revoke permissions for. + + Int32 + + Int32 + + + None + + + Project + + Name of project containing the resource. + + String + + String + + + None + + + ResourceId + + Id of the resource to revoke permissions to. + + String + + String + + + None + + + ResourceType + + Resource type to revoke permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. + + + VariableGroup + Queue + SecureFile + Environment + + ResourceType + + ResourceType + + + None + + + + + + AllPipelines + + Revoke access for all pipelines. This is equivalent to enable the restrictions on the resource. + + SwitchParameter + + SwitchParameter + + + False + + + Organization + + Name of the Azure DevOps organization. Defaults to the current organization. + + String + + String + + + None + + + PipelineId + + Id of the pipeline to revoke permissions for. + + Int32 + + Int32 + + + None + + + Project + + Name of project containing the resource. + + String + + String + + + None + + + ResourceId + + Id of the resource to revoke permissions to. + + String + + String + + + None + + + ResourceType + + Resource type to revoke permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. + + ResourceType + + ResourceType + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Revoke-ADOPSPipelinePermission -Pipeline 42 -ResourceType SecureFile -ResourceId '88d047be-7e59-4eda-b1fe-1614c7a69e16' -Project 'MyProject' + + Revoke pipeline 42 permission to the secure file with id 88d047be-7e59-4eda-b1fe-1614c7a69e16 in project MyProject. + + + + + + + + Save-ADOPSPipelineTask + Save + ADOPSPipelineTask + + Saves the zip file of any task installed in an Azure DevOps organization. + + + + This command will download a zip file containing any task from yourAzure DevOps organization for you to explore or security review. + + + + Save-ADOPSPipelineTask + + FileName + + Filename to output the zipfile as. + + String + + String + + + None + + + Organization + + The organization where your tasks are installed. + + String + + String + + + None + + + Path + + Path where zip files will be output. + + String + + String + + + None + + + TaskId + + Id of the task to download. Can be found using the command `Get-ADOPSPipelineTask`. + + String + + String + + + None + + + TaskVersion + + Version in the format 'major.minor.build' of the task to download. Can be found using the command `Get-ADOPSPipelineTask`. + + Version + + Version + + + None + + + + Save-ADOPSPipelineTask + + InputObject + + A list of tasks to download. This list must contain the following properties: id = guid of task name = task name for file output version = @{ major = 1 minor = 2 patch = 3 } + You may use the command `Get-ADOPSPipelineTask` to get a properly created object. + + PSObject[] + + PSObject[] + + + None + + + Organization + + The organization where your tasks are installed. + + String + + String + + + None + + + Path + + Path where zip files will be output. + + String + + String + + + None + + + + + + FileName + + Filename to output the zipfile as. + + String + + String + + + None + + + InputObject + + A list of tasks to download. This list must contain the following properties: id = guid of task name = task name for file output version = @{ major = 1 minor = 2 patch = 3 } + You may use the command `Get-ADOPSPipelineTask` to get a properly created object. + + PSObject[] + + PSObject[] + + + None + + + Organization + + The organization where your tasks are installed. + + String + + String + + + None + + + Path + + Path where zip files will be output. + + String + + String + + + None + + + TaskId + + Id of the task to download. Can be found using the command `Get-ADOPSPipelineTask`. + + String + + String + + + None + + + TaskVersion + + Version in the format 'major.minor.build' of the task to download. Can be found using the command `Get-ADOPSPipelineTask`. + + Version + + Version + + + None + + + + + + System.Management.Automation.PSObject[] + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Save-ADOPSPipelineTask -TaskId 6c731c3c-3c68-459a-a5c9-bde6e6595b5b -TaskVersion 2.206.1 + + This command will download a zip file to the current folder. The zip file will contain the task with task id "6c731c3c-3c68-459a-a5c9-bde6e6595b5b" (bash) The zip file will be named "6c731c3c-3c68-459a-a5c9-bde6e6595b5b.2.206.1.zip" (taskid.version.zip) + + + + -------------------------- Example 2 -------------------------- + PS C:\> Save-ADOPSPipelineTask -Path C:\temp\tasks\ -TaskId 6c731c3c-3c68-459a-a5c9-bde6e6595b5b -TaskVersion 2.206.1 -FileName MyBashTask.zip + + This command will download a zip file named "MyBashTask.zip" to the "C:\temp\tasks\" folder. The zip file will contain the task with task id "6c731c3c-3c68-459a-a5c9-bde6e6595b5b" (bash) + + + + -------------------------- Example 3 -------------------------- + PS C:\> $r = Get-ADOPSPipelineTask +PS C:\> $r.Where({$_.name -eq 'PowerShell'}) | Save-ADOPSPipelineTask -Path c:\temp\tasks\ + + This command uses the Get-ADOPSPipelineTask to get all installed pipeline tasks. It filters the tasks on the name "PowerShell" and downloads all matching tasks to the "c:\temp\tasks\" folder. The zip files will be named "TaskName-TaskId-version.zip" (for example: "PowerShell-e213ff0f-5d5c-4791-802d-52ea3e7be1f1-1.2.3.zip") + + + + -------------------------- Example 4 -------------------------- + PS C:\> Save-ADOPSPipelineTask -Path c:\temp\tasks\ -InputObject @( + @{ + id = "e213ff0f-5d5c-4791-802d-52ea3e7be1f1" + name = "PowerShell" + version = @{ + major = 1 + minor = 2 + patch = 3 + } + }, + @{ + id = "e213ff0f-5d5c-4791-802d-52ea3e7be1f1" + name = "PowerShell" + version = @{ + major = 2 + minor = 212 + patch = 0 + } + } +) + + This command uses a list of hashtables to download two different tasks. The properties listed in the example must be included in the objects to process. + + + + + + + + Set-ADOPSArtifactFeed + Set + ADOPSArtifactFeed + + Changes a setting for an artifact feed. + + + + Changes a setting for an artifact feed. Currently only supports Description and enabling or disabling upstreams. + + + + Set-ADOPSArtifactFeed + + Project + + Project where the feed is located + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + FeedId + + Feed Id in gui format or name. + + String + + String + + + None + + + Description + + Set the feed description + + String + + String + + + None + + + UpstreamEnabled + + Enable or Disable upstreams. If enabling it will also add all current upstream sources. + + Boolean + + Boolean + + + None + + + + + + Description + + Set the feed description + + String + + String + + + None + + + FeedId + + Feed Id in gui format or name. + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + Project + + Project where the feed is located + + String + + String + + + None + + + UpstreamEnabled + + Enable or Disable upstreams. If enabling it will also add all current upstream sources. + + Boolean + + Boolean + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> $i = (Get-ADOPSArtifactFeed -Project 'MyProject' -FeedId 'MyCustomFeed').id +PS C:\> Set-ADOPSArtifactFeed -Project 'MyProject' -FeedId $i -Description 'Update the description' -UpstreamEnabled:$true + + This command will set the description for artifact feed 'MyCustomFeed' in the 'MyProject' project. It will also enable upstreams and add all currently supported upstream sources. + + + + + + + + Set-ADOPSBuildDefinition + Set + ADOPSBuildDefinition + + This command will update a build definition. + + + + This command will update a build definition. Because the build definition object is multilevel and intricate, and you will need to have the correct definition revision and metadata, you must use `Get-ADOPSBuildDefinition` and alter the output object. + + + + Set-ADOPSBuildDefinition + + Organization + + The organization to set definitions in. + + String + + String + + + None + + + DefinitionObject + + Complete object returned from `Get-ADOPSBuildDefinition` + + Object + + Object + + + None + + + + + + DefinitionObject + + Complete object returned from `Get-ADOPSBuildDefinition` + + Object + + Object + + + None + + + Organization + + The organization to set definitions in. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> $Definition = Get-ADOPSBuildDefinition -Project MyProject -Id 123 +PS C:\> $Definition.queueStatus = 'disabled' +PS C:\> Set-ADOPSBuildDefinition -DefinitionObject $Definition + + This command will set the build definition with id 123 to status 'disabled' + + + + + + + + Set-ADOPSElasticPool + Set + ADOPSElasticPool + + Updates an Azure DevOps Elastic pool. + + + + Updates an Azure DevOps Elastic pool. + + + + Set-ADOPSElasticPool + + PoolId + + The unique Azure DevOps Pool id. + + Int32 + + Int32 + + + None + + + ElasticPoolObject + + The full request body in json or as a pscustom object. The Help function New-ElasticPoolObject can help generate a powershell object/json string. + | Name | Type | Description | |----------------------|---------------------|-------------------------------------------------------------------------------| | agentInteractiveUI | boolean | Set whether agents should be configured to run with interactive UI | | azureId | string | Azure string representing to location of the resource | | desiredIdle | integer | Number of agents to have ready waiting for jobs | | desiredSize | integer | The desired size of the pool | | maxCapacity | integer | Maximum number of nodes that will exist in the elastic pool | | maxSavedNodeCount | integer | Keep nodes in the pool on failure for investigation | | offlineSince | string | Timestamp the pool was first detected to be offline | | osType | OperatingSystemType | Operating system type of the nodes in the pool | | poolId | integer | Id of the associated TaskAgentPool | | recycleAfterEachUse | boolean | Discard node after each job completes | | serviceEndpointId | string | Id of the Service Endpoint used to connect to Azure | | serviceEndpointScope | string | Scope the Service Endpoint belongs to | | sizingAttempts | integer | The number of sizing attempts executed while trying to achieve a desired size | | state | ElasticPoolState | State of the pool | | timeToLiveMinutes | integer | The minimum time in minutes to keep idle agents alive | + + Object + + Object + + + None + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + + + + ElasticPoolObject + + The full request body in json or as a pscustom object. The Help function New-ElasticPoolObject can help generate a powershell object/json string. + | Name | Type | Description | |----------------------|---------------------|-------------------------------------------------------------------------------| | agentInteractiveUI | boolean | Set whether agents should be configured to run with interactive UI | | azureId | string | Azure string representing to location of the resource | | desiredIdle | integer | Number of agents to have ready waiting for jobs | | desiredSize | integer | The desired size of the pool | | maxCapacity | integer | Maximum number of nodes that will exist in the elastic pool | | maxSavedNodeCount | integer | Keep nodes in the pool on failure for investigation | | offlineSince | string | Timestamp the pool was first detected to be offline | | osType | OperatingSystemType | Operating system type of the nodes in the pool | | poolId | integer | Id of the associated TaskAgentPool | | recycleAfterEachUse | boolean | Discard node after each job completes | | serviceEndpointId | string | Id of the Service Endpoint used to connect to Azure | | serviceEndpointScope | string | Scope the Service Endpoint belongs to | | sizingAttempts | integer | The number of sizing attempts executed while trying to achieve a desired size | | state | ElasticPoolState | State of the pool | | timeToLiveMinutes | integer | The minimum time in minutes to keep idle agents alive | + + Object + + Object + + + None + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + PoolId + + The unique Azure DevOps Pool id. + + Int32 + + Int32 + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> $AzureDevOpsProject = Get-ADOPSProject -Project 'ADOPS' +$AzureVMSS = Get-AzVmss -VMScaleSetName 'adops-vmss' +$PoolInformation = Get-ADOPSPool -PoolName 'adopsPool' +$ElasticPoolObject = New-ADOPSElasticPoolObject -ServiceEndpointId 'd6040a29-d507-43b0-a72d-99a50cb3a9d3' -ServiceEndpointScope $AzureDevOpsProject.Id -AzureId $AzureVMSS.id -DesiredIdle 2 -MaxCapacity 2 +Set-ADOPSElasticPool -PoolId $PoolInformation.id -ElasticPoolObject $ElasticPoolObject + + Retrieves the Azure DevOps Project specified, the Azure Virtual Machine Scale-set. Generates a Elastic pool object using the New-ADOPSElasticPoolObject function and then updates the Elastic Pool id Azure using it's unique id. + + + + + + + + Set-ADOPSGitPermission + Set + ADOPSGitPermission + + Sets git ref permissions on a Azure DevOps git repo + + + + This function sets Git ref access rights for a user or group in Azure DevOps. You may select the same rule for Allow and Deny, but it should not work and may lead to unexpected behaviours. + The following settings are possible to set. bit name displayName --- ---- ----------- 1 Administer Administer 2 GenericRead Read 4 GenericContribute Contribute 8 ForcePush Force push (rewrite history and delete branches) 16 CreateBranch Create branch 32 CreateTag Create tag 64 ManageNote Manage notes 128 PolicyExempt Bypass policies when pushing 256 CreateRepository Create repository 512 DeleteRepository Delete repository 1024 RenameRepository Rename repository 2048 EditPolicies Edit policies 4096 RemoveOthersLocks Remove others' locks 8192 ManagePermissions Manage permissions 16384 PullRequestContribute Contribute to pull requests 32768 PullRequestBypassPolicy Bypass policies when completing pull requests + + + + Set-ADOPSGitPermission + + Organization + + Name of the Azure DevOps organization. + + String + + String + + + None + + + Project + + Project ID or Project Name where the repo is located. + + String + + String + + + None + + + Repository + + Repository ID or Repository Name to set access to. + + String + + String + + + None + + + Descriptor + + Descriptor of a user or group in Azure DevOps. Can be found using the `Get-ADOPSUser` or `Get-ADOPSGroup` command. + + String + + String + + + None + + + Allow + + Enum of access rights to grant a user or group. Can be either the topic or the value. + + + Administer + GenericRead + GenericContribute + ForcePush + CreateBranch + CreateTag + ManageNote + PolicyExempt + CreateRepository + DeleteRepository + RenameRepository + EditPolicies + RemoveOthersLocks + ManagePermissions + PullRequestContribute + PullRequestBypassPolicy + + AccessLevels[] + + AccessLevels[] + + + None + + + Deny + + Enum of access rights to deny a user or group. Can be either the topic or the value. + + + Administer + GenericRead + GenericContribute + ForcePush + CreateBranch + CreateTag + ManageNote + PolicyExempt + CreateRepository + DeleteRepository + RenameRepository + EditPolicies + RemoveOthersLocks + ManagePermissions + PullRequestContribute + PullRequestBypassPolicy + + AccessLevels[] + + AccessLevels[] + + + None + + + + + + Allow + + Enum of access rights to grant a user or group. Can be either the topic or the value. + + AccessLevels[] + + AccessLevels[] + + + None + + + Deny + + Enum of access rights to deny a user or group. Can be either the topic or the value. + + AccessLevels[] + + AccessLevels[] + + + None + + + Descriptor + + Descriptor of a user or group in Azure DevOps. Can be found using the `Get-ADOPSUser` or `Get-ADOPSGroup` command. + + String + + String + + + None + + + Organization + + Name of the Azure DevOps organization. + + String + + String + + + None + + + Project + + Project ID or Project Name where the repo is located. + + String + + String + + + None + + + Repository + + Repository ID or Repository Name to set access to. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> $s = @{ + ProjectId = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' + RepositoryId = '11111111-1111-1111-1111-111111111111' + Descriptor = 'aad.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + Allow = 'RenameRepository','ForcePush' +} +PS C:\> Set-ADOPSGitPermission @s + + This command will grant the user with descriptor `aad.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA` the force push, and rename repository rights on the repo with id `11111111-1111-1111-1111-111111111111` + + + + -------------------------- Example 2 -------------------------- + PS C:\> $s = @{ + ProjectId = Get-ADOPSProject MyProject | Select-Object -ExpandProperty id + RepositoryId = Get-ADOPSRepository -Project $projectId -Repository MyRepo | Select-Object -ExpandProperty id + Descriptor = Get-ADOPSUser -Name 'userEmail@domain.onmicrosoft.com' | Select-Object -ExpandProperty descriptor + Allow = 6235 +} +PS C:\> Set-ADOPSGitPermission @s + + This command will grant the user `userEmail@domain.onmicrosoft.com` the Administer, GenericRead, ForcePush, CreateBranch, ManageNote, EditPolicies, RemoveOthersLocks repository rights on the `MyRepo` repo + + + + -------------------------- Example 3 -------------------------- + PS C:\> $s = @{ + ProjectId = Get-ADOPSProject MyProject | Select-Object -ExpandProperty id + RepositoryId = Get-ADOPSRepository -Project $projectId -Repository MyRepo | Select-Object -ExpandProperty id + Descriptor = Get-ADOPSUser -Name 'userEmail@domain.onmicrosoft.com' | Select-Object -ExpandProperty descriptor + Deny = Administer +} +PS C:\> Set-ADOPSGitPermission @s + + This command will deny the user `userEmail@domain.onmicrosoft.com` the Administer repository rights on the `MyRepo` repo + + + + + + + + Set-ADOPSPipelineRetentionSettings + Set + ADOPSPipelineRetentionSettings + + Set pipeline retention settings for a project. + + + + This command will set the retention settings values for a project. + + + + Set-ADOPSPipelineRetentionSettings + + Organization + + The organization to get pipeline retention settings from. + + String + + String + + + None + + + Project + + The project to get pipeline retention settings from. + + String + + String + + + None + + + Values + + Pipeline retention settings to set. + + Object + + Object + + + None + + + + + + Organization + + The organization to get pipeline retention settings from. + + String + + String + + + None + + + Project + + The project to get pipeline retention settings from. + + String + + String + + + None + + + Values + + Pipeline retention settings to set. + + Object + + Object + + + None + + + + + + None + + + + + + + + + + System.Object + + + A Key/value custom object of all the pipeline retention value + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Set-ADOPSPipelineRetentionSettings -Project 'MyProject' -Values @{ + artifactsRetention = 51 + runRetention = 37 + pullRequestRunRetention = 4 + retainRunsPerProtectedBranch = 0 +} + + This command will set all pipeline retention settings. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Set-ADOPSPipelineRetentionSettings -Project 'MyProject' -Values @{ + artifactsRetention = 51 +} + + This command will set a single pipeline retention settings. + + + + + + + + Set-ADOPSPipelineSettings + Set + ADOPSPipelineSettings + + Set pipeline settings for a project. + + + + This command will set pipeline settings for a project. + + + + Set-ADOPSPipelineSettings + + Organization + + The organization to get pipeline settings from. + + String + + String + + + None + + + Project + + The project to get pipeline settings from. + + String + + String + + + None + + + Values + + Pipeline settings to set. + + Object + + Object + + + None + + + + + + Organization + + The organization to get pipeline settings from. + + String + + String + + + None + + + Project + + The project to get pipeline settings from. + + String + + String + + + None + + + Values + + Pipeline settings to set. + + Object + + Object + + + None + + + + + + None + + + + + + + + + + System.Object + + + A key/value custom object of all pipeline settings is returned. + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Set-ADOPSPipelineSettings -Project 'MyProject' -Values @{ + statusBadgesArePrivate +} + + This command will set a single setting `statusBadgesArePrivate` + + + + -------------------------- Example 2 -------------------------- + PS C:\> Set-ADOPSPipelineSettings -Project 'MyProject' -Values @{ + statusBadgesArePrivate = $false + disableImpliedYAMLCiTrigger = $true +} + + This command will set multiple settings + + + + + + + + Set-ADOPSProject + Set + ADOPSProject + + Updates Azure DevOps project information. + + + + This command will update information of an Azure DevOps project. + + + + Set-ADOPSProject + + Description + + Set the Project description + + String + + String + + + None + + + Organization + + The organization to update the project in. + + String + + String + + + None + + + ProjectId + + Project ID of the project to update. Only needed if no ProjectName is given. + + String + + String + + + None + + + Visibility + + Sets project visibility. + + + Private + Public + + String + + String + + + None + + + Wait + + Wait for the status update to be set to 'Complete', 'Failed', or 'Cancelled' + + + SwitchParameter + + + False + + + + Set-ADOPSProject + + Description + + Set the Project description + + String + + String + + + None + + + Organization + + The organization to update the project in. + + String + + String + + + None + + + ProjectName + + Project name of the project to update. Only needed if no ProjectId is given. + + String + + String + + + None + + + Visibility + + Sets project visibility. + + + Private + Public + + String + + String + + + None + + + Wait + + Wait for the status update to be set to 'Complete', 'Failed', or 'Cancelled' + + + SwitchParameter + + + False + + + + + + Description + + Set the Project description + + String + + String + + + None + + + Organization + + The organization to update the project in. + + String + + String + + + None + + + ProjectId + + Project ID of the project to update. Only needed if no ProjectName is given. + + String + + String + + + None + + + ProjectName + + Project name of the project to update. Only needed if no ProjectId is given. + + String + + String + + + None + + + Visibility + + Sets project visibility. + + String + + String + + + None + + + Wait + + Wait for the status update to be set to 'Complete', 'Failed', or 'Cancelled' + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Set-ADOPSProject -ProjectName 'myProject' -Description 'New description' -Visibility Private -Wait + + This command will set the properties 'Description' and 'Visibility' of the 'myProject' project. It will wait until the status of the update is completed, failed, or cancelled before returning the result. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Set-ADOPSProject -ProjectName 'myProject' -Description ([string]::Empty) + + This command will clear the 'myProject' project description. It will return the status even if the update is not yet completed. + + + + + + + + Set-ADOPSRepository + Set + ADOPSRepository + + Changes or updates repository settings + + + + This command changes or updates one or more repository settings. It can change one or all settings in one call. It will not ask for confirmation before updating or changing a value. + + + + Set-ADOPSRepository + + RepositoryId + + Repository ID. This can be found using Get-ADOPSRepository -Project MyProject + + String + + String + + + None + + + Project + + The project in which the repositoryID exists. + + String + + String + + + None + + + Organization + + The organization in which the repositoryID exists. + + String + + String + + + None + + + DefaultBranch + + Sets the default branch to this value. If only branch name is given it will automatically add 'refs/heads/' to the path. + + String + + String + + + None + + + IsDisabled + + Sets the isDisabled flag of the repository to true or false. + + Boolean + + Boolean + + + None + + + NewName + + Renames the directory to this name + + String + + String + + + None + + + + + + DefaultBranch + + Sets the default branch to this value. If only branch name is given it will automatically add 'refs/heads/' to the path. + + String + + String + + + None + + + IsDisabled + + Sets the isDisabled flag of the repository to true or false. + + Boolean + + Boolean + + + None + + + NewName + + Renames the directory to this name + + String + + String + + + None + + + Organization + + The organization in which the repositoryID exists. + + String + + String + + + None + + + Project + + The project in which the repositoryID exists. + + String + + String + + + None + + + RepositoryId + + Repository ID. This can be found using Get-ADOPSRepository -Project MyProject + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Set-ADOPSRepository -IsDisabled:$true -Project 'DummyProj' -RepositoryId 'd5f24968-f2ab-4048-bd65-58711420f6fa' + + This will set the repository with id d5f24968-f2ab-4048-bd65-58711420f6fa to status disabled. This repo will not be possible to work from after this action. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Set-ADOPSRepository -NewName 'NewName'-Project 'DummyProj' -RepositoryId 'd5f24968-f2ab-4048-bd65-58711420f6fa' + + This will rename the repository with id d5f24968-f2ab-4048-bd65-58711420f6fa to 'NewName'. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Set-ADOPSRepository -DefaultBranch 'defBranch' -Project 'DummyProj' -RepositoryId 'd5f24968-f2ab-4048-bd65-58711420f6fa' + + This will set the default branch of the repository with id d5f24968-f2ab-4048-bd65-58711420f6fa to 'refs/heads/defBranch'. If only branch name is given it will automatically add 'refs/heads/' to the branch path. + + + + -------------------------- Example 2 -------------------------- + PS C:\> Set-ADOPSRepository -IsDisabled:$false -NewName 'NewName' -DefaultBranch 'refs/heads/NewBranch' -Project 'DummyProj' -RepositoryId 'd5f24968-f2ab-4048-bd65-58711420f6fa' + + This willperform all of the following actions on the repository with id d5f24968-f2ab-4048-bd65-58711420f6fa: - Set status to enabled. + - Rename the repository to 'NewName' + - set the default branch to 'refs/heads/NewBranch' + + + + + + + + Set-ADOPSServiceConnection + Set + ADOPSServiceConnection + + Allows update of a Azure DevOps Service Connection. + + + + If a managed identity will be used for the connection. + + + + Set-ADOPSServiceConnection + + ConnectionName + + Name of Service Connection in Azure DevOps. + + String + + String + + + None + + + Description + + The description field of the Service connection. + + String + + String + + + None + + + EndpointOperation + + Undocumented. + + String + + String + + + None + + + ManagedIdentity + + If a managed identity is to be used by the Service Connection. + + + SwitchParameter + + + False + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + Project + + Name of the Azure DevOps project. + + String + + String + + + None + + + ServiceEndpointId + + The GUID of the Azure DevOps Service Endpoint. + + Guid + + Guid + + + None + + + SubscriptionId + + The subscription id that the service connection will be connected to. + + String + + String + + + None + + + SubscriptionName + + The subscription name that the service connection will be connected to. + + String + + String + + + None + + + TenantId + + The tenant id connected to your Azure AD and subscriptions. + + String + + String + + + None + + + + Set-ADOPSServiceConnection + + ConnectionName + + Name of Service Connection in Azure DevOps. + + String + + String + + + None + + + Description + + The description field of the Service connection. + + String + + String + + + None + + + EndpointOperation + + Undocumented. + + String + + String + + + None + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + Project + + Name of the Azure DevOps project. + + String + + String + + + None + + + ServiceEndpointId + + The GUID of the Azure DevOps Service Endpoint. + + Guid + + Guid + + + None + + + ServicePrincipal + + Azure AD Service principal, Application (Client) ID and valid secret. + + PSCredential + + PSCredential + + + None + + + SubscriptionId + + The subscription id that the service connection will be connected to. + + String + + String + + + None + + + SubscriptionName + + The subscription name that the service connection will be connected to. + + String + + String + + + None + + + TenantId + + The tenant id connected to your Azure AD and subscriptions. + + String + + String + + + None + + + + Set-ADOPSServiceConnection + + ConnectionName + + Name of Service Connection in Azure DevOps. + + String + + String + + + None + + + Description + + The description field of the Service connection. + + String + + String + + + None + + + EndpointOperation + + Undocumented. + + String + + String + + + None + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + Project + + Name of the Azure DevOps project. + + String + + String + + + None + + + ServiceEndpointId + + The GUID of the Azure DevOps Service Endpoint. + + Guid + + Guid + + + None + + + SubscriptionId + + The subscription id that the service connection will be connected to. + + String + + String + + + None + + + SubscriptionName + + The subscription name that the service connection will be connected to. + + String + + String + + + None + + + TenantId + + The tenant id connected to your Azure AD and subscriptions. + + String + + String + + + None + + + ServicePrincipalId + + Id of the Service Principal to be used for Federation. + + String + + String + + + None + + + WorkloadIdentityFederationIssuer + + Federation issuer. + + String + + String + + + None + + + WorkloadIdentityFederationSubject + + Federation Subject. + + String + + String + + + None + + + + + + ConnectionName + + Name of Service Connection in Azure DevOps. + + String + + String + + + None + + + Description + + The description field of the Service connection. + + String + + String + + + None + + + EndpointOperation + + Undocumented. + + String + + String + + + None + + + ManagedIdentity + + If a managed identity is to be used by the Service Connection. + + SwitchParameter + + SwitchParameter + + + False + + + Organization + + Name of Azure DevOps organization. + + String + + String + + + None + + + Project + + Name of the Azure DevOps project. + + String + + String + + + None + + + ServiceEndpointId + + The GUID of the Azure DevOps Service Endpoint. + + Guid + + Guid + + + None + + + ServicePrincipal + + Azure AD Service principal, Application (Client) ID and valid secret. + + PSCredential + + PSCredential + + + None + + + SubscriptionId + + The subscription id that the service connection will be connected to. + + String + + String + + + None + + + SubscriptionName + + The subscription name that the service connection will be connected to. + + String + + String + + + None + + + TenantId + + The tenant id connected to your Azure AD and subscriptions. + + String + + String + + + None + + + ServicePrincipalId + + Id of the Service Principal to be used for Federation. + + String + + String + + + None + + + WorkloadIdentityFederationIssuer + + Federation issuer. + + String + + String + + + None + + + WorkloadIdentityFederationSubject + + Federation Subject. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + $ApplicationName = 'demo-vmss-scaling-app' +$App = Get-AzADApplication -DisplayName $ApplicationName +$AppCreds = New-AzADAppCredential -ApplicationId $App.AppId -EndDate (Get-Date).AddDays(7) +$ServicePrincipal = Get-AzADServicePrincipal -ApplicationId $App.AppId + +$ServiceEndpoint = Get-ADOPSServiceConnection -Name "$ApplicationName-serviceconnection" -Project 'Azure' + +$ServiceConnectionParams = @{ + TenantId = '34ca78c2-d872-455a-9977-88e161bd4ac0' + SubscriptionName = 'MySubscriptionName' + SubscriptionId = '8671f1d1-0bb1-4be0-b73c-6a8f3b354cf6' + ConnectionName = $ServiceEndpoint.name + Project = 'Azure' + Organization = 'MyOrganization' + ServiceEndpointId = $ServiceEndpoint.id + Description = 'Service Principal to manage scaling of the VMSS.' + ServicePrincipal= [pscredential]::new($ServicePrincipal.AppId,$(ConvertTo-SecureString -AsPlainText -Force $AppCreds.SecretText)) +} +Set-ADOPSServiceConnection @ServiceConnectionParams + + Updates the Service Connection called 'demo-vmss-scaling-app-serviceconnection' in the project called 'Azure' with a new secret. + + + + + + + + Start-ADOPSPipeline + Start + ADOPSPipeline + + Starts an Azure DevOps Pipeline. + + + + Starts an Azure DevOps Pipeline. + + + + Start-ADOPSPipeline + + Name + + Name of the DevOps Pipeline. + + String + + String + + + None + + + Project + + Name of the DevOps Project. + + String + + String + + + None + + + Branch + + Name of the branch to use. + + String + + String + + + None + + + Organization + + Name of the DevOps Organization + + String + + String + + + None + + + + + + Branch + + Name of the branch to use. + + String + + String + + + None + + + Name + + Name of the DevOps Pipeline. + + String + + String + + + None + + + Organization + + Name of the DevOps Organization + + String + + String + + + None + + + Project + + Name of the DevOps Project. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + Start-ADOPSPipeline -Name 'myPipeline' -Project 'myProject' -Organization 'ADOPS' -Branch 'main' + + Starts the DevOps Pipeline 'myPipeline' in the project 'myProject'. + + + + -------------------------- Example 2 -------------------------- + Start-ADOPSPipeline -Name 'myPipeline' -Project 'myProject' -Organization 'ADOPS' -Branch 'refs/other/branch' + + Starts the DevOps Pipeline 'myPipeline' in the project 'myProject' using the git ref 'refs/other/branch'. + + + + + + + + Test-ADOPSYamlFile + Test + ADOPSYamlFile + + Test a yaml file against the Azure DevOps schema validator. + + + + This command will test a yaml file against the yaml validation schema in Azure DevOps. It uses the same validation as the built in Azure DevOps yaml editor. The command needs a pipeline id to run against, but will not replace any code in the pipeline. False positives are possible if you use nested or external repos in your yaml code for example by using templates. + + + + Test-ADOPSYamlFile + + Project + + The name of the project where the pipeline is. + + String + + String + + + None + + + File + + Path to the .yaml or .yml file + + String + + String + + + None + + + PipelineId + + Pipeline Id to run the verification against. Can be found by running the command + + + Get-ADOPSPipeline -Project MyProject -Name MyPipelineName + + Int32 + + Int32 + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + + + + File + + Path to the .yaml or .yml file + + String + + String + + + None + + + Organization + + Name of the organization to use. + + String + + String + + + None + + + PipelineId + + Pipeline Id to run the verification against. Can be found by running the command + + + Get-ADOPSPipeline -Project MyProject -Name MyPipelineName + + Int32 + + Int32 + + + None + + + Project + + The name of the project where the pipeline is. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Test-ADOPSYamlFile -Project MyProject -File C:\GoodYamlFile.yml -PipelineId 3 + + This command will validate "C:\GoodYamlFile" against pipeline id 3 in the "MyProject" project. + + + + + + \ No newline at end of file diff --git a/tests/Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml b/tests/Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml new file mode 100644 index 000000000..917bcd493 --- /dev/null +++ b/tests/Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml @@ -0,0 +1,700 @@ + + + + + Get-ADOPSAuditStreams + Get + ADOPSAuditStreams + + Gets all audit streams in an Azure DevOps organization. + + + + This commands lists all audit streams in an Azure DevOps organization. + + + + Get-ADOPSAuditStreams + + Organization + + The organization to get audit steams from. + + String + + String + + + None + + + + + + Organization + + The organization to get audit steams from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSAuditStreams + + This commands lists all audit streams in an Azure DevOps organization. + + + + + + + + Get-ADOPSOrganizationAdminOverview + Get + ADOPSOrganizationAdminOverview + + Get the organization admin overview data. + + + + Get the organization admin overview data. + + + + Get-ADOPSOrganizationAdminOverview + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + + + + Organization + + The identifier of the Azure DevOps organization. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSOrganizationAdminOverview + + Get the organization admin overview data. + + + + + + + + Get-ADOPSOrganizationAdvancedSecurity + Get + ADOPSOrganizationAdvancedSecurity + + Get the Azure DevOps organization wide Advanced Security Settings + + + + Get the Azure DevOps organization wide Advanced Security Settings + + + + Get-ADOPSOrganizationAdvancedSecurity + + Organization + + The organization to get the Advanced Security settings from. + + String + + String + + + None + + + + + + Organization + + The organization to get the Advanced Security settings from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSOrganizationAdvancedSecurity + + Get the Azure DevOps organization wide Advanced Security Settings + + + + + + + + Get-ADOPSOrganizationCommerceMeterUsage + Get + ADOPSOrganizationCommerceMeterUsage + + Get the Azure DevOps Organization Storage Usage + + + + Get the Azure DevOps Organization Storage Usage + + + + Get-ADOPSOrganizationCommerceMeterUsage + + Organization + + The organization to get the storage usage from. + + String + + String + + + None + + + MeterId + + The MeterId for commerce meter usage. + Other valid MeterIds are; + meterId meterKind ------- --------- 3efc2e47-d73e-4213-8368-3a8723ceb1cc artifacts 4bad9897-8d87-43bb-80be-5e6e8fefa3de commitment f44a67f2-53ae-4044-bd58-1c8aca386b98 commitment 3fa30bbe-3437-42d4-a978-0ef84811f470 commitment 2fa36572-3c3d-46be-ac59-6053cbb377b4 commitment e2c73ec7-cb60-4142-b8b2-e216b6c09c1a resource a7d460a9-a56d-4b64-837f-14728d6d54d4 resource 256caf12-9779-4531-99ac-b46e295130a3 resource + + String + + String + + + None + + + + + + MeterId + + The MeterId for commerce meter usage. + Other valid MeterIds are; + meterId meterKind ------- --------- 3efc2e47-d73e-4213-8368-3a8723ceb1cc artifacts 4bad9897-8d87-43bb-80be-5e6e8fefa3de commitment f44a67f2-53ae-4044-bd58-1c8aca386b98 commitment 3fa30bbe-3437-42d4-a978-0ef84811f470 commitment 2fa36572-3c3d-46be-ac59-6053cbb377b4 commitment e2c73ec7-cb60-4142-b8b2-e216b6c09c1a resource a7d460a9-a56d-4b64-837f-14728d6d54d4 resource 256caf12-9779-4531-99ac-b46e295130a3 resource + + String + + String + + + None + + + Organization + + The organization to get the storage usage from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSOrganizationCommerceMeterUsage + + Get the Azure DevOps Organization Storage Usage using default MeterId + + + + -------------------------- Example 2 -------------------------- + PS C:\> Get-ADOPSOrganizationCommerceMeterUsage -MeterId '3efc2e47-d73e-4213-8368-3a8723ceb1cc' + + Get the Azure DevOps Organization Storage Usage for MeterId '3efc2e47-d73e-4213-8368-3a8723ceb1cc' (Artifacts) + + + + + + + + Get-ADOPSOrganizationPipelineSettings + Get + ADOPSOrganizationPipelineSettings + + Get Azure DevOps organization wide pipeline settings + + + + Get Azure DevOps organization wide pipeline settings + + + + Get-ADOPSOrganizationPipelineSettings + + Organization + + The organization to get the pipeline settings from. + + String + + String + + + None + + + + + + Organization + + The organization to get the pipeline settings from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSOrganizationPipelineSettings + + Get Azure DevOps organization wide pipeline settings + + + + + + + + Get-ADOPSOrganizationPolicy + Get + ADOPSOrganizationPolicy + + Gets all organization policy in selected category or all organization policies across all categories. + + + + Gets all organization policy in selected category or all organization policies across all categories. + + + + Get-ADOPSOrganizationPolicy + + Organization + + The organization to get the policies from. + + String + + String + + + None + + + PolicyCategory + + The selected policy category. + + + Security + Privacy + ApplicationConnection + User + + String + + String + + + None + + + + + + Organization + + The organization to get the policies from. + + String + + String + + + None + + + PolicyCategory + + The selected policy category. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSOrganizationPolicy + + Gets all organization policies in all categories + + + + + + + + Get-ADOPSOrganizationRepositorySettings + Get + ADOPSOrganizationRepositorySettings + + Get Azure DevOps Organization wide Repository settings + + + + Get Azure DevOps Organization wide Repository settings + + + + Get-ADOPSOrganizationRepositorySettings + + Organization + + The organization to get the repository settings from. + + String + + String + + + None + + + + + + Organization + + The organization to get the repository settings from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSOrganizationRepositorySettings + + Get Azure DevOps Organization wide Repository settings + + + + + + + + Get-ADOPSResourceUsage + Get + ADOPSResourceUsage + + Gets all resource usage limits in an Azure DevOps organization. + + + + Gets all resource usage limits in an Azure DevOps organization. + + + + Get-ADOPSResourceUsage + + Organization + + The organization to get resource usage from. + + String + + String + + + None + + + + + + Organization + + The organization to get resource usage from. + + String + + String + + + None + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + -------------------------- Example 1 -------------------------- + PS C:\> Get-ADOPSResourceUsage + + Gets all resource usage limits in an Azure DevOps organization. + + + + + + \ No newline at end of file diff --git a/tests/Azdo/AdopsMaesterTests.psd1 b/tests/Azdo/AdopsMaesterTests.psd1 new file mode 100644 index 000000000..16c2669d2 --- /dev/null +++ b/tests/Azdo/AdopsMaesterTests.psd1 @@ -0,0 +1,158 @@ +# +# Module manifest for module 'AdopsMaesterTests' +# +# Generated by: Sebastian Claesson +# +# Generated on: 2025-05-11 +# + +@{ + + # Script module or binary module file associated with this manifest. + RootModule = '.\AdopsMaesterTests.psm1' + + # Version number of this module. + ModuleVersion = '0.0.1' + + # Supported PSEditions + CompatiblePSEditions = 'Core' + + # ID used to uniquely identify this module + GUID = 'c5ce0ae6-0db8-40d6-8d89-6e4824657b1c' + + # Author of this module + Author = 'Sebastian Claesson' + + # Company or vendor of this module + CompanyName = '' + + # Copyright statement for this module + Copyright = '(c) Sebastian Claesson. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'Maester tests for Azure DevOps using Pester' + + # Minimum version of the PowerShell engine required by this module + PowerShellVersion = '7.2' + + # Name of the PowerShell host required by this module + # PowerShellHostName = '' + + # Minimum version of the PowerShell host required by this module + # PowerShellHostVersion = '' + + # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # DotNetFrameworkVersion = '' + + # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # ClrVersion = '' + + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' + + # Modules that must be imported into the global environment prior to importing this module + # RequiredModules = @() + + # Assemblies that must be loaded prior to importing this module + # RequiredAssemblies = @() + + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + # ScriptsToProcess = @() + + # Type files (.ps1xml) to be loaded when importing this module + # TypesToProcess = @() + + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + # NestedModules = @() + + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + FunctionsToExport = 'Test-AzdoAllowRequestAccessToken', + 'Test-AzdoAllowTeamAdminsInvitationsAccessToken', + 'Test-AzdoArtifactsExternalPackageProtectionToken', + 'Test-AzdoAuditStreams', 'Test-AzdoEnforceAADConditionalAccess', + 'Test-AzdoExternalGuestAccess', 'Test-AzdoFeedbackCollection', + 'Test-AzdoLogAuditEvents', + 'Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects', + 'Test-AzdoOrganizationBadgesArePrivate', + 'Test-AzdoOrganizationCreationClassicBuildPipelines', + 'Test-AzdoOrganizationCreationClassicReleasePipelines', + 'Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines', + 'Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines', + 'Test-AzdoOrganizationLimitVariablesAtQueueTime', + 'Test-AzdoOrganizationOwner', + 'Test-AzdoOrganizationProtectAccessToRepositories', + 'Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos', + 'Test-AzdoOrganizationRepositorySettingsGravatarImages', + 'Test-AzdoOrganizationStageChooser', + 'Test-AzdoOrganizationStorageUsage', + 'Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks', + 'Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks', + 'Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation', + 'Test-AzdoOrganizationTriggerPullRequestGitHubRepositories', + 'Test-AzdoProjectCollectionAdministrators', + 'Test-AzdoPublicProjects', 'Test-AzdoResourceUsageProjects', + 'Test-AzdoResourceUsageWorkItemTags', 'Test-AzdoSSHAuthentication', + 'Test-AzdoThirdPartyAccessViaOauth' + + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + CmdletsToExport = @() + + # Variables to export from this module + # VariablesToExport = @() + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + AliasesToExport = @() + + # DSC resources to export from this module + # DscResourcesToExport = @() + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + # FileList = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = 'Azure', 'DevOps', 'AZDO', 'AzureDevOps', 'ADO', 'Maester', 'MaesterTests' + + # A URL to the license for this module. + LicenseUri = 'https://github.com/AZDOPS/AZDOPS/blob/main/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/AZDOPS/AZDOPS/' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + + } # End of PrivateData hashtable + + # HelpInfo URI of this module + # HelpInfoURI = '' + + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' + +} + diff --git a/tests/Azdo/AdopsMaesterTests.psm1 b/tests/Azdo/AdopsMaesterTests.psm1 new file mode 100644 index 000000000..1bcc47b18 --- /dev/null +++ b/tests/Azdo/AdopsMaesterTests.psm1 @@ -0,0 +1,1014 @@ +#region Test-AzdoAllowRequestAccessToken + +function Test-AzdoAllowRequestAccessToken { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $UserPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'User' + $Policy = $UserPolicies.policy | where-object -property name -eq 'Policy.AllowRequestAccessToken' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "When enabled, this policy allows users to request access, triggering email notifications to administrators for review and approval." + } + else { + $resultMarkdown = "Well done. Disabling the policy stops these requests and notifications." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" -Description "$Description" + + return $result +} +#endregion Test-AzdoAllowRequestAccessToken + +#region Test-AzdoAllowTeamAdminsInvitationsAccessToken + +function Test-AzdoAllowTeamAdminsInvitationsAccessToken { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'User' + $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.AllowTeamAdminsInvitationsAccessToken' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Team and project administrators is allowed to invite new users" + } + else { + $resultMarkdown = "Well done. Enrolling to your Azure DevOps organization should be a controlled process." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" -Description "$Description" + + return $result +} +#endregion Test-AzdoAllowTeamAdminsInvitationsAccessToken + +#region Test-AzdoArtifactsExternalPackageProtectionToken + +function Test-AzdoArtifactsExternalPackageProtectionToken { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.ArtifactsExternalPackageProtectionToken' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Well done. Your Azure DevOps tenant limits access to externally sourced packages when internally sources packages are already present." + } + else { + $resultMarkdown = "Your tenant should prefer to use internal source packages when present" + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoArtifactsExternalPackageProtectionToken + +#region Test-AzdoAuditStreams + +function Test-AzdoAuditStreams { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $AuditStreams = Get-ADOPSAuditStreams + + if ($AuditStreams) { + if ('Enabled' -in $AuditStreams.status) { + $resultMarkdown = "Well done. Audit logs have been configured for long-term storage and purge protection." + $result = $true + } + else { + $resultMarkdown = "Audit Streams have been configured for long-term storage and purge protection but is not enabled." + $result = $false + } + } + else { + $resultMarkdown = "Audit Streams have not been configured for long-term storage and purge protection." + $result = $false + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoAuditStreams + +#region Test-AzdoEnforceAADConditionalAccess + +function Test-AzdoEnforceAADConditionalAccess { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.EnforceAADConditionalAccess' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Well done. Microsoft Entra ID always performs validation for any Conditional Access Policies (CAPs) set by tenant administrators." + } + else { + $resultMarkdown = "Your tenant should always perform validation for any Conditional Access Policies (CAPs) set by tenant administrators. " + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoEnforceAADConditionalAccess + +#region Test-AzdoExternalGuestAccess + +function Test-AzdoExternalGuestAccess { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'User' + $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.DisallowAadGuestUserAccess' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "External user(s) can be added to the organization to which they were invited and has immediate access. A guest user can add other guest users to the organization after being granted the Guest Inviter role in Microsoft Entra ID." + } + else { + $resultMarkdown = "Well done. External users should not be allowed access to your Azure DevOps organization" + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoExternalGuestAccess + +#region Test-AzdoFeedbackCollection + +function Test-AzdoFeedbackCollection { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Privacy' + $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.AllowFeedbackCollection' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Well done. Your Azure DevOps tenant allows feedback collection." + } + else { + $resultMarkdown = "You should have confidence that we're handling your data appropriately and for legitimate uses. Part of that assurance involves carefully restricting usage." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoFeedbackCollection + +#region Test-AzdoLogAuditEvents + +function Test-AzdoLogAuditEvents { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.LogAuditEvents' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Well done. Your tenant has auditing enabled, tracking events such as permission changes, deleted resources, log access and downloads with many other types of changes." + } + else { + $resultMarkdown = "Your tenant do not have logging enabled for Azure DevOps" + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoLogAuditEvents + +#region Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects + +function Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationAdvancedSecurity).enableOnCreate + + if ($result) { + $resultMarkdown = "Well done. New projects will by default have Advanced Security enabled." + } + else { + $resultMarkdown = "New projects must be manually enrolled in Advanced Security." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects + +#region Test-AzdoOrganizationBadgesArePrivate + +function Test-AzdoOrganizationBadgesArePrivate { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).statusBadgesArePrivate + + if ($result) { + $resultMarkdown = "Well done. Azure DevOps badges are private." + } + else { + $resultMarkdown = "Anonymous users can access the status badge API for all pipelines." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationBadgesArePrivate + +#region Test-AzdoOrganizationCreationClassicBuildPipelines + +function Test-AzdoOrganizationCreationClassicBuildPipelines { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PipelineCreation = (Get-ADOPSOrganizationPipelineSettings).disableClassicBuildPipelineCreation + + if ($PipelineCreation) { + $resultMarkdown = "Well done. No classic build pipelines can be created / imported. Existing ones will continue to work." + $result = $false + } + else { + $resultMarkdown = "Classic build pipelines can be created / imported." + $result = $true + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationCreationClassicBuildPipelines + +#region Test-AzdoOrganizationCreationClassicReleasePipelines + +function Test-AzdoOrganizationCreationClassicReleasePipelines { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PipelineCreation = (Get-ADOPSOrganizationPipelineSettings).disableClassicReleasePipelineCreation + + if ($PipelineCreation) { + $resultMarkdown = "Well done. No classic release pipelines, task groups, and deployment groups can be created / imported. Existing ones will continue to work." + $result = $false + } + else { + $resultMarkdown = "Classic release pipelines, task groups, and deployment groups can be created / imported." + $result = $true + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationCreationClassicReleasePipelines + +#region Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines + +function Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScope + + if ($result) { + $resultMarkdown = "Well done. Access tokens have reduced scope of access for all non-release pipelines." + } + else { + $resultMarkdown = "Non-Release Pipelines can run with collection scoped access tokens" + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines + +#region Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines + +function Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScopeForReleases + + if ($result) { + $resultMarkdown = "Well done. Access tokens have reduced scope of access for all classic release pipelines." + } + else { + $resultMarkdown = "Classic Release Pipelines can run with collection scoped access tokens" + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines + +#region Test-AzdoOrganizationLimitVariablesAtQueueTime + +function Test-AzdoOrganizationLimitVariablesAtQueueTime { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceSettableVar + + if ($result) { + $resultMarkdown = "Well done. With this option enabled, only those variables that are explicitly marked as ""Settable at queue time"" can be set" + } + else { + $auditEnforceSettableVar = (Get-ADOPSOrganizationPipelineSettings).auditEnforceSettableVar + if ($auditEnforceSettableVar) { + $resultMarkdown = "Auditing is configured, however usage is not restricted." + } + else { + $resultMarkdown = "Users can define new variables not defined by pipeline author, and may override system variables." + } + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationLimitVariablesAtQueueTime + +#region Test-AzdoOrganizationOwner + +function Test-AzdoOrganizationOwner { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + $Data = Get-ADOPSOrganizationAdminOverview + if ($data.'ms.vss-admin-web.organization-admin-overview-delay-load-data-provider'.exceptionType -eq 'AadGraphException') { + $resultMarkdown = "Workload identities cannot fetch Organization Owner." + Add-MtTestResultDetail -Result "BUG: Workload identities cannot fetch Organization Owner." -SkippedCustomReason "Workload identities cannot fetch Organization Owner." -SkippedBecause Custom -Description "$Description" + $result = $false + } + else { + $currentOwner = $data.'ms.vss-admin-web.organization-admin-overview-delay-load-data-provider'.currentOwner + if ($currentOwner.email -match '(?i)(adm|admin|btg|svc|service)') { + $resultMarkdown = "Well done. Azure DevOps organization owner should be a service account and not an individual." + $result = $true + } + else { + $resultMarkdown = "Azure DevOps organization owner should not be an individual ($($currentOwner.name)). Note: This might be a false positive." + $result = $false + } + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + } + return $result +} +#endregion Test-AzdoOrganizationOwner + +#region Test-AzdoOrganizationProtectAccessToRepositories + +function Test-AzdoOrganizationProtectAccessToRepositories { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceReferencedRepoScopedToken + + if ($result) { + $resultMarkdown = "Well done. Checks and approvals are applied when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." + } + else { + $resultMarkdown = "Checks and approvals are not applied when accessing repositories from YAML pipelines. Also, a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline is not generated." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationProtectAccessToRepositories + +#region Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos + +function Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "DisableTfvcRepositories").value + + if ($result) { + $resultMarkdown = "Well done. Team Foundation Version Control (TFVC) repositories cannot be created." + } + else { + $resultMarkdown = "Team Foundation Version Control (TFVC) can be created." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos + +#region Test-AzdoOrganizationRepositorySettingsGravatarImages + +function Test-AzdoOrganizationRepositorySettingsGravatarImages { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "GravatarEnabled").value + + if ($result) { + $resultMarkdown = "Gravatar images are exposed for users outside of your enterprise." + } + else { + $resultMarkdown = "Well done. Gravatar images are not exposed outside of your enterprise." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationRepositorySettingsGravatarImages + +#region Test-AzdoOrganizationStageChooser + +function Test-AzdoOrganizationStageChooser { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $StageChooser = (Get-ADOPSOrganizationPipelineSettings).disableStageChooser + + if ($result) { + $resultMarkdown = "Well done. Users will not be able to select stages to skip from the Queue Pipeline panel." + $result = $false + } + else { + $resultMarkdown = "Users are able to select stages to skip from the Queue Pipeline panel." + $result = $true + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationStageChooser + +#region Test-AzdoOrganizationStorageUsage + +function Test-AzdoOrganizationStorageUsage { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $StorageUsage = Get-ADOPSOrganizationCommerceMeterUsage -MeterId '3efc2e47-d73e-4213-8368-3a8723ceb1cc' + $availableQuantity = $StorageUsage.availableQuantity + + if ($availableQuantity -lt [double]::Parse('0,1')) { + $resultMarkdown = "Your storage is exceeding the usage limit or close to. '$availableQuantity' GB available." + $result = $false + } + else { + $resultMarkdown = + @' + Well done. You are not exceeding or approaching your storage usage limit. + Current usage: {0} GB + Max quantity: {1} GB +'@ -f $StorageUsage.currentQuantity, $StorageUsage.maxQuantity + $result = $true + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationStorageUsage + +#region Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks + +function Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).disableMarketplaceTasksVar + + if ($result) { + $resultMarkdown = "It is allowed to install and run tasks from the Marketplace." + } + else { + $resultMarkdown = "Well done. The ability to install and run tasks from the Marketplace has been restricted." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks + +#region Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks + +function Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).disableNode6TasksVar + + if ($result) { + $resultMarkdown = "Well done. Pipelines will fail if they utilize a task with a Node 6 execution handler." + } + else { + $resultMarkdown = "Pipeliens may utilize a task with Node 6 execution handler." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks + +#region Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation + +function Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enableShellTasksArgsSanitizing + + if ($result) { + $resultMarkdown = "Well done. Argument parameters for built-in shell tasks are validated to check for inputs that can inject commands into scripts." + } + else { + $resultMarkdown = "Argument parameters for built-in shell tasks may inject commands into scripts." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation + +#region Test-AzdoOrganizationTriggerPullRequestGitHubRepositories + +function Test-AzdoOrganizationTriggerPullRequestGitHubRepositories { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $settings = Get-ADOPSOrganizationPipelineSettings + $result = $settings.forkProtectionEnabled + + if ($result) { + if ($settings.requireCommentsForNonTeamMemberAndNonContributors) { + $AdditionalInfo = 'Only on pull requests from non-team members and contributors' + } + elseif ($settings.requireCommentsForNonTeamMembersOnly) { + $AdditionalInfo = 'Only on pull requests from non-team members' + } + else { + $AdditionalInfo = 'On all pull requests' + } + + $data = @' + Prevent pipelines from making secrets available to fork builds is set to '{0}'\ + Prevent pipelines from making fork builds have the same permissions as regular builds is set to '{1}'\ + Require a team member's comment before building a pull request is set to '{2}' ({3}) +'@ -f $settings.enforceNoAccessToSecretsFromForks, $settings.enforceJobAuthScopeForForks, $settings.isCommentRequiredForPullRequest, $AdditionalInfo + + $resultMarkdown = "Well done. You have configured building pull requests from forked GitHub repositories according to your requirements. $data" + } + else { + $resultMarkdown = "No limits building pull requests from forked GitHub repositories have been configured." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoOrganizationTriggerPullRequestGitHubRepositories + +#region Test-AzdoProjectCollectionAdministrators + +function Test-AzdoProjectCollectionAdministrators { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + function Get-NestedAdoMembership { + param ( + [Parameter()] + $Member + ) + + if ($Member.subjectKind -eq 'group') { + Write-Verbose "Finding members in group '$($Member.DisplayName)' - Descriptor '$($_.Descriptor)'" + Get-ADOPSMembership -Descriptor $Member.descriptor -Direction 'down' | Foreach-object { + Write-Verbose "Processing member '$($_.DisplayName)' - Descriptor '$($_.Descriptor)'" + Get-NestedAdoMembership -Member $_ + } + } + else { + Write-output $Member + } + } + + $PCA = Get-ADOPSGroup | Where-object -Property displayname -eq 'Project Collection Administrators' + $PCAMembers = Get-ADOPSMembership -Descriptor $PCA.descriptor -Direction 'down' + + # UniqueUserList + $UniqueUsersWithPCA = New-Object System.Collections.Arraylist + + # Users with PCA + $UserPCA = $PCAMembers | Where-Object { $_.subjectKind -ne 'group' } + $UserPCA | Foreach-object { + $UniqueUsersWithPCA.Add($_) | Out-Null + } + + # Groups with PCA + $GroupPCA = $PCAMembers | Where-Object { $_.subjectKind -eq 'group' } + + $GroupPCA | Foreach-object { + Get-NestedAdoMembership -Member $_ | Foreach-object { + if ($_.descriptor -notin $UniqueUsersWithPCA.descriptor) { + $UniqueUsersWithPCA.Add($_) | Out-Null + } + else { + Write-Verbose "$($_.subjectKind) - $($_.displayname) - $($_.descriptor) - has already been added." + } + } + + } + + if ($UniqueUsersWithPCA.Count -ge 4) { + $result = $false + $resultMarkdown = "Restrict direct user access (Current actively assigned users/service accounts; '$($UniqueUsersWithPCA.Count)') to Project Collection Administrators role. The role holds the highest authority within an organization or project collection. Members can Perform all operations for the entire collection, Manage settings, policies, and processes for the organization, create and manage all projects and extensions." + } + else { + $result = $true + $resultMarkdown = "Well done. Less than 4 users/service accounts are directly assigned to the Project Collection Administrators role." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoProjectCollectionAdministrators + +#region Test-AzdoPublicProjects + +function Test-AzdoPublicProjects { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.AllowAnonymousAccess' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Your Azure DevOps tenant allows the creation and use of public projects" + } + else { + $resultMarkdown = "Well done. Your tenant has disabled the use of public projects" + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoPublicProjects + +#region Test-AzdoResourceUsageProjects + +function Test-AzdoResourceUsageProjects { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $Projects = (Get-ADOPSResourceUsage).Projects + + $CurrentUsage = $($Projects.count / $Projects.limit).Tostring("P") + + if ($($Projects.count / $Projects.limit) -gt 0.9) { + $resultMarkdown = "Project Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" + $result = $false + } + else { + $resultMarkdown = "Well done. Project Resource Usage limit is at $CurrentUsage" + $result = $true + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoResourceUsageProjects + +#region Test-AzdoResourceUsageWorkItemTags + +function Test-AzdoResourceUsageWorkItemTags { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $WorkItemTags = (Get-ADOPSResourceUsage).'Work Item Tags' + + $CurrentUsage = $($WorkItemTags.count / $WorkItemTags.limit).Tostring("P") + + if ($($WorkItemTags.count / $WorkItemTags.limit) -gt 0.9) { + $resultMarkdown = "Work Item Tags Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" + $result = $false + } + else { + $resultMarkdown = "Well done. Work Item Tags Resource Usage limit is at $CurrentUsage" + $result = $true + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoResourceUsageWorkItemTags + +#region Test-AzdoSSHAuthentication + +function Test-AzdoSSHAuthentication { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $ApplicationPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'ApplicationConnection' + $Policy = $ApplicationPolicies.policy | where-object -property name -eq 'Policy.DisallowSecureShell' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Your tenant allows developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" + } + else { + $resultMarkdown = "Well done. Your tenant do not allow developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoSSHAuthentication + +#region Test-AzdoThirdPartyAccessViaOauth + +function Test-AzdoThirdPartyAccessViaOauth { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $ApplicationPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'ApplicationConnection' + $Policy = $ApplicationPolicies.policy | where-object -property name -eq 'Policy.DisallowOAuthAuthentication' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Your tenant have not restricted Azure DevOps OAuth apps to access resources in your organization through OAuth." + } + else { + $resultMarkdown = "Well done. Your tenant has restricted Azure DevOps OAuth apps to access resources in your organization through OAuth." + } + + $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" + + return $result +} +#endregion Test-AzdoThirdPartyAccessViaOauth + diff --git a/tests/Azdo/README.md b/tests/Azdo/README.md new file mode 100644 index 000000000..11d969bb7 --- /dev/null +++ b/tests/Azdo/README.md @@ -0,0 +1,12 @@ +# Custom Tests + +Welcome to the Custom Tests directory! This is where you can create and manage your own Pester tests tailored to your specific needs. If you have tests you'd like to add or modify, this is the place to do it. + +### Getting Started + +- **Naming Convention**: Make sure your test files end with `.Tests.ps1` for easy identification. +- **Customizing Tests**: If you need to customize the default tests provided elsewhere, simply copy them from the `tests` directory and modify them to suit your requirements. +- **Running Custom Tests**: To execute tests located in this `Custom` directory exclusively, use the `-Path` parameter with `Invoke-Maester`. For example: + + ```powershell + Invoke-Maester -Path ./Custom diff --git a/tests/Azdo/Test-Azdo.Tests.ps1 b/tests/Azdo/Test-Azdo.Tests.ps1 new file mode 100644 index 000000000..be444ea53 --- /dev/null +++ b/tests/Azdo/Test-Azdo.Tests.ps1 @@ -0,0 +1,163 @@ +BeforeAll { + Import-module $PSScriptRoot\AdopsMaesterTests.psd1 -force +} + +Describe "Azure DevOps" -Tag "Azure DevOps Security" { + It "AZDO.1000: Azure DevOps OAuth apps can access resources in your organization through OAuth. See https://aka.ms/vstspolicyoauth" -Tag "AZDO.1000" { + + Test-AzdoThirdPartyAccessViaOauth | Should -Be $false -Because "Your tenant should restrict Azure DevOps OAuth apps to access resources in your organization through OAuth." + } + + It "AZDO.1001: Identities can connect to your organization's Git repos through SSH. See https://aka.ms/vstspolicyssh" -Tag "AZDO.1001" { + + Test-AzdoSSHAuthentication | Should -Be $false -Because "Authentication towards your tenant should only be by Entra, do not allow developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" + } + + It "AZDO.1002: Log Audit Events. See https://aka.ms/log-audit-events" -Tag "AZDO.1002" { + + Test-AzdoLogAuditEvents | Should -Be $true -Because "Auditing should be enabled for Azure DevOps" + } + + It "AZDO.1003: Restricting public projects. See https://aka.ms/vsts-anon-access" -Tag "AZDO.1003" { + + Test-AzdoPublicProjects | Should -Be $false -Because "Public projects should be disabled for Azure DevOps" + } + + It "AZDO.1004: Additional protections when using public package registries. See https://aka.ms/upstreamBehaviorBlog" -Tag "AZDO.1004" { + + Test-AzdoArtifactsExternalPackageProtectionToken | Should -Be $true -Because "Limiting access to externally sourced packages when internally sources packages are already present in Azure DevOps" + } + + It "AZDO.1005: IP Conditional Access policy validation. See https://aka.ms/visual-studio-conditional-access-policy" -Tag "AZDO.1005" { + + Test-AzdoEnforceAADConditionalAccess | Should -Be $true -Because "Microsoft Entra ID should always perform validation for any Conditional Access Policies (CAPs) set by tenant administrators." + } + + It "AZDO.1006: External Users access. See https://aka.ms/vstspolicyguest" -Tag "AZDO.1006" { + + Test-AzdoExternalGuestAccess | Should -Be $false -Because "External users should not be allowed access to your Azure DevOps organization" + } + + It "AZDO.1007: Team and project administrator are allowed to invite new users. See https://aka.ms/azure-devops-invitations-policy" -Tag "AZDO.1007" { + + Test-AzdoAllowTeamAdminsInvitationsAccessToken | Should -Be $false -Because "Enrolling to your Azure DevOps organization should be a controlled process." + } + + It "AZDO.1008: Request access to Azure DevOps by e-mail notifications to administrators. See https://go.microsoft.com/fwlink/?linkid=2113172" -Tag "AZDO.1008" { + + Test-AzdoAllowRequestAccessToken | Should -Be $false -Because "You should prevent users from requesting access to your organization or projects" + } + + It "AZDO.1009: Feedback Collection. See https://aka.ms/ADOPrivacyPolicy" -Tag "AZDO.1009" { + + Test-AzdoFeedbackCollection | Should -Be $true -Because "You should have confidence that we're handling your data appropriately and for legitimate uses. Part of that assurance involves carefully restricting usage." + } + + It "AZDO.1010: Audit streaming. See https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops" -Tag "AZDO.1010" { + + Test-AzdoAuditStreams | Should -Be $true -Because "Setting up a stream also allows you to store more than 90-days worth of auditing data." + } + + It "AZDO.1011: Project Resource Limits. See https://learn.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops" -Tag "AZDO.1011" { + + Test-AzdoResourceUsageProjects | Should -Be $true -Because "Azure DevOps supports up to 1,000 projects within an organization." + } + + It "AZDO.1012: Work Items Tags Limits. See https://learn.microsoft.com/en-us/azure/devops/organizations/settings/work/object-limits?view=azure-devops" -Tag "AZDO.1012" { + + Test-AzdoResourceUsageWorkItemTags | Should -Be $true -Because "Azure DevOps supports up to 150,000 tag definitions per organization or collection." + } + + It "AZDO.1013: Organization Owner should not be an individual. See https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/change-organization-ownership?view=azure-devops" -Tag "AZDO.1013" { + + Test-AzdoOrganizationOwner | Should -Be $true -Because "Organization owners are automatically members of the 'Project Collection Administrators' group. As roles and responsibilities change, you can change the owner for your organization." + } + + It "AZDO.1014: Anonymous access to pipeline badges. See https://learn.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops&tabs=net%2Cbrowser#add-a-status-badge-to-your-repository" -Tag "AZDO.1014" { + + Test-AzdoOrganizationBadgesArePrivate | Should -Be $true -Because "Even in a private project, anonymous badge access is enabled by default. With anonymous badge access enabled, users outside your organization might be able to query information such as project names, branch names, job names, and build status through the badge status API." + } + + It "AZDO.1015: Limit variables that can be set at queue time. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#limit-variables-that-can-be-set-at-queue-time" -Tag "AZDO.1015" { + + Test-AzdoOrganizationLimitVariablesAtQueueTime | Should -Be $true -Because "Only those variables explicitly marked as 'Settable at queue time' can be set. In other words, you can set any variables at queue time unless this setting is turned on." + } + + It "AZDO.1016: Limit job authorization scope to current project for non-release pipelines. See https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope" -Tag "AZDO.1016" { + + Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines | Should -Be $true -Because "With this option enabled, you can reduce the scope of access for all classic release pipelines to the current project." + } + + It "AZDO.1017: Limit job authorization scope to current project for classic release pipelines. See https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope" -Tag "AZDO.1017" { + + Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines | Should -Be $true -Because "With this option enabled, you can reduce the scope of access for all non-release pipelines to the current project." + } + + It "AZDO.1018: Protect access to repositories in YAML pipelines. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#restrict-project-repository-and-service-connection-access" -Tag "AZDO.1018" { + + Test-AzdoOrganizationProtectAccessToRepositories | Should -Be $true -Because "Apply checks and approvals when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." + } + + It "AZDO.1019: Stage chooser. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops" -Tag "AZDO.1019" { + + Test-AzdoOrganizationStageChooser | Should -Be $false -Because "Users should not be able to select stages to skip from the Queue Pipeline panel" + } + + It "AZDO.1020: Creation of classic build pipelines. See https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines/" -Tag "AZDO.1020" { + + Test-AzdoOrganizationCreationClassicBuildPipelines | Should -Be $false -Because "Creating classic build pipelines should not be allowed." + } + + It "AZDO.1021: Creation of classic release pipelines. See https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines/" -Tag "AZDO.1021" { + + Test-AzdoOrganizationCreationClassicReleasePipelines | Should -Be $false -Because "Creating classic release pipelines should not be allowed." + } + + It "AZDO.1022: Limit building pull requests from forked GitHub repositories. See https://learn.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#validate-contributions-from-forks" -Tag "AZDO.1022" { + + Test-AzdoOrganizationTriggerPullRequestGitHubRepositories | Should -Be $true -Because "Azure Pipelines can automatically build and validate every pull request and commit to your GitHub repository. This should be configured according to your organizations requirements." + } + + It "AZDO.1023: Disable Marketplace tasks. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#prevent-malicious-code-execution" -Tag "AZDO.1023" { + + Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks | Should -Be $false -Because "Disable the ability to install and run tasks from the Marketplace, which gives you greater control over the code that executes in a pipeline." + } + + It "AZDO.1024: Disable Node 6 tasks. See https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2022/no-node-6-on-hosted-agents" -Tag "AZDO.1024" { + + Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks | Should -Be $true -Because "With this enabled, pipelines will fail if they utilize a task with a Node 6 execution handler." + } + + It "AZDO.1025: Enable shell tasks arguments validation. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#shellTasksValidation" -Tag "AZDO.1025" { + + Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation | Should -Be $true -Because "When this is enabled, argument parameters for built-in shell tasks are validated to check for inputs that can inject commands into scripts." + } + + It "AZDO.1026: Enable automatic enrollment to Advanced Security for Azure DevOps. See https://learn.microsoft.com/en-us/azure/devops/repos/security/configure-github-advanced-security-features?view=azure-devops&tabs=yaml#organization-level-onboarding" -Tag "AZDO.1026" { + + Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects | Should -Be $true -Because "Enable automatic enrollment for new git repositories to use GitHub Advanced Security for Azure DevOps. It adds GitHub Advanced Security's suite of security features to Azure Repos." + } + + It "AZDO.1027: Disable showing Gravatar images for users outside of your enterprise. See https://learn.microsoft.com/en-us/azure/devops/repos/git/repository-settings?view=azure-devops&tabs=browser#gravatar-images" -Tag "AZDO.1027" { + + Test-AzdoOrganizationRepositorySettingsGravatarImages | Should -Be $false -Because "Gravatar images should not be exposed outside of your enterprise." + } + + It "AZDO.1028: Disable creation of TFVC repositories. See https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2024/no-tfvc-in-new-projects" -Tag "AZDO.1028" { + + Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos | Should -Be $true -Because "Team Foundation Version Control (TFVC) has been deprecated." + } + + It "AZDO.1029: Storage Usage Limit. See https://learn.microsoft.com/en-us/azure/devops/artifacts/reference/limits?view=azure-devops" -Tag "AZDO.1029" { + + Test-AzdoOrganizationStorageUsage | Should -Be $true -Because "Storage Usage Limit should not be reached." + } + + It "AZDO.1030: Project Collection Administrators. See https://learn.microsoft.com/en-us/azure/devops/organizations/security/about-permissions?view=azure-devops&tabs=preview-page#permissions" -Tag "AZDO.1030" { + + Test-AzdoProjectCollectionAdministrators | Should -Be $true -Because "Users should not be directly assigned to 'Project Collection Administrator' as it is the most privileged role within Azure DevOps." + } + +} + + diff --git a/tests/Azdo/Test-AzdoAllowRequestAccessToken.md b/tests/Azdo/Test-AzdoAllowRequestAccessToken.md new file mode 100644 index 000000000..0ea27e02f --- /dev/null +++ b/tests/Azdo/Test-AzdoAllowRequestAccessToken.md @@ -0,0 +1,18 @@ +Request access to Azure DevOps by e-mail notifications to administrators SHOULD BE disabled. + +Rationale: Access control to Azure DevOps is to be a controlled process where access is granted and tracked. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies, locate the Request Access policy and toggle it to off. +4. Provide the URL to your internal process for gaining access. Users see this URL in the error report when they try to access the organization or a project within the organization that they don't have permission to access. + +**Results:** +Users already part of the organization: If they lack permission to access a specific project, they get a 404 error. To maintain confidentiality, the 404 error doesn’t reveal whether the project exists and so doesn't provide a link to request access. +Users not part of the organization: If they attempt to access a resource, they get a 401 error, which includes a link to the configured custom URL for requesting access. + +#### Related links + +* [Azure DevOps Security - Disable your organization's Request Access policy](https://go.microsoft.com/fwlink/?linkid=2113172) diff --git a/tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md b/tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md new file mode 100644 index 000000000..37ec1b6f4 --- /dev/null +++ b/tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md @@ -0,0 +1,19 @@ +Access to Azure DevOps SHOULD BE a controlled process provided by the IAM team. + +Rationale: By default, all administrators can invite new users to their Azure DevOps organization. +Disabling this policy prevents Team and Project Administrators from inviting new users. +However, Project Collection Administrators (PCAs) can still add new users to the organization regardless of the policy status. +Additionally, if a user is already a member of the organization, Project and Team Administrators can add that user to specific projects. + +#### Remediation action: +Disable the policy to stops these invitations. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies, locate the Allow team and project administrators to invite new users policy and toggle it to off. +4. Now, only Project Collection Administrators can invite new users to Azure DevOps. + +> Project and Team Administrators can directly add users to their projects through the permissions blade. However, if they attempt to add users through the Add Users button located in the Organization settings > Users section, it's not visible to them. Adding a user directly through Project settings > Permissions doesn't result in the user appearing automatically in the Organization settings > Users list. For the user to be reflected in the Users list, they must sign in to the system. + +#### Related links + +* [Azure DevOps Security - Restrict administrators from inviting new users](https://aka.ms/azure-devops-invitations-policy) diff --git a/tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md b/tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md new file mode 100644 index 000000000..d2a0a3281 --- /dev/null +++ b/tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md @@ -0,0 +1,22 @@ +Externally sourced package versions SHOULD BE manually approved for internal use, to prevent malicious packages from a public registry being inadvertently consumed. + +Rationale: Previously, Azure Artifacts feeds presented package versions from all of its upstream sources. This includes package versions that were originally pushed to an Azure Artifacts feed (internally sourced) and package versions from common public repositories like npmjs.com , NuGet.org , Maven Central, and PyPI (externally sourced). + +Configure a policy for additional security for your private feeds by limiting access to externally sourced packages when internally sources packages are already present. This provides a new layer of security, which prevents malicious packages from a public registry being inadvertently consumed. These changes will not affect any package versions that are already in use or cached in your feed. + +#### Remediation action: +Enable the policy to opt-in for additional protective behavior. +1. Sign in to your organization +2. Choose Organization settings. +3. Click on policies under the security section +4. In the security policies section, toggle on ‘Additional protections when using public package registries’ + +**Results:** +The security behavior applies: +when an internally sourced version is already in your feed, or +when consuming a package from your feed for the first time (i.e. it is not yet in your feed), and at least one of the versions available from an upstream is internally sourced. +With the new behavior, any versions from the public registry will be blocked and not made available to download. You are able to configure the upstream behavior to allow externally sourced package versions if you choose to. + +#### Related links + +* [Microsoft Devblogs - Changes to Azure Artifacts Upstream Behavior](https://devblogs.microsoft.com/devops/changes-to-azure-artifact-upstream-behavior/) diff --git a/tests/Azdo/Test-AzdoAuditStreams.md b/tests/Azdo/Test-AzdoAuditStreams.md new file mode 100644 index 000000000..5443440c1 --- /dev/null +++ b/tests/Azdo/Test-AzdoAuditStreams.md @@ -0,0 +1,24 @@ +Audit logs SHOULD BE retained according to your organizations needs and protected from purging. + +Rationale: Send auditing data to other Security Incident and Event Management (SIEM) tools and open new possibilities, such as the ability to trigger alerts for specific events, create views on auditing data, and perform anomaly detection. Setting up a stream also allows you to store more than 90-days worth of auditing data, which is the maximum amount of data that Azure DevOps keeps for your organizations. + +#### Remediation action: +Create an audit stream, which sends data to other locations for further processing. + + +1. Sign in to your organization +2. Choose Organization settings. +3. Select Auditing. +> If you don't see Auditing in Organization Settings, then auditing is not currently enabled for your organization. Someone in the organization owner or Project Collection Administrators (PCAs) group must enable Auditing in Organization Policies. You will then be able to see events on the Auditing page if you have the appropriate permissions. +1. Go to the Streams tab, and then select New stream. +2. Select the stream target that you want to configure, and then select from the following instructions to set up your stream target type. + 1. Splunk + 2. Event Grid + 3. Azure Monitor Log + +**Results:** +Audit streams represent a pipeline that flows audit events from your Azure DevOps organization to a stream target. Every half hour or less, new audit events are bundled and streamed to your targets. + +#### Related links + +* [Azure DevOps Security - Create audit streaming](https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops) \ No newline at end of file diff --git a/tests/Azdo/Test-AzdoEnforceAADConditionalAccess.md b/tests/Azdo/Test-AzdoEnforceAADConditionalAccess.md new file mode 100644 index 000000000..733377b63 --- /dev/null +++ b/tests/Azdo/Test-AzdoEnforceAADConditionalAccess.md @@ -0,0 +1,20 @@ +Conditional Access Policies SHOULD BE configured for Microsoft Entra ID-backed organizations. + +Rationale: When you sign in to the web portal of a Microsoft Entra ID-backed organization, Microsoft Entra ID always performs validation for any Conditional Access Policies (CAPs) set by tenant administrators. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies, and then toggle your policy to on or off as needed. + 1. If the “Enable IP Conditional Access policy Validation” organization policy is enabled, we check IP fencing policies on both web and non-interactive flows, such as non-Microsoft client flows like using a PAT with git operations. + 2. Sign-in policies might be enforced for PATs as well. Using PATs to make Microsoft Entra ID calls requires adherence to any sign-in policies that are set. For example, if a sign-in policy requires that a user sign in every seven days, you must also sign in every seven days to continue using PATs for Microsoft Entra ID requests. +> We support MFA policies on web flows only. For non-interactive flows, if they don't satisfy the conditional access policy, the user isn't prompted for MFA and gets blocked instead. +> We support IP-fencing conditional access policies (CAPs) for both IPv4 and IPv6 addresses. If your IPv6 address is being blocked, ensure that the tenant administrator configured CAPs to allow your IPv6 address. Additionally, consider including the IPv4-mapped address for any default IPv6 address in all CAP conditions. + +**Results:** +When you sign in to the web portal of a Microsoft Entra ID-backed organization, Microsoft Entra ID always performs validation for any Conditional Access Policies (CAPs) set by tenant administrators. + +#### Related links + +* [Azure DevOps Security - Conditional Access Policies support on Azure DevOps](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/change-application-access-policies?view=azure-devops#cap-support-on-azure-devops) diff --git a/tests/Azdo/Test-AzdoExternalGuestAccess.md b/tests/Azdo/Test-AzdoExternalGuestAccess.md new file mode 100644 index 000000000..0e2a47fc8 --- /dev/null +++ b/tests/Azdo/Test-AzdoExternalGuestAccess.md @@ -0,0 +1,13 @@ +External guest access to Azure DevOps SHOULD BE a controlled process. + +Rationale: External guest access can introduce potential security risks if not managed properly. + +#### Remediation action: +Disable the "External guest access" policy to prevent external guest access if there's no business need for it. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies, locate the External guest access policy and toggle it to off. + +#### Related links + +* [Azure DevOps Security - Manage external guest access](https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-overview?view=azure-devops#manage-external-guest-access) \ No newline at end of file diff --git a/tests/Azdo/Test-AzdoFeedbackCollection.md b/tests/Azdo/Test-AzdoFeedbackCollection.md new file mode 100644 index 000000000..c194b0d1d --- /dev/null +++ b/tests/Azdo/Test-AzdoFeedbackCollection.md @@ -0,0 +1,14 @@ +Providing/collecting customer feedback to the product team for Azure DevOps SHOULD BE enabled. + +Rationale: You should have confidence that Microsoft is handling your data appropriately and for legitimate uses. Part of that assurance involves carefully restricting usage. + +#### Remediation action: +Enable the policy to allow Microsoft to collect feedback. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies, locate the "Allow Microsoft to collect feedback from users" policy and toggle it to on. + +#### Related links + +* [Azure DevOps Privacy Policy](https://aka.ms/ADOPrivacyPolicy) +* [Manage Privacy policies for admins to control user feedback collection](https://learn.microsoft.com/en-us/azure/devops/organizations/security/data-protection?view=azure-devops#managing-privacy-policies-for-admins-to-control-user-feedback-collection) diff --git a/tests/Azdo/Test-AzdoLogAuditEvents.md b/tests/Azdo/Test-AzdoLogAuditEvents.md new file mode 100644 index 000000000..23936caf1 --- /dev/null +++ b/tests/Azdo/Test-AzdoLogAuditEvents.md @@ -0,0 +1,19 @@ +Auditing SHOULD BE enabled. + +Rationale: Keeping track of activities within your Azure DevOps environment is crucial for security and compliance. Auditing helps you monitor and log these activities, providing transparency and accountability. + +#### Remediation action: +Enable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies under the Security header. +4. Switch the Log Audit Events button to ON. + +**Results:** +Auditing is enabled for the organization. Refresh the page to see Auditing appear in the sidebar. Audit events start appearing in Auditing Logs and through any configured audit streams. + + +#### Related links + +* [Learn - Enable and disable auditing](https://learn.microsoft.com/en-us/azure/devops/organizations/audit/azure-devops-auditing?view=azure-devops&tabs=preview-page#enable-and-disable-auditing) +* [Learn - Review audit log](https://learn.microsoft.com/en-us/azure/devops/organizations/audit/azure-devops-auditing?view=azure-devops&tabs=preview-page#review-audit-log) diff --git a/tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md b/tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md new file mode 100644 index 000000000..70c86d2b0 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md @@ -0,0 +1,19 @@ +GitHub advanced Security for Azure DevOps SHOULD BE automatically enabled for new projects. + +Rationale: Newly created projects should have Advanced Security enabled upon creation. + +#### Remediation action: +Organization-level onboarding +1. Sign in to your organization +2. Go to your Organization settings for your Azure DevOps organization. +3. Select Repositories. +4. Select Enable all and see an estimate for the number of active committers for your organization appear. +5. Select Begin billing to activate Advanced Security for every existing repository in each project in your organization. +6. Select Automatically enable Advanced Security for new repositories so that any newly created projects have Advanced Security enabled upon creation. + +**Results:** +Newly created projects have Advanced Security enabled upon creation. + +#### Related links + +* [Learn - GitHub Advanced Security for Azure DevOps](https://learn.microsoft.com/en-us/azure/devops/repos/security/configure-github-advanced-security-features?view=azure-devops&tabs=yaml#organization-level-onboarding) diff --git a/tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.md b/tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.md new file mode 100644 index 000000000..5014067ef --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.md @@ -0,0 +1,17 @@ +Status badges in Azure DevOps SHOULD BE disabled. + +Rationale: Even in a private project, anonymous badge access is enabled by default. With anonymous badge access enabled, users outside your organization might be able to query information such as project names, branch names, job names, and build status through the badge status API. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Settings under Pipelines. +4. Enable the policy "Disable anonymous access to badges" + +**Results:** +Users outside of your organization cannot query information regarding your project names, branch names, job names and build statuses through the badge status API. + +#### Related links + +* [Learn - Status badges](https://learn.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops&tabs=net%2Cbrowser#add-a-status-badge-to-your-repository) diff --git a/tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md b/tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md new file mode 100644 index 000000000..fba250f3c --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md @@ -0,0 +1,23 @@ +Creating classic build pipelines SHOULD BE disabled. + +Rationale: YAML pipelines offer the best security for your Azure Pipelines. In contrast to classic build and release pipelines. + +#### Remediation action: +Enable the policy to disable creation of classic build pipelines. +1. Sign in to your organization +2. Choose Organization settings. +3. Under the Pipelines section choose Settings. +4. In the General section, toggle on Disable creation of classic build pipelines. + +**Results:** +*How the feature works* +If you turned on the toggle to Disable creation of classic build and classic release pipelines, then no classic build pipeline, classic release pipeline, task groups, and deployment groups can be created. + +The user interface will not show the Releases, Task groups, and Deployment groups left-side menu items if you have none of them. + +*Existing classic pipelines* +If you have classic build pipelines, classic release pipelines, task groups, or deployment groups, you’ll still be able to edit and run them. The Pipelines left-side menu will continue to show the corresponding menu items. However, the buttons to create new ones will be disabled. + +#### Related links + +* [Devblog - Disable creation of classic pipelines](https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines/) diff --git a/tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md b/tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md new file mode 100644 index 000000000..6ccb91f67 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md @@ -0,0 +1,23 @@ +Creating classic release pipelines SHOULD BE disabled. + +Rationale: YAML pipelines offer the best security for your Azure Pipelines. In contrast to classic build and release pipelines. + +#### Remediation action: +Enable the policy to disable creation of classic release pipelines. +1. Sign in to your organization +2. Choose Organization settings. +3. Under the Pipelines section choose Settings. +4. In the General section, toggle on Disable creation of classic release pipelines. + +**Results:** +*How the feature works* +If you turned on the toggle to Disable creation of classic build and classic release pipelines, then no classic build pipeline, classic release pipeline, task groups, and deployment groups can be created. + +The user interface will not show the Releases, Task groups, and Deployment groups left-side menu items if you have none of them. + +*Existing classic pipelines* +If you have classic build pipelines, classic release pipelines, task groups, or deployment groups, you’ll still be able to edit and run them. The Pipelines left-side menu will continue to show the corresponding menu items. However, the buttons to create new ones will be disabled. + +#### Related links + +* [Devblog - Disable creation of classic pipelines](https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines/) diff --git a/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md b/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md new file mode 100644 index 000000000..0a7ba924f --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md @@ -0,0 +1,14 @@ +YAML & build pipelines SHOULD have restricted access to only those repositories that are in the same project as the pipeline. + +Rationale: If the scope is not restricted at either the organization level or project level, then every job in your YAML pipeline gets a collection scoped job access token. In other words, your pipeline has access to any repository in any project of your organization. If an adversary is able to gain access to a single pipeline in a single project, they will be able to gain access to any repository in your organization. This is why, it is recommended that you restrict the scope at the highest level (organization settings) in order to contain the attack to a single project. + +#### Remediation action: +Enable the policy to restrict the job authorization scope. +1. Sign in to your organization +2. Choose Organization settings. +3. Under the Pipelines section choose Settings. +4. In the General section, toggle on "Limit job authorization scope to current project for non-release pipelines". + +#### Related links + +* [Learn - Job Authorization Scope](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope) diff --git a/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md b/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md new file mode 100644 index 000000000..d91e16f3c --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md @@ -0,0 +1,14 @@ +Release pipelines SHOULD have restricted access to only those repositories that are in the same project as the pipeline. + +Rationale: If the scope is not restricted at either the organization level or project level, then every job in your YAML pipeline gets a collection scoped job access token. In other words, your pipeline has access to any repository in any project of your organization. If an adversary is able to gain access to a single pipeline in a single project, they will be able to gain access to any repository in your organization. This is why, it is recommended that you restrict the scope at the highest level (organization settings) in order to contain the attack to a single project. + +#### Remediation action: +Enable the policy to restrict the job authorization scope. +1. Sign in to your organization +2. Choose Organization settings. +3. Under the Pipelines section choose Settings. +4. In the General section, toggle on "Limit job authorization scope to current project for non-release pipelines". + +#### Related links + +* [Learn - Job Authorization Scope](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope) diff --git a/tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md b/tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md new file mode 100644 index 000000000..e74bc3616 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md @@ -0,0 +1,17 @@ +User defined variables SHOULD NOT be able to override system variables or variables not defined by the pipeline author. + +Rationale: Only those variables explicitly marked as "Settable at queue time" can be set by user. + +#### Remediation action: +Enable the policy to limit variables that can be set at queue time. +1. Sign in to your organization +2. Choose Organization settings. +3. Under the Pipelines section choose Settings. +4. In the General section, toggle on "Limit variables that can be set at queue time". + +**Results:** +Only those variables explicitly marked as "Settable at queue time" can be set at queue time. + +#### Related links + +* [Learn - Secure use of variables in a pipeline](https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#limit-variables-that-can-be-set-at-queue-time) diff --git a/tests/Azdo/Test-AzdoOrganizationOwner.md b/tests/Azdo/Test-AzdoOrganizationOwner.md new file mode 100644 index 000000000..d36eb3092 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationOwner.md @@ -0,0 +1,16 @@ +Azure DevOps organization owner SHOULD not be provided to regular users. + +Rationale: Owner has full administrative control over the organization, including the ability to manage users, projects, and settings. They can also configure security policies, access levels, and billing information. Ensure that the owner is aware of their responsibilities and has the necessary permissions to effectively manage the organization. + +> This query will check if the owner has any of the following words as part of it's name, to conclude that it's not a regular user; (adm|admin|btg|svc|service) + +#### Remediation action: +Ensure that the owner is not a regular user. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Overview > Change owner. +4. Select an identity from the dropdown menu, or search for a identity by entering the identity's name, and then select Change. + +#### Related links + +* [Learn - Change the organization owner](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/change-organization-ownership?view=azure-devops) diff --git a/tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.md b/tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.md new file mode 100644 index 000000000..c337c87e5 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.md @@ -0,0 +1,17 @@ +Access to repositories in YAML pipelines SHOULD apply checks and approval before accessing repositories. + +Rationale: To enhance security, consider separating your projects, using branch policies, and adding more security measures for forks. Minimize the scope of service connections and use the most secure authentication methods. + +#### Remediation action: +Enable the policy to apply checks and approvals. +1. Sign in to your organization +2. Choose Organization settings. +3. Under the Pipelines section choose Settings. +4. In the General section, toggle on "Protect access to repositories in YAML pipelines". + +**Results:** +Apply checks and approvals when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline. + +#### Related links + +* [Learn - Restrict project, repository, and service connection access](https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#restrict-project-repository-and-service-connection-access) diff --git a/tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md b/tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md new file mode 100644 index 000000000..78d9793ff --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md @@ -0,0 +1,17 @@ +Creation of Team Foundation Version Control (TFVC) repositories SHOULD BE disabled. + +Rationale: Over the past several years, we added no new features to Team Foundation Version Control (TFVC). Git is the preferred version control system in Azure Repos. Furthermore, all the improvements we made in the past few years in terms of security, performance, and accessibility were only made to Git repositories. + +#### Remediation action: +Enable the policy to disable the creation of TFVC repositories. +1. Sign in to your organization +2. Choose Organization settings. +3. Under the Repos section choose Repositories. +4. In the All Repositories Settings section, toggle on "Disable creation of TFVC repositories". + +**Results:** +Disable creation of TFVC repositories. You can still see and work on TFVC repositories created before. + +#### Related links + +* [Learn - Removal of RFVC in new projects](https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2024/no-tfvc-in-new-projects) diff --git a/tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md b/tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md new file mode 100644 index 000000000..e0ea1828b --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md @@ -0,0 +1,12 @@ +Gravatar images SHOULD NOT be exposed for users outside your enterprise. + +Rationale: Gravatar images should not be exposed for external users. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. On your Azure DevOps organization page, select Organization settings at lower left, and then select Repositories in the left navigation. +2. On the All Repositories page, set Gravatar images to On or Off. + +#### Related links + +* [Learn - Gravatar images](https://learn.microsoft.com/en-us/azure/devops/repos/git/repository-settings?view=azure-devops&tabs=browser#gravatar-images) diff --git a/tests/Azdo/Test-AzdoOrganizationStageChooser.md b/tests/Azdo/Test-AzdoOrganizationStageChooser.md new file mode 100644 index 000000000..dbfe6c3b4 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationStageChooser.md @@ -0,0 +1,14 @@ +Users SHOULD NOT be able to skip stages defined by the pipeline author. + +Rationale: Users should not be able to select stages to skip from the Queue Pipeline panel. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Settings under Pieplines, locate the "Disable stage chooser" policy and toggle it to on. + +#### Related links + +* [Learn - Stages](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/stages?view=azure-devops&tabs=yaml) +* [Learn - Azure DevOps pipeline security](https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops) diff --git a/tests/Azdo/Test-AzdoOrganizationStorageUsage.md b/tests/Azdo/Test-AzdoOrganizationStorageUsage.md new file mode 100644 index 000000000..444346af8 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationStorageUsage.md @@ -0,0 +1,15 @@ +Azure Artifacts provides 2 GiB of free storage for each organization. Once your organization reaches the maximum storage limit, you won’t be able to publish new artifacts. To continue, you can either delete some of your existing artifacts or increase your storage limit. + +Rationale: Storage limit should not be met. + +#### Remediation action: +- [Configure retention policies](https://learn.microsoft.com/en-us/azure/devops/artifacts/how-to/delete-and-recover-packages?view=azure-devops&tabs=nuget#delete-packages-automatically-with-retention-policies) +- [Set up billing](https://learn.microsoft.com/en-us/azure/devops/organizations/billing/set-up-billing-for-your-organization-vs?view=azure-devops#set-up-billing) +- Increate Artifacts storage limit + - [Set up billing for your organization.](https://learn.microsoft.com/en-us/azure/devops/organizations/billing/set-up-billing-for-your-organization-vs?view=azure-devops#set-up-billing) + - Sign in to your Azure DevOps organization, select Organization settings > Billing, and select No limit, pay for what you use from the Usage limit dropdown. + - Select Save when you're done. + +#### Related links + +* [Learn - Package size and count limits](https://learn.microsoft.com/en-us/azure/devops/artifacts/reference/limits?view=azure-devops) diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md new file mode 100644 index 000000000..9774f1cdf --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md @@ -0,0 +1,17 @@ +Disable the ability to install and run tasks from the Marketplace, which gives you greater control over the code that executes in a pipeline. + +Rationale: Tasks from marketplace should be reviewed and approved. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Settings under Pipelines. +4. Go to the section "Task restrictions" and turn on "Disable marketplace tasks" + +**Results:** +With this enabled, pipelines will not use tasks installed from the Marketplace. Jobs which depend on Marketplace tasks will fail. + +#### Related links + +* [Learn - Prevent malicious code execution](https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#prevent-malicious-code-execution) diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md new file mode 100644 index 000000000..7960705db --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md @@ -0,0 +1,16 @@ +Disable Node 6 tasks. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Settings under Pipelines. +4. Go to the section "Task restrictions" and turn on "Disable Node 6 tasks" + +**Results:** +With this enabled, pipelines will fail if they utilize a task with a Node 6 execution handler. + +#### Related links + +* [Learn - Prevent malicious code execution](https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#prevent-malicious-code-execution) +* [Learn - Remove Node 6 and Node 10 runners from Microsoft-hosted agents](https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2022/no-node-6-on-hosted-agents) diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md new file mode 100644 index 000000000..b4aa4942c --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md @@ -0,0 +1,23 @@ +Enable Shell Task Validation to prevent code injection. + +Rationale: Code injection through arguments parameters should be prevented. + +#### Remediation action: +Enable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Settings under Pipelines. +4. Go to the section "Task restrictions" and turn on "Enable shell tasks arguments validation" + +**Results:** +This validation applies to the arguments parameter in the following specific tasks: +- PowerShell +- BatchScript +- Bash +- Ssh +- AzureFileCopy +- WindowsMachineFileCopy + +#### Related links + +* [Learn - Shell Tasks Validation](https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#shellTasksValidation) diff --git a/tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md b/tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md new file mode 100644 index 000000000..f4745c129 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md @@ -0,0 +1,14 @@ +Azure DevOps pipelines SHOULD NOT automatically build on every pull request and commit from a GitHub repository. + +Rationale: Code should not be automatically built from GitHub. + +#### Remediation action: +Enable the policy to stop building from GitHub repositories. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Settings under Pipelines. +4. Go to the section "Triggers" and turn on "Limit building pull requests from forked GitHub repositories" + +#### Related links + +* [Learn - Validate contributions from forks](https://learn.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#validate-contributions-from-forks) diff --git a/tests/Azdo/Test-AzdoProjectCollectionAdministrators.md b/tests/Azdo/Test-AzdoProjectCollectionAdministrators.md new file mode 100644 index 000000000..e7806b138 --- /dev/null +++ b/tests/Azdo/Test-AzdoProjectCollectionAdministrators.md @@ -0,0 +1,15 @@ +Project Collection Administrator is a highly privileged role, and membership should be restricted. + +Rationale: Description of Project Collection Administrators: +- Hold the highest authority within an organization or project collection. +- Perform all operations for the entire collection. +- Manage settings, policies, and processes for the organization. +- Create and manage all projects and extensions. + +#### Remediation action: +Restrict access to the Project Collection Administrators group. +Preferably use privileged access management to provide auditing and just-in-time access. + +#### Related links + +* [Learn - Azure DevOps Permissions](https://learn.microsoft.com/en-us/azure/devops/organizations/security/about-permissions?view=azure-devops&tabs=preview-page#permissions) diff --git a/tests/Azdo/Test-AzdoPublicProjects.md b/tests/Azdo/Test-AzdoPublicProjects.md new file mode 100644 index 000000000..3e2d5e662 --- /dev/null +++ b/tests/Azdo/Test-AzdoPublicProjects.md @@ -0,0 +1,18 @@ +Public projects SHOULD BE disabled. + +Rationale: When you choose public visibility, anyone on the internet can view your project. With private visibility, only users you give access to can view your project. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies under the Security section +4. Locate the "Allow public projects" policy and toggle it to off. + +**Results:** +External users are not able to access your project. + +#### Related links + +* [Learn - Public projects](https://aka.ms/vsts-anon-access) +* [Learn - Change project to public or private](https://learn.microsoft.com/en-us/azure/devops/organizations/projects/make-project-public?view=azure-devops) diff --git a/tests/Azdo/Test-AzdoResourceUsageProjects.md b/tests/Azdo/Test-AzdoResourceUsageProjects.md new file mode 100644 index 000000000..2874dbd37 --- /dev/null +++ b/tests/Azdo/Test-AzdoResourceUsageProjects.md @@ -0,0 +1,8 @@ +Azure DevOps supports up to 1,000 projects within an organization. + +#### Remediation action: +Re-use or delete projects that are stale. + +#### Related links + +* [Learn - About projects and scaling your organization](https://learn.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops) diff --git a/tests/Azdo/Test-AzdoResourceUsageWorkItemTags.md b/tests/Azdo/Test-AzdoResourceUsageWorkItemTags.md new file mode 100644 index 000000000..b89296bf1 --- /dev/null +++ b/tests/Azdo/Test-AzdoResourceUsageWorkItemTags.md @@ -0,0 +1,5 @@ +Azure DevOps supports up to 150,000 tag definitions per organization or collection. + +#### Related links + +* [Learn - Work tracking, process, and project limits](https://learn.microsoft.com/en-us/azure/devops/organizations/settings/work/object-limits?view=azure-devops) diff --git a/tests/Azdo/Test-AzdoSSHAuthentication.md b/tests/Azdo/Test-AzdoSSHAuthentication.md new file mode 100644 index 000000000..faad2de75 --- /dev/null +++ b/tests/Azdo/Test-AzdoSSHAuthentication.md @@ -0,0 +1,17 @@ +Connecting to Azure DevOps using SSH should be disabled. + +Rationale: Oauth is the prefered and most secure authentication method. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies under the Security section. +4. Locate the "SSH authentication" policy and toggle it to off. + +**Results:** +Users can no longer user SSH to connect to Azure DevOps. + +#### Related links + +* [Learn - Use SSH key authentication](https://aka.ms/vstspolicyssh) diff --git a/tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.md b/tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.md new file mode 100644 index 000000000..972e0b69e --- /dev/null +++ b/tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.md @@ -0,0 +1,18 @@ +Third-party application access via OAuth should be disabled. + +Rationale: Third-party application access should not be used for Azure DevOps. + +#### Remediation action: +Disable the policy to stops these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies, locate the Request Access policy and toggle it to off. +4. Provide the URL to your internal process for gaining access. Users see this URL in the error report when they try to access the organization or a project within the organization that they don't have permission to access. + +**Results:** +When you deny access to an authentication method, no application can access your organization through that method. Any application that previously had access encounter authentication errors and lose access. + +#### Related links + +* [Learn - Change application connection & security policies for your organization](https://aka.ms/vstspolicyoauth) +* [Learn - Use Azure DevOps OAuth 2.0 to create a web app](https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/azure-devops-oauth?view=azure-devops) From 90404c8970b482e8dbc4db3fd8f6504eba05b669 Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Date: Sun, 11 May 2025 23:31:23 +0200 Subject: [PATCH 02/22] Adding readme --- tests/Azdo/README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/Azdo/README.md b/tests/Azdo/README.md index 11d969bb7..914dd7290 100644 --- a/tests/Azdo/README.md +++ b/tests/Azdo/README.md @@ -1,12 +1,14 @@ -# Custom Tests +# Azure DevOps Tests - Preview -Welcome to the Custom Tests directory! This is where you can create and manage your own Pester tests tailored to your specific needs. If you have tests you'd like to add or modify, this is the place to do it. +With the new Azure DevOps tests we will perform a healthcheck towards your Azure DevOps organization. +The base of the tests will be from https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-overview?view=azure-devops and recommendations/considerations from professionals in the field. -### Getting Started +The tests uses the [Adops module](https://github.com/AZDOPS/AZDOPS) +However code review is still on-going to include the new functionality in tests/azdo/adops/ADOPS.psm1 module. +I have generated this module on my own until it's officially released and included in this PR. +The offical release depends on the pull requests; +- https://github.com/AZDOPS/AZDOPS/pull/245 +- https://github.com/AZDOPS/AZDOPS/pull/246 -- **Naming Convention**: Make sure your test files end with `.Tests.ps1` for easy identification. -- **Customizing Tests**: If you need to customize the default tests provided elsewhere, simply copy them from the `tests` directory and modify them to suit your requirements. -- **Running Custom Tests**: To execute tests located in this `Custom` directory exclusively, use the `-Path` parameter with `Invoke-Maester`. For example: - - ```powershell - Invoke-Maester -Path ./Custom +I have also built it a bit different, including readmes in markdown format under tests/azdo. +And instead of having each test by itself, it's in a module (Code to compile and test module/functions is not included in this branch, let me know if it should be bundled or how I can adapt to how other test suits are built) \ No newline at end of file From 8d9f3c26ad64965dc7c83e53ffe77c46cbf216fe Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Date: Sun, 11 May 2025 23:37:07 +0200 Subject: [PATCH 03/22] adding updated readme --- tests/Azdo/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Azdo/README.md b/tests/Azdo/README.md index 914dd7290..2ff0df235 100644 --- a/tests/Azdo/README.md +++ b/tests/Azdo/README.md @@ -1,14 +1,14 @@ # Azure DevOps Tests - Preview -With the new Azure DevOps tests we will perform a healthcheck towards your Azure DevOps organization. +Azure DevOps tests we will perform a healthcheck towards your Azure DevOps organization. The base of the tests will be from https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-overview?view=azure-devops and recommendations/considerations from professionals in the field. -The tests uses the [Adops module](https://github.com/AZDOPS/AZDOPS) -However code review is still on-going to include the new functionality in tests/azdo/adops/ADOPS.psm1 module. +The tests uses the [Adops module](https://github.com/AZDOPS/AZDOPS), +However code review is still on-going to include the new functionality in bundled tests/azdo/adops/ADOPS.psm1 module. I have generated this module on my own until it's officially released and included in this PR. The offical release depends on the pull requests; - https://github.com/AZDOPS/AZDOPS/pull/245 - https://github.com/AZDOPS/AZDOPS/pull/246 -I have also built it a bit different, including readmes in markdown format under tests/azdo. -And instead of having each test by itself, it's in a module (Code to compile and test module/functions is not included in this branch, let me know if it should be bundled or how I can adapt to how other test suits are built) \ No newline at end of file +I have included readmes in markdown format under tests/azdo. +And instead of having each test by itself, it's in a module (Code to compile and test module/functions is not included in this branch, let me know if it should be bundled or how I can adapt to how other test suits are built) imported in a BeforeAll block of the Test-Azdo.Tests.ps1 \ No newline at end of file From aa8b9361809947bfd0216b0c8e20dfe587f3aa18 Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Date: Tue, 9 Dec 2025 13:29:42 +0100 Subject: [PATCH 04/22] Adding Azdo tests --- tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 | 164 - tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 | 4439 ------ tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml | 11788 ---------------- .../Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml | 700 - tests/Azdo/AdopsMaesterTests.psd1 | 158 - tests/Azdo/AdopsMaesterTests.psm1 | 1014 -- tests/Azdo/Test-Azdo.Tests.ps1 | 34 +- .../Azdo/Test-AzdoAllowRequestAccessToken.ps1 | 26 + ...oAllowTeamAdminsInvitationsAccessToken.ps1 | 26 + ...rtifactsExternalPackageProtectionToken.ps1 | 26 + tests/Azdo/Test-AzdoAuditStreams.ps1 | 33 + .../Test-AzdoEnforceAADConditionalAccess.ps1 | 26 + tests/Azdo/Test-AzdoExternalGuestAccess.ps1 | 26 + tests/Azdo/Test-AzdoFeedbackCollection.ps1 | 26 + tests/Azdo/Test-AzdoLogAuditEvents.ps1 | 26 + .../Test-AzdoOrganizationArtifactFeed.ps1 | 27 + ...cEnrollmentAdvancedSecurityNewProjects.ps1 | 25 + .../Test-AzdoOrganizationBadgesArePrivate.ps1 | 25 + ...anizationCreationClassicBuildPipelines.ps1 | 27 + ...izationCreationClassicReleasePipelines.ps1 | 27 + ...bAuthorizationScopeNonReleasePipelines.ps1 | 25 + ...tJobAuthorizationScopeReleasePipelines.ps1 | 25 + ...oOrganizationLimitVariablesAtQueueTime.ps1 | 31 + tests/Azdo/Test-AzdoOrganizationOwner.ps1 | 36 + ...rganizationProtectAccessToRepositories.ps1 | 25 + ...sitorySettingsDisableCreationTFVCRepos.ps1 | 25 + ...zationRepositorySettingsGravatarImages.ps1 | 25 + .../Test-AzdoOrganizationStageChooser.ps1 | 27 + .../Test-AzdoOrganizationStorageUsage.ps1 | 33 + ...askRestrictionsDisableMarketplaceTasks.ps1 | 25 + ...ationTaskRestrictionsDisableNode6Tasks.ps1 | 25 + ...estrictionsShellTaskArgumentValidation.ps1 | 25 + ...onTriggerPullRequestGitHubRepositories.ps1 | 42 + ...st-AzdoProjectCollectionAdministrators.ps1 | 79 + tests/Azdo/Test-AzdoPublicProjects.ps1 | 26 + tests/Azdo/Test-AzdoResourceUsageProjects.ps1 | 29 + .../Test-AzdoResourceUsageWorkItemTags.ps1 | 29 + tests/Azdo/Test-AzdoSSHAuthentication.ps1 | 26 + .../Test-AzdoThirdPartyAccessViaOauth.ps1 | 26 + 39 files changed, 963 insertions(+), 18264 deletions(-) delete mode 100644 tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 delete mode 100644 tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 delete mode 100644 tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml delete mode 100644 tests/Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml delete mode 100644 tests/Azdo/AdopsMaesterTests.psd1 delete mode 100644 tests/Azdo/AdopsMaesterTests.psm1 create mode 100644 tests/Azdo/Test-AzdoAllowRequestAccessToken.ps1 create mode 100644 tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 create mode 100644 tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 create mode 100644 tests/Azdo/Test-AzdoAuditStreams.ps1 create mode 100644 tests/Azdo/Test-AzdoEnforceAADConditionalAccess.ps1 create mode 100644 tests/Azdo/Test-AzdoExternalGuestAccess.ps1 create mode 100644 tests/Azdo/Test-AzdoFeedbackCollection.ps1 create mode 100644 tests/Azdo/Test-AzdoLogAuditEvents.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationArtifactFeed.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationOwner.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationStageChooser.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationStorageUsage.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 create mode 100644 tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 create mode 100644 tests/Azdo/Test-AzdoProjectCollectionAdministrators.ps1 create mode 100644 tests/Azdo/Test-AzdoPublicProjects.ps1 create mode 100644 tests/Azdo/Test-AzdoResourceUsageProjects.ps1 create mode 100644 tests/Azdo/Test-AzdoResourceUsageWorkItemTags.ps1 create mode 100644 tests/Azdo/Test-AzdoSSHAuthentication.ps1 create mode 100644 tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 diff --git a/tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 b/tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 deleted file mode 100644 index 2c8934daa..000000000 --- a/tests/Azdo/ADOPS/1.1.2/ADOPS.psd1 +++ /dev/null @@ -1,164 +0,0 @@ -# -# Module manifest for module 'ADOPS' -# -# Generated by: the PwrOps collective -# -# Generated on: 2025-04-11 -# - -@{ - -# Script module or binary module file associated with this manifest. -RootModule = '.\ADOPS.psm1' - -# Version number of this module. -ModuleVersion = '1.1.2' - -# Supported PSEditions -CompatiblePSEditions = 'Core' - -# ID used to uniquely identify this module -GUID = '2fe11b9f-f7ec-4dca-84d8-2eda6ec6c86f' - -# Author of this module -Author = 'the PwrOps collective' - -# Company or vendor of this module -CompanyName = '' - -# Copyright statement for this module -Copyright = '(c) the PwrOps collective. All rights reserved.' - -# Description of the functionality provided by this module -Description = 'Manage Azure DevOps using PowerShell' - -# Minimum version of the PowerShell engine required by this module -PowerShellVersion = '7.2' - -# Name of the PowerShell host required by this module -# PowerShellHostName = '' - -# Minimum version of the PowerShell host required by this module -# PowerShellHostVersion = '' - -# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. -# DotNetFrameworkVersion = '' - -# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. -# ClrVersion = '' - -# Processor architecture (None, X86, Amd64) required by this module -# ProcessorArchitecture = '' - -# Modules that must be imported into the global environment prior to importing this module -RequiredModules = @(@{ModuleName = 'AzAuth'; GUID = '6efad2e7-3439-46fb-862d-eea4ebd67bc4'; ModuleVersion = '2.2.2'; }) - -# Assemblies that must be loaded prior to importing this module -# RequiredAssemblies = @() - -# Script files (.ps1) that are run in the caller's environment prior to importing this module. -# ScriptsToProcess = @() - -# Type files (.ps1xml) to be loaded when importing this module -# TypesToProcess = @() - -# Format files (.ps1xml) to be loaded when importing this module -# FormatsToProcess = @() - -# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -# NestedModules = @() - -# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. -FunctionsToExport = 'Connect-ADOPS', 'Disconnect-ADOPS', 'Get-ADOPSAgentQueue', - 'Get-ADOPSArtifactFeed', 'Get-ADOPSAuditActions', - 'Get-ADOPSAuditStreams', 'Get-ADOPSBuildDefinition', - 'Get-ADOPSConnection', 'Get-ADOPSElasticPool', 'Get-ADOPSFileContent', - 'Get-ADOPSGroup', 'Get-ADOPSNode', - 'Get-ADOPSOrganizationAdminOverview', - 'Get-ADOPSOrganizationAdvancedSecurity', - 'Get-ADOPSOrganizationCommerceMeterUsage', - 'Get-ADOPSOrganizationPipelineSettings', - 'Get-ADOPSOrganizationPolicy', - 'Get-ADOPSOrganizationRepositorySettings', 'Get-ADOPSPipeline', - 'Get-ADOPSPipelineRetentionSettings', 'Get-ADOPSPipelineSettings', - 'Get-ADOPSPipelineTask', 'Get-ADOPSPool', 'Get-ADOPSProject', - 'Get-ADOPSRepository', 'Get-ADOPSResourceUsage', - 'Get-ADOPSServiceConnection', 'Get-ADOPSUsageData', 'Get-ADOPSUser', - 'Get-ADOPSVariableGroup', 'Get-ADOPSWiki', - 'Grant-ADOPSPipelinePermission', 'Import-ADOPSRepository', - 'Initialize-ADOPSRepository', 'Invoke-ADOPSRestMethod', - 'New-ADOPSArtifactFeed', 'New-ADOPSAuditStream', - 'New-ADOPSBuildPolicy', 'New-ADOPSElasticpool', - 'New-ADOPSElasticPoolObject', 'New-ADOPSEnvironment', - 'New-ADOPSGitBranch', 'New-ADOPSGitFile', 'New-ADOPSGroupEntitlement', - 'New-ADOPSMergePolicy', 'New-ADOPSPipeline', 'New-ADOPSProject', - 'New-ADOPSRepository', 'New-ADOPSServiceConnection', - 'New-ADOPSUserStory', 'New-ADOPSVariableGroup', 'New-ADOPSWiki', - 'Remove-ADOPSRepository', 'Remove-ADOPSVariableGroup', - 'Revoke-ADOPSPipelinePermission', 'Save-ADOPSPipelineTask', - 'Set-ADOPSArtifactFeed', 'Set-ADOPSBuildDefinition', - 'Set-ADOPSElasticPool', 'Set-ADOPSGitPermission', - 'Set-ADOPSPipelineRetentionSettings', 'Set-ADOPSPipelineSettings', - 'Set-ADOPSProject', 'Set-ADOPSRepository', - 'Set-ADOPSServiceConnection', 'Start-ADOPSPipeline', - 'Test-ADOPSConnection', 'Test-ADOPSYamlFile', 'Get-ADOPSMembership' - -# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. -CmdletsToExport = @() - -# Variables to export from this module -# VariablesToExport = @() - -# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. -AliasesToExport = @() - -# DSC resources to export from this module -# DscResourcesToExport = @() - -# List of all modules packaged with this module -# ModuleList = @() - -# List of all files packaged with this module -# FileList = @() - -# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. -PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - Tags = 'Azure','DevOps','AZDO','AzureDevOps','ADO' - - # A URL to the license for this module. - LicenseUri = 'https://github.com/AZDOPS/AZDOPS/blob/main/LICENSE' - - # A URL to the main website for this project. - ProjectUri = 'https://github.com/AZDOPS/AZDOPS/' - - # A URL to an icon representing this module. - # IconUri = '' - - # ReleaseNotes of this module - # ReleaseNotes = '' - - # Prerelease string of this module - # Prerelease = '' - - # Flag to indicate whether the module requires explicit user acceptance for install/update/save - # RequireLicenseAcceptance = $false - - # External dependent modules of this module - # ExternalModuleDependencies = @() - - } # End of PSData hashtable - - } # End of PrivateData hashtable - -# HelpInfo URI of this module -# HelpInfoURI = '' - -# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. -# DefaultCommandPrefix = '' - -} - diff --git a/tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 b/tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 deleted file mode 100644 index c02a5cf56..000000000 --- a/tests/Azdo/ADOPS/1.1.2/ADOPS.psm1 +++ /dev/null @@ -1,4439 +0,0 @@ -#region PreCode _PreModule_Requires - -#Requires -Modules @{ ModuleName="AzAuth"; ModuleVersion="2.2.2" } - -$script:AzTokenCache = 'adops.cache' - -$script:loginMethod = 'Default' -#endregion PreCode _PreModule_Requires - -#region GitAccessLevels - -[Flags()] enum AccessLevels { - Administer = 1 - GenericRead = 2 - GenericContribute = 4 - ForcePush = 8 - CreateBranch = 16 - CreateTag = 32 - ManageNote = 64 - PolicyExempt = 128 - CreateRepository = 256 - DeleteRepository = 512 - RenameRepository = 1024 - EditPolicies = 2048 - RemoveOthersLocks = 4096 - ManagePermissions = 8192 - PullRequestContribute = 16384 - PullRequestBypassPolicy = 32768 -} -#endregion GitAccessLevels - -#region ResourceType - -enum ResourceType { - VariableGroup - Queue - SecureFile - Environment -} -#endregion ResourceType - -#region SkipTest - -class SkipTest : Attribute { - [string[]]$TestNames - - SkipTest([string[]]$Name) { - $this.TestNames = $Name - } -} -#endregion SkipTest - -#region ConvertRetentionSettingsGetToPatch - -<# -.SYNOPSIS -Convert to responses from GET _apis/build/retention & PATCH _apis/build/retention -into the PATCH _apis/build/retention body property names. - -.DESCRIPTION -Below are the GET & PATCH _apis/build/retention request/respones. - -Notice the GET and PATCH responses are the same, while the PATCH body uses different property names. -This function will convert the GET/PATCH responses to match the PATCH request body properties. - -GET _apis/build/retention --- -{ - "purgeArtifacts": { "min": 1, "max": 60, "value": 51 }, - "purgeRuns": { "min": 1, "max": 60, "value": 51 }, - "purgePullRequestRuns": { "min": 1, "max": 60, "value": 51 }, - "retainRunsPerProtectedBranch": null -} - -POST _apis/build/retention -Request Body: -{ - "artifactsRetention": { "min": 1, "max": 60, "value": 51 }, - "runRetention": { "min": 1, "max": 60, "value": 51 }, - "pullRequestRunRetention": { "min": 1, "max": 60, "value": 51 }, - "retainRunsPerProtectedBranch": { "min": 1, "max": 60, "value": 51 }, -} - -Response Body: -{ - "purgeArtifacts": { "min": 1, "max": 60, "value": 51 }, - "purgeRuns": { "min": 1, "max": 60, "value": 51 }, - "purgePullRequestRuns": { "min": 1, "max": 60, "value": 51 }, - "retainRunsPerProtectedBranch": null -} - -.NOTES -Research notes aligning UX, GET, PATCH fields: - -@{ - # UX Label: Days to keep artifacts, symbols and attachments - # UX Field: PurgeArtifacts - # Get Field: purgeArtifacts - # Patch Field: artifactsRetention - purgeArtifacts = @{ - min = 1 - max = 60 - value = 51 - } - - # UX Label: Days to keep runs - # UX Field: PurgeRuns - # Get Field: purgeRuns - # Patch Field: runRetention - purgeRuns = @{ - min = 30 - max = 731 - value = 37 - } - - # UX Label: Days to keep pull request runs - # UX Field: PurgePullRequestRuns - # Get Field: purgePullRequestRuns - # Patch Field: pullRequestRunRetention - purgePullRequestRuns = @{ - min = 1 - max = 30 - value = 4 - } - - # UX Label: Number of recent runs to retain per pipeline - # UX Help Label: This many runs will also be retained per protected branch and default pipeline branch. (Azure Repos only) - # UX Field: runsToRetainPerProtectedBranch - # Get Field: retainRunsPerProtectedBranch - # Patch Field: retainRunsPerProtectedBranch - # BUG: Always null on return - retainRunsPerProtectedBranch = @{ - min = 0 - max = 50 - value = 0 - } -} -#> -function ConvertRetentionSettingsGetToPatch { - [CmdletBinding()] - [SkipTest('HasOrganizationParameter')] - param ( - [Parameter(Mandatory)] - $Response - ) - - $Settings = @{} - - $FieldMap = @{ - 'purgeArtifacts' = 'artifactsRetention' - 'purgeRuns' = 'runRetention' - 'purgePullRequestRuns' = 'pullRequestRunRetention' - - # Note: This field is bugged, it's always NULL on GET/PATCH response, I think its meant to be runsToRetainPerProtectedBranch - 'retainRunsPerProtectedBranch' = 'retainRunsPerProtectedBranch' - } - $Fields = $Response.psobject.Properties | Where-object Name -in $FieldMap.Keys - - foreach ($Field in $Fields) { - $Settings.$($FieldMap[$Field.Name]) = $Field.Value.value - } - - [pscustomobject]$Settings -} -#endregion ConvertRetentionSettingsGetToPatch - -#region ConvertRetentionSettingsToPatchBody - -<# -.SYNOPSIS -Converts Retention Settings Dictionary into RetentionSetting objects - -.DESCRIPTION -Converts Retention Settings Dictionary into RetentionSetting objects. - -.PARAMETER Values -Keyed dictionary of integers - -Example: -@{ - artifactsRetention = 51 - runRetention = 51, - ... -} - -.OUTPUTS -@{ - artifactsRetention = @{ - min = 0, - max = 0, - value = 51 - }, - runRetention = @{ - min = 0, - max = 0, - value = 51 - }, - ... -} -#> -function ConvertRetentionSettingsToPatchBody { - [CmdletBinding()] - [SkipTest('HasOrganizationParameter')] - param ( - [Parameter(Mandatory)] - $Values - ) - - $Settings = @{} - if ($Values -is [pscustomobject]) { - foreach ($ValueProperty in $Values.psobject.Properties) { - $Settings[$ValueProperty.Name] = @{ - value = $ValueProperty.Value - min = $null - max = $null - } - } - } - else { - foreach ($Value in $Values.GetEnumerator()) { - $Settings[$Value.key] = @{ - value = $Value.value - min = $null - max = $null - } - } - } - - [pscustomobject]$Settings -} -#endregion ConvertRetentionSettingsToPatchBody - -#region GetADOPSConfigFile - -function GetADOPSConfigFile { - param ( - [Parameter()] - [string]$ConfigPath = '~/.ADOPS/Config.json' - ) - - # Create config if not exists - if (-not (Test-Path $ConfigPath)) { - NewADOPSConfigFile - } - - Get-Content $ConfigPath | ConvertFrom-Json -AsHashtable -} -#endregion GetADOPSConfigFile - -#region GetADOPSDefaultOrganization - -function GetADOPSDefaultOrganization { - [CmdletBinding()] - [SkipTest('HasOrganizationParameter')] - param () - - $ADOPSConfig = GetADOPSConfigFile - - if ([string]::IsNullOrWhiteSpace($ADOPSConfig['Default']['Organization'])) { - throw 'No default organization found! Use Connect-ADOPS or set Organization parameter.' - } - else { - Write-Output $ADOPSConfig['Default']['Organization'] - } -} -#endregion GetADOPSDefaultOrganization - -#region GetADOPSOrganizationAccess - -function GetADOPSOrganizationAccess { - [CmdletBinding()] - [SkipTest('HasOrganizationParameter')] - param ( - [Parameter(Mandatory)] - [string]$AccountId, - - [Parameter()] - [string]$Token - ) - - (InvokeADOPSRestMethod -Method GET -Token $Token -Uri "https://app.vssps.visualstudio.com/_apis/accounts?memberId=$AccountId&api-version=7.1-preview.1").value.accountName -} -#endregion GetADOPSOrganizationAccess - -#region InvokeADOPSRestMethod - -function InvokeADOPSRestMethod { - [SkipTest('HasOrganizationParameter')] - param ( - [Parameter(Mandatory)] - [URI]$Uri, - - [Parameter()] - [Microsoft.PowerShell.Commands.WebRequestMethod]$Method, - - [Parameter()] - [string]$Body, - - [Parameter()] - [string]$ContentType = 'application/json', - - [Parameter()] - [switch]$FullResponse, - - [Parameter()] - [string]$OutFile, - - [Parameter()] - [string]$Token - ) - - if (-not $PSBoundParameters.ContainsKey('Token')) { - $Token = (NewAzToken).Token - } - - $InvokeSplat = @{ - 'Uri' = $Uri - 'Method' = $Method - 'Headers' = @{ - 'Authorization' = "Bearer $Token" - } - 'ContentType' = $ContentType - } - - if (-not [string]::IsNullOrEmpty($Body)) { - $InvokeSplat.Add('Body', $Body) - } - - if ($FullResponse) { - $InvokeSplat.Add('ResponseHeadersVariable', 'ResponseHeaders') - $InvokeSplat.Add('StatusCodeVariable', 'ResponseStatusCode') - } - - if ($OutFile) { - Write-Debug "$Method $Uri" - Invoke-RestMethod @InvokeSplat -OutFile $OutFile - } - else { - Write-Debug "$Method $Uri" - $Result = Invoke-RestMethod @InvokeSplat - - if ($Result -like "*Azure DevOps Services | Sign In*") { - throw 'Failed to call Azure DevOps API. Please login using Connect-ADOPS before running commands.' - } - elseif ($FullResponse) { - @{ Content = $Result; Headers = $ResponseHeaders; StatusCode = $ResponseStatusCode } - } - else { - $Result - } - } -} -#endregion InvokeADOPSRestMethod - -#region NewADOPSConfigFile - -function NewADOPSConfigFile { - param ( - [Parameter()] - [string]$ConfigPath = '~/.ADOPS/Config.json' - ) - - @{ - 'Default' = @{} - } | SetADOPSConfigFile -ConfigPath $ConfigPath -} -#endregion NewADOPSConfigFile - -#region NewAzToken - -function NewAzToken { - [CmdletBinding()] - [SkipTest('HasOrganizationParameter')] - param () - - $TokenSplat = @{ - Resource = '499b84ac-1321-427f-aa17-267ca6975798' - } - switch ($script:LoginMethod) { - 'Default' { - try { - $UserContext = GetADOPSConfigFile - - $TokenSplat['Username'] = $Usercontext['Default']['Identity'] - $TokenSplat['TenantId'] = $Usercontext['Default']['TenantId'] - Get-AzToken @TokenSplat -TokenCache $script:AzTokenCache - } - catch { - # Make sure we present the inner exception to users but with a nicer error message - if ($_.Exception.GetType().FullName -eq 'Azure.Identity.CredentialUnavailableException') { - $Exception = New-Object System.InvalidOperationException "Could not find existing token, please run the command Connect-ADOPS!", $_.Exception - $ErrorRecord = New-Object Management.Automation.ErrorRecord $Exception, 'ADOPSGetTokenError', ([System.Management.Automation.ErrorCategory]::InvalidOperation), $null - throw $ErrorRecord - } - else { - throw $_ - } - } - } - 'ManagedIdentity' { - Get-AzToken @TokenSplat -ManagedIdentity - } - 'OAuthToken' { - return $Script:ScriptToken - } - Default { - throw 'No login method was set, module file may have been corrupted!' - } - } -} -#endregion NewAzToken - -#region SetADOPSConfigFile - -function SetADOPSConfigFile { - [CmdletBinding()] - param ( - [Parameter()] - [string]$ConfigPath = '~/.ADOPS/Config.json', - - [Parameter(ValueFromPipeline)] - [object]$ConfigObject - ) - - $null = New-Item -Path '~/.ADOPS/' -ItemType Directory -ErrorAction SilentlyContinue - Set-Content -Path $ConfigPath -Value ($ConfigObject | ConvertTo-Json -Compress) -Force -} -#endregion SetADOPSConfigFile - -#region SetADOPSPipelinePermission - -function SetADOPSPipelinePermission { - [CmdletBinding()] - param ( - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [string]$Project, - - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [switch]$AllPipelines, - - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [int]$PipelineId, - - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [ResourceType]$ResourceType, - - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [string]$ResourceId, - - [Parameter(ParameterSetName = 'AllPipelines')] - [Parameter(ParameterSetName = 'SinglePipeline')] - [bool]$Authorized = $true, - - [Parameter(ParameterSetName = 'AllPipelines')] - [Parameter(ParameterSetName = 'SinglePipeline')] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $URI = "https://dev.azure.com/${Organization}/${Project}/_apis/pipelines/pipelinepermissions/${ResourceType}/${ResourceId}?api-version=7.1-preview.1" - $method = 'PATCH' - - $Body = switch ($PSCmdlet.ParameterSetName) { - 'AllPipelines' { - @{ - allPipelines = @{ - authorized = $Authorized - } - } - } - 'SinglePipeline' { - @{ - pipelines = @( - [ordered]@{ - id = $PipelineId - authorized = $Authorized - } - ) - } - } - 'Default' { - throw 'Invalid parameter set, this should not happen' - } - } - $Body = $Body | ConvertTo-Json -Depth 10 -Compress - - InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body -} -#endregion SetADOPSPipelinePermission - -#region Connect-ADOPS - -function Connect-ADOPS { - [CmdletBinding(DefaultParameterSetName = 'Interactive')] - param ( - [Parameter(Mandatory, ParameterSetName = 'Interactive')] - [Parameter(Mandatory, ParameterSetName = 'ManagedIdentity')] - [Parameter(Mandatory, ParameterSetName = 'OAuthToken')] - [string]$Organization, - - [Parameter(ParameterSetName = 'Interactive')] - [Parameter(ParameterSetName = 'ManagedIdentity')] - [Parameter(ParameterSetName = 'OAuthToken')] - [string]$TenantId, - - [Parameter(ParameterSetName = 'Interactive')] - [Parameter(ParameterSetName = 'ManagedIdentity')] - [Parameter(ParameterSetName = 'OAuthToken')] - [switch]$SkipVerification, - - [Parameter(ParameterSetName = 'Interactive')] - [switch]$Interactive, - - [Parameter(Mandatory, ParameterSetName = 'ManagedIdentity')] - [switch]$ManagedIdentity, - - [Parameter(Mandatory, ParameterSetName = 'OAuthToken')] - [String]$OAuthToken - ) - - $TokenSplat = @{ - Resource = '499b84ac-1321-427f-aa17-267ca6975798' - Scope = '.default' - } - - # Add TenantId if provided - if ($PSBoundParameters.ContainsKey('TenantId')) { - $TokenSplat.Add('TenantId', $TenantId) - } - - switch ($PSCmdlet.ParameterSetName) { - 'OAuthToken' { - $script:LoginMethod = 'OAuthToken' - $script:ScriptToken = @{ - Token = $OAuthToken - } - $Token = $OAuthToken - $TokenTenantId = 'NotSpecified' - $TokenIdentity = $null - } - 'ManagedIdentity' { - $TokenSplat.Add('ManagedIdentity', $true) - $script:LoginMethod = 'ManagedIdentity' - - $Token = Get-AzToken @TokenSplat - $TokenTenantId = $Token.TenantId - $TokenIdentity = $Token.Identity - } - 'Interactive' { - $TokenSplat.Add('TokenCache', $script:AzTokenCache) - $TokenSplat.Add('Interactive', $true) - - $Token = Get-AzToken @TokenSplat - $TokenTenantId = $Token.TenantId - $TokenIdentity = $Token.Identity - } - } - - if ($Organization -like "https://dev.azure.com/*") { - $Organization = ($Organization -split "/")[3] - } - - if (-not $PSBoundParameters.ContainsKey('SkipVerification')) { - # Get User context - $Me = InvokeADOPSRestMethod -Method GET -Token $Token -Uri 'https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1-preview.3' - - # Get available orgs - $Orgs = GetADOPSOrganizationAccess -AccountId $Me.publicAlias -Token $Token - - if ($Organization -notin $Orgs) { - throw "The connected account does not have access to the organization '$Organization'. Organizations available: $($Orgs -join ",")`nAre you connected to the correct tennant? $TokenTenantId" - } - } - else { - Write-Verbose 'Skipping organization access verification.' - $Me = @{ id = 'unverified' } - } - - # If user provided a token, we have not parsed the JWT for the email/id - if ($null -eq $TokenIdentity) { - # Instead take info from the DevOps response - if (-not [string]::IsNullOrWhiteSpace($Me.emailAddress)) { - $TokenIdentity = $Me.emailAddress - } - else { - $TokenIdentity = $Me.id - } - } - - $ADOPSConfig = GetADOPSConfigFile - $ADOPSConfig['Default'] = @{ - 'Identity' = $TokenIdentity - 'TenantId' = $TokenTenantId - 'Organization' = $Organization - } - - SetADOPSConfigFile -ConfigObject $ADOPSConfig - - Write-Output $ADOPSConfig['Default'] -} -#endregion Connect-ADOPS - -#region Disconnect-ADOPS - -function Disconnect-ADOPS { - [CmdletBinding()] - [SkipTest('HasOrganizationParameter')] - param () - - # Reset context - NewADOPSConfigFile - - Clear-AzTokenCache -TokenCache $script:AzTokenCache -} -#endregion Disconnect-ADOPS - -#region Get-ADOPSAgentQueue - -function Get-ADOPSAgentQueue { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter()] - [string]$QueueName - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - - if ($PSBoundParameters.ContainsKey('QueueName')) { - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/queues?queueName=${QueueName}&api-version=7.1" - } - else { - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/queues?api-version=7.1" - } - - $Method = 'GET' - $Queue = InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body - - if ($Queue.psobject.properties.name -contains 'value') { - Write-Output $Queue.value - } - else { - Write-Output $Queue - } -} -#endregion Get-ADOPSAgentQueue - -#region Get-ADOPSArtifactFeed - -function Get-ADOPSArtifactFeed { - [CmdletBinding(DefaultParameterSetName = 'All')] - param ( - [Parameter(ParameterSetName = 'All')] - [Parameter(ParameterSetName = 'FeedId', Mandatory)] - [string]$Project, - - [Parameter(ParameterSetName = 'All')] - [Parameter(ParameterSetName = 'FeedId')] - [string]$Organization, - - [Parameter(ParameterSetName = 'FeedId', Mandatory)] - [Alias('Name')] - [string]$FeedId - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://feeds.dev.azure.com/${Organization}" - if (-not ([string]::IsNullOrEmpty($Project))) { - $Uri = "${Uri}/${Project}" - } - $Uri = "${Uri}/_apis/packaging/feeds" - if (-not ([string]::IsNullOrEmpty($FeedId))) { - $Uri = "${Uri}/${FeedId}" - } - $Uri = "${Uri}?api-version=7.2-preview.1" - - $Method = 'Get' - - $InvokeSplat = @{ - Uri = $Uri - Method = $Method - } - - $res = InvokeADOPSRestMethod @InvokeSplat - if ( - (($res | Get-Member -MemberType NoteProperty).Name -contains 'count') -and - (($res | Get-Member -MemberType NoteProperty).Name -contains 'value') - ) { - Write-Output $res.value -NoEnumerate - } - else { - Write-Output $res - } -} -#endregion Get-ADOPSArtifactFeed - -#region Get-ADOPSAuditActions - -function Get-ADOPSAuditActions { - param ( - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - (InvokeADOPSRestMethod -Uri "https://auditservice.dev.azure.com/$Organization/_apis/audit/actions" -Method Get).value -} -#endregion Get-ADOPSAuditActions - -#region Get-ADOPSAuditStreams - -function Get-ADOPSAuditStreams { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - (InvokeADOPSRestMethod -Uri "https://auditservice.dev.azure.com/$Organization/_apis/audit/streams" -Method Get).value -} -#endregion Get-ADOPSAuditStreams - -#region Get-ADOPSBuildDefinition - -function Get-ADOPSBuildDefinition { - [CmdletBinding()] - Param( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [int]$Id - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if ($PSBoundParameters.ContainsKey('Id')) { - [int[]]$idList = $id - } - else { - [int[]]$idList = (InvokeADOPSRestMethod -Method GET -Uri "https://dev.azure.com/${Organization}/${Project}/_apis/build/definitions?api-version=7.2-preview.7").value.id - } - - [array]$res = @() - foreach ($definition in $idList) { - [array]$res += InvokeADOPSRestMethod -Method GET -Uri "https://dev.azure.com/${Organization}/${Project}/_apis/build/definitions/${definition}?api-version=7.2-preview.7" - } - - Write-Output $res -NoEnumerate -} -#endregion Get-ADOPSBuildDefinition - -#region Get-ADOPSConnection - -function Get-ADOPSConnection { - [SkipTest('HasOrganizationParameter')] - param () - - $res = GetADOPSConfigFile - $res['Default'] -} -#endregion Get-ADOPSConnection - -#region Get-ADOPSElasticPool - -function Get-ADOPSElasticPool { - [CmdletBinding()] - param ( - [Parameter()] - [int32]$PoolId, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if ($PSBoundParameters.ContainsKey('PoolId')) { - $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools/$PoolId`?api-version=7.1-preview.1" - } - else { - $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools?api-version=7.1-preview.1" - } - - $Method = 'GET' - $ElasticPoolInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body - if ($ElasticPoolInfo.psobject.properties.name -contains 'value') { - Write-Output $ElasticPoolInfo.value - } - else { - Write-Output $ElasticPoolInfo - } -} -#endregion Get-ADOPSElasticPool - -#region Get-ADOPSFileContent - -function Get-ADOPSFileContent { - param ( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter(Mandatory)] - [string]$RepositoryId, - - [Parameter(Mandatory)] - [string]$FilePath - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if (-Not $FilePath.StartsWith('/')) { - $FilePath = $FilePath.Insert(0, '/') - } - - $UrlEncodedFilePath = [System.Web.HttpUtility]::UrlEncode($FilePath) - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$RepositoryId/items?path=$UrlEncodedFilePath&api-version=7.1-preview.1" - - InvokeADOPSRestMethod -Uri $Uri -Method Get -} -#endregion Get-ADOPSFileContent - -#region Get-ADOPSGroup - -function Get-ADOPSGroup { - param ([Parameter()] - [string]$Organization, - - [Parameter()] - [string] - $Descriptor, - - [Parameter(DontShow)] - [string]$ContinuationToken - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if ($PSBoundParameters.ContainsKey('Descriptor')) { - $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/groups/$Descriptor`?api-version=7.2-preview.1" - - $Response = InvokeADOPSRestMethod -Uri $Uri -Method 'GET' - - return $Response - } - else { - if (-not [string]::IsNullOrEmpty($ContinuationToken)) { - $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/groups?continuationToken=$ContinuationToken&api-version=7.1-preview.1" - } - else { - $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/groups?api-version=7.1-preview.1" - } - } - - $Method = 'GET' - - $Response = InvokeADOPSRestMethod -FullResponse -Uri $Uri -Method $Method - - $Groups = $Response.Content.value - Write-Verbose "Found $($Response.Content.count) groups" - - if ($Response.Headers.ContainsKey('X-MS-ContinuationToken')) { - Write-Verbose "Found continuationToken. Will fetch more groups." - $parameters = [hashtable]$PSBoundParameters - $parameters.Add('ContinuationToken', $Response.Headers['X-MS-ContinuationToken']?[0]) - $Groups += Get-ADOPSGroup @parameters - } - - Write-Output $Groups -} - -#endregion Get-ADOPSGroup - -#region Get-ADOPSNode - -function Get-ADOPSNode { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [int32]$PoolId, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools/$PoolId/nodes?api-version=7.1-preview.1" - - $Method = 'GET' - $NodeInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method - - if ($NodeInfo.psobject.properties.name -contains 'value') { - Write-Output $NodeInfo.value - } - else { - Write-Output $NodeInfo - } -} -#endregion Get-ADOPSNode - -#region Get-ADOPSOrganizationAdminOverview - -function Get-ADOPSOrganizationAdminOverview { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization, - - [Parameter()] - [string[]] - $ContributionIds = @("ms.vss-admin-web.organization-admin-overview-delay-load-data-provider") - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Body = @{ - 'contributionIds' = $ContributionIds - } | ConvertTo-Json -Depth 100 - - $Uri = "https://dev.azure.com/$Organization/_apis/Contribution/HierarchyQuery?api-version=7.2-preview" - - $Response = InvokeADOPSRestMethod -Uri $Uri -Method Post -Body $Body - - if ($Response.dataProviderExceptions) { - $Response.dataProviderExceptions - } - else { - $Response.dataProviders - } - -} -#endregion Get-ADOPSOrganizationAdminOverview - -#region Get-ADOPSOrganizationAdvancedSecurity - -function Get-ADOPSOrganizationAdvancedSecurity { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://advsec.dev.azure.com/$Organization/_apis/Management/enablement" - - (InvokeADOPSRestMethod -Uri $Uri -Method Get) -} -#endregion Get-ADOPSOrganizationAdvancedSecurity - -#region Get-ADOPSOrganizationCommerceMeterUsage - -function Get-ADOPSOrganizationCommerceMeterUsage { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization, - - [Parameter()] - [string]$MeterId - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - - $AccountId = (InvokeADOPSRestMethod -Method GET -Uri 'https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1-preview.3').publicAlias - - # GetADOPSOrganizationAccess could have been used instead. - # However it requires token and only returns accountName - - # Get available orgs - $Orgs = (InvokeADOPSRestMethod -Method GET -Uri "https://app.vssps.visualstudio.com/_apis/accounts?memberId=$AccountId&api-version=7.1-preview.1").value - - $OrganizationId = ($Orgs | Where-object accountName -eq $Organization).AccountId - - if ($PSBoundParameters.ContainsKey('MeterId')) { - InvokeADOPSRestMethod -Uri "https://azdevopscommerce.dev.azure.com/$OrganizationId/_apis/AzComm/MeterUsage2/$MeterId" -Method Get - } - else { - (InvokeADOPSRestMethod -Uri "https://azdevopscommerce.dev.azure.com/$OrganizationId/_apis/AzComm/MeterUsage2" -Method Get).value - } - -} -#endregion Get-ADOPSOrganizationCommerceMeterUsage - -#region Get-ADOPSOrganizationPipelineSettings - -function Get-ADOPSOrganizationPipelineSettings { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Body = '{ - "contributionIds": [ - "ms.vss-build-web.pipelines-org-settings-data-provider" - ] - }' - - $Uri = "https://dev.azure.com/$Organization/_apis/Contribution/HierarchyQuery?api-version=7.1-preview" - - (InvokeADOPSRestMethod -Uri $Uri -Method Post -Body $Body).dataProviders.'ms.vss-build-web.pipelines-org-settings-data-provider' - -} -#endregion Get-ADOPSOrganizationPipelineSettings - -#region Get-ADOPSOrganizationPolicy - -function Get-ADOPSOrganizationPolicy { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization, - - [Parameter()] - [ValidateSet( - 'Security', - 'Privacy', - 'ApplicationConnection', - 'User' - )] - [string] - $PolicyCategory - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/_settings/organizationPolicy?__rt=fps&__ver=2" - $Data = InvokeADOPSRestMethod -Uri $Uri -Method Get - if ($PSBoundParameters.ContainsKey('PolicyCategory')) { - switch ($PolicyCategory) { - 'Security' { - $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.security - } - 'Privacy' { - $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.privacy - } - 'ApplicationConnection' { - $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.applicationConnection - } - 'User' { - $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.user - } - } - } - else { - $Policies = $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.psobject.Properties.name | ForEach-Object { - $Data.fps.dataProviders.data.'ms.vss-admin-web.organization-policies-data-provider'.policies.$_.policy - } - } - - Write-Output $Policies -} -#endregion Get-ADOPSOrganizationPolicy - -#region Get-ADOPSOrganizationRepositorySettings - -function Get-ADOPSOrganizationRepositorySettings { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Body = '{ - "contributionIds": [ - "ms.vss-build-web.pipelines-org-settings-data-provider" - ] - }' - - $Uri = "https://dev.azure.com/$Organization/_api/_versioncontrol/AllGitRepositoriesOptions?__v=5" - - (InvokeADOPSRestMethod -Uri $Uri -Method Get).__wrappedArray - -} -#endregion Get-ADOPSOrganizationRepositorySettings - -#region Get-ADOPSPipeline - -function Get-ADOPSPipeline { - [CmdletBinding()] - param ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Name, - - [Parameter()] - [int]$Revision, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/pipelines?api-version=7.1-preview.1" - - $InvokeSplat = @{ - Method = 'Get' - Uri = $URI - } - - $AllPipelines = (InvokeADOPSRestMethod @InvokeSplat).value - - if ($PSBoundParameters.ContainsKey('Name')) { - $Pipelines = $AllPipelines | Where-Object { $_.name -eq $Name } - if (-not $Pipelines) { - throw "The specified PipelineName $Name was not found amongst pipelines: $($AllPipelines.name -join ', ')!" - } - } - else { - $Pipelines = $AllPipelines - } - - $return = @() - - foreach ($Pipeline in $Pipelines) { - - $pipelineRevision = [Uri]::EscapeDataString($PSBoundParameters.ContainsKey('Revision') ? $Revision : $Pipeline.revision) - $pipelineUrl = "https://dev.azure.com/$Organization/$Project/_apis/pipelines/$($Pipeline.id)?api-version=7.1-preview.1&pipelineVersion=$pipelineRevision" - - $InvokeSplat = @{ - Method = 'Get' - Uri = $pipelineUrl - } - - $result = InvokeADOPSRestMethod @InvokeSplat - - $return += $result - } - - return $return -} - -#endregion Get-ADOPSPipeline - -#region Get-ADOPSPipelineRetentionSettings - -function Get-ADOPSPipelineRetentionSettings { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/build/retention?api-version=7.2-preview.1" - $Response = InvokeADOPSRestMethod -Uri $Uri -Method Get - - $Settings = ConvertRetentionSettingsGetToPatch -Response $Response - - Write-Output $Settings -} -#endregion Get-ADOPSPipelineRetentionSettings - -#region Get-ADOPSPipelineSettings - -function Get-ADOPSPipelineSettings { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/build/generalsettings?api-version=7.1-preview.1" - $Settings = InvokeADOPSRestMethod -Uri $Uri -Method Get - - Write-Output $Settings -} -#endregion Get-ADOPSPipelineSettings - -#region Get-ADOPSPipelineTask - -function Get-ADOPSPipelineTask { - param ( - [Parameter()] - [string]$Name, - - [Parameter()] - [string]$Organization, - - [Parameter()] - [int]$Version - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/tasks?api-version=7.1-preview.1" - - $result = InvokeADOPSRestMethod -Uri $Uri -Method Get - - $ReturnValue = $result | ConvertFrom-Json -AsHashtable | Select-Object -ExpandProperty value - - if (-Not [string]::IsNullOrEmpty($Name)) { - $ReturnValue = $ReturnValue | Where-Object -Property name -EQ $Name - } - if ($Version) { - $ReturnValue = $ReturnValue | Where-Object -FilterScript { $_.version.major -eq $Version } - } - - $ReturnValue -} -#endregion Get-ADOPSPipelineTask - -#region Get-ADOPSPool - -function Get-ADOPSPool { - [CmdletBinding(DefaultParameterSetName = 'All')] - param ( - [Parameter(Mandatory, ParameterSetName = 'PoolId')] - [int32]$PoolId, - - [Parameter(Mandatory, ParameterSetName = 'PoolName')] - [string]$PoolName, - - # Include legacy pools - [Parameter(ParameterSetName = 'All')] - [switch]$IncludeLegacy, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - switch ($PSCmdlet.ParameterSetName) { - 'PoolId' { $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/pools/$PoolId`?api-version=7.1-preview.1" } - 'PoolName' { $uri = "https://dev.azure.com/$Organization/_apis/distributedtask/pools?poolName=$PoolName`&api-version=7.1-preview.1" } - 'All' { $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/pools?api-version=7.1-preview.1" } - } - - $Method = 'GET' - $PoolInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method - - if ($PoolInfo.psobject.properties.name -contains 'value') { - $PoolInfo = $PoolInfo.value - } - if ((-not ($IncludeLegacy.IsPresent)) -and $PSCmdlet.ParameterSetName -eq 'All') { - $PoolInfo = $PoolInfo | Where-Object { $_.IsLegacy -eq $false } - } - Write-Output $PoolInfo -} -#endregion Get-ADOPSPool - -#region Get-ADOPSProject - -function Get-ADOPSProject { - [CmdletBinding(DefaultParameterSetName = 'All')] - param ( - [Parameter(ParameterSetName = 'All')] - [Parameter(ParameterSetName = 'ByName')] - [Parameter(ParameterSetName = 'ById')] - [string]$Organization, - - [Parameter(ParameterSetName = 'ByName')] - [Alias('Project')] - [string]$Name, - - [Parameter(ParameterSetName = 'ById')] - [string]$Id - - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/_apis/projects?api-version=7.1-preview.4" - - $Method = 'GET' - $ProjectInfo = (InvokeADOPSRestMethod -Uri $Uri -Method $Method).value - - if ($PSCmdlet.ParameterSetName -eq 'ByName') { - $ProjectInfo = $ProjectInfo | Where-Object -Property Name -eq $Name - } - elseif ($PSCmdlet.ParameterSetName -eq 'ById') { - $ProjectInfo = $ProjectInfo | Where-Object -Property Id -eq $Id - } - - Write-Output $ProjectInfo -} -#endregion Get-ADOPSProject - -#region Get-ADOPSRepository - -function Get-ADOPSRepository { - [CmdletBinding()] - param( - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [string]$Repository, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if ($PSBoundParameters.ContainsKey('Repository')) { - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$Repository`?api-version=7.1-preview.1" - } - else { - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories?api-version=7.1-preview.1" - } - - try { - $result = InvokeADOPSRestMethod -Uri $Uri -Method Get - } - catch [Microsoft.PowerShell.Commands.HttpResponseException] { - $ErrorMessage = $_.ErrorDetails.Message | ConvertFrom-Json - if ($ErrorMessage.message -like "TF401019:*") { - Write-Verbose "The Git repository with name or identifier $Repository does not exist or you do not have permissions for the operation you are attempting." - $result = $null - } - elseif ($ErrorMessage.message -like "TF200016:*") { - Write-Verbose "The following project does not exist: $Project. Verify that the name of the project is correct and that the project exists on the specified Azure DevOps Server." - $result = $null - } - else { - Throw $_ - } - } - - if ($result.psobject.properties.name -contains 'value') { - Write-Output -InputObject $result.value - } - else { - Write-Output -InputObject $result - } -} -#endregion Get-ADOPSRepository - -#region Get-ADOPSResourceUsage - -function Get-ADOPSResourceUsage { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - (InvokeADOPSRestMethod -Uri "https://dev.azure.com/$Organization/_apis/ResourceUsage" -Method Get).value -} -#endregion Get-ADOPSResourceUsage - -#region Get-ADOPSServiceConnection - -function Get-ADOPSServiceConnection { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Name, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization, - - [Parameter()] - [switch] - $IncludeFailed - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/serviceendpoint/endpoints?includeFailed=$IncludeFailed&api-version=7.1-preview.4" - - $InvokeSplat = @{ - Method = 'Get' - Uri = $URI - } - - $ServiceConnections = (InvokeADOPSRestMethod @InvokeSplat).value - - if ($PSBoundParameters.ContainsKey('Name')) { - $ServiceConnection = $ServiceConnections | Where-Object { $_.name -eq $Name } - if (-not $ServiceConnection) { - throw "The specified ServiceConnectionName $Name was not found amongst Connections: $($ServiceConnections.name -join ', ')!" - } - return $ServiceConnection - } - else { - return $ServiceConnections - } - -} -#endregion Get-ADOPSServiceConnection - -#region Get-ADOPSUsageData - -function Get-ADOPSUsageData { - param( - [Parameter()] - [ValidateSet('Private', 'Public')] - [string]$ProjectVisibility = 'Public', - - [Parameter()] - [Switch]$SelfHosted, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if ($SelfHosted.IsPresent) { - $Hosted = $false - } - else { - $Hosted = $true - } - - $URI = "https://dev.azure.com/$Organization/_apis/distributedtask/resourceusage?parallelismTag=${ProjectVisibility}&poolIsHosted=${Hosted}&includeRunningRequests=true" - $Method = 'Get' - - $InvokeSplat = @{ - Method = $Method - Uri = $URI - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion Get-ADOPSUsageData - -#region Get-ADOPSUser - -function Get-ADOPSUser { - [CmdletBinding(DefaultParameterSetName = 'Default')] - param ( - [Parameter(Mandatory, ParameterSetName = 'Name', Position = 0)] - [string]$Name, - - [Parameter(Mandatory, ParameterSetName = 'Descriptor', Position = 0)] - [string]$Descriptor, - - [Parameter()] - [string]$Organization, - - [Parameter(ParameterSetName = 'Default', DontShow)] - [string]$ContinuationToken - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if ($PSCmdlet.ParameterSetName -eq 'Default') { - $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/users?api-version=7.1-preview.1" - $Method = 'GET' - if (-not [string]::IsNullOrEmpty($ContinuationToken)) { - $Uri += "&continuationToken=$ContinuationToken" - } - $Response = (InvokeADOPSRestMethod -FullResponse -Uri $Uri -Method $Method) - $Users = $Response.Content.value - Write-Verbose "Found $($Response.Content.count) users" - - if ($Response.Headers.ContainsKey('X-MS-ContinuationToken')) { - Write-Verbose "Found continuationToken. Will fetch more users." - $parameters = [hashtable]$PSBoundParameters - $parameters.Add('ContinuationToken', $Response.Headers['X-MS-ContinuationToken']?[0]) - $Users += Get-ADOPSUser @parameters - } - Write-Output $Users - } - elseif ($PSCmdlet.ParameterSetName -eq 'Name') { - $Uri = "https://vsaex.dev.azure.com/$Organization/_apis/UserEntitlements?`$filter=name eq '$Name'&`$orderBy=name Ascending&api-version=7.1-preview.3" - $Method = 'GET' - $Users = (InvokeADOPSRestMethod -Uri $Uri -Method $Method).members.user - if ($null -eq $Users) { - Get-ADOPSUser | Where-Object -Property displayName -eq $Name - } - Write-Output $Users - } - elseif ($PSCmdlet.ParameterSetName -eq 'Descriptor') { - $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/users/$Descriptor`?api-version=7.1-preview.1" - $Method = 'GET' - $User = (InvokeADOPSRestMethod -Uri $Uri -Method $Method) - Write-Output $User - } -} -#endregion Get-ADOPSUser - -#region Get-ADOPSVariableGroup - -function Get-ADOPSVariableGroup { - [CmdletBinding(DefaultParameterSetName = 'All')] - param ( - [Parameter(ParameterSetName = 'All')] - [Parameter(ParameterSetName = 'Name')] - [Parameter(ParameterSetName = 'Id')] - [string]$Organization, - - [Parameter(Mandatory, ParameterSetName = 'All')] - [Parameter(Mandatory, ParameterSetName = 'Name')] - [Parameter(Mandatory, ParameterSetName = 'Id')] - [string]$Project, - - [Parameter(ParameterSetName = 'Name')] - [string]$Name, - - [Parameter(ParameterSetName = 'Id')] - [int]$Id - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Method = 'Get' - - if ($PSCmdlet.ParameterSetName -eq 'Name') { - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/variablegroups?groupName=$Name&api-version=7.2-preview.2" - } - else { - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/variablegroups?api-version=7.2-preview.2" - } - - $InvokeSplat = @{ - Uri = $Uri - Method = $Method - } - - $result = (InvokeADOPSRestMethod @InvokeSplat).value - - if ($PSCmdlet.ParameterSetName -eq 'Id') { - $result = $result.Where({ $_.Id -eq $Id }) - } - - Write-Output $result -} -#endregion Get-ADOPSVariableGroup - -#region Get-ADOPSWiki - -function Get-ADOPSWiki { - param ( - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [string]$WikiId, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $BaseUri = "https://dev.azure.com/$Organization/$Project/_apis/wiki/wikis" - - if ($WikiId) { - $Uri = "${BaseUri}/${WikiId}?api-version=7.1-preview.2" - } - else { - $Uri = "${BaseUri}?api-version=7.1-preview.2" - } - - $Method = 'Get' - - $res = InvokeADOPSRestMethod -Uri $URI -Method $Method - - if ($res.psobject.properties.name -contains 'value') { - Write-Output -InputObject $res.value - } - else { - Write-Output -InputObject $res - } -} -#endregion Get-ADOPSWiki - -#region Grant-ADOPSPipelinePermission - -function Grant-ADOPSPipelinePermission { - [CmdletBinding()] - param ( - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [string]$Project, - - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [switch]$AllPipelines, - - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [int]$PipelineId, - - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [ResourceType]$ResourceType, - - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [string]$ResourceId, - - [Parameter(ParameterSetName = 'AllPipelines')] - [Parameter(ParameterSetName = 'SinglePipeline')] - [string]$Organization - ) - - SetADOPSPipelinePermission @PSBoundParameters -Authorized $true -} -#endregion Grant-ADOPSPipelinePermission - -#region Import-ADOPSRepository - -function Import-ADOPSRepository { - [CmdLetBinding(DefaultParameterSetName = 'RepositoryName')] - param ( - [Parameter(Mandatory)] - [string]$GitSource, - - [Parameter(Mandatory, ParameterSetName = 'RepositoryId')] - [string]$RepositoryId, - - [Parameter(Mandatory, ParameterSetName = 'RepositoryName')] - [string]$RepositoryName, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [string]$Organization, - - [Parameter()] - [switch]$Wait - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - switch ($PSCmdlet.ParameterSetName) { - 'RepositoryName' { $RepoIdentifier = $RepositoryName } - 'RepositoryId' { $RepoIdentifier = $RepositoryId } - Default {} - } - $InvokeSplat = @{ - URI = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$RepoIdentifier/importRequests?api-version=7.1-preview.1" - Method = 'Post' - Body = "{""parameters"":{""gitSource"":{""url"":""$GitSource""}}}" - } - - $repoImport = InvokeADOPSRestMethod @InvokeSplat - - if ($PSBoundParameters.ContainsKey('Wait')) { - # There appears to be a bug in this API where sometimes you don't get the correct status Uri back. Fix it by constructing a correct one instead. - $verifyUri = "https://dev.azure.com/$Organization/$Project/_apis$($repoImport.url.Split('_apis')[1])" - while ($repoImport.status -ne 'completed') { - $repoImport = InvokeADOPSRestMethod -Uri $verifyUri -Method Get - Start-Sleep -Seconds 1 - } - } - - $repoImport -} -#endregion Import-ADOPSRepository - -#region Initialize-ADOPSRepository - -function Initialize-ADOPSRepository { - [CmdletBinding()] - [SkipTest('HasOrganizationParameter')] - param ( - [Parameter()] - [string]$Message = 'Repo initialized using ADOPS module.', - - [Parameter()] - [string]$Branch = 'Main', - - [Parameter(Mandatory)] - [string]$RepositoryId, - - [Parameter()] - [ValidateSet('Actionscript.gitignore', 'Ada.gitignore', 'Agda.gitignore', 'Android.gitignore', 'AppceleratorTitanium.gitignore', 'AppEngine.gitignore', 'ArchLinuxPackages.gitignore', 'Autotools.gitignore', 'C++.gitignore', 'C.gitignore', 'CakePHP.gitignore', 'CFWheels.gitignore', 'ChefCookbook.gitignore', 'Clojure.gitignore', 'CMake.gitignore', 'CodeIgniter.gitignore', 'CommonLisp.gitignore', 'Composer.gitignore', 'Concrete5.gitignore', 'Coq.gitignore', 'CraftCMS.gitignore', 'CUDA.gitignore', 'D.gitignore', 'Dart.gitignore', 'Delphi.gitignore', 'DM.gitignore', 'Drupal.gitignore', 'Eagle.gitignore', 'Elisp.gitignore', 'Elixir.gitignore', 'Elm.gitignore', 'EPiServer.gitignore', 'Erlang.gitignore', 'ExpressionEngine.gitignore', 'ExtJs.gitignore', 'Fancy.gitignore', 'Finale.gitignore', 'ForceDotCom.gitignore', 'Fortran.gitignore', 'FuelPHP.gitignore', 'gcov.gitignore', 'GitBook.gitignore', 'Go.gitignore', 'Godot.gitignore', 'Gradle.gitignore', 'Grails.gitignore', 'GWT.gitignore', 'Haskell.gitignore', 'Idris.gitignore', 'IGORPro.gitignore', 'Java.gitignore', 'Jboss.gitignore', 'Jekyll.gitignore', 'JENKINS_HOME.gitignore', 'Joomla.gitignore', 'Julia.gitignore', 'KiCAD.gitignore', 'Kohana.gitignore', 'Kotlin.gitignore', 'LabVIEW.gitignore', 'Laravel.gitignore', 'Leiningen.gitignore', 'LemonStand.gitignore', 'Lilypond.gitignore', 'Lithium.gitignore', 'Lua.gitignore', 'Magento.gitignore', 'Maven.gitignore', 'Mercury.gitignore', 'MetaProgrammingSystem.gitignore', 'nanoc.gitignore', 'Nim.gitignore', 'Node.gitignore', 'Objective-C.gitignore', 'OCaml.gitignore', 'Opa.gitignore', 'opencart.gitignore', 'OracleForms.gitignore', 'Packer.gitignore', 'Perl.gitignore', 'Phalcon.gitignore', 'PlayFramework.gitignore', 'Plone.gitignore', 'Prestashop.gitignore', 'Processing.gitignore', 'PureScript.gitignore', 'Python.gitignore', 'Qooxdoo.gitignore', 'Qt.gitignore', 'R.gitignore', 'Rails.gitignore', 'Raku.gitignore', 'RhodesRhomobile.gitignore', 'ROS.gitignore', 'Ruby.gitignore', 'Rust.gitignore', 'Sass.gitignore', 'Scala.gitignore', 'Scheme.gitignore', 'SCons.gitignore', 'Scrivener.gitignore', 'Sdcc.gitignore', 'SeamGen.gitignore', 'SketchUp.gitignore', 'Smalltalk.gitignore', 'stella.gitignore', 'SugarCRM.gitignore', 'Swift.gitignore', 'Symfony.gitignore', 'SymphonyCMS.gitignore', 'Terraform.gitignore', 'TeX.gitignore', 'Textpattern.gitignore', 'TurboGears2.gitignore', 'Typo3.gitignore', 'Umbraco.gitignore', 'Unity.gitignore', 'UnrealEngine.gitignore', 'VisualStudio.gitignore', 'VVVV.gitignore', 'Waf.gitignore', 'WordPress.gitignore', 'Xojo.gitignore', 'Yeoman.gitignore', 'Yii.gitignore', 'ZendFramework.gitignore', 'Zephir.gitignore')] - [string[]]$NewContentTemplate, - - [Parameter()] - [switch]$Readme, - - [Parameter()] - [string]$Path, - - [Parameter()] - [string]$Content = 'Repo initialized using ADOPS module.' - ) - - $Organization = GetADOPSDefaultOrganization - - $Uri = "https://dev.azure.com/$Organization/_apis/git/repositories/$RepositoryId/pushes?api-version=7.2-preview.3" - - if ($Branch -notmatch '^refs/.*') { - $Branch = 'refs/heads/' + $Branch - } - - $changes = @() - - if ($Readme -or ( [String]::IsNullOrEmpty($Path) -and ($newContentTemplate.Count -eq 0) )) { - $changes += @{ - changeType = 1 - item = @{path = "/README.md" } - newContentTemplate = @{ - name = "README.md" - type = "readme" - } - } - } - - if (-not ([string]::IsNullOrEmpty($Path))) { - $changes += @{ - changeType = "add" - item = @{ - path = $Path - } - newContent = @{ - content = $Content - contentType = "rawtext" - } - } - } - - if ($newContentTemplate.Count -eq 1) { - $changes += @{ - changeType = 1 - item = @{path = "/.gitignore" } - newContentTemplate = @{ - name = $newContentTemplate[0] - type = 'gitignore' - } - } - } - - if ($newContentTemplate.Count -gt 1) { - foreach ($t in $newContentTemplate) { - $changes += @{ - changeType = 1 - item = @{path = "/$t" } - newContentTemplate = @{ - name = $t - type = 'gitignore' - } - } - } - } - - $Body = @{ - commits = @( - @{ - comment = $Message - changes = $changes - } - ) - refUpdates = @( - @{ - name = $Branch.ToLower() - oldObjectId = "0000000000000000000000000000000000000000" - } - ) - } - - - - - $InvokeSplat = @{ - Uri = $Uri - Method = 'Post' - Body = $Body | ConvertTo-Json -Compress -Depth 100 - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion Initialize-ADOPSRepository - -#region Invoke-ADOPSRestMethod - -function Invoke-ADOPSRestMethod { - [SkipTest('HasOrganizationParameter')] - param ( - [Parameter(Mandatory)] - [string]$Uri, - - [Parameter()] - [Microsoft.PowerShell.Commands.WebRequestMethod]$Method = 'Get', - - [Parameter()] - [string]$Body - ) - - If ( ($Uri -NotLike "*dev.azure.com*") -and ($Uri -NotLike "*visualstudio.com*")) { - $Organization = GetADOPSDefaultOrganization - $Uri = "https://dev.azure.com/$Organization/$Uri" - } - - $InvokeSplat = @{ - Uri = $Uri - Method = $Method - } - - if (-Not [String]::IsNullOrEmpty($Body)) { - $InvokeSplat.Add('Body', $Body) - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion Invoke-ADOPSRestMethod - -#region New-ADOPSArtifactFeed - -function New-ADOPSArtifactFeed { - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Name, - - [Parameter()] - [string]$Description, - - [Parameter()] - [Alias('UpstreamEnabled')] - [switch]$IncludeUpstream - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://feeds.dev.azure.com/$Organization/$Project/_apis/packaging/feeds?api-version=7.2-preview.1" - - $body = [ordered]@{ - name = $name - upstreamEnabled = $IncludeUpstream.IsPresent - hideDeletedPackageVersions = $true - project = @{ - visibility = 'Private' - } - } - - if (-not [string]::IsNullOrEmpty($Description)) { - $body.Add('description', $Description) - } - - if ($IncludeUpstream.IsPresent) { - $upstreamSources = @( - @{ - name = "npmjs" - protocol = "npm" - location = "https://registry.npmjs.org/" - displayLocation = "https://registry.npmjs.org/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "NuGet Gallery" - protocol = "nuget" - location = "https://api.nuget.org/v3/index.json" - displayLocation = "https://api.nuget.org/v3/index.json" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "PowerShell Gallery" - protocol = "nuget" - location = "https://www.powershellgallery.com/api/v2/" - displayLocation = "https://www.powershellgallery.com/api/v2/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "PyPI" - protocol = "pypi" - location = "https://pypi.org/" - displayLocation = "https://pypi.org/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "Maven Central" - protocol = "Maven" - location = "https://repo.maven.apache.org/maven2/" - displayLocation = "https://repo.maven.apache.org/maven2/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "Google Maven Repository" - protocol = "Maven" - location = "https://dl.google.com/android/maven2/" - displayLocation = "https://dl.google.com/android/maven2/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "JitPack" - protocol = "Maven" - location = "https://jitpack.io/" - displayLocation = "https://jitpack.io/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "Gradle Plugins" - protocol = "Maven" - location = "https://plugins.gradle.org/m2/" - displayLocation = "https://plugins.gradle.org/m2/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "crates.io" - protocol = "Cargo" - location = "https://index.crates.io/" - displayLocation = "https://index.crates.io/" - upstreamSourceType = "public" - status = "ok" - } - ) - $body.Add('upstreamSources', $upstreamSources) - } - - $users = Get-ADOPSUser - $buildService = $users.Where({ $_.displayName -eq "$Project build service ($Organization)" }) - if ($buildService.Count -eq 0) { - Write-Verbose "Failed to find build service account. Not adding it as contributor." - } - else { - $buildServiceDescriptorObject = invokeADOPSRestMethod -Uri "https://vssps.dev.azure.com/$Organization/_apis/Identities?identityIds=$($buildService.originId)" -Method Get - $permissions = @( - @{ - identityDescriptor = "$($buildServiceDescriptorObject.Descriptor.IdentityType);$($buildServiceDescriptorObject.Descriptor.Identifier)" - role = 'contributor' - identityId = $buildServiceDescriptorObject.Id - } - ) - $body.Add('permissions', $permissions) - } - - $InvokeSplat = @{ - Uri = $Uri - Method = 'Post' - Body = $body | ConvertTo-Json -Compress - } - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSArtifactFeed - -#region New-ADOPSAuditStream - -function New-ADOPSAuditStream { - [CmdletBinding(DefaultParameterSetName = 'AzureMonitorLogs')] - param ( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory, ParameterSetName = 'AzureMonitorLogs')] - [ValidatePattern('^[a-fA-F0-9]{8}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{12}$', ErrorMessage = 'WorkspaceId should be in GUID format.')] - [string]$WorkspaceId, - - [Parameter(Mandatory, ParameterSetName = 'AzureMonitorLogs')] - [string]$SharedKey, - - [Parameter(Mandatory, ParameterSetName = 'Splunk')] - [ValidatePattern('^(http|HTTP)[sS]?:\/\/', ErrorMessage = 'SplunkUrl must start with http:// or https://')] - [string]$SplunkUrl, - - [Parameter(Mandatory, ParameterSetName = 'Splunk')] - [ValidatePattern('^[a-fA-F0-9]{8}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{12}$', ErrorMessage = 'SplunkEventCollectorToken should be in GUID format.')] - [string]$SplunkEventCollectorToken, - - [Parameter(Mandatory, ParameterSetName = 'AzureEventGrid')] - [ValidatePattern('^(http|HTTP)[sS]?:\/\/', ErrorMessage = 'EventGridTopicHostname must start with http:// or https://')] - [string]$EventGridTopicHostname, - - [Parameter(Mandatory, ParameterSetName = 'AzureEventGrid')] - [ValidatePattern('^[A-Za-z0-9+\/]*={0,2}$', ErrorMessage = 'EventGridTopicAccessKey should be Base64 encoded')] - [string]$EventGridTopicAccessKey - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Body = switch ($PSCmdlet.ParameterSetName) { - 'AzureMonitorLogs' { - [ordered]@{ - consumerType = 'AzureMonitorLogs' - consumerInputs = [Ordered]@{ - WorkspaceId = $WorkspaceId - SharedKey = $SharedKey - } - } | ConvertTo-Json -Compress - } - 'Splunk' { - [ordered]@{ - consumerType = 'Splunk' - consumerInputs = [Ordered]@{ - SplunkUrl = $SplunkUrl - SplunkEventCollectorToken = $SplunkEventCollectorToken - } - } | ConvertTo-Json -Compress - } - 'AzureEventGrid' { - [ordered]@{ - consumerType = 'AzureEventGrid' - consumerInputs = [ordered]@{ - EventGridTopicHostname = $EventGridTopicHostname - EventGridTopicAccessKey = $EventGridTopicAccessKey - } - } | ConvertTo-Json -Compress - } - } - $InvokeSplat = @{ - Uri = "https://auditservice.dev.azure.com/$Organization/_apis/audit/streams?api-version=7.1-preview.1" - Method = 'Post' - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSAuditStream - -#region New-ADOPSBuildPolicy - -function New-ADOPSBuildPolicy { - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$RepositoryId, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Branch, - - [Parameter(Mandatory)] - [int]$PipelineId, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Displayname, - - [Parameter()] - [string[]]$FilenamePatterns - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if (-Not ($Branch -match '^\w+/\w+/\w+$')) { - $Branch = "refs/heads/$Branch" - } - $GitBranchRef = $Branch - - $settings = [ordered]@{ - scope = @( - [ordered]@{ - repositoryId = $RepositoryId - refName = $GitBranchRef - matchKind = "exact" - } - ) - buildDefinitionId = $PipelineId.ToString() - queueOnSourceUpdateOnly = $false - manualQueueOnly = $false - displayName = $Displayname - validDuration = "0" - } - - if ($FilenamePatterns.Count -gt 0) { - $settings.Add('filenamePatterns', $FilenamePatterns) - } - - $Body = [ordered]@{ - type = [ordered]@{ - id = "0609b952-1397-4640-95ec-e00a01b2c241" - } - isBlocking = $true - isEnabled = $true - settings = $settings - } - - $Body = $Body | ConvertTo-Json -Depth 10 -Compress - - $InvokeSplat = @{ - Uri = "https://dev.azure.com/$Organization/$Project/_apis/policy/configurations?api-version=7.1-preview.1" - Method = 'POST' - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSBuildPolicy - -#region New-ADOPSElasticpool - -function New-ADOPSElasticPool { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$PoolName, - - [Parameter(Mandatory)] - $ElasticPoolObject, - - [Parameter()] - [string]$ProjectId, - - [Parameter()] - [switch]$AuthorizeAllPipelines, - - [Parameter()] - [switch]$AutoProvisionProjectPools, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if ($PSBoundParameters.ContainsKey('ProjectId')) { - $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools?poolName=$PoolName`&authorizeAllPipelines=$AuthorizeAllPipelines`&autoProvisionProjectPools=$AutoProvisionProjectPools`&projectId=$ProjectId`&api-version=7.1-preview.1" - } - else { - $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools?poolName=$PoolName`&authorizeAllPipelines=$AuthorizeAllPipelines`&autoProvisionProjectPools=$AutoProvisionProjectPools`&api-version=7.1-preview.1" - } - - if ($ElasticPoolObject.gettype().name -eq 'String') { - $Body = $ElasticPoolObject - } - else { - try { - $Body = $ElasticPoolObject | ConvertTo-Json -Depth 100 - } - catch { - throw "Unable to convert the content of the ElasticPoolObject to json." - } - } - - $Method = 'POST' - $ElasticPoolInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body - Write-Output $ElasticPoolInfo -} -#endregion New-ADOPSElasticpool - -#region New-ADOPSElasticPoolObject - -function New-ADOPSElasticPoolObject { - [SkipTest('HasOrganizationParameter')] - [CmdletBinding()] - param ( - # Service Endpoint Id - [Parameter(Mandatory)] - [guid] - $ServiceEndpointId, - - # Service Endpoint Scope - [Parameter(Mandatory)] - [guid] - $ServiceEndpointScope, - - # Azure Id - [Parameter(Mandatory)] - [string] - $AzureId, - - # Operating System Type - [Parameter()] - [ValidateSet('linux', 'windows')] - [string] - $OsType = 'linux', - - # MaxCapacity - [Parameter()] - [int] - $MaxCapacity = 1, - - # DesiredIdle - [Parameter()] - [int] - $DesiredIdle = 0, - - # Recycle VM after each use - [Parameter()] - [boolean] - $RecycleAfterEachUse = $false, - - # Desired Size of pool - [Parameter()] - [int] - $DesiredSize = 0, - - # Agent Interactive UI - [Parameter()] - [boolean] - $AgentInteractiveUI = $false, - - # Time before scaling down - [Parameter()] - [Alias('TimeToLiveMinues')] - [int] - $TimeToLiveMinutes = 15, - - # maxSavedNodeCount - [Parameter()] - [int] - $MaxSavedNodeCount = 0, - - # Output Type - [Parameter()] - [ValidateSet('json', 'pscustomobject')] - [string] - $OutputType = 'pscustomobject' - ) - - if ($DesiredIdle -gt $MaxCapacity) { - throw "The desired idle count cannot be larger than the max capacity." - } - - $ElasticPoolObject = [PSCustomObject]@{ - serviceEndpointId = $ServiceEndpointId - serviceEndpointScope = $ServiceEndpointScope - azureId = $AzureId - maxCapacity = $MaxCapacity - desiredIdle = $DesiredIdle - recycleAfterEachUse = $RecycleAfterEachUse - maxSavedNodeCount = $MaxSavedNodeCount - osType = $OsType - desiredSize = $DesiredSize - agentInteractiveUI = $AgentInteractiveUI - timeToLiveMinutes = $TimeToLiveMinutes - } - - if ($OutputType -eq 'json') { - $ElasticPoolObject = $ElasticPoolObject | ConvertTo-Json -Depth 100 - } - - Write-Output $ElasticPoolObject -} -#endregion New-ADOPSElasticPoolObject - -#region New-ADOPSEnvironment - -function New-ADOPSEnvironment { - param ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Name, - - [Parameter()] - [string]$Description, - - [Parameter()] - [string]$AdminGroup, - - [Parameter()] - [switch]$SkipAdmin - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$organization/$project/_apis/distributedtask/environments?api-version=7.1-preview.1" - - $Body = [Ordered]@{ - name = $Name - description = $Description - } | ConvertTo-Json -Compress - - $InvokeSplat = @{ - Uri = $Uri - Method = 'Post' - Body = $Body - } - - Write-Verbose "Setting up environment" - $Environment = InvokeADOPSRestMethod @InvokeSplat - - if ($PSBoundParameters.ContainsKey('SkipAdmin')) { - Write-Verbose 'Skipped admin group' - } - else { - $secUri = "https://dev.azure.com/$organization/_apis/securityroles/scopes/distributedtask.environmentreferencerole/roleassignments/resources/$($Environment.project.id)_$($Environment.id)?api-version=7.1-preview.1" - - if ([string]::IsNullOrEmpty($AdminGroup)) { - $AdmGroupPN = "[$project]\Project Administrators" - } - else { - $AdmGroupPN = $AdminGroup - } - $ProjAdm = (Get-ADOPSGroup | Where-Object { $_.principalName -eq $AdmGroupPN }).originId - - $SecInvokeSplat = @{ - Uri = $secUri - Method = 'Put' - Body = "[{`"userId`":`"$ProjAdm`",`"roleName`":`"Administrator`"}]" - } - - try { - $SecResult = InvokeADOPSRestMethod @SecInvokeSplat - } - catch { - Write-Error 'Failed to update environment security. The environment may still have been created.' - } - } - - Write-Output $Environment -} -#endregion New-ADOPSEnvironment - -#region New-ADOPSGitBranch - -function New-ADOPSGitBranch { - param ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidatePattern('^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}$', ErrorMessage = 'RepositoryId must be in GUID format (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)')] - [string]$RepositoryId, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$BranchName, - - [Parameter(Mandatory)] - [ValidateLength(40, 40)] - [string]$CommitId - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Body = @( - [ordered]@{ - name = "refs/heads/$BranchName" - oldObjectId = '0000000000000000000000000000000000000000' - newObjectId = $CommitId - } - ) - $Body = ConvertTo-Json -InputObject $Body -Compress - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$RepositoryId/refs?api-version=7.1-preview.1" - $InvokeSplat = @{ - Uri = $Uri - Method = 'Post' - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSGitBranch - -#region New-ADOPSGitFile - -function New-ADOPSGitFile { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter(Mandatory)] - [string]$Repository, - - [Parameter(Mandatory)] - [string]$File, - - [Parameter()] - [string]$FileName, - - [Parameter()] - [string]$Path, - - [Parameter()] - [string]$CommitMessage = 'File added using the ADOPS PowerShell module' - ) - - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if ([string]::IsNullOrEmpty($Path)) { - $Path = '/' - } - - if ([string]::IsNullOrEmpty($FileName)) { - $FileName = (Get-Item -Path $File).Name - } - - $newFilePath = "/${Path}/$FileName" -replace '/{2,}', '/' # Make sure there are never two or more slashes in a row. - - $repoDetails = Get-ADOPSRepository -Project $Project -Repository $Repository - - $refIduri = "$($repoDetails.url)/refs?filter=$($repoDetails.defaultBranch -replace '^refs/','')&includeStatuses=true&latestStatusesOnly=true&api-version=7.2-preview.2" - $refId = InvokeADOPSRestMethod -Uri $refIduri -Method Get | Select-Object -ExpandProperty value - - $body = [ordered]@{ - refUpdates = @( - [ordered]@{ - name = $repoDetails.defaultBranch - oldObjectId = $refId.objectId - } - ) - commits = @( - [ordered]@{ - comment = $CommitMessage - changes = @( - [ordered]@{ - changeType = "add" - item = [ordered]@{ - path = $newFilePath - } - newContent = [ordered]@{ - content = $(Get-Content $File -Raw) - contentType = "rawtext" - } - } - ) - } - ) - } | ConvertTo-Json -Depth 100 -Compress - - $Uri = "$($repoDetails.url)/pushes?api-version=7.2-preview.3" - $InvokeSplat = @{ - Method = 'Post' - Uri = $Uri - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSGitFile - -#region New-ADOPSGroupEntitlement - -function New-ADOPSGroupEntitlement { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$GroupOriginId, - - [Parameter(Mandatory)] - [ValidateSet('Express', 'Advanced', 'Stakeholder', 'Professional', 'EarlyAdopter')] - [string]$AccountLicenseType, - - [Parameter(Mandatory)] - [ValidateSet('projectReader', 'projectContributor', 'projectAdministrator', 'projectStakeholder')] - [string]$ProjectGroupType, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$ProjectId, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization, - - [Parameter()] - [switch]$Wait - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - # Group entitlements endpoint - $URI = "https://vsaex.dev.azure.com/$Organization/_apis/GroupEntitlements?api-version=7.1" - - # Initialize the request body - $Body = @{ - extensionRules = @( - @{ - id = 'ms.feed' - } - ) - group = @{ - origin = 'aad' - originId = $GroupOriginId - subjectKind = 'group' - } - licenseRule = @{ - licensingSource = 'account' - accountLicenseType = $AccountLicenseType - } - projectEntitlements = @( - @{ - group = @{ - groupType = $ProjectGroupType - } - projectRef = @{ - id = $ProjectId - } - } - ) - } - - $InvokeSplat = @{ - Method = 'Post' - Uri = $URI - Body = ($Body | ConvertTo-Json -Compress -Depth 10) - } - - $Out = InvokeADOPSRestMethod @InvokeSplat - - if ($PSBoundParameters.ContainsKey('Wait')) { - while ($Out.operationResult.status -eq 'inProgress') { - Start-Sleep -Seconds 1 - $Out = Invoke-ADOPSRestMethod -Uri $Out.operationResult.statusUrl -Method Get - } - } - - $Out -} -#endregion New-ADOPSGroupEntitlement - -#region New-ADOPSMergePolicy - -function New-ADOPSMergePolicy { - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$RepositoryId, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Branch, - - [Parameter()] - [Switch]$AllowNoFastForward, - - [Parameter()] - [Switch]$AllowSquash, - - [Parameter()] - [Switch]$AllowRebase, - - [Parameter()] - [Switch]$AllowRebaseMerge - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if (-Not ($Branch -match '^\w+/\w+/\w+$')) { - $Branch = "refs/heads/$Branch" - } - $GitBranchRef = $Branch - - $settings = [ordered]@{ - scope = @( - [ordered]@{ - repositoryId = $RepositoryId - refName = $GitBranchRef - matchKind = "exact" - } - ) - allowNoFastForward = $AllowNoFastForward.IsPresent - allowSquash = $AllowSquash.IsPresent - allowRebase = $AllowRebase.IsPresent - allowRebaseMerge = $AllowRebaseMerge.IsPresent - } - - - $Body = [ordered]@{ - type = [ordered]@{ - id = "fa4e907d-c16b-4a4c-9dfa-4916e5d171ab" - } - isBlocking = $true - isEnabled = $true - settings = $settings - } - - $Body = $Body | ConvertTo-Json -Depth 10 -Compress - - $InvokeSplat = @{ - Uri = "https://dev.azure.com/$Organization/$Project/_apis/policy/configurations?api-version=7.1-preview.1" - Method = 'POST' - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSMergePolicy - -#region New-ADOPSPipeline - -function New-ADOPSPipeline { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Name, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter(Mandatory)] - [ValidateScript( { - $_ -like '*.yaml' -or - $_ -like '*.yml' - }, - ErrorMessage = "Path must be to a yaml file in your repository like: folder/file.yaml or folder/file.yml")] - [string]$YamlPath, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Repository, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$FolderPath, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/pipelines?api-version=7.1-preview.1" - - try { - $RepositoryID = (Get-ADOPSRepository -Organization $Organization -Project $Project -Repository $Repository -ErrorAction Stop).id - } - catch { - throw "The specified Repository $Repository was not found." - } - - if ($null -eq $RepositoryID) { - throw "The specified Repository $Repository was not found." - } - - $Body = [ordered]@{ - "name" = $Name - "folder" = "\$FolderPath" - "configuration" = [ordered]@{ - "type" = "yaml" - "path" = $YamlPath - "repository" = [ordered]@{ - "id" = $RepositoryID - "type" = "azureReposGit" - } - } - } - $Body = $Body | ConvertTo-Json -Compress - - $InvokeSplat = @{ - Method = 'Post' - Uri = $URI - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSPipeline - -#region New-ADOPSProject - -function New-ADOPSProject { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [Alias('Project')] - [string]$Name, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Description, - - [Parameter(Mandatory)] - [ValidateSet('Private', 'Public')] - [string]$Visibility, - - [Parameter()] - [ValidateSet('Git', 'Tfvc')] - [string]$SourceControlType = 'Git', - - # The process type for the project, such as Basic, Agile, Scrum or CMMI - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$ProcessTypeName, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization, - - [Parameter()] - [switch]$Wait - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - # Get organization process templates - $URI = "https://dev.azure.com/$Organization/_apis/process/processes?api-version=7.1-preview.1" - - $InvokeSplat = @{ - Method = 'Get' - Uri = $URI - } - - $ProcessTemplates = (InvokeADOPSRestMethod @InvokeSplat).value - - if ([string]::IsNullOrWhiteSpace($ProcessTypeName)) { - $ProcessTemplateTypeId = $ProcessTemplates | Where-Object isDefault -eq $true | Select-Object -ExpandProperty id - } - else { - $ProcessTemplateTypeId = $ProcessTemplates | Where-Object name -eq $ProcessTypeName | Select-Object -ExpandProperty id - if ([string]::IsNullOrWhiteSpace($ProcessTemplateTypeId)) { - throw "The specified ProcessTypeName was not found amongst options: $($ProcessTemplates.name -join ', ')!" - } - } - - # Create project endpoint - $URI = "https://dev.azure.com/$Organization/_apis/projects?api-version=7.1-preview.4" - - $Body = [ordered]@{ - 'name' = $Name - 'visibility' = $Visibility - 'capabilities' = [ordered]@{ - 'versioncontrol' = [ordered]@{ - 'sourceControlType' = $SourceControlType - } - 'processTemplate' = [ordered]@{ - 'templateTypeId' = $ProcessTemplateTypeId - } - } - } - if (-not [string]::IsNullOrEmpty($Description)) { - $Body.Add('description', $Description) - } - $Body = $Body | ConvertTo-Json -Compress - - $InvokeSplat = @{ - Method = 'Post' - Uri = $URI - Body = $Body - } - - $Out = InvokeADOPSRestMethod @InvokeSplat - - if ($PSBoundParameters.ContainsKey('Wait')) { - $projectCreated = $Out.status - while ($projectCreated -ne 'succeeded') { - $projectCreated = (Invoke-ADOPSRestMethod -Uri $Out.url -Method Get).status - Start-Sleep -Seconds 1 - } - $Out = Get-ADOPSProject -Project $Name - } - - $Out -} -#endregion New-ADOPSProject - -#region New-ADOPSRepository - -function New-ADOPSRepository { - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Name, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $ProjectID = (Get-ADOPSProject -Name $Project -Organization $Organization).id - - $URI = "https://dev.azure.com/$Organization/_apis/git/repositories?api-version=7.1-preview.1" - $Body = "{""name"":""$Name"",""project"":{""id"":""$ProjectID""}}" - - $InvokeSplat = @{ - Uri = $URI - Method = 'Post' - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSRepository - -#region New-ADOPSServiceConnection - -function New-ADOPSServiceConnection { - [cmdletbinding(DefaultParameterSetName = 'ServicePrincipal')] - param( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [string]$TenantId, - - [Parameter(Mandatory)] - [string]$SubscriptionName, - - [Parameter(Mandatory)] - [string]$SubscriptionId, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [string]$ConnectionName, - - [Parameter()] - [string]$Description, - - [Parameter(Mandatory, ParameterSetName = 'ServicePrincipal')] - [Parameter(Mandatory, ParameterSetName = 'ManagedServiceIdentity')] - [pscredential]$ServicePrincipal, - - [Parameter(Mandatory, ParameterSetName = 'ManagedServiceIdentity')] - [switch]$ManagedIdentity, - - [Parameter(Mandatory, ParameterSetName = 'WorkloadIdentityFederation')] - [switch]$WorkloadIdentityFederation, - - [Parameter(ParameterSetName = 'WorkloadIdentityFederation')] - [string]$AzureScope, - - [Parameter(ParameterSetName = 'WorkloadIdentityFederation')] - [ValidateSet('Manual', 'Automatic')] - [string] - $CreationMode = 'Automatic' - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - # Get ProjectId - $ProjectInfo = Get-ADOPSProject -Organization $Organization -Project $Project - - # Set connection name if not set by parameter - if (-not $ConnectionName) { - $ConnectionName = $SubscriptionName -replace ' ' - } - - switch ($PSCmdlet.ParameterSetName) { - - 'ServicePrincipal' { - $authorization = [ordered]@{ - parameters = [ordered]@{ - tenantid = $TenantId - serviceprincipalid = $ServicePrincipal.UserName - authenticationType = 'spnKey' - serviceprincipalkey = $ServicePrincipal.GetNetworkCredential().Password - } - scheme = 'ServicePrincipal' - } - - $data = [ordered]@{ - subscriptionId = $SubscriptionId - subscriptionName = $SubscriptionName - environment = 'AzureCloud' - scopeLevel = 'Subscription' - creationMode = 'Manual' - } - } - - 'ManagedServiceIdentity' { - $authorization = [ordered]@{ - parameters = [ordered]@{ - tenantid = $TenantId - serviceprincipalid = $ServicePrincipal.UserName - serviceprincipalkey = $ServicePrincipal.GetNetworkCredential().Password - } - scheme = 'ManagedServiceIdentity' - } - - $data = [ordered]@{ - subscriptionId = $SubscriptionId - subscriptionName = $SubscriptionName - environment = 'AzureCloud' - scopeLevel = 'Subscription' - } - } - - 'WorkloadIdentityFederation' { - if ($PSBoundParameters.ContainsKey('AzureScope')) { - $AuthParams = [ordered]@{ - tenantid = $TenantId - scope = $AzureScope - } - } - else { - $AuthParams = [ordered]@{ - tenantid = $TenantId - } - } - - $authorization = [ordered]@{ - parameters = $AuthParams - scheme = 'WorkloadIdentityFederation' - } - - $data = [ordered]@{ - subscriptionId = $SubscriptionId - subscriptionName = $SubscriptionName - environment = 'AzureCloud' - scopeLevel = 'Subscription' - creationMode = $CreationMode - isDraft = ($CreationMode = 'Manual') ? $True : $False - } - } - } - - # Create body for the API call - $Body = [ordered]@{ - data = $data - name = $ConnectionName - description = $Description - type = 'AzureRM' - url = 'https://management.azure.com/' - authorization = $authorization - isShared = $false - isReady = $true - serviceEndpointProjectReferences = @( - [ordered]@{ - projectReference = [ordered]@{ - id = $ProjectInfo.Id - name = $Project - } - name = $ConnectionName - } - ) - } | ConvertTo-Json -Depth 10 - - # Run function - $URI = "https://dev.azure.com/$Organization/$Project/_apis/serviceendpoint/endpoints?api-version=7.2-preview.4" - $InvokeSplat = @{ - Uri = $URI - Method = 'POST' - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSServiceConnection - -#region New-ADOPSUserStory - -function New-ADOPSUserStory { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Title, - - [Parameter(Mandatory)] - [string]$ProjectName, - - [Parameter()] - [string]$Description, - - [Parameter()] - [string]$Tags, - - [Parameter()] - [string]$Priority, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $URI = "https://dev.azure.com/$Organization/$ProjectName/_apis/wit/workitems/`$User Story?api-version=7.1-preview.3" - $Method = 'POST' - - $desc = $Description.Replace('"', "'") - $Body = "[ - { - `"op`": `"add`", - `"path`": `"/fields/System.Title`", - `"value`": `"$($Title)`" - }, - { - `"op`": `"add`", - `"path`": `"/fields/System.Description`", - `"value`": `"$($desc)`" - }, - { - `"op`": `"add`", - `"path`": `"/fields/System.Tags`", - `"value`": `"$($Tags)`" - }, - { - `"op`": `"add`", - `"path`": `"/fields/Microsoft.VSTS.Common.Priority`", - `"value`": `"$($Priority)`" - }, - ]" - - $ContentType = 'application/json-patch+json' - - $InvokeSplat = @{ - Uri = $URI - ContentType = $ContentType - Method = $Method - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSUserStory - -#region New-ADOPSVariableGroup - -function New-ADOPSVariableGroup { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$VariableGroupName, - - [Parameter(Mandatory, ParameterSetName = 'VariableSingle')] - [string]$VariableName, - - [Parameter(Mandatory, ParameterSetName = 'VariableSingle')] - [string]$VariableValue, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter(ParameterSetName = 'VariableSingle')] - [switch]$IsSecret, - - [Parameter(Mandatory, ParameterSetName = 'VariableHashtable')] - [ValidateScript( - { - $_ | ForEach-Object { $_.Keys -Contains 'Name' -and $_.Keys -Contains 'IsSecret' -and $_.Keys -Contains 'Value' -and $_.Keys.count -eq 3 } - }, - ErrorMessage = 'The hashtable must contain the following keys: Name, IsSecret, Value')] - [hashtable[]]$VariableHashtable, - - [Parameter()] - [string]$Description, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $ProjectInfo = Get-ADOPSProject -Organization $Organization -Project $Project - - $URI = "https://dev.azure.com/${Organization}/_apis/distributedtask/variablegroups?api-version=7.1-preview.2" - $Method = 'POST' - - if ($VariableName) { - $Body = @{ - Name = $VariableGroupName - Description = $Description - Type = 'Vsts' - variableGroupProjectReferences = @(@{ - Name = $VariableGroupName - Description = $Description - projectReference = @{ - Id = $ProjectInfo.Id - } - }) - variables = @{ - $VariableName = @{ - isSecret = $IsSecret.IsPresent - value = $VariableValue - } - } - } | ConvertTo-Json -Depth 10 - } - else { - - $Variables = @{} - foreach ($Hashtable in $VariableHashtable) { - $Variables.Add( - $Hashtable.Name, @{ - isSecret = $Hashtable.IsSecret - value = $Hashtable.Value - } - ) - } - - $Body = @{ - Name = $VariableGroupName - Description = $Description - Type = 'Vsts' - variableGroupProjectReferences = @(@{ - Name = $VariableGroupName - Description = $Description - projectReference = @{ - Id = $($ProjectInfo.Id) - } - }) - variables = $Variables - } | ConvertTo-Json -Depth 10 - } - - InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body -} -#endregion New-ADOPSVariableGroup - -#region New-ADOPSWiki - -function New-ADOPSWiki { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$WikiName, - - [Parameter(Mandatory)] - [string]$WikiRepository, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [string]$WikiRepositoryPath = '/', - - [Parameter()] - [string]$GitBranch = 'main', - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - try { - $ProjectId = (Get-ADOPSProject -Project $Project).id - } - catch { - throw "The specified Project $Project was not found." - } - if ($null -eq $ProjectId) { - throw "The specified Project $Project was not found." - } - - try { - $RepositoryId = (Get-ADOPSRepository -Project $Project -Repository $WikiRepository).id - } - catch { - throw "The specified Repository $WikiRepository was not found." - } - - if ($null -eq $RepositoryID) { - throw "The specified Repository $WikiRepository was not found." - } - - $URI = "https://dev.azure.com/$Organization/_apis/wiki/wikis?api-version=7.1-preview.2" - - $Method = 'Post' - $Body = [ordered]@{ - 'type' = 'codeWiki' - 'name' = $WikiName - 'projectId' = $ProjectId - 'repositoryId' = $RepositoryId - 'mappedPath' = $WikiRepositoryPath - 'version' = @{'version' = $GitBranch } - } - - $InvokeSplat = @{ - Uri = $URI - Method = $Method - Body = $Body | ConvertTo-Json -Compress - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion New-ADOPSWiki - -#region Remove-ADOPSRepository - -function Remove-ADOPSRepository { - [CmdletBinding()] - param( - [Parameter(Mandatory)] - [string]$RepositoryID, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$RepositoryID`?api-version=7.1-preview.1" - - $result = InvokeADOPSRestMethod -Uri $Uri -Method Delete - - if ($result.psobject.properties.name -contains 'value') { - Write-Output -InputObject $result.value - } - else { - Write-Output -InputObject $result - } -} -#endregion Remove-ADOPSRepository - -#region Remove-ADOPSVariableGroup - -function Remove-ADOPSVariableGroup { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$VariableGroupName, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/variablegroups?api-version=7.1-preview.2" - $VariableGroups = (InvokeADOPSRestMethod -Uri $Uri -Method 'Get').value - - $GroupToRemove = $VariableGroups | Where-Object name -eq $VariableGroupName - if ($null -eq $GroupToRemove) { - throw "Could not find group $VariableGroupName! Groups found: $($VariableGroups.name -join ', ')." - } - - $ProjectId = (Get-ADOPSProject -Organization $Organization -Project $Project).id - - $URI = "https://dev.azure.com/$Organization/_apis/distributedtask/variablegroups/$($GroupToRemove.id)?projectIds=$ProjectId&api-version=7.1-preview.2" - $null = InvokeADOPSRestMethod -Uri $Uri -Method 'Delete' -} -#endregion Remove-ADOPSVariableGroup - -#region Revoke-ADOPSPipelinePermission - -function Revoke-ADOPSPipelinePermission { - [CmdletBinding()] - param ( - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [string]$Project, - - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [switch]$AllPipelines, - - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [int]$PipelineId, - - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [ResourceType]$ResourceType, - - [Parameter(Mandatory, ParameterSetName = 'AllPipelines')] - [Parameter(Mandatory, ParameterSetName = 'SinglePipeline')] - [string]$ResourceId, - - [Parameter(ParameterSetName = 'AllPipelines')] - [Parameter(ParameterSetName = 'SinglePipeline')] - [string]$Organization - ) - - SetADOPSPipelinePermission @PSBoundParameters -Authorized $false -} -#endregion Revoke-ADOPSPipelinePermission - -#region Save-ADOPSPipelineTask - -function Save-ADOPSPipelineTask { - [CmdletBinding(DefaultParameterSetName = 'InputData')] - param ( - [Parameter(ParameterSetName = 'InputData')] - [Parameter(ParameterSetName = 'InputObject')] - [string]$Organization, - - [Parameter(ParameterSetName = 'InputData')] - [Parameter(ParameterSetName = 'InputObject')] - [string]$Path = '.', - - [Parameter(Mandatory, ParameterSetName = 'InputData')] - [string]$TaskId, - - [Parameter(Mandatory, ParameterSetName = 'InputData')] - [version]$TaskVersion, - - [Parameter(ParameterSetName = 'InputData')] - [string]$FileName, - - [Parameter(Mandatory, ParameterSetName = 'InputObject', ValueFromPipeline, Position = 0)] - [psobject[]]$InputObject - ) - begin { - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - } - process { - switch ($PSCmdlet.ParameterSetName) { - 'InputData' { - if ([string]::IsNullOrEmpty($FileName)) { - $FileName = "$TaskId.$($TaskVersion.ToString(3)).zip" - } - if (-Not $FileName -match '.zip$' ) { - $FileName = "$FileName.zip" - } - - [array]$FilesToDownload = @{ - TaskId = $TaskId - TaskVersionString = $TaskVersion.ToString(3) - OutputFile = Join-Path -Path $Path -ChildPath $FileName - } - } - 'InputObject' { - [array]$FilesToDownload = foreach ($o in $InputObject) { - @{ - TaskId = $o.id - TaskVersionString = "$($o.version.major).$($o.version.minor).$($o.version.patch)" - OutputFile = Join-Path -Path $Path -ChildPath "$($o.name)-$($o.id)-$($o.version.major).$($o.version.minor).$($o.version.patch).zip" - } - } - } - } - - foreach ($File in $FilesToDownload) { - $Url = "https://dev.azure.com/$Organization/_apis/distributedtask/tasks/$($File.TaskId)/$($File.TaskversionString)" - InvokeADOPSRestMethod -Uri $Url -Method Get -OutFile $File.OutputFile - } - } - end {} -} -#endregion Save-ADOPSPipelineTask - -#region Set-ADOPSArtifactFeed - -function Set-ADOPSArtifactFeed { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [Alias('Name')] - [string]$FeedId, - - [Parameter()] - [string]$Description, - - [Parameter()] - [Alias('IncludeUpstream')] - [bool]$UpstreamEnabled - ) - - if ( - -not ($PSBoundParameters.ContainsKey('Description')) -and - -not ($PSBoundParameters.ContainsKey('UpstreamEnabled')) - ) { - Write-Verbose 'Nothing to do. Exiting early' - } - else { - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://feeds.dev.azure.com/${Organization}/${Project}/_apis/packaging/feeds/${FeedId}?api-version=7.2-preview.1" - $Method = 'Patch' - - $Body = [ordered]@{} - if ($PSBoundParameters.ContainsKey('Description')) { - $Body['description'] = $Description - } - if ($PSBoundParameters.ContainsKey('UpstreamEnabled')) { - $Body['upstreamEnabled'] = $UpstreamEnabled - if ($UpstreamEnabled -eq $true) { - $upstreamSources = @( - @{ - name = "npmjs" - protocol = "npm" - location = "https://registry.npmjs.org/" - displayLocation = "https://registry.npmjs.org/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "NuGet Gallery" - protocol = "nuget" - location = "https://api.nuget.org/v3/index.json" - displayLocation = "https://api.nuget.org/v3/index.json" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "PowerShell Gallery" - protocol = "nuget" - location = "https://www.powershellgallery.com/api/v2/" - displayLocation = "https://www.powershellgallery.com/api/v2/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "PyPI" - protocol = "pypi" - location = "https://pypi.org/" - displayLocation = "https://pypi.org/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "Maven Central" - protocol = "Maven" - location = "https://repo.maven.apache.org/maven2/" - displayLocation = "https://repo.maven.apache.org/maven2/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "Google Maven Repository" - protocol = "Maven" - location = "https://dl.google.com/android/maven2/" - displayLocation = "https://dl.google.com/android/maven2/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "JitPack" - protocol = "Maven" - location = "https://jitpack.io/" - displayLocation = "https://jitpack.io/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "Gradle Plugins" - protocol = "Maven" - location = "https://plugins.gradle.org/m2/" - displayLocation = "https://plugins.gradle.org/m2/" - upstreamSourceType = "public" - status = "ok" - } - @{ - name = "crates.io" - protocol = "Cargo" - location = "https://index.crates.io/" - displayLocation = "https://index.crates.io/" - upstreamSourceType = "public" - status = "ok" - } - ) - $body.Add('upstreamSources', $upstreamSources) - } - } - - $Body = $Body | ConvertTo-Json -Compress - - $InvokeSplat = @{ - Uri = $Uri - Method = $Method - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat - } -} -#endregion Set-ADOPSArtifactFeed - -#region Set-ADOPSBuildDefinition - -function Set-ADOPSBuildDefinition { - [CmdletBinding()] - Param( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [Alias('Definition')] - [Object]$DefinitionObject - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $project = $DefinitionObject.project.id - $id = $DefinitionObject.id - - $Uri = "https://dev.azure.com/${Organization}/${project}/_apis/build/definitions/${id}?api-version=7.2-preview.7" - $Method = 'Put' - - if (-Not ($DefinitionObject -is [string])) { - $DefinitionObject = $DefinitionObject | ConvertTo-Json -Compress -Depth 100 - } - - $InvokeSplat = @{ - Uri = $Uri - Method = $Method - Body = $DefinitionObject - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion Set-ADOPSBuildDefinition - -#region Set-ADOPSElasticPool - -function Set-ADOPSElasticPool { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [int]$PoolId, - - [Parameter(Mandatory)] - $ElasticPoolObject, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/_apis/distributedtask/elasticpools/$PoolId`?api-version=7.1-preview.1" - - if ($ElasticPoolObject.GetType().Name -eq 'String') { - $Body = $ElasticPoolObject - } - else { - try { - $Body = $ElasticPoolObject | ConvertTo-Json -Depth 100 - } - catch { - throw 'Unable to convert the content of the ElasticPoolObject to json.' - } - } - - $Method = 'PATCH' - $ElasticPoolInfo = InvokeADOPSRestMethod -Uri $Uri -Method $Method -Body $Body - Write-Output $ElasticPoolInfo -} -#endregion Set-ADOPSElasticPool - -#region Set-ADOPSGitPermission - -function Set-ADOPSGitPermission { - [CmdletBinding()] - param ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization, - - [Parameter(Mandatory)] - [Alias('ProjectId')] - [string]$Project, - - [Parameter(Mandatory)] - [Alias('RepositoryId')] - [string]$Repository, - - [Parameter(Mandatory)] - [ValidatePattern('^[a-z]{3,5}\.[a-zA-Z0-9]{40,}$', ErrorMessage = 'Descriptor must be in the descriptor format')] - [string]$Descriptor, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [AccessLevels[]]$Allow, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [AccessLevels[]]$Deny - ) - - # Allow input of names instead of IDs - if ($Project -notmatch '^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}$') { - $Project = Get-ADOPSProject -Name $Project | Select-Object -ExpandProperty id - if ($null -eq $Project) { - throw "No project named $Project found." - } - } - if ($Repository -notmatch '^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}$') { - $Repository = Get-ADOPSRepository -Repository $Repository -Project $Project - if ($null -eq $Repository) { - throw "No repository named $Repository in project $Project found." - } - } - - - if (-not $Allow -and -not $Deny) { - Write-Verbose 'No allow or deny rules set' - } - else { - if ($null -eq $Allow) { - $allowRules = 0 - } - else { - $allowRules = ([accesslevels]$Allow).value__ - } - if ($null -eq $Deny) { - $denyRules = 0 - } - else { - $denyRules = ([accesslevels]$Deny).value__ - } - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $SubjectDescriptor = (InvokeADOPSRestMethod -Uri "https://vssps.dev.azure.com/$Organization/_apis/identities?subjectDescriptors=$Descriptor&queryMembership=None&api-version=7.1-preview.1" -Method Get).value.descriptor - - $Body = [ordered]@{ - token = "repov2/$Project/$Repository" - merge = $true - accessControlEntries = @( - [ordered]@{ - allow = $allowRules - deny = $denyRules - descriptor = $SubjectDescriptor - } - ) - } | ConvertTo-Json -Compress -Depth 10 - - $Uri = "https://dev.azure.com/$Organization/_apis/accesscontrolentries/2e9eb7ed-3c0a-47d4-87c1-0ffdd275fd87?api-version=7.1-preview.1" - - $InvokeSplat = @{ - Uri = $Uri - Method = 'Post' - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat - } -} -#endregion Set-ADOPSGitPermission - -#region Set-ADOPSPipelineRetentionSettings - -function Set-ADOPSPipelineRetentionSettings { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - $Values - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/build/retention?api-version=7.1-preview.1" - - $Body = ConvertRetentionSettingsToPatchBody -Values $Values | ConvertTo-Json - Write-Debug $Body - - $Response = InvokeADOPSRestMethod -Uri $Uri -Method Patch -Body $Body - Write-Debug $Response - - $Settings = ConvertRetentionSettingsGetToPatch -Response $Response - - Write-Output $Settings -} -#endregion Set-ADOPSPipelineRetentionSettings - -#region Set-ADOPSPipelineSettings - -function Set-ADOPSPipelineSettings { - [CmdletBinding()] - param ( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter(Mandatory)] - $Values - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/build/generalsettings?api-version=7.1-preview.1" - - $Body = $Values | ConvertTo-Json -Compress - $Settings = InvokeADOPSRestMethod -Uri $Uri -Method 'PATCH' -Body $Body - - Write-Output $Settings -} -#endregion Set-ADOPSPipelineSettings - -#region Set-ADOPSProject - -function Set-ADOPSProject { - [CmdletBinding(DefaultParameterSetName = 'ProjectName')] - param ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization, - - [Parameter(Mandatory, ParameterSetName = 'ProjectId')] - [ValidateScript({ - [guid]::Parse($_) - }, ErrorMessage = 'ProjectID format is wrong.')] - [string]$ProjectId, - - [Parameter(Mandatory, ParameterSetName = 'ProjectName')] - [ValidateNotNullOrEmpty()] - [Alias('Project', 'Name')] - [string]$ProjectName, - - [Parameter()] - [string]$Description, - - [Parameter()] - [ValidateSet('Private', 'Public')] - [string]$Visibility, - - [Parameter()] - [switch]$Wait - ) - - if (-Not ($PSBoundParameters.ContainsKey('Description')) -and -Not ($PSBoundParameters.ContainsKey('Visibility'))) { - Write-Verbose 'Nothing to update. Exiting.' - } - else { - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - if ($PSCmdlet.ParameterSetName -eq 'ProjectName') { - $ProjectId = Get-ADOPSProject -Name $ProjectName | Select-Object -ExpandProperty id - } - - $uri = "https://dev.azure.com/${Organization}/_apis/projects/${ProjectId}?api-version=7.2-preview.4" - - - $body = [ordered]@{} - - if ($PSBoundParameters.ContainsKey('Description')) { - $body.Add('description', $Description) - } - - if (-not [string]::IsNullOrEmpty($Visibility)) { - $body.Add('visibility', $Visibility.ToLower()) - } - - $body = $body | ConvertTo-Json -Compress - - $InvokeSplat = [ordered]@{ - 'Uri' = $uri - 'Method' = 'Patch' - 'Body' = $body - } - - $res = InvokeADOPSRestMethod @InvokeSplat - - if ($PSBoundParameters.ContainsKey('Wait')) { - $i = 0 - while (($res.status -notin @('cancelled', 'failed', 'succeeded')) -and $i -le 20) { - $res = InvokeADOPSRestMethod -Uri $res.url -Method Get - $i++ - Start-Sleep -Seconds 1 - } - if ($i -ge 20) { - Write-Verbose 'Status still not complete, failed, or canceled. Please verify project update.' - } - } - - $res - } -} -#endregion Set-ADOPSProject - -#region Set-ADOPSRepository - -function Set-ADOPSRepository { - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$RepositoryId, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Project, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Organization, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$DefaultBranch, - - [Parameter()] - [bool]$IsDisabled, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$NewName - ) - - if ( ([string]::IsNullOrEmpty($DefaultBranch)) -and ([string]::IsNullOrEmpty($NewName)) -and (-Not $PSBoundParameters.ContainsKey('IsDisabled')) ) { - # Nothing to do, exit early - } - else { - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $URI = "https://dev.azure.com/${Organization}/${Project}/_apis/git/repositories/${RepositoryId}?api-version=7.1-preview.1" - - $InvokeSplat = @{ - URI = $Uri - Method = 'Patch' - } - - if ($PSBoundParameters.ContainsKey('IsDisabled') -and ($false -eq $IsDisabled)) { - # Enabling a repo needs to be done in a separate call before changing any other settings. - $Body = [ordered]@{ - 'isDisabled' = $IsDisabled - } - $InvokeSplat.Body = $Body | ConvertTo-Json -Compress - try { - InvokeADOPSRestMethod @InvokeSplat - } - catch { - if (($_.ErrorDetails.Message | ConvertFrom-Json).message -eq 'The repository change is not supported.') { - Write-Warning 'Failed to enable the repo. This is most likely because it is already enabled.' - } - else { - throw $_ - } - } - } - - $Body = [ordered]@{} - - if (-Not [string]::IsNullOrEmpty($NewName)) { - $Body.Add('name', $NewName) - } - - if (-Not [string]::IsNullOrEmpty($DefaultBranch)) { - if (-Not ($DefaultBranch -match '^\w+/\w+/\w+$')) { - $DefaultBranch = "refs/heads/$DefaultBranch" - } - $Body.Add('defaultBranch', $DefaultBranch) - } - - if ($body.Keys.Count -gt 0) { - $InvokeSplat.Body = $Body | ConvertTo-Json -Compress - try { - InvokeADOPSRestMethod @InvokeSplat - } - catch { - if (($_.ErrorDetails.Message | ConvertFrom-Json).message -like "TF401019*") { - Write-Warning 'Failed to update the repo. This may happen if the repo is disabled. Make sure it is enabled, or add -IsDisabled:$false' - } - else { - throw $_ - } - } - } - - if ($PSBoundParameters.ContainsKey('IsDisabled') -and ($true -eq $IsDisabled)) { - # Disabling a repo needs to be done in a separate call and after any other changes. - $Body = [ordered]@{ - 'isDisabled' = $IsDisabled - } - $InvokeSplat.Body = $Body | ConvertTo-Json -Compress - try { - InvokeADOPSRestMethod @InvokeSplat - } - catch { - if (($_.ErrorDetails.Message | ConvertFrom-Json).message -eq 'The repository change is not supported.') { - Write-Warning 'Failed to disable the repo. This is most likely because it is already disabled.' - } - else { - throw $_ - } - } - } - } -} -#endregion Set-ADOPSRepository - -#region Set-ADOPSServiceConnection - -function Set-ADOPSServiceConnection { - [CmdletBinding(DefaultParameterSetName = 'ServicePrincipal')] - param ( - [Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [string]$TenantId, - - [Parameter(Mandatory)] - [string]$SubscriptionName, - - [Parameter(Mandatory)] - [string]$SubscriptionId, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter(Mandatory)] - [guid]$ServiceEndpointId, - - [Parameter()] - [string]$ConnectionName, - - [Parameter()] - [string]$Description, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$EndpointOperation, - - [Parameter(Mandatory, ParameterSetName = 'ServicePrincipal')] - [pscredential]$ServicePrincipal, - - [Parameter(Mandatory, ParameterSetName = 'ManagedServiceIdentity')] - [switch]$ManagedIdentity, - - [Parameter(Mandatory, ParameterSetName = 'WorkloadIdentityFederation')] - [string]$ServicePrincipalId, - - [Parameter(Mandatory, ParameterSetName = 'WorkloadIdentityFederation')] - [string]$WorkloadIdentityFederationIssuer, - - [Parameter(Mandatory, ParameterSetName = 'WorkloadIdentityFederation')] - [string]$WorkloadIdentityFederationSubject - - ) - - process { - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - # Get ProjectId - $ProjectInfo = Get-ADOPSProject -Organization $Organization -Project $Project - - # Set connection name if not set by parameter - if (-not $ConnectionName) { - $ConnectionName = $SubscriptionName -replace " " - } - - switch ($PSCmdlet.ParameterSetName) { - - 'ServicePrincipal' { - $authorization = [ordered]@{ - parameters = [ordered]@{ - tenantid = $TenantId - serviceprincipalid = $ServicePrincipal.UserName - authenticationType = "spnKey" - serviceprincipalkey = $ServicePrincipal.GetNetworkCredential().Password - } - scheme = "ServicePrincipal" - } - - $data = [ordered]@{ - subscriptionId = $SubscriptionId - subscriptionName = $SubscriptionName - environment = "AzureCloud" - scopeLevel = "Subscription" - creationMode = "Manual" - } - } - - 'ManagedServiceIdentity' { - $authorization = [ordered]@{ - parameters = [ordered]@{ - tenantid = $TenantId - } - scheme = "ManagedServiceIdentity" - } - } - - 'WorkloadIdentityFederation' { - $authorization = [ordered]@{ - parameters = [ordered]@{ - tenantid = $TenantId - serviceprincipalid = $ServicePrincipalId - workloadIdentityFederationIssuer = $WorkloadIdentityFederationIssuer - workloadIdentityFederationSubject = $WorkloadIdentityFederationSubject - } - scheme = "WorkloadIdentityFederation" - } - - $data = [ordered]@{ - subscriptionId = $SubscriptionId - subscriptionName = $SubscriptionName - environment = "AzureCloud" - scopeLevel = "Subscription" - creationMode = "Manual" - } - } - } - - # Create body for the API call - $Body = [ordered]@{ - authorization = $authorization - data = $data - description = $Description - id = $ServiceEndpointId - isReady = $true - isShared = $false - name = $ConnectionName - serviceEndpointProjectReferences = @( - [ordered]@{ - projectReference = [ordered]@{ - id = $ProjectInfo.Id - name = $Project - } - name = $ConnectionName - } - ) - type = "AzureRM" - url = "https://management.azure.com/" - } | ConvertTo-Json -Depth 10 - - if ($PSBoundParameters.ContainsKey('EndpointOperation')) { - $URI = "https://dev.azure.com/$Organization/_apis/serviceendpoint/endpoints/$ServiceEndpointId`?operation=$EndpointOperation`&api-version=7.1-preview.4" - } - else { - $URI = "https://dev.azure.com/$Organization/_apis/serviceendpoint/endpoints/$ServiceEndpointId`?api-version=7.1-preview.4" - } - - $InvokeSplat = @{ - Uri = $URI - Method = "PUT" - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat - } -} -#endregion Set-ADOPSServiceConnection - -#region Start-ADOPSPipeline - -function Start-ADOPSPipeline { - param ( - [Parameter(Mandatory)] - [string]$Name, - - [Parameter(Mandatory)] - [string]$Project, - - [Parameter()] - [string]$Branch = 'main', - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $AllPipelinesURI = "https://dev.azure.com/$Organization/$Project/_apis/pipelines?api-version=7.1-preview.1" - $AllPipelines = InvokeADOPSRestMethod -Method Get -Uri $AllPipelinesURI - $PipelineID = ($AllPipelines.value | Where-Object -Property Name -EQ $Name).id - - if ([string]::IsNullOrEmpty($PipelineID)) { - throw "No pipeline with name $Name found." - } - - if ($Branch -notmatch '^refs/.*') { - $Branch = 'refs/heads/' + $Branch - } - $URI = "https://dev.azure.com/$Organization/$Project/_apis/pipelines/$PipelineID/runs?api-version=7.1-preview.1" - $Body = '{"stagesToSkip":[],"resources":{"repositories":{"self":{"refName":"' + $Branch + '"}}},"variables":{}}' - - $InvokeSplat = @{ - Method = 'Post' - Uri = $URI - Body = $Body - } - - InvokeADOPSRestMethod @InvokeSplat -} -#endregion Start-ADOPSPipeline - -#region Test-ADOPSYamlFile - -function Test-ADOPSYamlFile { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$Project, - - [Parameter(Mandatory)] - [ValidateScript({ - $_ -match '.*\.y[aA]{0,1}ml$' - }, ErrorMessage = 'Fileextension must be ".yaml" or ".yml"')] - [string]$File, - - [Parameter(Mandatory)] - [int]$PipelineId, - - [Parameter()] - [string]$Organization - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://dev.azure.com/$Organization/$Project/_apis/pipelines/$PipelineId/runs?api-version=7.1-preview.1" - - $FileData = Get-Content $File -Raw - - $Body = @{ - previewRun = $true - templateParameters = @{} - resources = @{} - yamlOverride = $FileData - } | ConvertTo-Json -Depth 10 -Compress - - $InvokeSplat = @{ - Uri = $URI - Method = 'Post' - Body = $Body - } - - try { - $Result = InvokeADOPSRestMethod @InvokeSplat - Write-Output "$file validation success." - } - catch [Microsoft.PowerShell.Commands.HttpResponseException] { - if ($_.ErrorDetails.Message) { - $r = $_.ErrorDetails.Message | ConvertFrom-Json - if ($r.typeName -like '*PipelineValidationException*') { - Write-Warning "Validation failed:`n$($r.message)" - } - else { - throw $_ - } - } - } -} -#endregion Test-ADOPSYamlFile - -function Get-ADOPSMembership { - param ([Parameter()] - [string]$Organization, - - [Parameter(Mandatory)] - [string] - $Descriptor, - - # The default value for direction is 'up' meaning return all memberships where the subject is a member (e.g. all groups the subject is a member of). Alternatively, passing the direction as 'down' will return all memberships where the subject is a container (e.g. all members of the subject group). - [Parameter()] - [string] - [ValidateSet('up', 'down')] - $Direction = 'up' - ) - - # If user didn't specify org, get it from saved context - if ([string]::IsNullOrEmpty($Organization)) { - $Organization = GetADOPSDefaultOrganization - } - - $Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/Memberships/$Descriptor`?direction=$Direction&depth=1&api-version=7.2-preview.1" - $Response = InvokeADOPSRestMethod -Uri $Uri -Method 'GET' - - switch ($Direction) { - 'up' { - if ($Response.value.count -ne 0) { - $Membership = $Response.value.containerDescriptor -} else { -$Membership = $Response.value -} - } - 'down' { - if ($Response.value.count -ne 0) { -$Membership = $Response.value.memberDescriptor -} else { -$Membership = $Response.value -} - - } - } - - $Membership | ForEach-Object -begin { - $Members = New-object System.Collections.ArrayList - } -process { - $MemberType = ($_).split('.')[0] - $Identity = $_ - switch ($MemberType) { - 'aadgp' { - Get-ADOPSGroup -Descriptor $Identity | ForEach-Object { $Members.Add($_) | Out-Null } - } - 'vssgp' { - Get-ADOPSGroup -Descriptor $Identity | ForEach-Object { $Members.Add($_) | Out-Null } - } - 'aad' { - Get-ADOPSUser -Descriptor $Identity | ForEach-Object { $Members.Add($_) | Out-Null } - } - } - } - - Write-Output $Members -} \ No newline at end of file diff --git a/tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml b/tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml deleted file mode 100644 index ab9a7ec77..000000000 --- a/tests/Azdo/ADOPS/1.1.2/en-US/ADOPS-help.xml +++ /dev/null @@ -1,11788 +0,0 @@ - - - - - Connect-ADOPS - Connect - ADOPS - - Establish a connection to Azure DevOps. - - - - Establish a OAuth connection to one or more Azure DevOps organizations. If the logged on user does not have access to the given organization you may need to specify TenantID. - - - - Connect-ADOPS - - Interactive - - Connect to an Azure DevOps organization using an interactive browser login. - - - SwitchParameter - - - False - - - Organization - - Name of the Azure DevOps organization or url of the organization like `https://dev.azure.com/MyOrg/` - - String - - String - - - None - - - SkipVerification - - This switch is necessary to set when using System.AccessToken as OAuth token in Azure DevOps. This will bypass the access verification. If this flag is set we will not check if the provided account can access the selected organization, and there may be access errors when API calls are made. - - - SwitchParameter - - - False - - - TenantId - - Specify a tenant to connect to, by id. - - String - - String - - - None - - - - Connect-ADOPS - - ManagedIdentity - - Connect to an Azure DevOps organization using a managed identity. - - - SwitchParameter - - - False - - - Organization - - Name of the Azure DevOps organization or url of the organization like `https://dev.azure.com/MyOrg/` - - String - - String - - - None - - - SkipVerification - - This switch is necessary to set when using System.AccessToken as OAuth token in Azure DevOps. This will bypass the access verification. If this flag is set we will not check if the provided account can access the selected organization, and there may be access errors when API calls are made. - - - SwitchParameter - - - False - - - TenantId - - Specify a tenant to connect to, by id. - - String - - String - - - None - - - - Connect-ADOPS - - OAuthToken - - Connect to an Azure DevOps organization using an existing access token. - - String - - String - - - None - - - Organization - - Name of the Azure DevOps organization or url of the organization like `https://dev.azure.com/MyOrg/` - - String - - String - - - None - - - SkipVerification - - This switch is necessary to set when using System.AccessToken as OAuth token in Azure DevOps. This will bypass the access verification. If this flag is set we will not check if the provided account can access the selected organization, and there may be access errors when API calls are made. - - - SwitchParameter - - - False - - - TenantId - - Specify a tenant to connect to, by id. - - String - - String - - - None - - - - - - Interactive - - Connect to an Azure DevOps organization using an interactive browser login. - - SwitchParameter - - SwitchParameter - - - False - - - ManagedIdentity - - Connect to an Azure DevOps organization using a managed identity. - - SwitchParameter - - SwitchParameter - - - False - - - OAuthToken - - Connect to an Azure DevOps organization using an existing access token. - - String - - String - - - None - - - Organization - - Name of the Azure DevOps organization or url of the organization like `https://dev.azure.com/MyOrg/` - - String - - String - - - None - - - SkipVerification - - This switch is necessary to set when using System.AccessToken as OAuth token in Azure DevOps. This will bypass the access verification. If this flag is set we will not check if the provided account can access the selected organization, and there may be access errors when API calls are made. - - SwitchParameter - - SwitchParameter - - - False - - - TenantId - - Specify a tenant to connect to, by id. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - Connect-ADOPS -Organization 'ADOPS' - - Connect to an Azure DevOps organization interactively. - - - - -------------------------- Example 2 -------------------------- - Connect-ADOPS -ManagedIdentity -Organization 'ADOPS' -TenantId $TenantId - - Connect to an Azure DevOps organization using a managed identity, specifying the tenant. - - - - -------------------------- Example 3 -------------------------- - Connect-ADOPS -OAuthToken $AccessToken -Organization 'ADOPS' -SkipVerification - - Connect to an Azure DevOps organization using an existing access token. It will not verify that the token has access to the selected organization, and may result in failed API calls. - - - - - - - - Disconnect-ADOPS - Disconnect - ADOPS - - Remove an established connection to Azure DevOps. - - - - Remove an established connection to Azure DevOps. - - - - Disconnect-ADOPS - - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Disconnect-ADOPS -Organization 'ADOPS' - - Removes the established connection to the organization "ADOPS". - - - - -------------------------- Example 2 -------------------------- - PS C:\> Disconnect-ADOPS - - Removes the established connection without specifying organization, if there is only one. - - - - - - - - Get-ADOPSAgentQueue - Get - ADOPSAgentQueue - - Gets one or more agent queues (pools) connected to a project - - - - This command will get all or one named agent queue. Agent queues is what the Azure DevOps GUI refers to ass pools, connected to a project, as in the `Project Settings` -> `Pipelines` -> `Agent Pools` page. - - - - Get-ADOPSAgentQueue - - Organization - - The organization to get queues from. - - String - - String - - - None - - - Project - - The project to get queues from. - - String - - String - - - None - - - QueueName - - Specifies one specific queue to get. - - String - - String - - - None - - - - - - Organization - - The organization to get queues from. - - String - - String - - - None - - - Project - - The project to get queues from. - - String - - String - - - None - - - QueueName - - Specifies one specific queue to get. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSAgentQueue -Project 'MyProject' - - This command lists all agent queues in the `MyProject` project - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSAgentQueue -Project 'MyProject' -QueueName 'MyQueue' - - This command gets the `MyQueue` agent queue in the `MyProject` project. If no queue named `MyQueue` exists it will return nothing. - - - - - - - - Get-ADOPSArtifactFeed - Get - ADOPSArtifactFeed - - Gets one or more artifact feeds - - - - This command gets one or more artifact feeds. If a project is given returns project scoped feeds. If no project is given it returns organization and project scoped feeds. - - - - Get-ADOPSArtifactFeed - - FeedId - - Id of feed to get. GUID format. - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - Project - - Project to get feed(s) from - - String - - String - - - None - - - - Get-ADOPSArtifactFeed - - Organization - - Name of the organization to use. - - String - - String - - - None - - - Project - - Project to get feed(s) from - - String - - String - - - None - - - - - - FeedId - - Id of feed to get. GUID format. - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - Project - - Project to get feed(s) from - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSArtifactFeed - - This command returns the all feeds in the organization. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSArtifactFeed -Project 'MyProject' -FeedId '222cc2c2-2cc2-222c-c2c2-c22cc222222c' - - This command returns the feed with feed id '222cc2c2-2cc2-222c-c2c2-c22cc222222c' - - - - - - - - Get-ADOPSAuditActions - Get - ADOPSAuditActions - - Gets all actions that Azure DevOps can log. - - - - This commands lists all actions that Azure DevOps gets in the audit log. https://learn.microsoft.com/en-us/azure/devops/organizations/audit/azure-devops-auditing?view=azure-devops&tabs=preview-page#review-audit-log - - - - Get-ADOPSAuditActions - - Organization - - The organization to get log data from. - - String - - String - - - None - - - - - - Organization - - The organization to get log data from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSAuditActions - - This commands lists all actions that Azure DevOps gets in the audit log. - - - - - - - - Get-ADOPSBuildDefinition - Get - ADOPSBuildDefinition - - This command gets one or more build definitions. - - - - This command gets one or more build definitions. If will always return the full definition object in order for it to be used to update a definition. If no Id is given it will return all build definitions from the selected project. - - - - Get-ADOPSBuildDefinition - - Organization - - The organization to get definitions from. - - String - - String - - - None - - - Project - - The project to get definition from. - - String - - String - - - None - - - Id - - build definition id to get. - - Int32 - - Int32 - - - None - - - - - - Id - - build definition id to get. - - Int32 - - Int32 - - - None - - - Organization - - The organization to get definitions from. - - String - - String - - - None - - - Project - - The project to get definition from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSBuildDefinition -Project myProject -Id 123 - - This command will return the build definition with id 123. If no definition id 123 is found it will throw an error. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSBuildDefinition -Project myProject - - This command will return all build definitions in project myProject. If no definitions are found it will return an empty array. - - - - -------------------------- Example 3 -------------------------- - PS C:\> $Pipeline = Get-ADOPSPipeline -Project myProject -Name myPipeline -PS C:\> Get-ADOPSBuildDefinition -Project myProject -Id $Pipeline.id - - This command will return the build definition of the pipeline named myPipeline. - - - - - - - - Get-ADOPSConnection - Get - ADOPSConnection - - This command gets the active ADOPS connection. - - - - This command gets the active ADOPS connection. If no connection is made it will return $null. - - - - Get-ADOPSConnection - - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSConnection - -Name Value ----- ----- -TenantId 97547e13-918b-47eb-a923-6e58efef2e6a -Identity my.mail@address -Organization MYConnectedOrg - - This command will output any current ADOPS connection - - - - - - - - Get-ADOPSElasticPool - Get - ADOPSElasticPool - - Gets one or more Azure DevOps Elastic Pools. - - - - Gets one or more Azure DevOps Elastic Pools. - - - - Get-ADOPSElasticPool - - PoolId - - The Id of the Azure DevOps pool. - - Int32 - - Int32 - - - None - - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - - - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - PoolId - - The Id of the Azure DevOps pool. - - Int32 - - Int32 - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - Get-ADOPSElasticPool -Organization ADOPS -PoolId 10 - - Get the Azure DevOps Elastic Pool with the Id of 10 in the ADOPS organization. - - - - -------------------------- Example 2 -------------------------- - Get-ADOPSElasticPool -Organization ADOPS - - Get all the Azure DevOps Elastic Pool in the ADOPS organization. - - - - - - - - Get-ADOPSFileContent - Get - ADOPSFileContent - - Gets the content of a single file in an Azure DevOps repository. - - - - This command gets the contents of a file in an Azure DevOps repository It will try to get the contents no matter the file type, and may fail if a binary or otherwise unreadable file is given. - - - - Get-ADOPSFileContent - - Organization - - The organization where the repository is located - - String - - String - - - None - - - Project - - The project where the repository is located - - String - - String - - - None - - - RepositoryId - - The id (GUID) of the repository. This can be found using the command Get-ADOPSRepository -Project ProjectName -Repository RepositoryName | Select-Object -ExpandProperty id - - String - - String - - - None - - - FilePath - - Full path to the file which contents are read. - - String - - String - - - None - - - - - - FilePath - - Full path to the file which contents are read. - - String - - String - - - None - - - Organization - - The organization where the repository is located - - String - - String - - - None - - - Project - - The project where the repository is located - - String - - String - - - None - - - RepositoryId - - The id (GUID) of the repository. This can be found using the command Get-ADOPSRepository -Project ProjectName -Repository RepositoryName | Select-Object -ExpandProperty id - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSFileContent -Project MyProject -RepositoryId a1c93080-9d68-4ab9-95cf-1a5f68740628 -FilePath /MyFile.ps1 - - This command will return the contents of the file "MyFile.ps1" located in the root of the repository "a1c93080-9d68-4ab9-95cf-1a5f68740628" in the "MyProject" project of your default logged in organization. - - - - - - - - Get-ADOPSGroup - Get - ADOPSGroup - - Gets all Azure DevOps groups - - - - This command returns all Azure DevOps groups in your organization. It will not filter groups. - - - - Get-ADOPSGroup - - Organization - - The organization to get groups from. - - String - - String - - - None - - - ContinuationToken - - Internal parameter. Used to get paged results - - String - - String - - - None - - - - - - ContinuationToken - - Internal parameter. Used to get paged results - - String - - String - - - None - - - Organization - - The organization to get groups from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSGroup - - Gets all Azure DevOps groups - - - - - - - - Get-ADOPSNode - Get - ADOPSNode - - Get one or more Elastic nodes currently in the Elastic pool. - - - - Get one or more Elastic nodes currently in the Elastic pool. - - - - Get-ADOPSNode - - PoolId - - The unique Id of the Elastic pool. - - Int32 - - Int32 - - - None - - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - - - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - PoolId - - The unique Id of the Elastic pool. - - Int32 - - Int32 - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - Get-ADOPSNode -Organization 'ADOPS' -PoolId 10 - - Get the elastic nodes in the elastic pool with id 10. - - - - - - - - Get-ADOPSPipeline - Get - ADOPSPipeline - - Get a specific pipeline or all pipelines in a specific DevOps project. - - - - Lists a specific Pipeline or all Pipelines in a Specific Project. - - - - Get-ADOPSPipeline - - Name - - Name of the pipeline. - - String - - String - - - None - - - Revision - - Revision of the pipeline. Omit to get latest. - - int - - int - - - None - - - Project - - The project to get pipelines from. - - String - - String - - - None - - - Organization - - The organization to get pipelines from. - - String - - String - - - None - - - - - - Name - - Name of the pipeline. - - String - - String - - - None - - - Revision - - Revision of the pipeline. Omit to get latest. - - int - - int - - - None - - - Organization - - The organization to get pipelines from. - - String - - String - - - None - - - Project - - The project to get pipelines from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSPipeline -Organization $OrganizationName -Project $Project -Name $Name - - Get pipeline with name $Name from $Project. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSPipeline -Organization $OrganizationName -Project $Project -Name $Name -Revision $Revision - - Get pipeline with name $Name and specific $Revision from $Project - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSPipeline -Organization $OrganizationName -Project $Project - - List all Pipelines in $Project - - - - -------------------------- Example 3 -------------------------- - PS C:\> Get-ADOPSPipeline -Project $Project - - List all Pipelines in $Project and uses Default Organization. - - - - - - - - Get-ADOPSPipelineRetentionSettings - Get - ADOPSPipelineRetentionSettings - - Returns all pipeline retention settings. - - - - This command will return all pipeline retention settings for a Azure DevOps Project. - - - - Get-ADOPSPipelineRetentionSettings - - Organization - - The organization to get pipeline retention settings from. - - String - - String - - - None - - - Project - - The project to get pipeline retention settings from. - - String - - String - - - None - - - - - - Organization - - The organization to get pipeline retention settings from. - - String - - String - - - None - - - Project - - The project to get pipeline retention settings from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - A Key/value custom object of all the pipeline retention values. - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSPipelineRetentionSettings -Project 'MyProject' - - This command will get all the pipeline retention settings values for a project. - - - - - - - - Get-ADOPSPipelineSettings - Get - ADOPSPipelineSettings - - Returns all pipeline settings for a project. - - - - This command will return all pipeline settings for a project. - - - - Get-ADOPSPipelineSettings - - Organization - - The organization to get pipeline settings from. - - String - - String - - - None - - - Project - - The project to get pipeline settings from. - - String - - String - - - None - - - - - - Organization - - The organization to get pipeline settings from. - - String - - String - - - None - - - Project - - The project to get pipeline settings from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - A key/value custom object of all pipeline settings is returned. - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSPipelineSettings -Project 'MyProject' - -enforceReferencedRepoScopedToken : True -disableClassicPipelineCreation : True -disableClassicBuildPipelineCreation : True -disableClassicReleasePipelineCreation : True -forkProtectionEnabled : True -buildsEnabledForForks : True -enforceJobAuthScopeForForks : True -enforceNoAccessToSecretsFromForks : True -isCommentRequiredForPullRequest : True -requireCommentsForNonTeamMembersOnly : False -requireCommentsForNonTeamMemberAndNonContributors : False -enableShellTasksArgsSanitizing : True -enableShellTasksArgsSanitizingAudit : False -disableImpliedYAMLCiTrigger : True -statusBadgesArePrivate : True -enforceSettableVar : True -enforceJobAuthScope : True -enforceJobAuthScopeForReleases : True -publishPipelineMetadata : True - - This command will return a value for specific setting with the name `statusBadgesArePrivate` - - - - - - - - Get-ADOPSPipelineTask - Get - ADOPSPipelineTask - - Returns one or more pipeline tasks. - - - - This command will return one or more pipeline tasks available in your organization. If no parameters is given it will return all tasks installed in your Azure DevOps environment. - - - - Get-ADOPSPipelineTask - - Name - - Name of the task to search for. Exact matches, no wildcards. - - String - - String - - - None - - - Organization - - The organization to search for tasks in. - - String - - String - - - None - - - Version - - Major version of task(s) to return - - Int32 - - Int32 - - - None - - - - - - Name - - Name of the task to search for. Exact matches, no wildcards. - - String - - String - - - None - - - Organization - - The organization to search for tasks in. - - String - - String - - - None - - - Version - - Major version of task(s) to return - - Int32 - - Int32 - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSPipelineTask - - This command will return all pipeline tasks and all versions available in your organization. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSPipelineTask -Name 'PowerShell' - - This command will return pipeline tasks with the exact name "PowerShell", All versions available in your organization. - - - - -------------------------- Example 3 -------------------------- - PS C:\> Get-ADOPSPipelineTask -Version 1 - - This command will return all pipeline tasks with major version 1 available in your organization. - - - - -------------------------- Example 4 -------------------------- - PS C:\> Get-ADOPSPipelineTask -Name 'PowerShell' -Version 2 - - This command will return the PowerShell pipeline task version 2. - - - - - - - - Get-ADOPSPool - Get - ADOPSPool - - Get one or more Azure DevOps Agent pools. - - - - Get one or more Azure DevOps Agent pools. - - - - Get-ADOPSPool - - IncludeLegacy - - Includes all legacy agent pools. - - - SwitchParameter - - - False - - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - - Get-ADOPSPool - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - PoolId - - The Agent Pool id. - - Int32 - - Int32 - - - None - - - - Get-ADOPSPool - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - PoolName - - The name of the Agent pool. - - String - - String - - - None - - - - - - IncludeLegacy - - Includes all legacy agent pools. - - SwitchParameter - - SwitchParameter - - - False - - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - PoolId - - The Agent Pool id. - - Int32 - - Int32 - - - None - - - PoolName - - The name of the Agent pool. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - Get-ADOPSPool -Organization 'adops' -IncludeLegacy - - Get all the Azure DevOps Agent Pools in the Organization of adops and includes Legacy agent pools. - - - - -------------------------- Example 2 -------------------------- - Get-ADOPSPool -Organization 'adops' -PoolName 'agentpool1' - - Get the Azure DevOps Agent Pools with the name of agentpool1 in the Organization of adops. - - - - -------------------------- Example 3 -------------------------- - Get-ADOPSPool -Organization 'adops' -PoolId 10 - - Get the Azure DevOps Agent Pools with the pool id of 10 in the Organization of adops. - - - - - - get-agent-pools - https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/pools/get-agent-pools?view=azure-devops-rest-7.1 - - - - - - Get-ADOPSProject - Get - ADOPSProject - - Gets one or several projects in an Azure DevOps organization. - - - - Gets one or several projects in an Azure DevOps organization. - - - - Get-ADOPSProject - - Id - - The id (GUID) of the project to find. - - String - - String - - - None - - - Organization - - The name of the organization of the project. - - String - - String - - - None - - - - Get-ADOPSProject - - Name - - The name of the project to find. - - String - - String - - - None - - - Organization - - The name of the organization of the project. - - String - - String - - - None - - - - - - Id - - The id (GUID) of the project to find. - - String - - String - - - None - - - Name - - The name of the project to find. - - String - - String - - - None - - - Organization - - The name of the organization of the project. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSProject -Organization 'ADOPS' -Name 'ADOPSproj' - - Gets the project called "ADOPSproj" from the organization "ADOPS". - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSProject -Name 'ADOPSproj' - - Gets the project called "ADOPSproj" from the default organization. - - - - - - - - Get-ADOPSRepository - Get - ADOPSRepository - - Gets one or more repositories from a project in Azure DevOps. - - - - Gets one or more repositories from a project in Azure DevOps. - - - - Get-ADOPSRepository - - Project - - The project to get repositories from. - - String - - String - - - None - - - Repository - - The repository to get. - - String - - String - - - None - - - Organization - - The organization to get repositories from. - - String - - String - - - None - - - - - - Organization - - The organization to get repositories from. - - String - - String - - - None - - - Project - - The project to get repositories from. - - String - - String - - - None - - - Repository - - The repository to get. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSRepository -Organization 'ADOPS' -Project 'ADOPSproj' - - Gets all repositories from the project "ADOPSproj" in the organization "ADOPS". - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSRepository -Project 'ADOPSproj' -Repository 'ADOPSrepo' - - Gets the repository "ADOPSrepo" from the project "ADOPSproj", from the organization set as default amongst the established Azure DevOps connections. - - - - - - - - Get-ADOPSServiceConnection - Get - ADOPSServiceConnection - - Get a specific ServiceConnection or all ServiceConnections in a DevOps project. - - - - Lists a specific ServiceConnection or all ServiceConnections in a Project. - - - - Get-ADOPSServiceConnection - - Name - - Specify the name of a service connection to get. - - String - - String - - - None - - - Project - - The project to get ServiceConnection/s from. - - String - - String - - - None - - - Organization - - The organization to get ServiceConnection/s from. - - String - - String - - - None - - - IncludeFailed - - Includes Service Connections in failed & draft state. - - - SwitchParameter - - - False - - - - - - Name - - Specify the name of a service connection to get. - - String - - String - - - None - - - Organization - - The organization to get ServiceConnection/s from. - - String - - String - - - None - - - Project - - The project to get ServiceConnection/s from. - - String - - String - - - None - - - IncludeFailed - - Includes Service Connections in failed & draft state. - - SwitchParameter - - SwitchParameter - - - False - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSServiceConnection -Organization $OrganizationName -Project $Project -Name $Name - - Get ServiceConnection with name $Name from $Project. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSServiceConnection -Organization $OrganizationName -Project $Project - - List all ServiceConnection in $Project - - - - -------------------------- Example 3 -------------------------- - PS C:\> Get-ADOPSServiceConnection -Project $Project - - List all ServiceConnection in $Project and uses Default Organization. - - - - - - - - Get-ADOPSUsageData - Get - ADOPSUsageData - - Gets the usage data of your organization. - - - - This command gets usage data for you organization, such as - - Currently running requests - - Limits to your resource usage - - Parallel sessions - - Used minutes and hosts - - Note: This API is not public, and user data is dependent on current status, and is due to change with no notice. - - - - Get-ADOPSUsageData - - ProjectVisibility - - List usage data for private or public projects - - - Private - Public - - String - - String - - - None - - - Organization - - The organization to get usage data from. - - String - - String - - - None - - - SelfHosted - - If set, returns usage data for self hosted agents. If not set, returns usage data for Microsoft hosted agents. - - - SwitchParameter - - - False - - - - - - Organization - - The organization to get usage data from. - - String - - String - - - None - - - ProjectVisibility - - List usage data for private or public projects - - String - - String - - - None - - - SelfHosted - - If set, returns usage data for self hosted agents. If not set, returns usage data for Microsoft hosted agents. - - SwitchParameter - - SwitchParameter - - - False - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSUsageData - - This command will return usage statistics and data for your public projects (default), and Microsoft hosted agents (default). - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSUsageData -ProjectVisibility Private -SelfHosted - - This command will return usage statistics and data for your private projects, and self hosted agents. - - - - -------------------------- Example 3 -------------------------- - PS C:\> Get-ADOPSUsageData -ProjectVisibility Public - - This command will return usage statistics and data for your public projects (default), and Microsoft hosted agents (default). - - - - - - - - Get-ADOPSUser - Get - ADOPSUser - - Fetch one or more users - - - - Fetch user(s) by name or descriptor - - - - Get-ADOPSUser - - ContinuationToken - - Used to fetch the new page from the API. - - String - - String - - - None - - - Organization - - The organization to get users from. - - String - - String - - - None - - - - Get-ADOPSUser - - Descriptor - - A unique descriptor ID for the user. - - String - - String - - - None - - - Organization - - The organization to get users from. - - String - - String - - - None - - - - Get-ADOPSUser - - Name - - The query that will match name, display name, email fields. - - String - - String - - - None - - - Organization - - The organization to get users from. - - String - - String - - - None - - - - - - ContinuationToken - - Used to fetch the new page from the API. - - String - - String - - - None - - - Descriptor - - A unique descriptor ID for the user. - - String - - String - - - None - - - Name - - The query that will match name, display name, email fields. - - String - - String - - - None - - - Organization - - The organization to get users from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSUser - - Fetch all users. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSUser -Name 'john doe' - - Search for anyone that has `john doe` in it's name, display name or email. - - - - -------------------------- Example 3 -------------------------- - PS C:\> Get-ADOPSUser -Descriptor 'aad.am9obiBkb2Vqb2huIGRvZWpvaG4gZG9lam9obiBkb2U' - - Search for a single user that has the corresponding descriptor. - - - - - - user-entitlements/search-user-entitlements - https://docs.microsoft.com/en-us/rest/api/azure/devops/memberentitlementmanagement/user-entitlements/search-user-entitlements?view=azure-devops-rest-6.0 - - - users/get - https://docs.microsoft.com/en-us/rest/api/azure/devops/graph/users/get?view=azure-devops-rest-6.0 - - - users/list - https://docs.microsoft.com/en-us/rest/api/azure/devops/graph/users/list?view=azure-devops-rest-6.0 - - - - - - Get-ADOPSVariableGroup - Get - ADOPSVariableGroup - - This command gets one or more variable groups from Azure DevOps. - - - - This command gets one or more variable groups from Azure DevOps. If no matching variable group is found it outputs nothing. Searches can be done by Name or Id. - - - - Get-ADOPSVariableGroup - - Id - - Variable group id to search for. - - Int32 - - Int32 - - - None - - - Organization - - Organization to search in. - - String - - String - - - None - - - Project - - Project to search in. - - String - - String - - - None - - - - Get-ADOPSVariableGroup - - Name - - Variable group name to search for. - - String - - String - - - None - - - Organization - - Organization to search in. - - String - - String - - - None - - - Project - - Project to search in. - - String - - String - - - None - - - - - - Id - - Variable group id to search for. - - Int32 - - Int32 - - - None - - - Name - - Variable group name to search for. - - String - - String - - - None - - - Organization - - Organization to search in. - - String - - String - - - None - - - Project - - Project to search in. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSVariableGroup -Project MyAzureDevOpsProject - - This command will return all variable groups in the MyAzureDevOpsProject project. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSVariableGroup -Project MyAzureDevOpsProject -Name MyVariableGroup - - This command will return the "MyVariableGroup" variable group in the MyAzureDevOpsProject project. - - - - -------------------------- Example 3 -------------------------- - PS C:\> Get-ADOPSVariableGroup -Project MyAzureDevOpsProject -Id 4 - - This command will return the variable group with id 4 in the MyAzureDevOpsProject project. - - - - - - - - Get-ADOPSWiki - Get - ADOPSWiki - - Gets one or more wikis from a Azure DevOps project - - - - This command returns Azure DevOPs wikis from a project. If a wiki name is given it will return any wikis with that exact name. Wildcards are not supported. - - - - Get-ADOPSWiki - - Project - - The project to get wiki(s) from - - String - - String - - - None - - - WikiId - - Name or ID of a specific wiki to return. - - String - - String - - - None - - - Organization - - The organization to get wikis from. - - String - - String - - - None - - - - - - Organization - - The organization to get wikis from. - - String - - String - - - None - - - Project - - The project to get wiki(s) from - - String - - String - - - None - - - WikiId - - Name or ID of a specific wiki to return. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSWiki -Project MyProject -WikiId MyProject.wiki - - This command will return the MyProject.wiki wiki from the MyProject project. If no MyProjet.wiki exists it will return nothing. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSWiki -Project MyProject - - This command will return all wikis from the MyProject project. - - - - - - - - Grant-ADOPSPipelinePermission - Grant - ADOPSPipelinePermission - - Grants pipeline permission to a resource of type VariableGroup, Queue, SecureFile, or Environment. - - - - Grants pipeline permissions to a resource of type VariableGroup, Queue, SecureFile, or Environment. Specify a PipelineId to grant permissions to a single pipeline, or use the AllPipelines parameter to grant permissions to all pipelines. - - - - Grant-ADOPSPipelinePermission - - AllPipelines - - Grant access for all pipelines. This is equivalent to disable the restrictions on the resource. - - - SwitchParameter - - - False - - - Organization - - Name of the Azure DevOps organization. Defaults to the current organization. - - String - - String - - - None - - - Project - - Name of project containing the resource. - - String - - String - - - None - - - ResourceId - - Id of the resource to grant permissions to. - - String - - String - - - None - - - ResourceType - - Resource type to grant permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. - - - VariableGroup - Queue - SecureFile - Environment - - ResourceType - - ResourceType - - - None - - - - Grant-ADOPSPipelinePermission - - Organization - - Name of the Azure DevOps organization. Defaults to the current organization. - - String - - String - - - None - - - PipelineId - - Id of the pipeline to grant permissions for. - - Int32 - - Int32 - - - None - - - Project - - Name of project containing the resource. - - String - - String - - - None - - - ResourceId - - Id of the resource to grant permissions to. - - String - - String - - - None - - - ResourceType - - Resource type to grant permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. - - - VariableGroup - Queue - SecureFile - Environment - - ResourceType - - ResourceType - - - None - - - - - - AllPipelines - - Grant access for all pipelines. This is equivalent to disable the restrictions on the resource. - - SwitchParameter - - SwitchParameter - - - False - - - Organization - - Name of the Azure DevOps organization. Defaults to the current organization. - - String - - String - - - None - - - PipelineId - - Id of the pipeline to grant permissions for. - - Int32 - - Int32 - - - None - - - Project - - Name of project containing the resource. - - String - - String - - - None - - - ResourceId - - Id of the resource to grant permissions to. - - String - - String - - - None - - - ResourceType - - Resource type to grant permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. - - ResourceType - - ResourceType - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Grant-ADOPSPipelinePermission -Pipeline 42 -ResourceType SecureFile -ResourceId '88d047be-7e59-4eda-b1fe-1614c7a69e16' -Project 'MyProject' - - Grant pipeline 42 permission to the secure file with id 88d047be-7e59-4eda-b1fe-1614c7a69e16 in project MyProject. - - - - - - - - Import-ADOPSRepository - Import - ADOPSRepository - - Import an external public git repo to Azure DevOps repos. - - - - This command will import any public git repo using http or https git link. It requires a precreated empty git repo target. You van assign the target using repository name or repository Id - - - - Import-ADOPSRepository - - GitSource - - The source repo to fetch. Must be a public accessable http or https link. - - String - - String - - - None - - - Organization - - Your Azure DevOps organization. - - String - - String - - - None - - - Project - - The Azure DevOps project where your local repo exists. - - String - - String - - - None - - - RepositoryId - - Repository id where to import the GitSource. This must be an empty Azure DevOps repository. - - String - - String - - - None - - - Wait - - If defined this command will wait for the import to complete. Default will return the process started. - - - SwitchParameter - - - False - - - - Import-ADOPSRepository - - GitSource - - The source repo to fetch. Must be a public accessable http or https link. - - String - - String - - - None - - - Organization - - Your Azure DevOps organization. - - String - - String - - - None - - - Project - - The Azure DevOps project where your local repo exists. - - String - - String - - - None - - - RepositoryName - - Repository name where to import the GitSource. This must be an empty Azure DevOps repository. - - String - - String - - - None - - - Wait - - If defined this command will wait for the import to complete. Default will return the process started. - - - SwitchParameter - - - False - - - - - - GitSource - - The source repo to fetch. Must be a public accessable http or https link. - - String - - String - - - None - - - Organization - - Your Azure DevOps organization. - - String - - String - - - None - - - Project - - The Azure DevOps project where your local repo exists. - - String - - String - - - None - - - RepositoryId - - Repository id where to import the GitSource. This must be an empty Azure DevOps repository. - - String - - String - - - None - - - RepositoryName - - Repository name where to import the GitSource. This must be an empty Azure DevOps repository. - - String - - String - - - None - - - Wait - - If defined this command will wait for the import to complete. Default will return the process started. - - SwitchParameter - - SwitchParameter - - - False - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Import-ADOPSRepository -Project MyProject -GitSource https://github.com/AZDOPS/AZDOPS.git -RepositoryName MyLocalRepo - - This command will import all data from the `https://github.com/AZDOPS/AZDOPS.git` repo to the local Azure DevOps repo `MyLocalRepo` - - - - - - - - Initialize-ADOPSRepository - Initialize - ADOPSRepository - - This command will initialize a newly created Azure DevOps repository. - - - - This command will initialize a newly created Azure DevOps repository. It can do so by adding a default README.md template, Any of the existing .gitignore templates, or a custom file. - If only one .gitignore template is selected it will be named ".gitignore". If more than one is selected they will be named as the templates, and they will not work right away. Combine your chosen templates in to one and update it manually. - If a repository is already initiated this command will fail with any of a number of different messages depending on what files or templates are selected. - - - - Initialize-ADOPSRepository - - Message - - Commit message to use when initializing repository - - String - - String - - - None - - - Branch - - Which branch to initialize. Defaults to 'refs/heads/main' - - String - - String - - - None - - - RepositoryId - - Id (GUID) of the repository to initialize. - - String - - String - - - None - - - NewContentTemplate - - Initialize the repo with one or more of the existing .gitignore templates. If one template is selected it will be given the name '.gitignore' If 2 or more templates are given they will be named as the template name. - - - Actionscript.gitignore - Ada.gitignore - Agda.gitignore - Android.gitignore - AppceleratorTitanium.gitignore - AppEngine.gitignore - ArchLinuxPackages.gitignore - Autotools.gitignore - C++.gitignore - C.gitignore - CakePHP.gitignore - CFWheels.gitignore - ChefCookbook.gitignore - Clojure.gitignore - CMake.gitignore - CodeIgniter.gitignore - CommonLisp.gitignore - Composer.gitignore - Concrete5.gitignore - Coq.gitignore - CraftCMS.gitignore - CUDA.gitignore - D.gitignore - Dart.gitignore - Delphi.gitignore - DM.gitignore - Drupal.gitignore - Eagle.gitignore - Elisp.gitignore - Elixir.gitignore - Elm.gitignore - EPiServer.gitignore - Erlang.gitignore - ExpressionEngine.gitignore - ExtJs.gitignore - Fancy.gitignore - Finale.gitignore - ForceDotCom.gitignore - Fortran.gitignore - FuelPHP.gitignore - gcov.gitignore - GitBook.gitignore - Go.gitignore - Godot.gitignore - Gradle.gitignore - Grails.gitignore - GWT.gitignore - Haskell.gitignore - Idris.gitignore - IGORPro.gitignore - Java.gitignore - Jboss.gitignore - Jekyll.gitignore - JENKINS_HOME.gitignore - Joomla.gitignore - Julia.gitignore - KiCAD.gitignore - Kohana.gitignore - Kotlin.gitignore - LabVIEW.gitignore - Laravel.gitignore - Leiningen.gitignore - LemonStand.gitignore - Lilypond.gitignore - Lithium.gitignore - Lua.gitignore - Magento.gitignore - Maven.gitignore - Mercury.gitignore - MetaProgrammingSystem.gitignore - nanoc.gitignore - Nim.gitignore - Node.gitignore - Objective-C.gitignore - OCaml.gitignore - Opa.gitignore - opencart.gitignore - OracleForms.gitignore - Packer.gitignore - Perl.gitignore - Phalcon.gitignore - PlayFramework.gitignore - Plone.gitignore - Prestashop.gitignore - Processing.gitignore - PureScript.gitignore - Python.gitignore - Qooxdoo.gitignore - Qt.gitignore - R.gitignore - Rails.gitignore - Raku.gitignore - RhodesRhomobile.gitignore - ROS.gitignore - Ruby.gitignore - Rust.gitignore - Sass.gitignore - Scala.gitignore - Scheme.gitignore - SCons.gitignore - Scrivener.gitignore - Sdcc.gitignore - SeamGen.gitignore - SketchUp.gitignore - Smalltalk.gitignore - stella.gitignore - SugarCRM.gitignore - Swift.gitignore - Symfony.gitignore - SymphonyCMS.gitignore - Terraform.gitignore - TeX.gitignore - Textpattern.gitignore - TurboGears2.gitignore - Typo3.gitignore - Umbraco.gitignore - Unity.gitignore - UnrealEngine.gitignore - VisualStudio.gitignore - VVVV.gitignore - Waf.gitignore - WordPress.gitignore - Xojo.gitignore - Yeoman.gitignore - Yii.gitignore - ZendFramework.gitignore - Zephir.gitignore - - String[] - - String[] - - - None - - - Path - - If combined with the `-Content` parameter this will create a custom file with this content. If a path is given without `-Content` the default message 'Repo initialized using ADOPS module.' will be set in this file. - - String - - String - - - None - - - Content - - If combined with the `-Path` parameter this will create a custom file with this content. - - String - - String - - - None - - - Readme - - Initialize the repo with the default Azure DevOps README template - - - SwitchParameter - - - False - - - - - - Branch - - Which branch to initialize. Defaults to 'refs/heads/main' - - String - - String - - - None - - - Content - - If combined with the `-Path` parameter this will create a custom file with this content. - - String - - String - - - None - - - Message - - Commit message to use when initializing repository - - String - - String - - - None - - - Path - - If combined with the `-Content` parameter this will create a custom file with this content. If a path is given without `-Content` the default message 'Repo initialized using ADOPS module.' will be set in this file. - - String - - String - - - None - - - Readme - - Initialize the repo with the default Azure DevOps README template - - SwitchParameter - - SwitchParameter - - - False - - - RepositoryId - - Id (GUID) of the repository to initialize. - - String - - String - - - None - - - NewContentTemplate - - Initialize the repo with one or more of the existing .gitignore templates. If one template is selected it will be given the name '.gitignore' If 2 or more templates are given they will be named as the template name. - - String[] - - String[] - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> $r = New-ADOPSRepository -Name "myNewRepo" -Project 'myProject' -Organization 'myOrg' -PS C:\> Initialize-ADOPSRepository - - This command will initialize the repo with only the default "README.md" template. - - - - -------------------------- Example 2 -------------------------- - PS C:\> $r = New-ADOPSRepository -Name "myNewRepo" -Project 'myProject' -Organization 'myOrg' -PS C:\> Initialize-ADOPSRepository -RepositoryId $r.id -Message 'my initial commit message' -NewContentTemplate Actionscript.gitignore, Ada.gitignore, Android.gitignore -Path '/newCustomFile.txt' -Readme - - This command will initialize a repository containing the files - Actionscript.gitignore - - Ada.gitignore - - Android.gitignore - - newCustomFile.txt - - README.md - - newCustomFile.txt will contain the default message 'Repo initialized using ADOPS module.' - README.md is the default readme template from Azure DevOps - - - - -------------------------- Example 3 -------------------------- - PS C:\> $r = New-ADOPSRepository -Name "myNewRepo" -Project 'myProject' -Organization 'myOrg' -PS C:\> $content = @' -This is a multiline file. -It contains multiple lines, -Since, by definition, if it didn't it wouldn't be a multiline file -'@ -PS C:\> Initialize-ADOPSRepository -RepositoryId $r.id -Path '/newCustomFile.txt' -Content $content - - This command will initialize the repo by creating the file 'newCustomFile.txt' in the root folder, and setting contents of the file. - - - - - - - - Invoke-ADOPSRestMethod - Invoke - ADOPSRestMethod - - Invoke a ADOPS rest API call. - - - - This command invokes a API call to Azure DevOps using the standard ADOPS logged in account. If you do not give the full Uri to call, for example 'https://dev.azure.com/myOrganization/_api/GetStuff', it will append 'https://dev.azure.com/$Organization', where $Organization is the one you are connected to, to your URI. - - - - Invoke-ADOPSRestMethod - - Uri - - Full Uri to call, including "https://dev.azure.com/organization" - - String - - String - - - None - - - Method - - API method to use - - - Default - Get - Head - Post - Put - Delete - Trace - Options - Merge - Patch - - WebRequestMethod - - WebRequestMethod - - - None - - - Body - - Body to include in the API call - - String - - String - - - None - - - - - - Body - - Body to include in the API call - - String - - String - - - None - - - Method - - API method to use - - WebRequestMethod - - WebRequestMethod - - - None - - - Uri - - Full Uri to call, including "https://dev.azure.com/organization" - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Invoke-ADOPSRestMethod -Uri "https://dev.azure.com/$Organization/_apis/distributedtask/tasks" - - This command will return whatever this API endpoint returns. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Invoke-ADOPSRestMethod -Uri "/_apis/distributedtask/tasks" - - This command will append 'https://dev.azure.com/$Organization' to URI and return whatever the full API endpoint 'https://dev.azure.com/$Organization/_apis/distributedtask/tasks' returns. - $Organization is taken from the current ADOPS connection. - - - - -------------------------- Example 3 -------------------------- - PS C:\> Invoke-ADOPSRestMethod -Uri "https://dev.azure.com/$Organization/_apis/distributedtask/tasks" -Method Post -Body $BodyObject - - This command will perform a Post call to the API endpoint. It will perform the API call using the logged in ADOPS credentials. It will include the body object in the call. It will not format or validate any of the input values. - - - - - - - - New-ADOPSArtifactFeed - New - ADOPSArtifactFeed - - This command creates a new artifact feed - - - - This command creates a new artifact feed. It will try to add the Project build account as contributor to the feed in order for builds to be able to automatically publish packages. If -IncludeUpstream is set it will add all currently available upstreams. - - - - New-ADOPSArtifactFeed - - Project - - Project in which the feed will be created. - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - Name - - Feed name - - String - - String - - - None - - - Description - - Add a description to the feed description field. - - String - - String - - - None - - - IncludeUpstream - - Enable upstreams and add all currently available upstreams - - - SwitchParameter - - - False - - - - - - Description - - Add a description to the feed description field. - - String - - String - - - None - - - IncludeUpstream - - Enable upstreams and add all currently available upstreams - - SwitchParameter - - SwitchParameter - - - False - - - Name - - Feed name - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - Project - - Project in which the feed will be created. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSArtifactFeed -Project 'MyProject' -Name 'MyNewFeed' - - This command will add a new feed named `MyNewFeed` to the `MyProject` project. It will try to add the account `MyProject build service (ConnectedOrg)` as contributor. It will not enable upstreams. - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSArtifactFeed -Project 'MyProject' -Name 'MyNewFeed' -IncludeUpstreams - - This command will add a new feed named `MyNewFeed` to the `MyProject` project. It will try to add the account `MyProject build service (ConnectedOrg)` as contributor. It will enable upstreams and add all currently available sources. - - - - - - - - New-ADOPSAuditStream - New - ADOPSAuditStream - - Sets up an audit stream from Azure DevOps to supported external logging services. - - - - This command sets up a log stream from Azure DevOps to an external supported service. It only sets up log forwarding. You need to enable logging separately from this command. (Organization settings -> Policies -> Log audit events) - Supported services are - Azure Monitor - - Splunk - - Azure Event grid - - - - New-ADOPSAuditStream - - EventGridTopicAccessKey - - Access key to Azure Eventgrid - - String - - String - - - None - - - EventGridTopicHostname - - Azure Eventgrid host to send logs to. Must start with 'http://' or 'https://' - - String - - String - - - None - - - Organization - - The organization to set up forwarding from. - - String - - String - - - None - - - - New-ADOPSAuditStream - - Organization - - The organization to set up forwarding from. - - String - - String - - - None - - - SharedKey - - Azure Monitor shared access key - - String - - String - - - None - - - WorkspaceId - - Azure monitor workspace ID. Must be in GUID format. - - String - - String - - - None - - - - New-ADOPSAuditStream - - Organization - - The organization to set up forwarding from. - - String - - String - - - None - - - SplunkEventCollectorToken - - Splunk event collector token. Must be in GUID format. - - String - - String - - - None - - - SplunkUrl - - URL to Splunk log host. Must start with 'http://' or 'https://' - - String - - String - - - None - - - - - - EventGridTopicAccessKey - - Access key to Azure Eventgrid - - String - - String - - - None - - - EventGridTopicHostname - - Azure Eventgrid host to send logs to. Must start with 'http://' or 'https://' - - String - - String - - - None - - - Organization - - The organization to set up forwarding from. - - String - - String - - - None - - - SharedKey - - Azure Monitor shared access key - - String - - String - - - None - - - SplunkEventCollectorToken - - Splunk event collector token. Must be in GUID format. - - String - - String - - - None - - - SplunkUrl - - URL to Splunk log host. Must start with 'http://' or 'https://' - - String - - String - - - None - - - WorkspaceId - - Azure monitor workspace ID. Must be in GUID format. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSAuditStream -WorkspaceId "11111111-1111-1111-1111-111111111111" -SharedKey "123456" - - This command sets up log forwarding to Azure Monitor using the workspace id '11111111-1111-1111-1111-111111111111' and the shared key '123456' - - - - -------------------------- Example 2 -------------------------- - PS C:\> New-ADOPSAuditStream -SplunkUrl 'http://Splunkurl' -SplunkEventCollectorToken '11111111-1111-1111-1111-111111111111' - - This command sets up log forwarding to splunk using the token '11111111-1111-1111-1111-111111111111' and the splunk url 'http://Splunkurl' - - - - -------------------------- Example 3 -------------------------- - PS C:\> $Bytes = [System.Text.Encoding]::Unicode.GetBytes('EventGridTopicAccessKey') -PS C:\> $Base64 =[Convert]::ToBase64String($Bytes) -PS C:\> New-ADOPSAuditStream -EventGridTopicHostname 'http://eventgridUri' -EventGridTopicAccessKey $Base64 - - This command sets up log forwarding to Azure eventgrid using the hostname 'http://eventgridUri' and the access key 'EventGridTopicAccessKey' Access key must be base64 encoded. - - - - - - - - New-ADOPSBuildPolicy - New - ADOPSBuildPolicy - - Creates a build validation policy for a repository - - - - This command creates a build validation policy for a repository. It only creates the policy. You have to create the validation pipeline to be run manually. - - - - New-ADOPSBuildPolicy - - Project - - Project where the pipeline and repository is located - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - RepositoryId - - Id of the repository in which to create the policy. Can be found using the `Get-ADOPSRepository` command - - String - - String - - - None - - - Branch - - git branch to trigger on. Accepts both branch name ('main') or full git path ('refs/heads/main') If only path name is given it will append "refs/heads/" - - String - - String - - - None - - - PipelineId - - PipelineId, or build definition id. Can be found using the `Get-ADOPSPipeline` command - - Int32 - - Int32 - - - None - - - Displayname - - Display name of the policy setting - - String - - String - - - None - - - filenamePatterns - - Filter validation trigger on repository path(s) - - String[] - - String[] - - - None - - - - - - Branch - - git branch to trigger on. Accepts both branch name ('main') or full git path ('refs/heads/main') If only path name is given it will append "refs/heads/" - - String - - String - - - None - - - Displayname - - Display name of the policy setting - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - PipelineId - - PipelineId, or build definition id. Can be found using the `Get-ADOPSPipeline` command - - Int32 - - Int32 - - - None - - - Project - - Project where the pipeline and repository is located - - String - - String - - - None - - - RepositoryId - - Id of the repository in which to create the policy. Can be found using the `Get-ADOPSRepository` command - - String - - String - - - None - - - filenamePatterns - - Filter validation trigger on repository path(s) - - String[] - - String[] - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSBuildPolicy -Project 'MyProject' -RepositoryId 62a7c071-1a60-44a9-a151-d17cd4d367da -Branch 'main' -PipelineId 1 -Displayname 'MyVerificationPolicy' -filenamePatterns '/root/*','/side/*' - - This command will create a policy named `MyVerificationPolicy` on the repository with id `62a7c071-1a60-44a9-a151-d17cd4d367da` in the `MyProject` project, that triggers on branch `main`. It will filter the validation to run only on changes in folder `/root/` or `/side/`. - - - - -------------------------- Example 2 -------------------------- - PS C:\> $ADOPSProject = 'MyProject' -PS C:\> $RepoId = Get-ADOPSRepository -Project $ADOPSProject -Repository 'MyRepo' | Select-Object -ExpandProperty Id -PS C:\> $PipelineId = Get-ADOPSPipeline -Project $ADOPSProject -Name 'MyPipeline' | Select-Object -ExpandProperty id - -PS C:\> New-ADOPSBuildPolicy -Project $ADOPSProject -RepositoryId $RepoId -Branch 'refs/heads/main' -PipelineId $PipelineId -Displayname 'MyVerificationPolicy' - - This command will create a policy named `MyVerificationPolicy` running the `MyPipeline` pipeline on the repository `MyRepo` in the `MyProject` project, that triggers on branch `main`. - - - - - - - - New-ADOPSElasticPool - New - ADOPSElasticPool - - Create an Azure DevOps Elastic pool. - - - - Create an Azure DevOps Elastic pool. - - - - New-ADOPSElasticPool - - PoolName - - Name of the Elastic Pool. - - String - - String - - - None - - - ElasticPoolObject - - The full request body in json or as a pscustom object. The Help function New-ElasticPoolObject can help generate a powershell object/json string. - | Name | Type | Description | |----------------------|---------------------|-------------------------------------------------------------------------------| | agentInteractiveUI | boolean | Set whether agents should be configured to run with interactive UI | | azureId | string | Azure string representing to location of the resource | | desiredIdle | integer | Number of agents to have ready waiting for jobs | | desiredSize | integer | The desired size of the pool | | maxCapacity | integer | Maximum number of nodes that will exist in the elastic pool | | maxSavedNodeCount | integer | Keep nodes in the pool on failure for investigation | | offlineSince | string | Timestamp the pool was first detected to be offline | | osType | OperatingSystemType | Operating system type of the nodes in the pool | | poolId | integer | Id of the associated TaskAgentPool | | recycleAfterEachUse | boolean | Discard node after each job completes | | serviceEndpointId | string | Id of the Service Endpoint used to connect to Azure | | serviceEndpointScope | string | Scope the Service Endpoint belongs to | | sizingAttempts | integer | The number of sizing attempts executed while trying to achieve a desired size | | state | ElasticPoolState | State of the pool | | timeToLiveMinutes | integer | The minimum time in minutes to keep idle agents alive | - - Object - - Object - - - None - - - ProjectId - - If provided, a new TaskAgentQueue will be created in the specified project. - - String - - String - - - None - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - AuthorizeAllPipelines - - Setting to determine if all pipelines are authorized to use this TaskAgentPool by default. - - - SwitchParameter - - - False - - - AutoProvisionProjectPools - - Setting to automatically provision TaskAgentQueues in every project for the new pool. - - - SwitchParameter - - - False - - - - - - AuthorizeAllPipelines - - Setting to determine if all pipelines are authorized to use this TaskAgentPool by default. - - SwitchParameter - - SwitchParameter - - - False - - - AutoProvisionProjectPools - - Setting to automatically provision TaskAgentQueues in every project for the new pool. - - SwitchParameter - - SwitchParameter - - - False - - - ElasticPoolObject - - The full request body in json or as a pscustom object. The Help function New-ElasticPoolObject can help generate a powershell object/json string. - | Name | Type | Description | |----------------------|---------------------|-------------------------------------------------------------------------------| | agentInteractiveUI | boolean | Set whether agents should be configured to run with interactive UI | | azureId | string | Azure string representing to location of the resource | | desiredIdle | integer | Number of agents to have ready waiting for jobs | | desiredSize | integer | The desired size of the pool | | maxCapacity | integer | Maximum number of nodes that will exist in the elastic pool | | maxSavedNodeCount | integer | Keep nodes in the pool on failure for investigation | | offlineSince | string | Timestamp the pool was first detected to be offline | | osType | OperatingSystemType | Operating system type of the nodes in the pool | | poolId | integer | Id of the associated TaskAgentPool | | recycleAfterEachUse | boolean | Discard node after each job completes | | serviceEndpointId | string | Id of the Service Endpoint used to connect to Azure | | serviceEndpointScope | string | Scope the Service Endpoint belongs to | | sizingAttempts | integer | The number of sizing attempts executed while trying to achieve a desired size | | state | ElasticPoolState | State of the pool | | timeToLiveMinutes | integer | The minimum time in minutes to keep idle agents alive | - - Object - - Object - - - None - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - PoolName - - Name of the Elastic Pool. - - String - - String - - - None - - - ProjectId - - If provided, a new TaskAgentQueue will be created in the specified project. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - $Params = @{ - AuthorizeAllPipelines = $true - AutoProvisionProjectPools = $true - PoolName = ManagedPool1 -} -New-ADOPSElasticPool @Params -ElasticPoolObject @" -{ - "serviceEndpointId": "44868479-e856-42bf-9a2b-74bb500d8e36", - "serviceEndpointScope": "421eb3c8-1ca4-4a53-b93c-58997b9eb5e1", - "azureId": "/subscriptions/d83a7278-278c-4671-9a3e-a4cd81cd1194/resourceGroups/RG-TEST/providers/Microsoft.Compute/virtualMachineScaleSets/vmss-test", - "maxCapacity": 1, - "desiredIdle": 0, - "recycleAfterEachUse": true, - "maxSavedNodeCount": 0, - "osType": "linux", - "state": "online", - "offlineSince": null, - "desiredSize": 0, - "sizingAttempts": 0, - "agentInteractiveUI": false, - "timeToLiveMinutes": 15 -} -"@ - - To find your serviceEndpointScope, use Get-ADOPSProject as the scope is the project where the Service connection is bound. Create a Azure DevOps Elastic pool that Auto provisions in project and Authorizes the pool to be consumed by all pipelines. It also attaches to a Virtual Machine Scale Set using the azureId. Full description of the request body can be found at: https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/elasticpools/create?view=azure-devops-rest-7.1 - - - - -------------------------- Example 2 -------------------------- - $Params = @{ - AuthorizeAllPipelines = $true - AutoProvisionProjectPools = $true - PoolName = ManagedPool1 -} - -$ElasticPoolObject = New-ADOPSElasticPoolObject -ServiceEndpointId '44868479-e856-42bf-9a2b-74bb500d8e36' -ServiceEndpointScope '421eb3c8-1ca4-4a53-b93c-58997b9eb5e1' -AzureId '/subscriptions/8961f1f1-0bd1-4be0-b73c-6a8f3b304cf6/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/virtualMachineScaleSets/vmss-test' - -New-ADOPSElasticPool @Params -ElasticPoolObject $ElasticPoolObject - - To find your serviceEndpointScope, use Get-ADOPSProject as the scope is the project where the Service connection is bound. Create a Azure DevOps Elastic pool that Auto provisions in project and Authorizes the pool to be consumed by all pipelines. It also attaches to a Virtual Machine Scale Set using the azureId. Full description of the request body can be found at: https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/elasticpools/create?view=azure-devops-rest-7.1 - - - - - - elasticpools-create - https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/elasticpools/create?view=azure-devops-rest-7.1 - - - - - - New-ADOPSElasticPoolObject - New - ADOPSElasticPoolObject - - Creates an Elastic pool object that can be used. - - - - Creates an Elastic pool object that can be used. - - - - New-ADOPSElasticPoolObject - - ServiceEndpointId - - Id of the Service connection used to connect to Azure. (Get-ADOPSServiceConnection -Project MyAzureDevOpsProject -Name MyServiceConnection).id - - Guid - - Guid - - - None - - - ServiceEndpointScope - - Scope the Service Endpoint belongs to. Azure DevOps project ID (Get-ADOPSProject -Project MyAzureDevOpsProject).id - - Guid - - Guid - - - None - - - MaxSavedNodeCount - - Keep nodes in the pool on failure for investigation - - Int32 - - Int32 - - - 0 - - - OutputType - - In what format should the output be in. - - - json - pscustomobject - - String - - String - - - pscustomobject - - - AzureId - - The full resource id for the Virtual Machine Scale Set in Azure. /subscriptions/subscriptionID/resourceGroups/resourceGroup/providers/Microsoft.Compute/virtualMachineScaleSets/vmssName - - String - - String - - - None - - - OsType - - Operating system type of the nodes in the pool - - - linux - windows - - String - - String - - - linux - - - MaxCapacity - - Maximum number of nodes that will exist in the elastic pool - - Int32 - - Int32 - - - 1 - - - DesiredIdle - - Number of agents to have ready waiting for jobs - - Int32 - - Int32 - - - 0 - - - RecycleAfterEachUse - - Discard node after each job completes - - Boolean - - Boolean - - - False - - - DesiredSize - - The desired size of the pool - - Int32 - - Int32 - - - 0 - - - AgentInteractiveUI - - Set whether agents should be configured to run with interactive UI - - Boolean - - Boolean - - - False - - - TimeToLiveMinutes - - The minimum time in minutes to keep idle agents alive - - Int32 - - Int32 - - - 15 - - - - - - AgentInteractiveUI - - Set whether agents should be configured to run with interactive UI - - Boolean - - Boolean - - - False - - - AzureId - - The full resource id for the Virtual Machine Scale Set in Azure. /subscriptions/subscriptionID/resourceGroups/resourceGroup/providers/Microsoft.Compute/virtualMachineScaleSets/vmssName - - String - - String - - - None - - - DesiredIdle - - Number of agents to have ready waiting for jobs - - Int32 - - Int32 - - - 0 - - - DesiredSize - - The desired size of the pool - - Int32 - - Int32 - - - 0 - - - MaxCapacity - - Maximum number of nodes that will exist in the elastic pool - - Int32 - - Int32 - - - 1 - - - MaxSavedNodeCount - - Keep nodes in the pool on failure for investigation - - Int32 - - Int32 - - - 0 - - - OsType - - Operating system type of the nodes in the pool - - String - - String - - - linux - - - OutputType - - In what format should the output be in. - - String - - String - - - pscustomobject - - - RecycleAfterEachUse - - Discard node after each job completes - - Boolean - - Boolean - - - False - - - ServiceEndpointId - - Id of the Service connection used to connect to Azure. (Get-ADOPSServiceConnection -Project MyAzureDevOpsProject -Name MyServiceConnection).id - - Guid - - Guid - - - None - - - ServiceEndpointScope - - Scope the Service Endpoint belongs to. Azure DevOps project ID (Get-ADOPSProject -Project MyAzureDevOpsProject).id - - Guid - - Guid - - - None - - - TimeToLiveMinutes - - The minimum time in minutes to keep idle agents alive - - Int32 - - Int32 - - - 15 - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - $AzureDevOpsProject = Get-ADOPSProject -Project 'Azure' -$AzureVMSS = Get-AzVmss -VMScaleSetName 'vmss-test' -New-ADOPSElasticPoolObject -ServiceEndpointId '44868479-e856-42bf-9a2b-74bb500d8e36' -ServiceEndpointScope $AzureDevOpsProject.Id -AzureId $AzureVMSS.id - - Gets the Azure DevOps project where the Service endpoint is provisioned. Gets the Azure Virtual Machine Scale Set named vmss-test. Creates a new Azure DevOps Elastic Pool object with the correct references to the Azure path to the VMSS and DevOps project scope. - - - - -------------------------- Example 2 -------------------------- - New-ADOPSElasticPoolObject -ServiceEndpointId '44868479-e856-42bf-9a2b-74bb500d8e36' -ServiceEndpointScope '421eb3c8-1ca4-4a53-b93c-58997b9eb5e1' -AzureId '/subscriptions/8961f1f1-0bd1-4be0-b73c-6a8f3b304cf6/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/virtualMachineScaleSets/vmss-test' - - Creates a new Azure DevOps Elastic Pool by using the VMSS 'vmss-test' in the '8961f1f1-0bd1-4be0-b73c-6a8f3b304cf6' subscription. - - - - - - elasticpools-create - https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/elasticpools/create?view=azure-devops-rest-7.1 - - - - - - New-ADOPSEnvironment - New - ADOPSEnvironment - - Creates a new environment and sets up a better default access rule to it. - - - - This command creates a new environment in Azure DevOps. It also adds an admin to it that is not the creator, in order to fix this issue (https://developercommunity.visualstudio.com/t/Pipeline-environment-security-enhancemen/1236351?space=21&q=environment+permission&sort=votes&page=4)It does not support adding resources to the created environment. - - - - New-ADOPSEnvironment - - Organization - - Organization to connect to - - String - - String - - - None - - - Project - - Project to create the environment in - - String - - String - - - None - - - Name - - Name of the environment - - String - - String - - - None - - - Description - - Description of the environment - - String - - String - - - None - - - AdminGroup - - Custom group principalName to add as administrator in the environment. Can be found using the `Get-ADOPSGroup` command. - - String - - String - - - None - - - SkipAdmin - - Skip adding an administrative group. This will lead to only the creator of the environment having administrative rights on the environment. - - - SwitchParameter - - - False - - - - - - AdminGroup - - Custom group principalName to add as administrator in the environment. Can be found using the `Get-ADOPSGroup` command. - - String - - String - - - None - - - Description - - Description of the environment - - String - - String - - - None - - - Name - - Name of the environment - - String - - String - - - None - - - Organization - - Organization to connect to - - String - - String - - - None - - - Project - - Project to create the environment in - - String - - String - - - None - - - SkipAdmin - - Skip adding an administrative group. This will lead to only the creator of the environment having administrative rights on the environment. - - SwitchParameter - - SwitchParameter - - - False - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSEnvironment -Project 'myProject' -Name 'MyEnv' - - This command will create an environment named 'MyEnv' in the 'MyProject' project. It will add the '[myProject]\Project Administrators' group as administrator to the environment. - - - - -------------------------- Example 2 -------------------------- - PS C:\> New-ADOPSEnvironment -Project 'myProject' -Name 'MyEnv' -SkipAdmin - - This command will create an environment named 'MyEnv' in the 'MyProject' project. It will not add any group as administrator to the environment resulting in only the user running the command having admin access to the environment (default behaviour) - - - - -------------------------- Example 3 -------------------------- - PS C:\> New-ADOPSEnvironment -Project 'myProject' -Name 'MyEnv' -Description 'MyEnvironment description' -AdminGroup '[myProject]\myProject Team' - - This command will create an environment named 'MyEnv' with the description 'MyEnvironment description' in the 'MyProject' project. It will also add the '[myProject]\myProject Team' group as administrator to the environment. - - - - - - - - New-ADOPSGitBranch - New - ADOPSGitBranch - - Create a new branch in a Azure DevOps git repo. - - - - This command creates a new branch in an Azure DevOps git repo without the need to clone the repo or have any git tools installed. It creates the branch from the submitted commit id. - - - - New-ADOPSGitBranch - - Organization - - The organization to connect to - - String - - String - - - None - - - RepositoryId - - The ID of the repository in GUID format. This can be found using the `Get-ADOPSRepository` command - - String - - String - - - None - - - Project - - The project where the repository is located - - String - - String - - - None - - - BranchName - - Name of the new branch to create - - String - - String - - - None - - - CommitId - - Full hash commit id to use as base of the new branch. This can be found using `Invoke-ADOPSRestMethod` (see example), or by running the command `git log -1 --format=format:"%H"` inside a git repository. - - String - - String - - - None - - - - - - BranchName - - Name of the new branch to create - - String - - String - - - None - - - CommitId - - Full hash commit id to use as base of the new branch. This can be found using `Invoke-ADOPSRestMethod` (see example), or by running the command `git log -1 --format=format:"%H"` inside a git repository. - - String - - String - - - None - - - Organization - - The organization to connect to - - String - - String - - - None - - - Project - - The project where the repository is located - - String - - String - - - None - - - RepositoryId - - The ID of the repository in GUID format. This can be found using the `Get-ADOPSRepository` command - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSGitBranch -RepositoryId 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' -Project 'MyProj' -BranchName 'NewBranch' -CommitId '12345678910111213141516171819202122232425' - - This will create a new branch in the repository with id 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' from the commit with id '12345678910111213141516171819202122232425' with name 'NewBranch' - - - - -------------------------- Example 2 -------------------------- - PS C:\> $Project = 'myProject' -PS C:\> $RepoName = 'myRepo' -PS C:\> $BranchName = 'NewBranch' -PS C:\> $repo = Get-ADOPSRepository -Project $Project -Repository $RepoName -PS C:\> $AllCommits = Invoke-ADOPSRestMethod -Uri $repo._links.commits.href -Method Get | Select-Object -ExpandProperty value -PS C:\> $LatestCommit = ($AllCommits | Sort-Object -Property @{Expression = {$_.author.date}} -Descending | Select-Object -First 1).commitId -PS C:\> New-ADOPSGitBranch -RepositoryId $repo.id -Project $Project -BranchName $BranchName -CommitId $CommitId - - This command shows how to get all git history directly from a repository using the `Invoke-ADOPSRestMethod` command and use this to create a new branch from the latest commit. - - - - - - - - New-ADOPSGitFile - New - ADOPSGitFile - - This command uploads a file to an Azure DevOps repository using the API. - - - - This command uploads a file to an Azure DevOps repository using the API. If no other than required parameters are given it will be named as the source file and located in the root ("/") folder of the repository, with a default commit messadge of "File added using the ADOPS PowerShell module". You may override target filename, path, and commit message. - - - - New-ADOPSGitFile - - Organization - - The organization to connect to - - String - - String - - - None - - - Project - - The project where the repository is located - - String - - String - - - None - - - Repository - - Repository name where the file will be uploaded. - - String - - String - - - None - - - File - - Path to the local file to upload to the Azure DevOps repository. - - String - - String - - - None - - - FileName - - If set this will give the file a new filename in the Azure DevOps repository. - - String - - String - - - None - - - Path - - Sets the path to the file, relative to the root in Azure DevOps. Must use slash ('/') as path separator. - - String - - String - - - None - - - CommitMessage - - Override the default commit message - - String - - String - - - None - - - - - - CommitMessage - - Override the default commit message - - String - - String - - - None - - - File - - Path to the local file to upload to the Azure DevOps repository. - - String - - String - - - None - - - FileName - - If set this will give the file a new filename in the Azure DevOps repository. - - String - - String - - - None - - - Organization - - The organization to connect to - - String - - String - - - None - - - Path - - Sets the path to the file, relative to the root in Azure DevOps. Must use slash ('/') as path separator. - - String - - String - - - None - - - Project - - The project where the repository is located - - String - - String - - - None - - - Repository - - Repository name where the file will be uploaded. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSGitFile -Project 'myProject' -Repository 'myRepo' -File .\myFileToUplad.txt - - This will add the file 'myFileToUplad.txt' in the root ('/') folder of the 'myRepo'. It will contain the same data as the local file. It will be commited using the default commit message 'File added using the ADOPS PowerShell module' - - - - -------------------------- Example 2 -------------------------- - PS C:\> New-ADOPSGitFile -Project 'myProject' -Repository 'myRepo' -File .\myFileToUplad.txt -FileName 'newFileName.txt' -Path 'root/folder' - - This will add the file 'newFileName.txt' in the relative folder '/root/folder' folder of the 'myRepo'. It will contain the same data as the loca file '.\myFileToUpload.txt'. It will be commited using the default commit message 'File added using the ADOPS PowerShell module' Please note the path uses slash ('/') as path separator and is always relative to the root. - - - - -------------------------- Example 3 -------------------------- - PS C:\> New-ADOPSGitFile -Project 'myProject' -Repository 'myRepo' -File .\myFileToUplad.txt -CommitMessage 'Added this file' - - This will add the file 'myFileToUplad.txt' in the root ('/') folder of the 'myRepo'. It will contain the same data as the local file. It will be commited using the commit message 'Added this file' - - - - - - - - New-ADOPSGroupEntitlement - New - ADOPSGroupEntitlement - - Adds a group entitlement in Azure DevOps. - - - - Adds a group entitlement in Azure DevOps, allowing you to manage licensing and access levels for Entra ID groups. - - - - New-ADOPSGroupEntitlement - - GroupOriginId - - The ID of the Entra ID group to add entitlements for. - - String - - String - - - None - - - AccountLicenseType - - The type of license to assign to the group. - - - Express - Advanced - Stakeholder - Professional - EarlyAdopter - - String - - String - - - None - - - ProjectGroupType - - The type of access to grant in the project. - - - projectReader - projectContributor - projectAdministrator - projectStakeholder - - String - - String - - - None - - - ProjectId - - The ID of the project to grant access to. - - String - - String - - - None - - - Organization - - The Azure DevOps organization. - - String - - String - - - None - - - Wait - - If specified, waits for the operation to complete before returning. - - - SwitchParameter - - - False - - - - - - GroupOriginId - - The ID of the Entra ID group to add entitlements for. - - String - - String - - - None - - - AccountLicenseType - - The type of license to assign to the group. - - String - - String - - - None - - - ProjectGroupType - - The type of access to grant in the project. - - String - - String - - - None - - - ProjectId - - The ID of the project to grant access to. - - String - - String - - - None - - - Organization - - The Azure DevOps organization. - - String - - String - - - None - - - Wait - - If specified, waits for the operation to complete before returning. - - SwitchParameter - - SwitchParameter - - - False - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSGroupEntitlement -GroupOriginId "01d0472d-9949-421e-81d8-fcb5668a394d" -AccountLicenseType "Express" -ProjectGroupType "projectContributor" -ProjectId "8130f18e-f65b-431d-a777-5d4a6f3468ba" - - Adds a group entitlement for the specified Entra ID group with Express license and Contributor access to the specified project. - - - - -------------------------- Example 2 -------------------------- - PS C:\> New-ADOPSGroupEntitlement -GroupOriginId "01d0472d-9949-421e-81d8-fcb5668a394d" -AccountLicenseType "Stakeholder" -ProjectGroupType "projectReader" -ProjectId "8130f18e-f65b-431d-a777-5d4a6f3468ba" -Organization "MyOrg" -Wait - - Adds a group entitlement with Stakeholder license and Reader access to the specified project in the "MyOrg" organization, waiting for the operation to complete. - - - - - - - - New-ADOPSMergePolicy - New - ADOPSMergePolicy - - This command sets the Limit merge types setting of a repository git branch. - - - - This setting creates and enables a merge restriction policy on a git branch on a Azure DevOps repository. It will allow only those merge types activated. - WARNING: It is possible to allow no merge types at all, effectively preventing any code from being merged to a repository. Make sure to activate at least one policy. - - - - New-ADOPSMergePolicy - - Project - - Project where the pipeline and repository is located - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - RepositoryId - - Id of the repository in which to create the policy. Can be found using the `Get-ADOPSRepository` command - - String - - String - - - None - - - Branch - - git branch to trigger on. Accepts both branch name ('main') or full git path ('refs/heads/main') If only path name is given it will append "refs/heads/" - - String - - String - - - None - - - allowNoFastForward - - Enables basic merge (No fast forward) - - - SwitchParameter - - - False - - - allowRebase - - Enables Rebase and fast-forward - - - SwitchParameter - - - False - - - allowRebaseMerge - - Enables Rebase with merge commit - - - SwitchParameter - - - False - - - allowSquash - - Enables Squash merge - - - SwitchParameter - - - False - - - - - - Branch - - git branch to trigger on. Accepts both branch name ('main') or full git path ('refs/heads/main') If only path name is given it will append "refs/heads/" - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - Project - - Project where the pipeline and repository is located - - String - - String - - - None - - - RepositoryId - - Id of the repository in which to create the policy. Can be found using the `Get-ADOPSRepository` command - - String - - String - - - None - - - allowNoFastForward - - Enables basic merge (No fast forward) - - SwitchParameter - - SwitchParameter - - - False - - - allowRebase - - Enables Rebase and fast-forward - - SwitchParameter - - SwitchParameter - - - False - - - allowRebaseMerge - - Enables Rebase with merge commit - - SwitchParameter - - SwitchParameter - - - False - - - allowSquash - - Enables Squash merge - - SwitchParameter - - SwitchParameter - - - False - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSMergePolicy -Project 'MyProject' -RepositoryId 62a7c071-1a60-44a9-a151-d17cd4d367da -Branch 'main'-allowSquash - - This command will create a merge policy only allowing squash merges to the main branch of the repository with id 62a7c071-1a60-44a9-a151-d17cd4d367da - - - - -------------------------- Example 2 -------------------------- - PS C:\> $ADOPSProject = 'MyProject' -PS C:\> $RepoId = Get-ADOPSRepository -Project $ADOPSProject -Repository 'MyRepo' | Select-Object -ExpandProperty Id - -PS C:\> New-ADOPSMergePolicy -Project $ADOPSProject -RepositoryId $RepoId -Branch 'refs/heads/main' -allowNoFastForward -allowSquash -allowRebase -allowRebaseMerge - - This command will create a merge policy allowing any type of merges to the main branch of the repository MyRepo - - - - - - - - New-ADOPSPipeline - New - ADOPSPipeline - - Create a new pipeline from a existing yaml file in your repository. - - - - Create a new pipeline from a existing yaml file in your repository. - - - - New-ADOPSPipeline - - Name - - Name of the pipeline. - - String - - String - - - None - - - Project - - The project to add the pipeline to. - - String - - String - - - None - - - YamlPath - - The path to the Yaml file in your repository. Like: File.yaml or Folder/Folder/File.yaml - Only allows extensions .yaml or .yml - - String - - String - - - None - - - Repository - - The Repository Name where the Pipeline Yaml file is located. - - String - - String - - - None - - - FolderPath - - Folderpath to add your pipelines to under Devops Pipelines. Like: PipelineFolder or PipelineFolder1\PipelineSubFolder2 - - String - - String - - - None - - - Organization - - The organization to add pipeline to. - - String - - String - - - None - - - - - - FolderPath - - Folderpath to add your pipelines to under Devops Pipelines. Like: PipelineFolder or PipelineFolder1\PipelineSubFolder2 - - String - - String - - - None - - - Name - - Name of the pipeline. - - String - - String - - - None - - - Organization - - The organization to add pipeline to. - - String - - String - - - None - - - Project - - The project to add the pipeline to. - - String - - String - - - None - - - Repository - - The Repository Name where the Pipeline Yaml file is located. - - String - - String - - - None - - - YamlPath - - The path to the Yaml file in your repository. Like: File.yaml or Folder/Folder/File.yaml - Only allows extensions .yaml or .yml - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSPipeline -Name $PipelineName -Project $ProjectName -YamlPath 'pipelines/pipeline1.yaml' -Repository $RepositoryName -Organization $OrganizationName - - Create pipeline in $ProjectName - - - - -------------------------- Example 2 -------------------------- - PS C:\> New-ADOPSPipeline -Name $PipelineName -Project $ProjectName -YamlPath 'pipelines/pipeline1.yaml' -Repository $RepositoryName -FolderPath 'folder1\folder2' - - Create pipeline in $ProjectName and use Default Organization and put the pipeline in subfolder folder2 under DevOps Pipelines. - - - - - - - - New-ADOPSProject - New - ADOPSProject - - Creates a new project in Azure DevOps. - - - - Creates a new project in Azure DevOps. - - - - New-ADOPSProject - - Name - - The name of the new project. - - String - - String - - - None - - - Description - - The description of the new project. - - String - - String - - - None - - - Visibility - - The visibility of the project. - - - Private - Public - - String - - String - - - None - - - SourceControlType - - The source control type to use in the project. The default is git. - - - Git - Tfvc - - String - - String - - - Git - - - ProcessTypeName - - The name of a process to use in the project. - This can be the name of an existing process such as Basic, Agile, Scrum or CMMI. It can also be the name of a custom process created in the organization. - - String - - String - - - None - - - Organization - - The organization to create the project in. - - String - - String - - - None - - - Wait - - If defined this command will wait for the project to be created and return the project object. Default will return the process started. - - - SwitchParameter - - - False - - - - - - Description - - The description of the new project. - - String - - String - - - None - - - Name - - The name of the new project. - - String - - String - - - None - - - Organization - - The organization to create the project in. - - String - - String - - - None - - - ProcessTypeName - - The name of a process to use in the project. - This can be the name of an existing process such as Basic, Agile, Scrum or CMMI. It can also be the name of a custom process created in the organization. - - String - - String - - - None - - - SourceControlType - - The source control type to use in the project. The default is git. - - String - - String - - - Git - - - Visibility - - The visibility of the project. - - String - - String - - - None - - - Wait - - If defined this command will wait for the project to be created and return the project object. Default will return the process started. - - SwitchParameter - - SwitchParameter - - - False - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSProject -Name 'ADOPSproj' -Description 'an example project' -Visibility Public -Organization 'ADOPS' - - Creates a new, public project called "ADOPSproj" with a description in the organization "ADOPS". - - - - -------------------------- Example 2 -------------------------- - PS C:\> New-ADOPSProject -Name 'ADOPSproj' -Visibility Private -ProcessTypeName 'Agile' - - Creates a new, private project called "ADOPSproj" with the process type "Agile" in the default organization. - - - - -------------------------- Example 3 -------------------------- - PS C:\> New-ADOPSProject -Name 'ADOPSproj' -Visibility Private -ProcessTypeName 'MyOwnProcess' -SourceControlType 'Tfvc' - - Creates a new, private project called "ADOPSproj" with an existing, custom process type "MyOwnProcess" in the default organization, with the source control type set to Team Foundation Version Control. - - - - - - - - New-ADOPSRepository - New - ADOPSRepository - - Create a new Azure DevOps Git repository - - - - This command will create a new Git repository in your Azure DevOps project. - - - - New-ADOPSRepository - - Name - - The name of the new Git repo. - - String - - String - - - None - - - Project - - The project in which the repo will be created. - - String - - String - - - None - - - Organization - - The organization in which the repo will be created. - - String - - String - - - None - - - - - - Name - - The name of the new Git repo. - - String - - String - - - None - - - Organization - - The organization in which the repo will be created. - - String - - String - - - None - - - Project - - The project in which the repo will be created. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSRepository -Project 'MyProject' -Name 'MyNewRepo' - - This command will add a new empty repo named 'MyNewRepo' in the MyProject project - - - - - - - - New-ADOPSServiceConnection - New - ADOPSServiceConnection - - Create an Azure DevOps Service Connection to Azure. - - - - Creates an Azure DevOps Service Connection to Azure subscription using an existing Service Principal. Assign the necessary permissions in Azure for the service principal. - - - - New-ADOPSServiceConnection - - AzureScope - - resource group ID where the app registration will be granted contributor access. /subscriptions/4ba9b4a1-dc0d-4ec8-adaf-061771ccd1da/resourceGroups/MyAzureRG - - String - - String - - - None - - - ConnectionName - - Name of Service Connection in Azure DevOps. - - String - - String - - - None - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - Project - - Name of Azure DevOps project. - - String - - String - - - None - - - SubscriptionId - - The subscription id that the service connection will be connected to. - - String - - String - - - None - - - SubscriptionName - - The subscription name that the service connection will be connected to. - - String - - String - - - None - - - TenantId - - The tenant id connected to your Azure AD and subscriptions. - - String - - String - - - None - - - WorkloadIdentityFederation - - If Workload identity federation will be used for the connection. - - - SwitchParameter - - - False - - - CreationMode - - Sets the creation mode. - - String - - String - - - None - - - Description - - Description for the Service Connection. - - String - - String - - - None - - - - New-ADOPSServiceConnection - - ConnectionName - - Name of Service Connection in Azure DevOps. - - String - - String - - - None - - - ManagedIdentity - - If a managed identity will be used for the connection. - - - SwitchParameter - - - False - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - Project - - Name of Azure DevOps project. - - String - - String - - - None - - - ServicePrincipal - - Azure AD Service principal, Application (Client) ID and valid secret. - - PSCredential - - PSCredential - - - None - - - SubscriptionId - - The subscription id that the service connection will be connected to. - - String - - String - - - None - - - SubscriptionName - - The subscription name that the service connection will be connected to. - - String - - String - - - None - - - TenantId - - The tenant id connected to your Azure AD and subscriptions. - - String - - String - - - None - - - Description - - Description for the Service Connection. - - String - - String - - - None - - - - New-ADOPSServiceConnection - - ConnectionName - - Name of Service Connection in Azure DevOps. - - String - - String - - - None - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - Project - - Name of Azure DevOps project. - - String - - String - - - None - - - ServicePrincipal - - Azure AD Service principal, Application (Client) ID and valid secret. - - PSCredential - - PSCredential - - - None - - - SubscriptionId - - The subscription id that the service connection will be connected to. - - String - - String - - - None - - - SubscriptionName - - The subscription name that the service connection will be connected to. - - String - - String - - - None - - - TenantId - - The tenant id connected to your Azure AD and subscriptions. - - String - - String - - - None - - - Description - - Description for the Service Connection. - - String - - String - - - None - - - - - - AzureScope - - resource group ID where the app registration will be granted contributor access. /subscriptions/4ba9b4a1-dc0d-4ec8-adaf-061771ccd1da/resourceGroups/MyAzureRG - - String - - String - - - None - - - ConnectionName - - Name of Service Connection in Azure DevOps. - - String - - String - - - None - - - ManagedIdentity - - If a managed identity will be used for the connection. - - SwitchParameter - - SwitchParameter - - - False - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - Project - - Name of Azure DevOps project. - - String - - String - - - None - - - ServicePrincipal - - Azure AD Service principal, Application (Client) ID and valid secret. - - PSCredential - - PSCredential - - - None - - - SubscriptionId - - The subscription id that the service connection will be connected to. - - String - - String - - - None - - - SubscriptionName - - The subscription name that the service connection will be connected to. - - String - - String - - - None - - - TenantId - - The tenant id connected to your Azure AD and subscriptions. - - String - - String - - - None - - - WorkloadIdentityFederation - - If Workload identity federation will be used for the connection. - - SwitchParameter - - SwitchParameter - - - False - - - CreationMode - - Sets the creation mode. - - String - - String - - - None - - - Description - - Description for the Service Connection. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - $Params = @{ - TenantId = "32238a3e-4aae-4a9d-a3be-5c2912088b9b" - SubscriptionName = "My Subscription" - SubscriptionId = "34dacce0-5332-4b27-a804-4352202aca27" - ServicePrincipal = Get-Credential - Project = "My DevOps Project" - ConnectionName = "My Service Connection Name" -} -New-ADOPSServiceConnection @Params - - Creates an Azure DevOps Service Connection to an Azure subscription using an existing Service Principal object in Azure AD. - - - - -------------------------- Example 2 -------------------------- - $Params = @{ - TenantId = "32238a3e-4aae-4a9d-a3be-5c2912088b9b" - SubscriptionName = "My Subscription" - SubscriptionId = "34dacce0-5332-4b27-a804-4352202aca27" - ServicePrincipal = Get-Credential - Project = "My DevOps Project" - ConnectionName = "My Service Connection Name" - ManagedIdentity = $true -} -New-ADOPSServiceConnection @Params - - Creates an Azure DevOps Service Connection to an Azure subscription using a managed service principal in Azure AD. - - - - -------------------------- Example 3 -------------------------- - $Params = @{ - TenantId = "32238a3e-4aae-4a9d-a3be-5c2912088b9b" - SubscriptionName = "My Subscription" - SubscriptionId = "34dacce0-5332-4b27-a804-4352202aca27" - Project = "My DevOps Project" - ConnectionName = "My Service Connection Name" - WorkloadIdentityFederation = $true - AzureScope = (Get-AzResourceGroup -Name MyAzureRG).ResourceId -} -New-ADOPSServiceConnection @Params - - Creates an Azure DevOps Service Connection to an Azure resource group using workload identity federation (OAuth) and automatically creating an app registration in Azure AD. - - - - - - serviceconnection-create - https://docs.microsoft.com/en-us/rest/api/azure/devops/serviceendpoint/endpoints/create?view=azure-devops-rest-6.0 - - - - - - New-ADOPSUserStory - New - ADOPSUserStory - - Create User Story in DevOps Project - - - - Creates a User Story (Work Item) in an Azure DevOps Project. - - - - New-ADOPSUserStory - - Title - - Title of the User Story. - - String - - String - - - None - - - ProjectName - - Name of the DevOps project to use. - - String - - String - - - None - - - Description - - Description text for the User Story work item. - - String - - String - - - None - - - Tags - - Tags to use for the user story. Multiple tags can be added and separated by comma. - - String - - String - - - None - - - Priority - - User Story priority. - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - - - - Description - - Description text for the User Story work item. - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - Priority - - User Story priority. - - String - - String - - - None - - - ProjectName - - Name of the DevOps project to use. - - String - - String - - - None - - - Tags - - Tags to use for the user story. Multiple tags can be added and separated by comma. - - String - - String - - - None - - - Title - - Title of the User Story. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - New-ADOPSUserStory -Organization 'myOrganization' -ProjectName 'myProject' -Title 'My user story' -Description 'User story description' -Tags 'ADOPS' -Priority 1 - - Creates a user story with the title 'My user story' in the DevOps Project 'myProject'. - - - - -------------------------- Example 2 -------------------------- - New-ADOPSUserStory -Organization 'myOrganization' -ProjectName 'myProject' -Title 'My user story' -Description 'User story description' -Tags 'ADOPS,Important' -Priority 1 - - Creates a user story with multiple tags. - - - - - - - - New-ADOPSVariableGroup - New - ADOPSVariableGroup - - Creates a new variable group in an Azure DevOps project. - - - - Creates a new variable group in an Azure DevOps project. - The command only supports a single variable being created together with the variable group. - - - - New-ADOPSVariableGroup - - Description - - The description of the variable group. - - String - - String - - - None - - - IsSecret - - Whether or not the variable created should be considered a secret. - - - SwitchParameter - - - False - - - Organization - - The organization of the project to create the variable group in. - - String - - String - - - None - - - Project - - The name of the project to create the variable group in. - - String - - String - - - None - - - VariableGroupName - - The name of the new variable group. - - String - - String - - - None - - - VariableName - - The name of the variable to be created with the variable group. - - String - - String - - - None - - - VariableValue - - The value of the variable to be created with the variable group. - - String - - String - - - None - - - - New-ADOPSVariableGroup - - Description - - The description of the variable group. - - String - - String - - - None - - - Organization - - The organization of the project to create the variable group in. - - String - - String - - - None - - - Project - - The name of the project to create the variable group in. - - String - - String - - - None - - - VariableGroupName - - The name of the new variable group. - - String - - String - - - None - - - VariableHashtable - - A hashtable containing the variable to be created with the variable group. - - - @( @{ Name = "Key1" value = "Key1Value" IsSecret = $true }, @{ Name = "Key2" value = "Key2Value" IsSecret = $false } ) - - Hashtable[] - - Hashtable[] - - - None - - - - - - Description - - The description of the variable group. - - String - - String - - - None - - - IsSecret - - Whether or not the variable created should be considered a secret. - - SwitchParameter - - SwitchParameter - - - False - - - Organization - - The organization of the project to create the variable group in. - - String - - String - - - None - - - Project - - The name of the project to create the variable group in. - - String - - String - - - None - - - VariableGroupName - - The name of the new variable group. - - String - - String - - - None - - - VariableHashtable - - A hashtable containing the variable to be created with the variable group. - - - @( @{ Name = "Key1" value = "Key1Value" IsSecret = $true }, @{ Name = "Key2" value = "Key2Value" IsSecret = $false } ) - - Hashtable[] - - Hashtable[] - - - None - - - VariableName - - The name of the variable to be created with the variable group. - - String - - String - - - None - - - VariableValue - - The value of the variable to be created with the variable group. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> $Hashtable = @( - @{ - Name = "Key1" - value = "Key1Value" - IsSecret = $true - }, - @{ - Name = "Key2" - value = "Key2Value" - IsSecret = $false - } - ) - -PS C:\> New-ADOPSVariableGroup -Project 'ADOPSproj' -Organization 'ADOPS' -VariableGroupName 'ADOPSvars' -Description 'vargroup example' -VariableHashtable $Hashtable - - Creates a new variable group called "ADOPSvars" in the project "ADOPSproj" in the organization "ADOPS" with a description, providing a hashtable to create one non-secret variable called "Key2" with the value "Key2Value" and one secret variable called "Key1" with the value "Key1Value". - - - - -------------------------- Example 2 -------------------------- - PS C:\> New-ADOPSVariableGroup -Project 'ADOPSproj' -VariableGroupName 'ADOPSvars' -VariableName 'var-name' -VariableValue 'var-value' - - Creates a new variable group called "ADOPSvars" in the project "ADOPSproj" in the default organization, creating a variable called "var-name" with the value "var-value". - - - - - - - - New-ADOPSWiki - New - ADOPSWiki - - Creates a new Azure DevOps code wiki. - - - - This command lets you create a new Azure DevOps code wiki. This wiki is editable by changing markdown files directly in a repository in your Azure DevOps project. - - - - New-ADOPSWiki - - WikiName - - Name of the wiki. - - String - - String - - - None - - - WikiRepository - - The Git repository that will host the markdown wiki files. - - String - - String - - - None - - - Project - - The project in which to create the wiki. - - String - - String - - - None - - - WikiRepositoryPath - - Path relative to the root where the wiki home will be. - - String - - String - - - '/' - - - GitBranch - - The git branch used for wiki markdown pages. - - String - - String - - - Main - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - - - - GitBranch - - The git branch used for wiki markdown pages. - - String - - String - - - Main - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - Project - - The project in which to create the wiki. - - String - - String - - - None - - - WikiName - - Name of the wiki. - - String - - String - - - None - - - WikiRepository - - The Git repository that will host the markdown wiki files. - - String - - String - - - None - - - WikiRepositoryPath - - Path relative to the root where the wiki home will be. - - String - - String - - - '/' - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> New-ADOPSWiki -Project 'myproject' -WikiName 'MyWikiName' -WikiRepository 'MyWikiRepo' - - This command will create a new wiki named "MyWikiName", in the "MyProject" project in your current default logged in Azure DevOps environment. It will store the markdown files in the "MyWikiRepo" repository, using the root folder in the main branch. - - - - -------------------------- Example 2 -------------------------- - PS C:\> New-ADOPSWiki -Project 'myproject' -WikiName 'MyWikiName' -WikiRepository 'MyWikiRepo' -WikiRepositoryPath '/MyWikiFiles' -GitBranch 'PublicWiki' - - This command will create a new wiki named "MyWikiName", in the "MyProject" project in your current default logged in Azure DevOps environment. It will store the markdown files in the "MyWikiRepo" repository. The Markdown files will be stored in the "/MyWikiFiles" subfolder of the repository. The wiki will be published from the "PublicWiki" branch of the "MyWikiRepo" repository. - - - - - - - - Remove-ADOPSRepository - Remove - ADOPSRepository - - Deletes a repository from an Azure DevOps organization. - - - - This command deletes a repository from an Azure DevOps organization. - - - - Remove-ADOPSRepository - - RepositoryID - - The repositoru ID, GUID, to delet. - - String - - String - - - None - - - Project - - The project in which the repository exists. - - String - - String - - - None - - - Organization - - The organization to connect to. - - String - - String - - - None - - - - - - Organization - - The organization to connect to. - - String - - String - - - None - - - Project - - The project in which the repository exists. - - String - - String - - - None - - - RepositoryID - - The repositoru ID, GUID, to delet. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Remove-ADOPSRepository -Project MyProject -RepositoryID 1bb0b904-27b7-414d-811e-f51085e7ad02 - - This command will delete the repository with id '1bb0b904-27b7-414d-811e-f51085e7ad02' from the 'MyProject' project. - - - - - - - - Remove-ADOPSVariableGroup - Remove - ADOPSVariableGroup - - Removes a variable group from Azure DevOps. - - - - Removes a variable group from Azure DevOps. - - - - Remove-ADOPSVariableGroup - - VariableGroupName - - Name of the variable group to remove. - - String - - String - - - None - - - Project - - Name of the Azure DevOps project. - - String - - String - - - None - - - Organization - - Name of the Azure DevOps organization. - - String - - String - - - None - - - - - - Organization - - Name of the Azure DevOps organization. - - String - - String - - - None - - - Project - - Name of the Azure DevOps project. - - String - - String - - - None - - - VariableGroupName - - Name of the variable group to remove. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - Remove-ADOPSVariableGroup -Organization 'ADOPS' -Project 'ADOPSproj' -VariableGroupName 'ExampleVarGroup' - - Removes the variable group called "ExampleVarGroup" from the project "ADOPSproj" in the organization "ADOPS". - - - - -------------------------- Example 2 -------------------------- - Remove-ADOPSVariableGroup -Project 'ADOPSproj' -VariableGroupName 'ExampleVarGroup' - - Removes the variable group called "ExampleVarGroup" from the project "ADOPSproj" in the default organization. - - - - - - - - Revoke-ADOPSPipelinePermission - Revoke - ADOPSPipelinePermission - - Revokes pipeline permission to a resource of type VariableGroup, Queue, SecureFile, or Environment. - - - - Revokes pipeline permissions to a resource of type VariableGroup, Queue, SecureFile, or Environment. Specify a PipelineId to grant permissions to a single pipeline, or use the AllPipelines parameter to grant permissions to all pipelines. - - - - Revoke-ADOPSPipelinePermission - - AllPipelines - - Revoke access for all pipelines. This is equivalent to enable the restrictions on the resource. - - - SwitchParameter - - - False - - - Organization - - Name of the Azure DevOps organization. Defaults to the current organization. - - String - - String - - - None - - - Project - - Name of project containing the resource. - - String - - String - - - None - - - ResourceId - - Id of the resource to revoke permissions to. - - String - - String - - - None - - - ResourceType - - Resource type to revoke permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. - - - VariableGroup - Queue - SecureFile - Environment - - ResourceType - - ResourceType - - - None - - - - Revoke-ADOPSPipelinePermission - - Organization - - Name of the Azure DevOps organization. Defaults to the current organization. - - String - - String - - - None - - - PipelineId - - Id of the pipeline to revoke permissions for. - - Int32 - - Int32 - - - None - - - Project - - Name of project containing the resource. - - String - - String - - - None - - - ResourceId - - Id of the resource to revoke permissions to. - - String - - String - - - None - - - ResourceType - - Resource type to revoke permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. - - - VariableGroup - Queue - SecureFile - Environment - - ResourceType - - ResourceType - - - None - - - - - - AllPipelines - - Revoke access for all pipelines. This is equivalent to enable the restrictions on the resource. - - SwitchParameter - - SwitchParameter - - - False - - - Organization - - Name of the Azure DevOps organization. Defaults to the current organization. - - String - - String - - - None - - - PipelineId - - Id of the pipeline to revoke permissions for. - - Int32 - - Int32 - - - None - - - Project - - Name of project containing the resource. - - String - - String - - - None - - - ResourceId - - Id of the resource to revoke permissions to. - - String - - String - - - None - - - ResourceType - - Resource type to revoke permissions to. Valid values are VariableGroup, Queue, SecureFile, and Environment. - - ResourceType - - ResourceType - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Revoke-ADOPSPipelinePermission -Pipeline 42 -ResourceType SecureFile -ResourceId '88d047be-7e59-4eda-b1fe-1614c7a69e16' -Project 'MyProject' - - Revoke pipeline 42 permission to the secure file with id 88d047be-7e59-4eda-b1fe-1614c7a69e16 in project MyProject. - - - - - - - - Save-ADOPSPipelineTask - Save - ADOPSPipelineTask - - Saves the zip file of any task installed in an Azure DevOps organization. - - - - This command will download a zip file containing any task from yourAzure DevOps organization for you to explore or security review. - - - - Save-ADOPSPipelineTask - - FileName - - Filename to output the zipfile as. - - String - - String - - - None - - - Organization - - The organization where your tasks are installed. - - String - - String - - - None - - - Path - - Path where zip files will be output. - - String - - String - - - None - - - TaskId - - Id of the task to download. Can be found using the command `Get-ADOPSPipelineTask`. - - String - - String - - - None - - - TaskVersion - - Version in the format 'major.minor.build' of the task to download. Can be found using the command `Get-ADOPSPipelineTask`. - - Version - - Version - - - None - - - - Save-ADOPSPipelineTask - - InputObject - - A list of tasks to download. This list must contain the following properties: id = guid of task name = task name for file output version = @{ major = 1 minor = 2 patch = 3 } - You may use the command `Get-ADOPSPipelineTask` to get a properly created object. - - PSObject[] - - PSObject[] - - - None - - - Organization - - The organization where your tasks are installed. - - String - - String - - - None - - - Path - - Path where zip files will be output. - - String - - String - - - None - - - - - - FileName - - Filename to output the zipfile as. - - String - - String - - - None - - - InputObject - - A list of tasks to download. This list must contain the following properties: id = guid of task name = task name for file output version = @{ major = 1 minor = 2 patch = 3 } - You may use the command `Get-ADOPSPipelineTask` to get a properly created object. - - PSObject[] - - PSObject[] - - - None - - - Organization - - The organization where your tasks are installed. - - String - - String - - - None - - - Path - - Path where zip files will be output. - - String - - String - - - None - - - TaskId - - Id of the task to download. Can be found using the command `Get-ADOPSPipelineTask`. - - String - - String - - - None - - - TaskVersion - - Version in the format 'major.minor.build' of the task to download. Can be found using the command `Get-ADOPSPipelineTask`. - - Version - - Version - - - None - - - - - - System.Management.Automation.PSObject[] - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Save-ADOPSPipelineTask -TaskId 6c731c3c-3c68-459a-a5c9-bde6e6595b5b -TaskVersion 2.206.1 - - This command will download a zip file to the current folder. The zip file will contain the task with task id "6c731c3c-3c68-459a-a5c9-bde6e6595b5b" (bash) The zip file will be named "6c731c3c-3c68-459a-a5c9-bde6e6595b5b.2.206.1.zip" (taskid.version.zip) - - - - -------------------------- Example 2 -------------------------- - PS C:\> Save-ADOPSPipelineTask -Path C:\temp\tasks\ -TaskId 6c731c3c-3c68-459a-a5c9-bde6e6595b5b -TaskVersion 2.206.1 -FileName MyBashTask.zip - - This command will download a zip file named "MyBashTask.zip" to the "C:\temp\tasks\" folder. The zip file will contain the task with task id "6c731c3c-3c68-459a-a5c9-bde6e6595b5b" (bash) - - - - -------------------------- Example 3 -------------------------- - PS C:\> $r = Get-ADOPSPipelineTask -PS C:\> $r.Where({$_.name -eq 'PowerShell'}) | Save-ADOPSPipelineTask -Path c:\temp\tasks\ - - This command uses the Get-ADOPSPipelineTask to get all installed pipeline tasks. It filters the tasks on the name "PowerShell" and downloads all matching tasks to the "c:\temp\tasks\" folder. The zip files will be named "TaskName-TaskId-version.zip" (for example: "PowerShell-e213ff0f-5d5c-4791-802d-52ea3e7be1f1-1.2.3.zip") - - - - -------------------------- Example 4 -------------------------- - PS C:\> Save-ADOPSPipelineTask -Path c:\temp\tasks\ -InputObject @( - @{ - id = "e213ff0f-5d5c-4791-802d-52ea3e7be1f1" - name = "PowerShell" - version = @{ - major = 1 - minor = 2 - patch = 3 - } - }, - @{ - id = "e213ff0f-5d5c-4791-802d-52ea3e7be1f1" - name = "PowerShell" - version = @{ - major = 2 - minor = 212 - patch = 0 - } - } -) - - This command uses a list of hashtables to download two different tasks. The properties listed in the example must be included in the objects to process. - - - - - - - - Set-ADOPSArtifactFeed - Set - ADOPSArtifactFeed - - Changes a setting for an artifact feed. - - - - Changes a setting for an artifact feed. Currently only supports Description and enabling or disabling upstreams. - - - - Set-ADOPSArtifactFeed - - Project - - Project where the feed is located - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - FeedId - - Feed Id in gui format or name. - - String - - String - - - None - - - Description - - Set the feed description - - String - - String - - - None - - - UpstreamEnabled - - Enable or Disable upstreams. If enabling it will also add all current upstream sources. - - Boolean - - Boolean - - - None - - - - - - Description - - Set the feed description - - String - - String - - - None - - - FeedId - - Feed Id in gui format or name. - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - Project - - Project where the feed is located - - String - - String - - - None - - - UpstreamEnabled - - Enable or Disable upstreams. If enabling it will also add all current upstream sources. - - Boolean - - Boolean - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> $i = (Get-ADOPSArtifactFeed -Project 'MyProject' -FeedId 'MyCustomFeed').id -PS C:\> Set-ADOPSArtifactFeed -Project 'MyProject' -FeedId $i -Description 'Update the description' -UpstreamEnabled:$true - - This command will set the description for artifact feed 'MyCustomFeed' in the 'MyProject' project. It will also enable upstreams and add all currently supported upstream sources. - - - - - - - - Set-ADOPSBuildDefinition - Set - ADOPSBuildDefinition - - This command will update a build definition. - - - - This command will update a build definition. Because the build definition object is multilevel and intricate, and you will need to have the correct definition revision and metadata, you must use `Get-ADOPSBuildDefinition` and alter the output object. - - - - Set-ADOPSBuildDefinition - - Organization - - The organization to set definitions in. - - String - - String - - - None - - - DefinitionObject - - Complete object returned from `Get-ADOPSBuildDefinition` - - Object - - Object - - - None - - - - - - DefinitionObject - - Complete object returned from `Get-ADOPSBuildDefinition` - - Object - - Object - - - None - - - Organization - - The organization to set definitions in. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> $Definition = Get-ADOPSBuildDefinition -Project MyProject -Id 123 -PS C:\> $Definition.queueStatus = 'disabled' -PS C:\> Set-ADOPSBuildDefinition -DefinitionObject $Definition - - This command will set the build definition with id 123 to status 'disabled' - - - - - - - - Set-ADOPSElasticPool - Set - ADOPSElasticPool - - Updates an Azure DevOps Elastic pool. - - - - Updates an Azure DevOps Elastic pool. - - - - Set-ADOPSElasticPool - - PoolId - - The unique Azure DevOps Pool id. - - Int32 - - Int32 - - - None - - - ElasticPoolObject - - The full request body in json or as a pscustom object. The Help function New-ElasticPoolObject can help generate a powershell object/json string. - | Name | Type | Description | |----------------------|---------------------|-------------------------------------------------------------------------------| | agentInteractiveUI | boolean | Set whether agents should be configured to run with interactive UI | | azureId | string | Azure string representing to location of the resource | | desiredIdle | integer | Number of agents to have ready waiting for jobs | | desiredSize | integer | The desired size of the pool | | maxCapacity | integer | Maximum number of nodes that will exist in the elastic pool | | maxSavedNodeCount | integer | Keep nodes in the pool on failure for investigation | | offlineSince | string | Timestamp the pool was first detected to be offline | | osType | OperatingSystemType | Operating system type of the nodes in the pool | | poolId | integer | Id of the associated TaskAgentPool | | recycleAfterEachUse | boolean | Discard node after each job completes | | serviceEndpointId | string | Id of the Service Endpoint used to connect to Azure | | serviceEndpointScope | string | Scope the Service Endpoint belongs to | | sizingAttempts | integer | The number of sizing attempts executed while trying to achieve a desired size | | state | ElasticPoolState | State of the pool | | timeToLiveMinutes | integer | The minimum time in minutes to keep idle agents alive | - - Object - - Object - - - None - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - - - - ElasticPoolObject - - The full request body in json or as a pscustom object. The Help function New-ElasticPoolObject can help generate a powershell object/json string. - | Name | Type | Description | |----------------------|---------------------|-------------------------------------------------------------------------------| | agentInteractiveUI | boolean | Set whether agents should be configured to run with interactive UI | | azureId | string | Azure string representing to location of the resource | | desiredIdle | integer | Number of agents to have ready waiting for jobs | | desiredSize | integer | The desired size of the pool | | maxCapacity | integer | Maximum number of nodes that will exist in the elastic pool | | maxSavedNodeCount | integer | Keep nodes in the pool on failure for investigation | | offlineSince | string | Timestamp the pool was first detected to be offline | | osType | OperatingSystemType | Operating system type of the nodes in the pool | | poolId | integer | Id of the associated TaskAgentPool | | recycleAfterEachUse | boolean | Discard node after each job completes | | serviceEndpointId | string | Id of the Service Endpoint used to connect to Azure | | serviceEndpointScope | string | Scope the Service Endpoint belongs to | | sizingAttempts | integer | The number of sizing attempts executed while trying to achieve a desired size | | state | ElasticPoolState | State of the pool | | timeToLiveMinutes | integer | The minimum time in minutes to keep idle agents alive | - - Object - - Object - - - None - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - PoolId - - The unique Azure DevOps Pool id. - - Int32 - - Int32 - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> $AzureDevOpsProject = Get-ADOPSProject -Project 'ADOPS' -$AzureVMSS = Get-AzVmss -VMScaleSetName 'adops-vmss' -$PoolInformation = Get-ADOPSPool -PoolName 'adopsPool' -$ElasticPoolObject = New-ADOPSElasticPoolObject -ServiceEndpointId 'd6040a29-d507-43b0-a72d-99a50cb3a9d3' -ServiceEndpointScope $AzureDevOpsProject.Id -AzureId $AzureVMSS.id -DesiredIdle 2 -MaxCapacity 2 -Set-ADOPSElasticPool -PoolId $PoolInformation.id -ElasticPoolObject $ElasticPoolObject - - Retrieves the Azure DevOps Project specified, the Azure Virtual Machine Scale-set. Generates a Elastic pool object using the New-ADOPSElasticPoolObject function and then updates the Elastic Pool id Azure using it's unique id. - - - - - - - - Set-ADOPSGitPermission - Set - ADOPSGitPermission - - Sets git ref permissions on a Azure DevOps git repo - - - - This function sets Git ref access rights for a user or group in Azure DevOps. You may select the same rule for Allow and Deny, but it should not work and may lead to unexpected behaviours. - The following settings are possible to set. bit name displayName --- ---- ----------- 1 Administer Administer 2 GenericRead Read 4 GenericContribute Contribute 8 ForcePush Force push (rewrite history and delete branches) 16 CreateBranch Create branch 32 CreateTag Create tag 64 ManageNote Manage notes 128 PolicyExempt Bypass policies when pushing 256 CreateRepository Create repository 512 DeleteRepository Delete repository 1024 RenameRepository Rename repository 2048 EditPolicies Edit policies 4096 RemoveOthersLocks Remove others' locks 8192 ManagePermissions Manage permissions 16384 PullRequestContribute Contribute to pull requests 32768 PullRequestBypassPolicy Bypass policies when completing pull requests - - - - Set-ADOPSGitPermission - - Organization - - Name of the Azure DevOps organization. - - String - - String - - - None - - - Project - - Project ID or Project Name where the repo is located. - - String - - String - - - None - - - Repository - - Repository ID or Repository Name to set access to. - - String - - String - - - None - - - Descriptor - - Descriptor of a user or group in Azure DevOps. Can be found using the `Get-ADOPSUser` or `Get-ADOPSGroup` command. - - String - - String - - - None - - - Allow - - Enum of access rights to grant a user or group. Can be either the topic or the value. - - - Administer - GenericRead - GenericContribute - ForcePush - CreateBranch - CreateTag - ManageNote - PolicyExempt - CreateRepository - DeleteRepository - RenameRepository - EditPolicies - RemoveOthersLocks - ManagePermissions - PullRequestContribute - PullRequestBypassPolicy - - AccessLevels[] - - AccessLevels[] - - - None - - - Deny - - Enum of access rights to deny a user or group. Can be either the topic or the value. - - - Administer - GenericRead - GenericContribute - ForcePush - CreateBranch - CreateTag - ManageNote - PolicyExempt - CreateRepository - DeleteRepository - RenameRepository - EditPolicies - RemoveOthersLocks - ManagePermissions - PullRequestContribute - PullRequestBypassPolicy - - AccessLevels[] - - AccessLevels[] - - - None - - - - - - Allow - - Enum of access rights to grant a user or group. Can be either the topic or the value. - - AccessLevels[] - - AccessLevels[] - - - None - - - Deny - - Enum of access rights to deny a user or group. Can be either the topic or the value. - - AccessLevels[] - - AccessLevels[] - - - None - - - Descriptor - - Descriptor of a user or group in Azure DevOps. Can be found using the `Get-ADOPSUser` or `Get-ADOPSGroup` command. - - String - - String - - - None - - - Organization - - Name of the Azure DevOps organization. - - String - - String - - - None - - - Project - - Project ID or Project Name where the repo is located. - - String - - String - - - None - - - Repository - - Repository ID or Repository Name to set access to. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> $s = @{ - ProjectId = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' - RepositoryId = '11111111-1111-1111-1111-111111111111' - Descriptor = 'aad.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - Allow = 'RenameRepository','ForcePush' -} -PS C:\> Set-ADOPSGitPermission @s - - This command will grant the user with descriptor `aad.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA` the force push, and rename repository rights on the repo with id `11111111-1111-1111-1111-111111111111` - - - - -------------------------- Example 2 -------------------------- - PS C:\> $s = @{ - ProjectId = Get-ADOPSProject MyProject | Select-Object -ExpandProperty id - RepositoryId = Get-ADOPSRepository -Project $projectId -Repository MyRepo | Select-Object -ExpandProperty id - Descriptor = Get-ADOPSUser -Name 'userEmail@domain.onmicrosoft.com' | Select-Object -ExpandProperty descriptor - Allow = 6235 -} -PS C:\> Set-ADOPSGitPermission @s - - This command will grant the user `userEmail@domain.onmicrosoft.com` the Administer, GenericRead, ForcePush, CreateBranch, ManageNote, EditPolicies, RemoveOthersLocks repository rights on the `MyRepo` repo - - - - -------------------------- Example 3 -------------------------- - PS C:\> $s = @{ - ProjectId = Get-ADOPSProject MyProject | Select-Object -ExpandProperty id - RepositoryId = Get-ADOPSRepository -Project $projectId -Repository MyRepo | Select-Object -ExpandProperty id - Descriptor = Get-ADOPSUser -Name 'userEmail@domain.onmicrosoft.com' | Select-Object -ExpandProperty descriptor - Deny = Administer -} -PS C:\> Set-ADOPSGitPermission @s - - This command will deny the user `userEmail@domain.onmicrosoft.com` the Administer repository rights on the `MyRepo` repo - - - - - - - - Set-ADOPSPipelineRetentionSettings - Set - ADOPSPipelineRetentionSettings - - Set pipeline retention settings for a project. - - - - This command will set the retention settings values for a project. - - - - Set-ADOPSPipelineRetentionSettings - - Organization - - The organization to get pipeline retention settings from. - - String - - String - - - None - - - Project - - The project to get pipeline retention settings from. - - String - - String - - - None - - - Values - - Pipeline retention settings to set. - - Object - - Object - - - None - - - - - - Organization - - The organization to get pipeline retention settings from. - - String - - String - - - None - - - Project - - The project to get pipeline retention settings from. - - String - - String - - - None - - - Values - - Pipeline retention settings to set. - - Object - - Object - - - None - - - - - - None - - - - - - - - - - System.Object - - - A Key/value custom object of all the pipeline retention value - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Set-ADOPSPipelineRetentionSettings -Project 'MyProject' -Values @{ - artifactsRetention = 51 - runRetention = 37 - pullRequestRunRetention = 4 - retainRunsPerProtectedBranch = 0 -} - - This command will set all pipeline retention settings. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Set-ADOPSPipelineRetentionSettings -Project 'MyProject' -Values @{ - artifactsRetention = 51 -} - - This command will set a single pipeline retention settings. - - - - - - - - Set-ADOPSPipelineSettings - Set - ADOPSPipelineSettings - - Set pipeline settings for a project. - - - - This command will set pipeline settings for a project. - - - - Set-ADOPSPipelineSettings - - Organization - - The organization to get pipeline settings from. - - String - - String - - - None - - - Project - - The project to get pipeline settings from. - - String - - String - - - None - - - Values - - Pipeline settings to set. - - Object - - Object - - - None - - - - - - Organization - - The organization to get pipeline settings from. - - String - - String - - - None - - - Project - - The project to get pipeline settings from. - - String - - String - - - None - - - Values - - Pipeline settings to set. - - Object - - Object - - - None - - - - - - None - - - - - - - - - - System.Object - - - A key/value custom object of all pipeline settings is returned. - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Set-ADOPSPipelineSettings -Project 'MyProject' -Values @{ - statusBadgesArePrivate -} - - This command will set a single setting `statusBadgesArePrivate` - - - - -------------------------- Example 2 -------------------------- - PS C:\> Set-ADOPSPipelineSettings -Project 'MyProject' -Values @{ - statusBadgesArePrivate = $false - disableImpliedYAMLCiTrigger = $true -} - - This command will set multiple settings - - - - - - - - Set-ADOPSProject - Set - ADOPSProject - - Updates Azure DevOps project information. - - - - This command will update information of an Azure DevOps project. - - - - Set-ADOPSProject - - Description - - Set the Project description - - String - - String - - - None - - - Organization - - The organization to update the project in. - - String - - String - - - None - - - ProjectId - - Project ID of the project to update. Only needed if no ProjectName is given. - - String - - String - - - None - - - Visibility - - Sets project visibility. - - - Private - Public - - String - - String - - - None - - - Wait - - Wait for the status update to be set to 'Complete', 'Failed', or 'Cancelled' - - - SwitchParameter - - - False - - - - Set-ADOPSProject - - Description - - Set the Project description - - String - - String - - - None - - - Organization - - The organization to update the project in. - - String - - String - - - None - - - ProjectName - - Project name of the project to update. Only needed if no ProjectId is given. - - String - - String - - - None - - - Visibility - - Sets project visibility. - - - Private - Public - - String - - String - - - None - - - Wait - - Wait for the status update to be set to 'Complete', 'Failed', or 'Cancelled' - - - SwitchParameter - - - False - - - - - - Description - - Set the Project description - - String - - String - - - None - - - Organization - - The organization to update the project in. - - String - - String - - - None - - - ProjectId - - Project ID of the project to update. Only needed if no ProjectName is given. - - String - - String - - - None - - - ProjectName - - Project name of the project to update. Only needed if no ProjectId is given. - - String - - String - - - None - - - Visibility - - Sets project visibility. - - String - - String - - - None - - - Wait - - Wait for the status update to be set to 'Complete', 'Failed', or 'Cancelled' - - SwitchParameter - - SwitchParameter - - - False - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Set-ADOPSProject -ProjectName 'myProject' -Description 'New description' -Visibility Private -Wait - - This command will set the properties 'Description' and 'Visibility' of the 'myProject' project. It will wait until the status of the update is completed, failed, or cancelled before returning the result. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Set-ADOPSProject -ProjectName 'myProject' -Description ([string]::Empty) - - This command will clear the 'myProject' project description. It will return the status even if the update is not yet completed. - - - - - - - - Set-ADOPSRepository - Set - ADOPSRepository - - Changes or updates repository settings - - - - This command changes or updates one or more repository settings. It can change one or all settings in one call. It will not ask for confirmation before updating or changing a value. - - - - Set-ADOPSRepository - - RepositoryId - - Repository ID. This can be found using Get-ADOPSRepository -Project MyProject - - String - - String - - - None - - - Project - - The project in which the repositoryID exists. - - String - - String - - - None - - - Organization - - The organization in which the repositoryID exists. - - String - - String - - - None - - - DefaultBranch - - Sets the default branch to this value. If only branch name is given it will automatically add 'refs/heads/' to the path. - - String - - String - - - None - - - IsDisabled - - Sets the isDisabled flag of the repository to true or false. - - Boolean - - Boolean - - - None - - - NewName - - Renames the directory to this name - - String - - String - - - None - - - - - - DefaultBranch - - Sets the default branch to this value. If only branch name is given it will automatically add 'refs/heads/' to the path. - - String - - String - - - None - - - IsDisabled - - Sets the isDisabled flag of the repository to true or false. - - Boolean - - Boolean - - - None - - - NewName - - Renames the directory to this name - - String - - String - - - None - - - Organization - - The organization in which the repositoryID exists. - - String - - String - - - None - - - Project - - The project in which the repositoryID exists. - - String - - String - - - None - - - RepositoryId - - Repository ID. This can be found using Get-ADOPSRepository -Project MyProject - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Set-ADOPSRepository -IsDisabled:$true -Project 'DummyProj' -RepositoryId 'd5f24968-f2ab-4048-bd65-58711420f6fa' - - This will set the repository with id d5f24968-f2ab-4048-bd65-58711420f6fa to status disabled. This repo will not be possible to work from after this action. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Set-ADOPSRepository -NewName 'NewName'-Project 'DummyProj' -RepositoryId 'd5f24968-f2ab-4048-bd65-58711420f6fa' - - This will rename the repository with id d5f24968-f2ab-4048-bd65-58711420f6fa to 'NewName'. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Set-ADOPSRepository -DefaultBranch 'defBranch' -Project 'DummyProj' -RepositoryId 'd5f24968-f2ab-4048-bd65-58711420f6fa' - - This will set the default branch of the repository with id d5f24968-f2ab-4048-bd65-58711420f6fa to 'refs/heads/defBranch'. If only branch name is given it will automatically add 'refs/heads/' to the branch path. - - - - -------------------------- Example 2 -------------------------- - PS C:\> Set-ADOPSRepository -IsDisabled:$false -NewName 'NewName' -DefaultBranch 'refs/heads/NewBranch' -Project 'DummyProj' -RepositoryId 'd5f24968-f2ab-4048-bd65-58711420f6fa' - - This willperform all of the following actions on the repository with id d5f24968-f2ab-4048-bd65-58711420f6fa: - Set status to enabled. - - Rename the repository to 'NewName' - - set the default branch to 'refs/heads/NewBranch' - - - - - - - - Set-ADOPSServiceConnection - Set - ADOPSServiceConnection - - Allows update of a Azure DevOps Service Connection. - - - - If a managed identity will be used for the connection. - - - - Set-ADOPSServiceConnection - - ConnectionName - - Name of Service Connection in Azure DevOps. - - String - - String - - - None - - - Description - - The description field of the Service connection. - - String - - String - - - None - - - EndpointOperation - - Undocumented. - - String - - String - - - None - - - ManagedIdentity - - If a managed identity is to be used by the Service Connection. - - - SwitchParameter - - - False - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - Project - - Name of the Azure DevOps project. - - String - - String - - - None - - - ServiceEndpointId - - The GUID of the Azure DevOps Service Endpoint. - - Guid - - Guid - - - None - - - SubscriptionId - - The subscription id that the service connection will be connected to. - - String - - String - - - None - - - SubscriptionName - - The subscription name that the service connection will be connected to. - - String - - String - - - None - - - TenantId - - The tenant id connected to your Azure AD and subscriptions. - - String - - String - - - None - - - - Set-ADOPSServiceConnection - - ConnectionName - - Name of Service Connection in Azure DevOps. - - String - - String - - - None - - - Description - - The description field of the Service connection. - - String - - String - - - None - - - EndpointOperation - - Undocumented. - - String - - String - - - None - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - Project - - Name of the Azure DevOps project. - - String - - String - - - None - - - ServiceEndpointId - - The GUID of the Azure DevOps Service Endpoint. - - Guid - - Guid - - - None - - - ServicePrincipal - - Azure AD Service principal, Application (Client) ID and valid secret. - - PSCredential - - PSCredential - - - None - - - SubscriptionId - - The subscription id that the service connection will be connected to. - - String - - String - - - None - - - SubscriptionName - - The subscription name that the service connection will be connected to. - - String - - String - - - None - - - TenantId - - The tenant id connected to your Azure AD and subscriptions. - - String - - String - - - None - - - - Set-ADOPSServiceConnection - - ConnectionName - - Name of Service Connection in Azure DevOps. - - String - - String - - - None - - - Description - - The description field of the Service connection. - - String - - String - - - None - - - EndpointOperation - - Undocumented. - - String - - String - - - None - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - Project - - Name of the Azure DevOps project. - - String - - String - - - None - - - ServiceEndpointId - - The GUID of the Azure DevOps Service Endpoint. - - Guid - - Guid - - - None - - - SubscriptionId - - The subscription id that the service connection will be connected to. - - String - - String - - - None - - - SubscriptionName - - The subscription name that the service connection will be connected to. - - String - - String - - - None - - - TenantId - - The tenant id connected to your Azure AD and subscriptions. - - String - - String - - - None - - - ServicePrincipalId - - Id of the Service Principal to be used for Federation. - - String - - String - - - None - - - WorkloadIdentityFederationIssuer - - Federation issuer. - - String - - String - - - None - - - WorkloadIdentityFederationSubject - - Federation Subject. - - String - - String - - - None - - - - - - ConnectionName - - Name of Service Connection in Azure DevOps. - - String - - String - - - None - - - Description - - The description field of the Service connection. - - String - - String - - - None - - - EndpointOperation - - Undocumented. - - String - - String - - - None - - - ManagedIdentity - - If a managed identity is to be used by the Service Connection. - - SwitchParameter - - SwitchParameter - - - False - - - Organization - - Name of Azure DevOps organization. - - String - - String - - - None - - - Project - - Name of the Azure DevOps project. - - String - - String - - - None - - - ServiceEndpointId - - The GUID of the Azure DevOps Service Endpoint. - - Guid - - Guid - - - None - - - ServicePrincipal - - Azure AD Service principal, Application (Client) ID and valid secret. - - PSCredential - - PSCredential - - - None - - - SubscriptionId - - The subscription id that the service connection will be connected to. - - String - - String - - - None - - - SubscriptionName - - The subscription name that the service connection will be connected to. - - String - - String - - - None - - - TenantId - - The tenant id connected to your Azure AD and subscriptions. - - String - - String - - - None - - - ServicePrincipalId - - Id of the Service Principal to be used for Federation. - - String - - String - - - None - - - WorkloadIdentityFederationIssuer - - Federation issuer. - - String - - String - - - None - - - WorkloadIdentityFederationSubject - - Federation Subject. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - $ApplicationName = 'demo-vmss-scaling-app' -$App = Get-AzADApplication -DisplayName $ApplicationName -$AppCreds = New-AzADAppCredential -ApplicationId $App.AppId -EndDate (Get-Date).AddDays(7) -$ServicePrincipal = Get-AzADServicePrincipal -ApplicationId $App.AppId - -$ServiceEndpoint = Get-ADOPSServiceConnection -Name "$ApplicationName-serviceconnection" -Project 'Azure' - -$ServiceConnectionParams = @{ - TenantId = '34ca78c2-d872-455a-9977-88e161bd4ac0' - SubscriptionName = 'MySubscriptionName' - SubscriptionId = '8671f1d1-0bb1-4be0-b73c-6a8f3b354cf6' - ConnectionName = $ServiceEndpoint.name - Project = 'Azure' - Organization = 'MyOrganization' - ServiceEndpointId = $ServiceEndpoint.id - Description = 'Service Principal to manage scaling of the VMSS.' - ServicePrincipal= [pscredential]::new($ServicePrincipal.AppId,$(ConvertTo-SecureString -AsPlainText -Force $AppCreds.SecretText)) -} -Set-ADOPSServiceConnection @ServiceConnectionParams - - Updates the Service Connection called 'demo-vmss-scaling-app-serviceconnection' in the project called 'Azure' with a new secret. - - - - - - - - Start-ADOPSPipeline - Start - ADOPSPipeline - - Starts an Azure DevOps Pipeline. - - - - Starts an Azure DevOps Pipeline. - - - - Start-ADOPSPipeline - - Name - - Name of the DevOps Pipeline. - - String - - String - - - None - - - Project - - Name of the DevOps Project. - - String - - String - - - None - - - Branch - - Name of the branch to use. - - String - - String - - - None - - - Organization - - Name of the DevOps Organization - - String - - String - - - None - - - - - - Branch - - Name of the branch to use. - - String - - String - - - None - - - Name - - Name of the DevOps Pipeline. - - String - - String - - - None - - - Organization - - Name of the DevOps Organization - - String - - String - - - None - - - Project - - Name of the DevOps Project. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - Start-ADOPSPipeline -Name 'myPipeline' -Project 'myProject' -Organization 'ADOPS' -Branch 'main' - - Starts the DevOps Pipeline 'myPipeline' in the project 'myProject'. - - - - -------------------------- Example 2 -------------------------- - Start-ADOPSPipeline -Name 'myPipeline' -Project 'myProject' -Organization 'ADOPS' -Branch 'refs/other/branch' - - Starts the DevOps Pipeline 'myPipeline' in the project 'myProject' using the git ref 'refs/other/branch'. - - - - - - - - Test-ADOPSYamlFile - Test - ADOPSYamlFile - - Test a yaml file against the Azure DevOps schema validator. - - - - This command will test a yaml file against the yaml validation schema in Azure DevOps. It uses the same validation as the built in Azure DevOps yaml editor. The command needs a pipeline id to run against, but will not replace any code in the pipeline. False positives are possible if you use nested or external repos in your yaml code for example by using templates. - - - - Test-ADOPSYamlFile - - Project - - The name of the project where the pipeline is. - - String - - String - - - None - - - File - - Path to the .yaml or .yml file - - String - - String - - - None - - - PipelineId - - Pipeline Id to run the verification against. Can be found by running the command - - - Get-ADOPSPipeline -Project MyProject -Name MyPipelineName - - Int32 - - Int32 - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - - - - File - - Path to the .yaml or .yml file - - String - - String - - - None - - - Organization - - Name of the organization to use. - - String - - String - - - None - - - PipelineId - - Pipeline Id to run the verification against. Can be found by running the command - - - Get-ADOPSPipeline -Project MyProject -Name MyPipelineName - - Int32 - - Int32 - - - None - - - Project - - The name of the project where the pipeline is. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Test-ADOPSYamlFile -Project MyProject -File C:\GoodYamlFile.yml -PipelineId 3 - - This command will validate "C:\GoodYamlFile" against pipeline id 3 in the "MyProject" project. - - - - - - \ No newline at end of file diff --git a/tests/Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml b/tests/Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml deleted file mode 100644 index 917bcd493..000000000 --- a/tests/Azdo/ADOPS/1.1.2/en-US/rename-me-help.xml +++ /dev/null @@ -1,700 +0,0 @@ - - - - - Get-ADOPSAuditStreams - Get - ADOPSAuditStreams - - Gets all audit streams in an Azure DevOps organization. - - - - This commands lists all audit streams in an Azure DevOps organization. - - - - Get-ADOPSAuditStreams - - Organization - - The organization to get audit steams from. - - String - - String - - - None - - - - - - Organization - - The organization to get audit steams from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSAuditStreams - - This commands lists all audit streams in an Azure DevOps organization. - - - - - - - - Get-ADOPSOrganizationAdminOverview - Get - ADOPSOrganizationAdminOverview - - Get the organization admin overview data. - - - - Get the organization admin overview data. - - - - Get-ADOPSOrganizationAdminOverview - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - - - - Organization - - The identifier of the Azure DevOps organization. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSOrganizationAdminOverview - - Get the organization admin overview data. - - - - - - - - Get-ADOPSOrganizationAdvancedSecurity - Get - ADOPSOrganizationAdvancedSecurity - - Get the Azure DevOps organization wide Advanced Security Settings - - - - Get the Azure DevOps organization wide Advanced Security Settings - - - - Get-ADOPSOrganizationAdvancedSecurity - - Organization - - The organization to get the Advanced Security settings from. - - String - - String - - - None - - - - - - Organization - - The organization to get the Advanced Security settings from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSOrganizationAdvancedSecurity - - Get the Azure DevOps organization wide Advanced Security Settings - - - - - - - - Get-ADOPSOrganizationCommerceMeterUsage - Get - ADOPSOrganizationCommerceMeterUsage - - Get the Azure DevOps Organization Storage Usage - - - - Get the Azure DevOps Organization Storage Usage - - - - Get-ADOPSOrganizationCommerceMeterUsage - - Organization - - The organization to get the storage usage from. - - String - - String - - - None - - - MeterId - - The MeterId for commerce meter usage. - Other valid MeterIds are; - meterId meterKind ------- --------- 3efc2e47-d73e-4213-8368-3a8723ceb1cc artifacts 4bad9897-8d87-43bb-80be-5e6e8fefa3de commitment f44a67f2-53ae-4044-bd58-1c8aca386b98 commitment 3fa30bbe-3437-42d4-a978-0ef84811f470 commitment 2fa36572-3c3d-46be-ac59-6053cbb377b4 commitment e2c73ec7-cb60-4142-b8b2-e216b6c09c1a resource a7d460a9-a56d-4b64-837f-14728d6d54d4 resource 256caf12-9779-4531-99ac-b46e295130a3 resource - - String - - String - - - None - - - - - - MeterId - - The MeterId for commerce meter usage. - Other valid MeterIds are; - meterId meterKind ------- --------- 3efc2e47-d73e-4213-8368-3a8723ceb1cc artifacts 4bad9897-8d87-43bb-80be-5e6e8fefa3de commitment f44a67f2-53ae-4044-bd58-1c8aca386b98 commitment 3fa30bbe-3437-42d4-a978-0ef84811f470 commitment 2fa36572-3c3d-46be-ac59-6053cbb377b4 commitment e2c73ec7-cb60-4142-b8b2-e216b6c09c1a resource a7d460a9-a56d-4b64-837f-14728d6d54d4 resource 256caf12-9779-4531-99ac-b46e295130a3 resource - - String - - String - - - None - - - Organization - - The organization to get the storage usage from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSOrganizationCommerceMeterUsage - - Get the Azure DevOps Organization Storage Usage using default MeterId - - - - -------------------------- Example 2 -------------------------- - PS C:\> Get-ADOPSOrganizationCommerceMeterUsage -MeterId '3efc2e47-d73e-4213-8368-3a8723ceb1cc' - - Get the Azure DevOps Organization Storage Usage for MeterId '3efc2e47-d73e-4213-8368-3a8723ceb1cc' (Artifacts) - - - - - - - - Get-ADOPSOrganizationPipelineSettings - Get - ADOPSOrganizationPipelineSettings - - Get Azure DevOps organization wide pipeline settings - - - - Get Azure DevOps organization wide pipeline settings - - - - Get-ADOPSOrganizationPipelineSettings - - Organization - - The organization to get the pipeline settings from. - - String - - String - - - None - - - - - - Organization - - The organization to get the pipeline settings from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSOrganizationPipelineSettings - - Get Azure DevOps organization wide pipeline settings - - - - - - - - Get-ADOPSOrganizationPolicy - Get - ADOPSOrganizationPolicy - - Gets all organization policy in selected category or all organization policies across all categories. - - - - Gets all organization policy in selected category or all organization policies across all categories. - - - - Get-ADOPSOrganizationPolicy - - Organization - - The organization to get the policies from. - - String - - String - - - None - - - PolicyCategory - - The selected policy category. - - - Security - Privacy - ApplicationConnection - User - - String - - String - - - None - - - - - - Organization - - The organization to get the policies from. - - String - - String - - - None - - - PolicyCategory - - The selected policy category. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSOrganizationPolicy - - Gets all organization policies in all categories - - - - - - - - Get-ADOPSOrganizationRepositorySettings - Get - ADOPSOrganizationRepositorySettings - - Get Azure DevOps Organization wide Repository settings - - - - Get Azure DevOps Organization wide Repository settings - - - - Get-ADOPSOrganizationRepositorySettings - - Organization - - The organization to get the repository settings from. - - String - - String - - - None - - - - - - Organization - - The organization to get the repository settings from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSOrganizationRepositorySettings - - Get Azure DevOps Organization wide Repository settings - - - - - - - - Get-ADOPSResourceUsage - Get - ADOPSResourceUsage - - Gets all resource usage limits in an Azure DevOps organization. - - - - Gets all resource usage limits in an Azure DevOps organization. - - - - Get-ADOPSResourceUsage - - Organization - - The organization to get resource usage from. - - String - - String - - - None - - - - - - Organization - - The organization to get resource usage from. - - String - - String - - - None - - - - - - None - - - - - - - - - - System.Object - - - - - - - - - - - - - - -------------------------- Example 1 -------------------------- - PS C:\> Get-ADOPSResourceUsage - - Gets all resource usage limits in an Azure DevOps organization. - - - - - - \ No newline at end of file diff --git a/tests/Azdo/AdopsMaesterTests.psd1 b/tests/Azdo/AdopsMaesterTests.psd1 deleted file mode 100644 index 16c2669d2..000000000 --- a/tests/Azdo/AdopsMaesterTests.psd1 +++ /dev/null @@ -1,158 +0,0 @@ -# -# Module manifest for module 'AdopsMaesterTests' -# -# Generated by: Sebastian Claesson -# -# Generated on: 2025-05-11 -# - -@{ - - # Script module or binary module file associated with this manifest. - RootModule = '.\AdopsMaesterTests.psm1' - - # Version number of this module. - ModuleVersion = '0.0.1' - - # Supported PSEditions - CompatiblePSEditions = 'Core' - - # ID used to uniquely identify this module - GUID = 'c5ce0ae6-0db8-40d6-8d89-6e4824657b1c' - - # Author of this module - Author = 'Sebastian Claesson' - - # Company or vendor of this module - CompanyName = '' - - # Copyright statement for this module - Copyright = '(c) Sebastian Claesson. All rights reserved.' - - # Description of the functionality provided by this module - Description = 'Maester tests for Azure DevOps using Pester' - - # Minimum version of the PowerShell engine required by this module - PowerShellVersion = '7.2' - - # Name of the PowerShell host required by this module - # PowerShellHostName = '' - - # Minimum version of the PowerShell host required by this module - # PowerShellHostVersion = '' - - # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. - # DotNetFrameworkVersion = '' - - # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. - # ClrVersion = '' - - # Processor architecture (None, X86, Amd64) required by this module - # ProcessorArchitecture = '' - - # Modules that must be imported into the global environment prior to importing this module - # RequiredModules = @() - - # Assemblies that must be loaded prior to importing this module - # RequiredAssemblies = @() - - # Script files (.ps1) that are run in the caller's environment prior to importing this module. - # ScriptsToProcess = @() - - # Type files (.ps1xml) to be loaded when importing this module - # TypesToProcess = @() - - # Format files (.ps1xml) to be loaded when importing this module - # FormatsToProcess = @() - - # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess - # NestedModules = @() - - # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. - FunctionsToExport = 'Test-AzdoAllowRequestAccessToken', - 'Test-AzdoAllowTeamAdminsInvitationsAccessToken', - 'Test-AzdoArtifactsExternalPackageProtectionToken', - 'Test-AzdoAuditStreams', 'Test-AzdoEnforceAADConditionalAccess', - 'Test-AzdoExternalGuestAccess', 'Test-AzdoFeedbackCollection', - 'Test-AzdoLogAuditEvents', - 'Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects', - 'Test-AzdoOrganizationBadgesArePrivate', - 'Test-AzdoOrganizationCreationClassicBuildPipelines', - 'Test-AzdoOrganizationCreationClassicReleasePipelines', - 'Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines', - 'Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines', - 'Test-AzdoOrganizationLimitVariablesAtQueueTime', - 'Test-AzdoOrganizationOwner', - 'Test-AzdoOrganizationProtectAccessToRepositories', - 'Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos', - 'Test-AzdoOrganizationRepositorySettingsGravatarImages', - 'Test-AzdoOrganizationStageChooser', - 'Test-AzdoOrganizationStorageUsage', - 'Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks', - 'Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks', - 'Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation', - 'Test-AzdoOrganizationTriggerPullRequestGitHubRepositories', - 'Test-AzdoProjectCollectionAdministrators', - 'Test-AzdoPublicProjects', 'Test-AzdoResourceUsageProjects', - 'Test-AzdoResourceUsageWorkItemTags', 'Test-AzdoSSHAuthentication', - 'Test-AzdoThirdPartyAccessViaOauth' - - # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. - CmdletsToExport = @() - - # Variables to export from this module - # VariablesToExport = @() - - # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. - AliasesToExport = @() - - # DSC resources to export from this module - # DscResourcesToExport = @() - - # List of all modules packaged with this module - # ModuleList = @() - - # List of all files packaged with this module - # FileList = @() - - # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. - PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - Tags = 'Azure', 'DevOps', 'AZDO', 'AzureDevOps', 'ADO', 'Maester', 'MaesterTests' - - # A URL to the license for this module. - LicenseUri = 'https://github.com/AZDOPS/AZDOPS/blob/main/LICENSE' - - # A URL to the main website for this project. - ProjectUri = 'https://github.com/AZDOPS/AZDOPS/' - - # A URL to an icon representing this module. - # IconUri = '' - - # ReleaseNotes of this module - # ReleaseNotes = '' - - # Prerelease string of this module - # Prerelease = '' - - # Flag to indicate whether the module requires explicit user acceptance for install/update/save - # RequireLicenseAcceptance = $false - - # External dependent modules of this module - # ExternalModuleDependencies = @() - - } # End of PSData hashtable - - } # End of PrivateData hashtable - - # HelpInfo URI of this module - # HelpInfoURI = '' - - # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. - # DefaultCommandPrefix = '' - -} - diff --git a/tests/Azdo/AdopsMaesterTests.psm1 b/tests/Azdo/AdopsMaesterTests.psm1 deleted file mode 100644 index 1bcc47b18..000000000 --- a/tests/Azdo/AdopsMaesterTests.psm1 +++ /dev/null @@ -1,1014 +0,0 @@ -#region Test-AzdoAllowRequestAccessToken - -function Test-AzdoAllowRequestAccessToken { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $UserPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'User' - $Policy = $UserPolicies.policy | where-object -property name -eq 'Policy.AllowRequestAccessToken' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "When enabled, this policy allows users to request access, triggering email notifications to administrators for review and approval." - } - else { - $resultMarkdown = "Well done. Disabling the policy stops these requests and notifications." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" -Description "$Description" - - return $result -} -#endregion Test-AzdoAllowRequestAccessToken - -#region Test-AzdoAllowTeamAdminsInvitationsAccessToken - -function Test-AzdoAllowTeamAdminsInvitationsAccessToken { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'User' - $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.AllowTeamAdminsInvitationsAccessToken' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Team and project administrators is allowed to invite new users" - } - else { - $resultMarkdown = "Well done. Enrolling to your Azure DevOps organization should be a controlled process." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" -Description "$Description" - - return $result -} -#endregion Test-AzdoAllowTeamAdminsInvitationsAccessToken - -#region Test-AzdoArtifactsExternalPackageProtectionToken - -function Test-AzdoArtifactsExternalPackageProtectionToken { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' - $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.ArtifactsExternalPackageProtectionToken' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Well done. Your Azure DevOps tenant limits access to externally sourced packages when internally sources packages are already present." - } - else { - $resultMarkdown = "Your tenant should prefer to use internal source packages when present" - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoArtifactsExternalPackageProtectionToken - -#region Test-AzdoAuditStreams - -function Test-AzdoAuditStreams { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $AuditStreams = Get-ADOPSAuditStreams - - if ($AuditStreams) { - if ('Enabled' -in $AuditStreams.status) { - $resultMarkdown = "Well done. Audit logs have been configured for long-term storage and purge protection." - $result = $true - } - else { - $resultMarkdown = "Audit Streams have been configured for long-term storage and purge protection but is not enabled." - $result = $false - } - } - else { - $resultMarkdown = "Audit Streams have not been configured for long-term storage and purge protection." - $result = $false - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoAuditStreams - -#region Test-AzdoEnforceAADConditionalAccess - -function Test-AzdoEnforceAADConditionalAccess { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' - $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.EnforceAADConditionalAccess' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Well done. Microsoft Entra ID always performs validation for any Conditional Access Policies (CAPs) set by tenant administrators." - } - else { - $resultMarkdown = "Your tenant should always perform validation for any Conditional Access Policies (CAPs) set by tenant administrators. " - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoEnforceAADConditionalAccess - -#region Test-AzdoExternalGuestAccess - -function Test-AzdoExternalGuestAccess { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'User' - $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.DisallowAadGuestUserAccess' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "External user(s) can be added to the organization to which they were invited and has immediate access. A guest user can add other guest users to the organization after being granted the Guest Inviter role in Microsoft Entra ID." - } - else { - $resultMarkdown = "Well done. External users should not be allowed access to your Azure DevOps organization" - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoExternalGuestAccess - -#region Test-AzdoFeedbackCollection - -function Test-AzdoFeedbackCollection { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Privacy' - $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.AllowFeedbackCollection' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Well done. Your Azure DevOps tenant allows feedback collection." - } - else { - $resultMarkdown = "You should have confidence that we're handling your data appropriately and for legitimate uses. Part of that assurance involves carefully restricting usage." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoFeedbackCollection - -#region Test-AzdoLogAuditEvents - -function Test-AzdoLogAuditEvents { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' - $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.LogAuditEvents' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Well done. Your tenant has auditing enabled, tracking events such as permission changes, deleted resources, log access and downloads with many other types of changes." - } - else { - $resultMarkdown = "Your tenant do not have logging enabled for Azure DevOps" - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoLogAuditEvents - -#region Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects - -function Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationAdvancedSecurity).enableOnCreate - - if ($result) { - $resultMarkdown = "Well done. New projects will by default have Advanced Security enabled." - } - else { - $resultMarkdown = "New projects must be manually enrolled in Advanced Security." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects - -#region Test-AzdoOrganizationBadgesArePrivate - -function Test-AzdoOrganizationBadgesArePrivate { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).statusBadgesArePrivate - - if ($result) { - $resultMarkdown = "Well done. Azure DevOps badges are private." - } - else { - $resultMarkdown = "Anonymous users can access the status badge API for all pipelines." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationBadgesArePrivate - -#region Test-AzdoOrganizationCreationClassicBuildPipelines - -function Test-AzdoOrganizationCreationClassicBuildPipelines { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $PipelineCreation = (Get-ADOPSOrganizationPipelineSettings).disableClassicBuildPipelineCreation - - if ($PipelineCreation) { - $resultMarkdown = "Well done. No classic build pipelines can be created / imported. Existing ones will continue to work." - $result = $false - } - else { - $resultMarkdown = "Classic build pipelines can be created / imported." - $result = $true - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationCreationClassicBuildPipelines - -#region Test-AzdoOrganizationCreationClassicReleasePipelines - -function Test-AzdoOrganizationCreationClassicReleasePipelines { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $PipelineCreation = (Get-ADOPSOrganizationPipelineSettings).disableClassicReleasePipelineCreation - - if ($PipelineCreation) { - $resultMarkdown = "Well done. No classic release pipelines, task groups, and deployment groups can be created / imported. Existing ones will continue to work." - $result = $false - } - else { - $resultMarkdown = "Classic release pipelines, task groups, and deployment groups can be created / imported." - $result = $true - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationCreationClassicReleasePipelines - -#region Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines - -function Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScope - - if ($result) { - $resultMarkdown = "Well done. Access tokens have reduced scope of access for all non-release pipelines." - } - else { - $resultMarkdown = "Non-Release Pipelines can run with collection scoped access tokens" - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines - -#region Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines - -function Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScopeForReleases - - if ($result) { - $resultMarkdown = "Well done. Access tokens have reduced scope of access for all classic release pipelines." - } - else { - $resultMarkdown = "Classic Release Pipelines can run with collection scoped access tokens" - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines - -#region Test-AzdoOrganizationLimitVariablesAtQueueTime - -function Test-AzdoOrganizationLimitVariablesAtQueueTime { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).enforceSettableVar - - if ($result) { - $resultMarkdown = "Well done. With this option enabled, only those variables that are explicitly marked as ""Settable at queue time"" can be set" - } - else { - $auditEnforceSettableVar = (Get-ADOPSOrganizationPipelineSettings).auditEnforceSettableVar - if ($auditEnforceSettableVar) { - $resultMarkdown = "Auditing is configured, however usage is not restricted." - } - else { - $resultMarkdown = "Users can define new variables not defined by pipeline author, and may override system variables." - } - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationLimitVariablesAtQueueTime - -#region Test-AzdoOrganizationOwner - -function Test-AzdoOrganizationOwner { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - $Data = Get-ADOPSOrganizationAdminOverview - if ($data.'ms.vss-admin-web.organization-admin-overview-delay-load-data-provider'.exceptionType -eq 'AadGraphException') { - $resultMarkdown = "Workload identities cannot fetch Organization Owner." - Add-MtTestResultDetail -Result "BUG: Workload identities cannot fetch Organization Owner." -SkippedCustomReason "Workload identities cannot fetch Organization Owner." -SkippedBecause Custom -Description "$Description" - $result = $false - } - else { - $currentOwner = $data.'ms.vss-admin-web.organization-admin-overview-delay-load-data-provider'.currentOwner - if ($currentOwner.email -match '(?i)(adm|admin|btg|svc|service)') { - $resultMarkdown = "Well done. Azure DevOps organization owner should be a service account and not an individual." - $result = $true - } - else { - $resultMarkdown = "Azure DevOps organization owner should not be an individual ($($currentOwner.name)). Note: This might be a false positive." - $result = $false - } - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - } - return $result -} -#endregion Test-AzdoOrganizationOwner - -#region Test-AzdoOrganizationProtectAccessToRepositories - -function Test-AzdoOrganizationProtectAccessToRepositories { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).enforceReferencedRepoScopedToken - - if ($result) { - $resultMarkdown = "Well done. Checks and approvals are applied when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." - } - else { - $resultMarkdown = "Checks and approvals are not applied when accessing repositories from YAML pipelines. Also, a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline is not generated." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationProtectAccessToRepositories - -#region Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos - -function Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "DisableTfvcRepositories").value - - if ($result) { - $resultMarkdown = "Well done. Team Foundation Version Control (TFVC) repositories cannot be created." - } - else { - $resultMarkdown = "Team Foundation Version Control (TFVC) can be created." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos - -#region Test-AzdoOrganizationRepositorySettingsGravatarImages - -function Test-AzdoOrganizationRepositorySettingsGravatarImages { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "GravatarEnabled").value - - if ($result) { - $resultMarkdown = "Gravatar images are exposed for users outside of your enterprise." - } - else { - $resultMarkdown = "Well done. Gravatar images are not exposed outside of your enterprise." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationRepositorySettingsGravatarImages - -#region Test-AzdoOrganizationStageChooser - -function Test-AzdoOrganizationStageChooser { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $StageChooser = (Get-ADOPSOrganizationPipelineSettings).disableStageChooser - - if ($result) { - $resultMarkdown = "Well done. Users will not be able to select stages to skip from the Queue Pipeline panel." - $result = $false - } - else { - $resultMarkdown = "Users are able to select stages to skip from the Queue Pipeline panel." - $result = $true - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationStageChooser - -#region Test-AzdoOrganizationStorageUsage - -function Test-AzdoOrganizationStorageUsage { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $StorageUsage = Get-ADOPSOrganizationCommerceMeterUsage -MeterId '3efc2e47-d73e-4213-8368-3a8723ceb1cc' - $availableQuantity = $StorageUsage.availableQuantity - - if ($availableQuantity -lt [double]::Parse('0,1')) { - $resultMarkdown = "Your storage is exceeding the usage limit or close to. '$availableQuantity' GB available." - $result = $false - } - else { - $resultMarkdown = - @' - Well done. You are not exceeding or approaching your storage usage limit. - Current usage: {0} GB - Max quantity: {1} GB -'@ -f $StorageUsage.currentQuantity, $StorageUsage.maxQuantity - $result = $true - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationStorageUsage - -#region Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks - -function Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).disableMarketplaceTasksVar - - if ($result) { - $resultMarkdown = "It is allowed to install and run tasks from the Marketplace." - } - else { - $resultMarkdown = "Well done. The ability to install and run tasks from the Marketplace has been restricted." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks - -#region Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks - -function Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).disableNode6TasksVar - - if ($result) { - $resultMarkdown = "Well done. Pipelines will fail if they utilize a task with a Node 6 execution handler." - } - else { - $resultMarkdown = "Pipeliens may utilize a task with Node 6 execution handler." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks - -#region Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation - -function Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).enableShellTasksArgsSanitizing - - if ($result) { - $resultMarkdown = "Well done. Argument parameters for built-in shell tasks are validated to check for inputs that can inject commands into scripts." - } - else { - $resultMarkdown = "Argument parameters for built-in shell tasks may inject commands into scripts." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation - -#region Test-AzdoOrganizationTriggerPullRequestGitHubRepositories - -function Test-AzdoOrganizationTriggerPullRequestGitHubRepositories { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $settings = Get-ADOPSOrganizationPipelineSettings - $result = $settings.forkProtectionEnabled - - if ($result) { - if ($settings.requireCommentsForNonTeamMemberAndNonContributors) { - $AdditionalInfo = 'Only on pull requests from non-team members and contributors' - } - elseif ($settings.requireCommentsForNonTeamMembersOnly) { - $AdditionalInfo = 'Only on pull requests from non-team members' - } - else { - $AdditionalInfo = 'On all pull requests' - } - - $data = @' - Prevent pipelines from making secrets available to fork builds is set to '{0}'\ - Prevent pipelines from making fork builds have the same permissions as regular builds is set to '{1}'\ - Require a team member's comment before building a pull request is set to '{2}' ({3}) -'@ -f $settings.enforceNoAccessToSecretsFromForks, $settings.enforceJobAuthScopeForForks, $settings.isCommentRequiredForPullRequest, $AdditionalInfo - - $resultMarkdown = "Well done. You have configured building pull requests from forked GitHub repositories according to your requirements. $data" - } - else { - $resultMarkdown = "No limits building pull requests from forked GitHub repositories have been configured." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoOrganizationTriggerPullRequestGitHubRepositories - -#region Test-AzdoProjectCollectionAdministrators - -function Test-AzdoProjectCollectionAdministrators { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - function Get-NestedAdoMembership { - param ( - [Parameter()] - $Member - ) - - if ($Member.subjectKind -eq 'group') { - Write-Verbose "Finding members in group '$($Member.DisplayName)' - Descriptor '$($_.Descriptor)'" - Get-ADOPSMembership -Descriptor $Member.descriptor -Direction 'down' | Foreach-object { - Write-Verbose "Processing member '$($_.DisplayName)' - Descriptor '$($_.Descriptor)'" - Get-NestedAdoMembership -Member $_ - } - } - else { - Write-output $Member - } - } - - $PCA = Get-ADOPSGroup | Where-object -Property displayname -eq 'Project Collection Administrators' - $PCAMembers = Get-ADOPSMembership -Descriptor $PCA.descriptor -Direction 'down' - - # UniqueUserList - $UniqueUsersWithPCA = New-Object System.Collections.Arraylist - - # Users with PCA - $UserPCA = $PCAMembers | Where-Object { $_.subjectKind -ne 'group' } - $UserPCA | Foreach-object { - $UniqueUsersWithPCA.Add($_) | Out-Null - } - - # Groups with PCA - $GroupPCA = $PCAMembers | Where-Object { $_.subjectKind -eq 'group' } - - $GroupPCA | Foreach-object { - Get-NestedAdoMembership -Member $_ | Foreach-object { - if ($_.descriptor -notin $UniqueUsersWithPCA.descriptor) { - $UniqueUsersWithPCA.Add($_) | Out-Null - } - else { - Write-Verbose "$($_.subjectKind) - $($_.displayname) - $($_.descriptor) - has already been added." - } - } - - } - - if ($UniqueUsersWithPCA.Count -ge 4) { - $result = $false - $resultMarkdown = "Restrict direct user access (Current actively assigned users/service accounts; '$($UniqueUsersWithPCA.Count)') to Project Collection Administrators role. The role holds the highest authority within an organization or project collection. Members can Perform all operations for the entire collection, Manage settings, policies, and processes for the organization, create and manage all projects and extensions." - } - else { - $result = $true - $resultMarkdown = "Well done. Less than 4 users/service accounts are directly assigned to the Project Collection Administrators role." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoProjectCollectionAdministrators - -#region Test-AzdoPublicProjects - -function Test-AzdoPublicProjects { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' - $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.AllowAnonymousAccess' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Your Azure DevOps tenant allows the creation and use of public projects" - } - else { - $resultMarkdown = "Well done. Your tenant has disabled the use of public projects" - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoPublicProjects - -#region Test-AzdoResourceUsageProjects - -function Test-AzdoResourceUsageProjects { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $Projects = (Get-ADOPSResourceUsage).Projects - - $CurrentUsage = $($Projects.count / $Projects.limit).Tostring("P") - - if ($($Projects.count / $Projects.limit) -gt 0.9) { - $resultMarkdown = "Project Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" - $result = $false - } - else { - $resultMarkdown = "Well done. Project Resource Usage limit is at $CurrentUsage" - $result = $true - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoResourceUsageProjects - -#region Test-AzdoResourceUsageWorkItemTags - -function Test-AzdoResourceUsageWorkItemTags { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $WorkItemTags = (Get-ADOPSResourceUsage).'Work Item Tags' - - $CurrentUsage = $($WorkItemTags.count / $WorkItemTags.limit).Tostring("P") - - if ($($WorkItemTags.count / $WorkItemTags.limit) -gt 0.9) { - $resultMarkdown = "Work Item Tags Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" - $result = $false - } - else { - $resultMarkdown = "Well done. Work Item Tags Resource Usage limit is at $CurrentUsage" - $result = $true - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoResourceUsageWorkItemTags - -#region Test-AzdoSSHAuthentication - -function Test-AzdoSSHAuthentication { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $ApplicationPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'ApplicationConnection' - $Policy = $ApplicationPolicies.policy | where-object -property name -eq 'Policy.DisallowSecureShell' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Your tenant allows developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" - } - else { - $resultMarkdown = "Well done. Your tenant do not allow developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoSSHAuthentication - -#region Test-AzdoThirdPartyAccessViaOauth - -function Test-AzdoThirdPartyAccessViaOauth { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $ApplicationPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'ApplicationConnection' - $Policy = $ApplicationPolicies.policy | where-object -property name -eq 'Policy.DisallowOAuthAuthentication' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Your tenant have not restricted Azure DevOps OAuth apps to access resources in your organization through OAuth." - } - else { - $resultMarkdown = "Well done. Your tenant has restricted Azure DevOps OAuth apps to access resources in your organization through OAuth." - } - - $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Description "$Description" - - return $result -} -#endregion Test-AzdoThirdPartyAccessViaOauth - diff --git a/tests/Azdo/Test-Azdo.Tests.ps1 b/tests/Azdo/Test-Azdo.Tests.ps1 index be444ea53..fca6f182a 100644 --- a/tests/Azdo/Test-Azdo.Tests.ps1 +++ b/tests/Azdo/Test-Azdo.Tests.ps1 @@ -1,5 +1,36 @@ BeforeAll { - Import-module $PSScriptRoot\AdopsMaesterTests.psd1 -force + . $PSScriptRoot/Test-AzdoAllowRequestAccessToken.ps1 + . $PSScriptRoot/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 + . $PSScriptRoot/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 + . $PSScriptRoot/Test-AzdoAuditStreams.ps1 + . $PSScriptRoot/Test-AzdoEnforceAADConditionalAccess.ps1 + . $PSScriptRoot/Test-AzdoExternalGuestAccess.ps1 + . $PSScriptRoot/Test-AzdoFeedbackCollection.ps1 + . $PSScriptRoot/Test-AzdoLogAuditEvents.ps1 + . $PSScriptRoot/Test-AzdoOrganizationArtifactFeed.ps1 + . $PSScriptRoot/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 + . $PSScriptRoot/Test-AzdoOrganizationBadgesArePrivate.ps1 + . $PSScriptRoot/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 + . $PSScriptRoot/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 + . $PSScriptRoot/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 + . $PSScriptRoot/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 + . $PSScriptRoot/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 + . $PSScriptRoot/Test-AzdoOrganizationOwner.ps1 + . $PSScriptRoot/Test-AzdoOrganizationProtectAccessToRepositories.ps1 + . $PSScriptRoot/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 + . $PSScriptRoot/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 + . $PSScriptRoot/Test-AzdoOrganizationStageChooser.ps1 + . $PSScriptRoot/Test-AzdoOrganizationStorageUsage.ps1 + . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 + . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 + . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 + . $PSScriptRoot/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 + . $PSScriptRoot/Test-AzdoProjectCollectionAdministrators.ps1 + . $PSScriptRoot/Test-AzdoPublicProjects.ps1 + . $PSScriptRoot/Test-AzdoResourceUsageProjects.ps1 + . $PSScriptRoot/Test-AzdoResourceUsageWorkItemTags.ps1 + . $PSScriptRoot/Test-AzdoSSHAuthentication.ps1 + . $PSScriptRoot/Test-AzdoThirdPartyAccessViaOauth.ps1 } Describe "Azure DevOps" -Tag "Azure DevOps Security" { @@ -153,6 +184,7 @@ Describe "Azure DevOps" -Tag "Azure DevOps Security" { Test-AzdoOrganizationStorageUsage | Should -Be $true -Because "Storage Usage Limit should not be reached." } + # Excluding test due to function missing in ADOPS. It "AZDO.1030: Project Collection Administrators. See https://learn.microsoft.com/en-us/azure/devops/organizations/security/about-permissions?view=azure-devops&tabs=preview-page#permissions" -Tag "AZDO.1030" { Test-AzdoProjectCollectionAdministrators | Should -Be $true -Because "Users should not be directly assigned to 'Project Collection Administrator' as it is the most privileged role within Azure DevOps." diff --git a/tests/Azdo/Test-AzdoAllowRequestAccessToken.ps1 b/tests/Azdo/Test-AzdoAllowRequestAccessToken.ps1 new file mode 100644 index 000000000..1fa7b9312 --- /dev/null +++ b/tests/Azdo/Test-AzdoAllowRequestAccessToken.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoAllowRequestAccessToken { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $UserPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'User' + $Policy = $UserPolicies.policy | where-object -property name -eq 'Policy.AllowRequestAccessToken' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "When enabled, this policy allows users to request access, triggering email notifications to administrators for review and approval." + } + else { + $resultMarkdown = "Well done. Disabling the policy stops these requests and notifications." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 b/tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 new file mode 100644 index 000000000..782f8d342 --- /dev/null +++ b/tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoAllowTeamAdminsInvitationsAccessToken { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'User' + $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.AllowTeamAdminsInvitationsAccessToken' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Team and project administrators is allowed to invite new users" + } + else { + $resultMarkdown = "Well done. Enrolling to your Azure DevOps organization should be a controlled process." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 b/tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 new file mode 100644 index 000000000..c48730b4d --- /dev/null +++ b/tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoArtifactsExternalPackageProtectionToken { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.ArtifactsExternalPackageProtectionToken' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Well done. Your Azure DevOps tenant limits access to externally sourced packages when internally sources packages are already present." + } + else { + $resultMarkdown = "Your tenant should prefer to use internal source packages when present" + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoAuditStreams.ps1 b/tests/Azdo/Test-AzdoAuditStreams.ps1 new file mode 100644 index 000000000..bf586ca23 --- /dev/null +++ b/tests/Azdo/Test-AzdoAuditStreams.ps1 @@ -0,0 +1,33 @@ +function Test-AzdoAuditStreams { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $AuditStreams = Get-ADOPSAuditStreams + + if ($AuditStreams) { + if ('Enabled' -in $AuditStreams.status) { + $resultMarkdown = "Well done. Audit logs have been configured for long-term storage and purge protection." + $result = $true + } + else { + $resultMarkdown = "Audit Streams have been configured for long-term storage and purge protection but is not enabled." + $result = $false + } + } + else { + $resultMarkdown = "Audit Streams have not been configured for long-term storage and purge protection." + $result = $false + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} diff --git a/tests/Azdo/Test-AzdoEnforceAADConditionalAccess.ps1 b/tests/Azdo/Test-AzdoEnforceAADConditionalAccess.ps1 new file mode 100644 index 000000000..c64a09844 --- /dev/null +++ b/tests/Azdo/Test-AzdoEnforceAADConditionalAccess.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoEnforceAADConditionalAccess { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.EnforceAADConditionalAccess' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Well done. Microsoft Entra ID always performs validation for any Conditional Access Policies (CAPs) set by tenant administrators." + } + else { + $resultMarkdown = "Your tenant should always perform validation for any Conditional Access Policies (CAPs) set by tenant administrators. " + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} \ No newline at end of file diff --git a/tests/Azdo/Test-AzdoExternalGuestAccess.ps1 b/tests/Azdo/Test-AzdoExternalGuestAccess.ps1 new file mode 100644 index 000000000..2fdee34eb --- /dev/null +++ b/tests/Azdo/Test-AzdoExternalGuestAccess.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoExternalGuestAccess { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'User' + $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.DisallowAadGuestUserAccess' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "External user(s) can be added to the organization to which they were invited and has immediate access. A guest user can add other guest users to the organization after being granted the Guest Inviter role in Microsoft Entra ID." + } + else { + $resultMarkdown = "Well done. External users should not be allowed access to your Azure DevOps organization" + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} diff --git a/tests/Azdo/Test-AzdoFeedbackCollection.ps1 b/tests/Azdo/Test-AzdoFeedbackCollection.ps1 new file mode 100644 index 000000000..b669e5bcf --- /dev/null +++ b/tests/Azdo/Test-AzdoFeedbackCollection.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoFeedbackCollection { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Privacy' + $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.AllowFeedbackCollection' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Well done. Your Azure DevOps tenant allows feedback collection." + } + else { + $resultMarkdown = "You should have confidence that we're handling your data appropriately and for legitimate uses. Part of that assurance involves carefully restricting usage." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Info' + + return $result +} diff --git a/tests/Azdo/Test-AzdoLogAuditEvents.ps1 b/tests/Azdo/Test-AzdoLogAuditEvents.ps1 new file mode 100644 index 000000000..0ad18bc5b --- /dev/null +++ b/tests/Azdo/Test-AzdoLogAuditEvents.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoLogAuditEvents { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.LogAuditEvents' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Well done. Your tenant has auditing enabled, tracking events such as permission changes, deleted resources, log access and downloads with many other types of changes." + } + else { + $resultMarkdown = "Your tenant do not have logging enabled for Azure DevOps" + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} \ No newline at end of file diff --git a/tests/Azdo/Test-AzdoOrganizationArtifactFeed.ps1 b/tests/Azdo/Test-AzdoOrganizationArtifactFeed.ps1 new file mode 100644 index 000000000..15f98f3db --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationArtifactFeed.ps1 @@ -0,0 +1,27 @@ +function Test-AzdoOrganizationProtectAccessToRepositories { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceReferencedRepoScopedToken + + if ($result) { + $resultMarkdown = "Well done. Checks and approvals are applied when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." + } + else { + $resultMarkdown = "Checks and approvals are not applied when accessing repositories from YAML pipelines. Also, a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline is not generated." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result + + ## https://learn.microsoft.com/en-us/azure/devops/artifacts/feeds/feed-permissions?view=azure-devops&tabs=nuget%2Cnugetserver22%2Cnugetserver +} diff --git a/tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 b/tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 new file mode 100644 index 000000000..ac669dc7e --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationAdvancedSecurity).enableOnCreate + + if ($result) { + $resultMarkdown = "Well done. New projects will by default have Advanced Security enabled." + } + else { + $resultMarkdown = "New projects must be manually enrolled in Advanced Security." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 b/tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 new file mode 100644 index 000000000..fe44b2537 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationBadgesArePrivate { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).statusBadgesArePrivate + + if ($result) { + $resultMarkdown = "Well done. Azure DevOps badges are private." + } + else { + $resultMarkdown = "Anonymous users can access the status badge API for all pipelines." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 b/tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 new file mode 100644 index 000000000..e9a398d08 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 @@ -0,0 +1,27 @@ +function Test-AzdoOrganizationCreationClassicBuildPipelines { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PipelineCreation = (Get-ADOPSOrganizationPipelineSettings).disableClassicBuildPipelineCreation + + if ($PipelineCreation) { + $resultMarkdown = "Well done. No classic build pipelines can be created / imported. Existing ones will continue to work." + $result = $false + } + else { + $resultMarkdown = "Classic build pipelines can be created / imported." + $result = $true + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 b/tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 new file mode 100644 index 000000000..b136519f6 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 @@ -0,0 +1,27 @@ +function Test-AzdoOrganizationCreationClassicReleasePipelines { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $PipelineCreation = (Get-ADOPSOrganizationPipelineSettings).disableClassicReleasePipelineCreation + + if ($PipelineCreation) { + $resultMarkdown = "Well done. No classic release pipelines, task groups, and deployment groups can be created / imported. Existing ones will continue to work." + $result = $false + } + else { + $resultMarkdown = "Classic release pipelines, task groups, and deployment groups can be created / imported." + $result = $true + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 b/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 new file mode 100644 index 000000000..878d70e75 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScope + + if ($result) { + $resultMarkdown = "Well done. Access tokens have reduced scope of access for all non-release pipelines." + } + else { + $resultMarkdown = "Non-Release Pipelines can run with collection scoped access tokens" + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 b/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 new file mode 100644 index 000000000..e3890d66b --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScopeForReleases + + if ($result) { + $resultMarkdown = "Well done. Access tokens have reduced scope of access for all classic release pipelines." + } + else { + $resultMarkdown = "Classic Release Pipelines can run with collection scoped access tokens" + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 b/tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 new file mode 100644 index 000000000..021ceffd4 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 @@ -0,0 +1,31 @@ +function Test-AzdoOrganizationLimitVariablesAtQueueTime { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceSettableVar + + if ($result) { + $resultMarkdown = "Well done. With this option enabled, only those variables that are explicitly marked as ""Settable at queue time"" can be set" + } + else { + $auditEnforceSettableVar = (Get-ADOPSOrganizationPipelineSettings).auditEnforceSettableVar + if ($auditEnforceSettableVar) { + $resultMarkdown = "Auditing is configured, however usage is not restricted." + } + else { + $resultMarkdown = "Users can define new variables not defined by pipeline author, and may override system variables." + } + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationOwner.ps1 b/tests/Azdo/Test-AzdoOrganizationOwner.ps1 new file mode 100644 index 000000000..8de4b5c64 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationOwner.ps1 @@ -0,0 +1,36 @@ +function Test-AzdoOrganizationOwner { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + $Data = Get-ADOPSOrganizationAdminOverview + if ($data.'ms.vss-admin-web.organization-admin-overview-delay-load-data-provider'.exceptionType -eq 'AadGraphException') { + $resultMarkdown = "Workload identities cannot fetch Organization Owner." + Add-MtTestResultDetail -Result "BUG: Workload identities cannot fetch Organization Owner." -SkippedCustomReason "Workload identities cannot fetch Organization Owner." -SkippedBecause Custom + $result = $false + } + else { + $currentOwner = $data.'ms.vss-admin-web.organization-admin-overview-delay-load-data-provider'.currentOwner + if ($currentOwner.email -match '(?i)(adm|admin|btg|svc|service)') { + $resultMarkdown = "Well done. Azure DevOps organization owner should be a service account and not an individual.`n`n%TestResult%" + $result = $true + } + else { + $resultMarkdown = "Azure DevOps organization owner should not be an individual ($($currentOwner.name)). Note: This might be a false positive.`n`n%TestResult%" + $result = $false + } + $markdown = "| Name | Id | E-mail |`n" + $markdown += "| --- | --- | --- |`n" + $markdown += "| $($currentOwner.name) | $($currentOwner.id) | $($currentOwner.email) |`n" + $resultMarkdown = $resultMarkdown -replace '%TestResult%', $markdown + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + } + return $result +} \ No newline at end of file diff --git a/tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 b/tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 new file mode 100644 index 000000000..4bc1188da --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationProtectAccessToRepositories { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceReferencedRepoScopedToken + + if ($result) { + $resultMarkdown = "Well done. Checks and approvals are applied when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." + } + else { + $resultMarkdown = "Checks and approvals are not applied when accessing repositories from YAML pipelines. Also, a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline is not generated." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 b/tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 new file mode 100644 index 000000000..ac5b472c2 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "DisableTfvcRepositories").value + + if ($result) { + $resultMarkdown = "Well done. Team Foundation Version Control (TFVC) repositories cannot be created." + } + else { + $resultMarkdown = "Team Foundation Version Control (TFVC) can be created." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 b/tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 new file mode 100644 index 000000000..001f5f374 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationRepositorySettingsGravatarImages { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "GravatarEnabled").value + + if ($result) { + $resultMarkdown = "Gravatar images are exposed for users outside of your enterprise." + } + else { + $resultMarkdown = "Well done. Gravatar images are not exposed outside of your enterprise." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Medium' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationStageChooser.ps1 b/tests/Azdo/Test-AzdoOrganizationStageChooser.ps1 new file mode 100644 index 000000000..7864fb89f --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationStageChooser.ps1 @@ -0,0 +1,27 @@ +function Test-AzdoOrganizationStageChooser { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $StageChooser = (Get-ADOPSOrganizationPipelineSettings).disableStageChooser + + if ($result) { + $resultMarkdown = "Well done. Users will not be able to select stages to skip from the Queue Pipeline panel." + $result = $false + } + else { + $resultMarkdown = "Users are able to select stages to skip from the Queue Pipeline panel." + $result = $true + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationStorageUsage.ps1 b/tests/Azdo/Test-AzdoOrganizationStorageUsage.ps1 new file mode 100644 index 000000000..1098bcc84 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationStorageUsage.ps1 @@ -0,0 +1,33 @@ +function Test-AzdoOrganizationStorageUsage { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $StorageUsage = Get-ADOPSOrganizationCommerceMeterUsage -MeterId '3efc2e47-d73e-4213-8368-3a8723ceb1cc' + $availableQuantity = $StorageUsage.availableQuantity + + if ($availableQuantity -lt [double]::Parse('0.1')) { + $resultMarkdown = "Your storage is exceeding the usage limit or close to. '$availableQuantity' GB available." + $result = $false + } + else { + $resultMarkdown = + @' + Well done. You are not exceeding or approaching your storage usage limit. + Current usage: {0} GB + Max quantity: {1} GB +'@ -f $StorageUsage.currentQuantity, $StorageUsage.maxQuantity + $result = $true + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} \ No newline at end of file diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 new file mode 100644 index 000000000..5f591043c --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).disableMarketplaceTasksVar + + if ($result) { + $resultMarkdown = "Well done. The ability to install and run tasks from the Marketplace has been restricted." + } + else { + $resultMarkdown = "It is allowed to install and run tasks from the Marketplace." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 new file mode 100644 index 000000000..1aeee8cb7 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).disableNode6TasksVar + + if ($result) { + $resultMarkdown = "Well done. Pipelines will fail if they utilize a task with a Node 6 execution handler." + } + else { + $resultMarkdown = "Pipeliens may utilize a task with Node 6 execution handler." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 new file mode 100644 index 000000000..50429e4bf --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 @@ -0,0 +1,25 @@ +function Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $result = (Get-ADOPSOrganizationPipelineSettings).enableShellTasksArgsSanitizing + + if ($result) { + $resultMarkdown = "Well done. Argument parameters for built-in shell tasks are validated to check for inputs that can inject commands into scripts." + } + else { + $resultMarkdown = "Argument parameters for built-in shell tasks may inject commands into scripts." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} diff --git a/tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 b/tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 new file mode 100644 index 000000000..ae9f5bd50 --- /dev/null +++ b/tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 @@ -0,0 +1,42 @@ +function Test-AzdoOrganizationTriggerPullRequestGitHubRepositories { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $settings = Get-ADOPSOrganizationPipelineSettings + $result = $settings.forkProtectionEnabled + + if ($result) { + if ($settings.requireCommentsForNonTeamMemberAndNonContributors) { + $AdditionalInfo = 'Only on pull requests from non-team members and contributors' + } + elseif ($settings.requireCommentsForNonTeamMembersOnly) { + $AdditionalInfo = 'Only on pull requests from non-team members' + } + else { + $AdditionalInfo = 'On all pull requests' + } + + $data = @' + Prevent pipelines from making secrets available to fork builds is set to '{0}'\ + Prevent pipelines from making fork builds have the same permissions as regular builds is set to '{1}'\ + Require a team member's comment before building a pull request is set to '{2}' ({3}) +'@ -f $settings.enforceNoAccessToSecretsFromForks, $settings.enforceJobAuthScopeForForks, $settings.isCommentRequiredForPullRequest, $AdditionalInfo + + $resultMarkdown = "Well done. You have configured building pull requests from forked GitHub repositories according to your requirements. $data" + } + else { + $resultMarkdown = "No limits building pull requests from forked GitHub repositories have been configured." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoProjectCollectionAdministrators.ps1 b/tests/Azdo/Test-AzdoProjectCollectionAdministrators.ps1 new file mode 100644 index 000000000..af76c4b0f --- /dev/null +++ b/tests/Azdo/Test-AzdoProjectCollectionAdministrators.ps1 @@ -0,0 +1,79 @@ +function Test-AzdoProjectCollectionAdministrators { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + function Get-NestedAdoMembership { + param ( + [Parameter()] + $Member + ) + + if ($Member.subjectKind -eq 'group') { + Write-Verbose "Finding members in group '$($Member.DisplayName)' - Descriptor '$($_.Descriptor)'" + Get-ADOPSMembership -Descriptor $Member.descriptor -Direction 'down' | Foreach-object { + Write-Verbose "Processing member '$($_.DisplayName)' - Descriptor '$($_.Descriptor)'" + Get-NestedAdoMembership -Member $_ + } + } + else { + Write-output $Member + } + } + + $PCA = Get-ADOPSGroup | Where-object -Property displayname -eq 'Project Collection Administrators' + $PCAMembers = Get-ADOPSMembership -Descriptor $PCA.descriptor -Direction 'down' + + # UniqueUserList + $UniqueUsersWithPCA = New-Object System.Collections.Arraylist + + # Users with PCA + $UserPCA = $PCAMembers | Where-Object { $_.subjectKind -ne 'group' } + $UserPCA | Foreach-object { + $UniqueUsersWithPCA.Add($_) | Out-Null + } + + # Groups with PCA + $GroupPCA = $PCAMembers | Where-Object { $_.subjectKind -eq 'group' } + + $GroupPCA | Foreach-object { + Get-NestedAdoMembership -Member $_ | Foreach-object { + if ($_.descriptor -notin $UniqueUsersWithPCA.descriptor) { + $UniqueUsersWithPCA.Add($_) | Out-Null + } + else { + Write-Verbose "$($_.subjectKind) - $($_.displayname) - $($_.descriptor) - has already been added." + } + } + + } + + if ($UniqueUsersWithPCA.Count -ge 4) { + $result = $false + $resultMarkdown = "Restrict direct user access ('$($UniqueUsersWithPCA.Count)') to Project Collection Administrators role. The role holds the highest authority within an organization or project collection. Members can Perform all operations for the entire collection, Manage settings, policies, and processes for the organization, create and manage all projects and extensions.`n`n%TestResult%" + } + else { + $result = $true + $resultMarkdown = "Well done. Less than 4 users/service accounts are directly assigned to the Project Collection Administrators role.`n`n%TestResult%" + } + $UniqueUsersWithPCA | ForEach-Object -Begin { + $markdown = "| DisplayName | Alias | E-mail |`n" + $markdown += "| --- | --- | --- |`n" + } -Process { + $markdown += "| $($_.displayName) | $($_.directoryAlias) | $($_.mailAddress) |`n" + } -end { + $resultMarkdown = $resultMarkdown -replace '%TestResult%', $markdown + } + + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} \ No newline at end of file diff --git a/tests/Azdo/Test-AzdoPublicProjects.ps1 b/tests/Azdo/Test-AzdoPublicProjects.ps1 new file mode 100644 index 000000000..d677b1f7f --- /dev/null +++ b/tests/Azdo/Test-AzdoPublicProjects.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoPublicProjects { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.AllowAnonymousAccess' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Your Azure DevOps tenant allows the creation and use of public projects" + } + else { + $resultMarkdown = "Well done. Your tenant has disabled the use of public projects" + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} \ No newline at end of file diff --git a/tests/Azdo/Test-AzdoResourceUsageProjects.ps1 b/tests/Azdo/Test-AzdoResourceUsageProjects.ps1 new file mode 100644 index 000000000..0c7304e3e --- /dev/null +++ b/tests/Azdo/Test-AzdoResourceUsageProjects.ps1 @@ -0,0 +1,29 @@ +function Test-AzdoResourceUsageProjects { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $Projects = (Get-ADOPSResourceUsage).Projects + + $CurrentUsage = $($Projects.count / $Projects.limit).Tostring("P") + + if ($($Projects.count / $Projects.limit) -gt 0.9) { + $resultMarkdown = "Project Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" + $result = $false + } + else { + $resultMarkdown = "Well done. Project Resource Usage limit is at $CurrentUsage" + $result = $true + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoResourceUsageWorkItemTags.ps1 b/tests/Azdo/Test-AzdoResourceUsageWorkItemTags.ps1 new file mode 100644 index 000000000..3934ebb37 --- /dev/null +++ b/tests/Azdo/Test-AzdoResourceUsageWorkItemTags.ps1 @@ -0,0 +1,29 @@ +function Test-AzdoResourceUsageWorkItemTags { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $WorkItemTags = (Get-ADOPSResourceUsage).'Work Item Tags' + + $CurrentUsage = $($WorkItemTags.count / $WorkItemTags.limit).Tostring("P") + + if ($($WorkItemTags.count / $WorkItemTags.limit) -gt 0.9) { + $resultMarkdown = "Work Item Tags Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" + $result = $false + } + else { + $resultMarkdown = "Well done. Work Item Tags Resource Usage limit is at $CurrentUsage" + $result = $true + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/tests/Azdo/Test-AzdoSSHAuthentication.ps1 b/tests/Azdo/Test-AzdoSSHAuthentication.ps1 new file mode 100644 index 000000000..6e1d9995a --- /dev/null +++ b/tests/Azdo/Test-AzdoSSHAuthentication.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoSSHAuthentication { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $ApplicationPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'ApplicationConnection' + $Policy = $ApplicationPolicies.policy | where-object -property name -eq 'Policy.DisallowSecureShell' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Your tenant allows developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" + } + else { + $resultMarkdown = "Well done. Your tenant do not allow developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} \ No newline at end of file diff --git a/tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 b/tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 new file mode 100644 index 000000000..df2b8e46c --- /dev/null +++ b/tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 @@ -0,0 +1,26 @@ +function Test-AzdoThirdPartyAccessViaOauth { + [CmdletBinding()] + [OutputType([bool])] + param() + + if ($null -eq (Get-ADOPSConnection)['Organization']) { + Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' + return $null + } + + $ApplicationPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'ApplicationConnection' + $Policy = $ApplicationPolicies.policy | where-object -property name -eq 'Policy.DisallowOAuthAuthentication' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Your tenant have not restricted Azure DevOps OAuth apps to access resources in your organization through OAuth." + } + else { + $resultMarkdown = "Well done. Your tenant has restricted Azure DevOps OAuth apps to access resources in your organization through OAuth." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} \ No newline at end of file From 8d7318a1eb4a9062c388bc9b4d0c523fbbb565d3 Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Date: Tue, 9 Dec 2025 13:32:31 +0100 Subject: [PATCH 05/22] Updating readme and setting functions in the correct folder --- .../public/azdo}/Test-AzdoAllowRequestAccessToken.md | 0 .../azdo}/Test-AzdoAllowRequestAccessToken.ps1 | 0 ...Test-AzdoAllowTeamAdminsInvitationsAccessToken.md | 0 ...est-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 | 0 ...st-AzdoArtifactsExternalPackageProtectionToken.md | 0 ...t-AzdoArtifactsExternalPackageProtectionToken.ps1 | 0 .../public/azdo}/Test-AzdoAuditStreams.md | 0 .../public/azdo}/Test-AzdoAuditStreams.ps1 | 0 .../azdo}/Test-AzdoEnforceAADConditionalAccess.md | 0 .../azdo}/Test-AzdoEnforceAADConditionalAccess.ps1 | 0 .../public/azdo}/Test-AzdoExternalGuestAccess.md | 0 .../public/azdo}/Test-AzdoExternalGuestAccess.ps1 | 0 .../public/azdo}/Test-AzdoFeedbackCollection.md | 0 .../public/azdo}/Test-AzdoFeedbackCollection.ps1 | 0 .../public/azdo}/Test-AzdoLogAuditEvents.md | 0 .../public/azdo}/Test-AzdoLogAuditEvents.ps1 | 0 .../azdo}/Test-AzdoOrganizationArtifactFeed.ps1 | 0 ...AutomaticEnrollmentAdvancedSecurityNewProjects.md | 0 ...utomaticEnrollmentAdvancedSecurityNewProjects.ps1 | 0 .../azdo}/Test-AzdoOrganizationBadgesArePrivate.md | 0 .../azdo}/Test-AzdoOrganizationBadgesArePrivate.ps1 | 0 ...-AzdoOrganizationCreationClassicBuildPipelines.md | 0 ...AzdoOrganizationCreationClassicBuildPipelines.ps1 | 0 ...zdoOrganizationCreationClassicReleasePipelines.md | 0 ...doOrganizationCreationClassicReleasePipelines.ps1 | 0 ...nLimitJobAuthorizationScopeNonReleasePipelines.md | 0 ...LimitJobAuthorizationScopeNonReleasePipelines.ps1 | 0 ...tionLimitJobAuthorizationScopeReleasePipelines.md | 0 ...ionLimitJobAuthorizationScopeReleasePipelines.ps1 | 0 ...Test-AzdoOrganizationLimitVariablesAtQueueTime.md | 0 ...est-AzdoOrganizationLimitVariablesAtQueueTime.ps1 | 0 .../public/azdo}/Test-AzdoOrganizationOwner.md | 0 .../public/azdo}/Test-AzdoOrganizationOwner.ps1 | 0 ...st-AzdoOrganizationProtectAccessToRepositories.md | 0 ...t-AzdoOrganizationProtectAccessToRepositories.ps1 | 0 ...tionRepositorySettingsDisableCreationTFVCRepos.md | 0 ...ionRepositorySettingsDisableCreationTFVCRepos.ps1 | 0 ...doOrganizationRepositorySettingsGravatarImages.md | 0 ...oOrganizationRepositorySettingsGravatarImages.ps1 | 0 .../azdo}/Test-AzdoOrganizationStageChooser.md | 0 .../azdo}/Test-AzdoOrganizationStageChooser.ps1 | 0 .../azdo}/Test-AzdoOrganizationStorageUsage.md | 0 .../azdo}/Test-AzdoOrganizationStorageUsage.ps1 | 0 ...izationTaskRestrictionsDisableMarketplaceTasks.md | 0 ...zationTaskRestrictionsDisableMarketplaceTasks.ps1 | 0 ...oOrganizationTaskRestrictionsDisableNode6Tasks.md | 0 ...OrganizationTaskRestrictionsDisableNode6Tasks.ps1 | 0 ...ionTaskRestrictionsShellTaskArgumentValidation.md | 0 ...onTaskRestrictionsShellTaskArgumentValidation.ps1 | 0 ...ganizationTriggerPullRequestGitHubRepositories.md | 0 ...anizationTriggerPullRequestGitHubRepositories.ps1 | 0 .../Test-AzdoProjectCollectionAdministrators.md | 0 .../Test-AzdoProjectCollectionAdministrators.ps1 | 0 .../public/azdo}/Test-AzdoPublicProjects.md | 0 .../public/azdo}/Test-AzdoPublicProjects.ps1 | 0 .../public/azdo}/Test-AzdoResourceUsageProjects.md | 0 .../public/azdo}/Test-AzdoResourceUsageProjects.ps1 | 0 .../azdo}/Test-AzdoResourceUsageWorkItemTags.md | 0 .../azdo}/Test-AzdoResourceUsageWorkItemTags.ps1 | 0 .../public/azdo}/Test-AzdoSSHAuthentication.md | 0 .../public/azdo}/Test-AzdoSSHAuthentication.ps1 | 0 .../azdo}/Test-AzdoThirdPartyAccessViaOauth.md | 0 .../azdo}/Test-AzdoThirdPartyAccessViaOauth.ps1 | 0 tests/Azdo/README.md | 12 +----------- 64 files changed, 1 insertion(+), 11 deletions(-) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoAllowRequestAccessToken.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoAllowRequestAccessToken.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoArtifactsExternalPackageProtectionToken.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoAuditStreams.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoAuditStreams.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoEnforceAADConditionalAccess.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoEnforceAADConditionalAccess.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoExternalGuestAccess.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoExternalGuestAccess.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoFeedbackCollection.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoFeedbackCollection.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoLogAuditEvents.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoLogAuditEvents.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationArtifactFeed.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationBadgesArePrivate.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationBadgesArePrivate.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationCreationClassicBuildPipelines.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationCreationClassicReleasePipelines.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationLimitVariablesAtQueueTime.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationOwner.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationOwner.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationProtectAccessToRepositories.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationProtectAccessToRepositories.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationRepositorySettingsGravatarImages.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationStageChooser.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationStageChooser.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationStorageUsage.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationStorageUsage.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoProjectCollectionAdministrators.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoProjectCollectionAdministrators.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoPublicProjects.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoPublicProjects.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoResourceUsageProjects.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoResourceUsageProjects.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoResourceUsageWorkItemTags.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoResourceUsageWorkItemTags.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoSSHAuthentication.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoSSHAuthentication.ps1 (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoThirdPartyAccessViaOauth.md (100%) rename {tests/Azdo => powershell/public/azdo}/Test-AzdoThirdPartyAccessViaOauth.ps1 (100%) diff --git a/tests/Azdo/Test-AzdoAllowRequestAccessToken.md b/powershell/public/azdo/Test-AzdoAllowRequestAccessToken.md similarity index 100% rename from tests/Azdo/Test-AzdoAllowRequestAccessToken.md rename to powershell/public/azdo/Test-AzdoAllowRequestAccessToken.md diff --git a/tests/Azdo/Test-AzdoAllowRequestAccessToken.ps1 b/powershell/public/azdo/Test-AzdoAllowRequestAccessToken.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoAllowRequestAccessToken.ps1 rename to powershell/public/azdo/Test-AzdoAllowRequestAccessToken.ps1 diff --git a/tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md b/powershell/public/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md similarity index 100% rename from tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md rename to powershell/public/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md diff --git a/tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 b/powershell/public/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 rename to powershell/public/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 diff --git a/tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md b/powershell/public/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md similarity index 100% rename from tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md rename to powershell/public/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md diff --git a/tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 b/powershell/public/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 rename to powershell/public/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 diff --git a/tests/Azdo/Test-AzdoAuditStreams.md b/powershell/public/azdo/Test-AzdoAuditStreams.md similarity index 100% rename from tests/Azdo/Test-AzdoAuditStreams.md rename to powershell/public/azdo/Test-AzdoAuditStreams.md diff --git a/tests/Azdo/Test-AzdoAuditStreams.ps1 b/powershell/public/azdo/Test-AzdoAuditStreams.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoAuditStreams.ps1 rename to powershell/public/azdo/Test-AzdoAuditStreams.ps1 diff --git a/tests/Azdo/Test-AzdoEnforceAADConditionalAccess.md b/powershell/public/azdo/Test-AzdoEnforceAADConditionalAccess.md similarity index 100% rename from tests/Azdo/Test-AzdoEnforceAADConditionalAccess.md rename to powershell/public/azdo/Test-AzdoEnforceAADConditionalAccess.md diff --git a/tests/Azdo/Test-AzdoEnforceAADConditionalAccess.ps1 b/powershell/public/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoEnforceAADConditionalAccess.ps1 rename to powershell/public/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 diff --git a/tests/Azdo/Test-AzdoExternalGuestAccess.md b/powershell/public/azdo/Test-AzdoExternalGuestAccess.md similarity index 100% rename from tests/Azdo/Test-AzdoExternalGuestAccess.md rename to powershell/public/azdo/Test-AzdoExternalGuestAccess.md diff --git a/tests/Azdo/Test-AzdoExternalGuestAccess.ps1 b/powershell/public/azdo/Test-AzdoExternalGuestAccess.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoExternalGuestAccess.ps1 rename to powershell/public/azdo/Test-AzdoExternalGuestAccess.ps1 diff --git a/tests/Azdo/Test-AzdoFeedbackCollection.md b/powershell/public/azdo/Test-AzdoFeedbackCollection.md similarity index 100% rename from tests/Azdo/Test-AzdoFeedbackCollection.md rename to powershell/public/azdo/Test-AzdoFeedbackCollection.md diff --git a/tests/Azdo/Test-AzdoFeedbackCollection.ps1 b/powershell/public/azdo/Test-AzdoFeedbackCollection.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoFeedbackCollection.ps1 rename to powershell/public/azdo/Test-AzdoFeedbackCollection.ps1 diff --git a/tests/Azdo/Test-AzdoLogAuditEvents.md b/powershell/public/azdo/Test-AzdoLogAuditEvents.md similarity index 100% rename from tests/Azdo/Test-AzdoLogAuditEvents.md rename to powershell/public/azdo/Test-AzdoLogAuditEvents.md diff --git a/tests/Azdo/Test-AzdoLogAuditEvents.ps1 b/powershell/public/azdo/Test-AzdoLogAuditEvents.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoLogAuditEvents.ps1 rename to powershell/public/azdo/Test-AzdoLogAuditEvents.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationArtifactFeed.ps1 b/powershell/public/azdo/Test-AzdoOrganizationArtifactFeed.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationArtifactFeed.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationArtifactFeed.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md b/powershell/public/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md rename to powershell/public/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md diff --git a/tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 b/powershell/public/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.md b/powershell/public/azdo/Test-AzdoOrganizationBadgesArePrivate.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.md rename to powershell/public/azdo/Test-AzdoOrganizationBadgesArePrivate.md diff --git a/tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 b/powershell/public/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md b/powershell/public/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md rename to powershell/public/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md diff --git a/tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 b/powershell/public/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md b/powershell/public/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md rename to powershell/public/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md diff --git a/tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 b/powershell/public/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md b/powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md rename to powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md diff --git a/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 b/powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md b/powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md rename to powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md diff --git a/tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 b/powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md b/powershell/public/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md rename to powershell/public/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md diff --git a/tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 b/powershell/public/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationOwner.md b/powershell/public/azdo/Test-AzdoOrganizationOwner.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationOwner.md rename to powershell/public/azdo/Test-AzdoOrganizationOwner.md diff --git a/tests/Azdo/Test-AzdoOrganizationOwner.ps1 b/powershell/public/azdo/Test-AzdoOrganizationOwner.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationOwner.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationOwner.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.md b/powershell/public/azdo/Test-AzdoOrganizationProtectAccessToRepositories.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.md rename to powershell/public/azdo/Test-AzdoOrganizationProtectAccessToRepositories.md diff --git a/tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 b/powershell/public/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md b/powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md rename to powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md diff --git a/tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 b/powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md b/powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md rename to powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md diff --git a/tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 b/powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationStageChooser.md b/powershell/public/azdo/Test-AzdoOrganizationStageChooser.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationStageChooser.md rename to powershell/public/azdo/Test-AzdoOrganizationStageChooser.md diff --git a/tests/Azdo/Test-AzdoOrganizationStageChooser.ps1 b/powershell/public/azdo/Test-AzdoOrganizationStageChooser.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationStageChooser.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationStageChooser.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationStorageUsage.md b/powershell/public/azdo/Test-AzdoOrganizationStorageUsage.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationStorageUsage.md rename to powershell/public/azdo/Test-AzdoOrganizationStorageUsage.md diff --git a/tests/Azdo/Test-AzdoOrganizationStorageUsage.ps1 b/powershell/public/azdo/Test-AzdoOrganizationStorageUsage.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationStorageUsage.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationStorageUsage.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md b/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md rename to powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 b/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md b/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md rename to powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 b/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md b/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md rename to powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md diff --git a/tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 b/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 diff --git a/tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md b/powershell/public/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md rename to powershell/public/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md diff --git a/tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 b/powershell/public/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 rename to powershell/public/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 diff --git a/tests/Azdo/Test-AzdoProjectCollectionAdministrators.md b/powershell/public/azdo/Test-AzdoProjectCollectionAdministrators.md similarity index 100% rename from tests/Azdo/Test-AzdoProjectCollectionAdministrators.md rename to powershell/public/azdo/Test-AzdoProjectCollectionAdministrators.md diff --git a/tests/Azdo/Test-AzdoProjectCollectionAdministrators.ps1 b/powershell/public/azdo/Test-AzdoProjectCollectionAdministrators.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoProjectCollectionAdministrators.ps1 rename to powershell/public/azdo/Test-AzdoProjectCollectionAdministrators.ps1 diff --git a/tests/Azdo/Test-AzdoPublicProjects.md b/powershell/public/azdo/Test-AzdoPublicProjects.md similarity index 100% rename from tests/Azdo/Test-AzdoPublicProjects.md rename to powershell/public/azdo/Test-AzdoPublicProjects.md diff --git a/tests/Azdo/Test-AzdoPublicProjects.ps1 b/powershell/public/azdo/Test-AzdoPublicProjects.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoPublicProjects.ps1 rename to powershell/public/azdo/Test-AzdoPublicProjects.ps1 diff --git a/tests/Azdo/Test-AzdoResourceUsageProjects.md b/powershell/public/azdo/Test-AzdoResourceUsageProjects.md similarity index 100% rename from tests/Azdo/Test-AzdoResourceUsageProjects.md rename to powershell/public/azdo/Test-AzdoResourceUsageProjects.md diff --git a/tests/Azdo/Test-AzdoResourceUsageProjects.ps1 b/powershell/public/azdo/Test-AzdoResourceUsageProjects.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoResourceUsageProjects.ps1 rename to powershell/public/azdo/Test-AzdoResourceUsageProjects.ps1 diff --git a/tests/Azdo/Test-AzdoResourceUsageWorkItemTags.md b/powershell/public/azdo/Test-AzdoResourceUsageWorkItemTags.md similarity index 100% rename from tests/Azdo/Test-AzdoResourceUsageWorkItemTags.md rename to powershell/public/azdo/Test-AzdoResourceUsageWorkItemTags.md diff --git a/tests/Azdo/Test-AzdoResourceUsageWorkItemTags.ps1 b/powershell/public/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoResourceUsageWorkItemTags.ps1 rename to powershell/public/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 diff --git a/tests/Azdo/Test-AzdoSSHAuthentication.md b/powershell/public/azdo/Test-AzdoSSHAuthentication.md similarity index 100% rename from tests/Azdo/Test-AzdoSSHAuthentication.md rename to powershell/public/azdo/Test-AzdoSSHAuthentication.md diff --git a/tests/Azdo/Test-AzdoSSHAuthentication.ps1 b/powershell/public/azdo/Test-AzdoSSHAuthentication.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoSSHAuthentication.ps1 rename to powershell/public/azdo/Test-AzdoSSHAuthentication.ps1 diff --git a/tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.md b/powershell/public/azdo/Test-AzdoThirdPartyAccessViaOauth.md similarity index 100% rename from tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.md rename to powershell/public/azdo/Test-AzdoThirdPartyAccessViaOauth.md diff --git a/tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 b/powershell/public/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 similarity index 100% rename from tests/Azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 rename to powershell/public/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 diff --git a/tests/Azdo/README.md b/tests/Azdo/README.md index 2ff0df235..8ac4cf643 100644 --- a/tests/Azdo/README.md +++ b/tests/Azdo/README.md @@ -1,14 +1,4 @@ # Azure DevOps Tests - Preview Azure DevOps tests we will perform a healthcheck towards your Azure DevOps organization. -The base of the tests will be from https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-overview?view=azure-devops and recommendations/considerations from professionals in the field. - -The tests uses the [Adops module](https://github.com/AZDOPS/AZDOPS), -However code review is still on-going to include the new functionality in bundled tests/azdo/adops/ADOPS.psm1 module. -I have generated this module on my own until it's officially released and included in this PR. -The offical release depends on the pull requests; -- https://github.com/AZDOPS/AZDOPS/pull/245 -- https://github.com/AZDOPS/AZDOPS/pull/246 - -I have included readmes in markdown format under tests/azdo. -And instead of having each test by itself, it's in a module (Code to compile and test module/functions is not included in this branch, let me know if it should be bundled or how I can adapt to how other test suits are built) imported in a BeforeAll block of the Test-Azdo.Tests.ps1 \ No newline at end of file +The base of the tests will be from https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-overview?view=azure-devops and recommendations/considerations from professionals in the field. \ No newline at end of file From fc83a14a8c0f1173ddf0a845a5f6e4e9b14e4b75 Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Date: Tue, 9 Dec 2025 14:05:30 +0100 Subject: [PATCH 06/22] Relocating parent folder --- .../public/{ => maester}/azdo/Test-AzdoAllowRequestAccessToken.md | 0 .../{ => maester}/azdo/Test-AzdoAllowRequestAccessToken.ps1 | 0 .../azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md | 0 .../azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 | 0 .../azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md | 0 .../azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 | 0 powershell/public/{ => maester}/azdo/Test-AzdoAuditStreams.md | 0 powershell/public/{ => maester}/azdo/Test-AzdoAuditStreams.ps1 | 0 .../{ => maester}/azdo/Test-AzdoEnforceAADConditionalAccess.md | 0 .../{ => maester}/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 | 0 .../public/{ => maester}/azdo/Test-AzdoExternalGuestAccess.md | 0 .../public/{ => maester}/azdo/Test-AzdoExternalGuestAccess.ps1 | 0 .../public/{ => maester}/azdo/Test-AzdoFeedbackCollection.md | 0 .../public/{ => maester}/azdo/Test-AzdoFeedbackCollection.ps1 | 0 powershell/public/{ => maester}/azdo/Test-AzdoLogAuditEvents.md | 0 powershell/public/{ => maester}/azdo/Test-AzdoLogAuditEvents.ps1 | 0 .../{ => maester}/azdo/Test-AzdoOrganizationArtifactFeed.ps1 | 0 ...oOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md | 0 ...OrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 | 0 .../{ => maester}/azdo/Test-AzdoOrganizationBadgesArePrivate.md | 0 .../{ => maester}/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 | 0 .../azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md | 0 .../azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 | 0 .../azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md | 0 .../azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 | 0 ...doOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md | 0 ...oOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 | 0 ...-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md | 0 ...AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 | 0 .../azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md | 0 .../azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 | 0 .../public/{ => maester}/azdo/Test-AzdoOrganizationOwner.md | 0 .../public/{ => maester}/azdo/Test-AzdoOrganizationOwner.ps1 | 0 .../azdo/Test-AzdoOrganizationProtectAccessToRepositories.md | 0 .../azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 | 0 ...-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md | 0 ...AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 | 0 .../azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md | 0 .../Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 | 0 .../{ => maester}/azdo/Test-AzdoOrganizationStageChooser.md | 0 .../{ => maester}/azdo/Test-AzdoOrganizationStageChooser.ps1 | 0 .../{ => maester}/azdo/Test-AzdoOrganizationStorageUsage.md | 0 .../{ => maester}/azdo/Test-AzdoOrganizationStorageUsage.ps1 | 0 ...est-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md | 0 ...st-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 | 0 .../Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md | 0 .../Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 | 0 ...AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md | 0 ...zdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 | 0 .../Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md | 0 .../Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 | 0 .../azdo/Test-AzdoProjectCollectionAdministrators.md | 0 .../azdo/Test-AzdoProjectCollectionAdministrators.ps1 | 0 powershell/public/{ => maester}/azdo/Test-AzdoPublicProjects.md | 0 powershell/public/{ => maester}/azdo/Test-AzdoPublicProjects.ps1 | 0 .../public/{ => maester}/azdo/Test-AzdoResourceUsageProjects.md | 0 .../public/{ => maester}/azdo/Test-AzdoResourceUsageProjects.ps1 | 0 .../{ => maester}/azdo/Test-AzdoResourceUsageWorkItemTags.md | 0 .../{ => maester}/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 | 0 .../public/{ => maester}/azdo/Test-AzdoSSHAuthentication.md | 0 .../public/{ => maester}/azdo/Test-AzdoSSHAuthentication.ps1 | 0 .../{ => maester}/azdo/Test-AzdoThirdPartyAccessViaOauth.md | 0 .../{ => maester}/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 | 0 tests/{ => Maester}/Azdo/README.md | 0 tests/{ => Maester}/Azdo/Test-Azdo.Tests.ps1 | 0 65 files changed, 0 insertions(+), 0 deletions(-) rename powershell/public/{ => maester}/azdo/Test-AzdoAllowRequestAccessToken.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoAllowRequestAccessToken.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoAuditStreams.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoAuditStreams.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoEnforceAADConditionalAccess.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoExternalGuestAccess.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoExternalGuestAccess.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoFeedbackCollection.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoFeedbackCollection.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoLogAuditEvents.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoLogAuditEvents.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationArtifactFeed.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationBadgesArePrivate.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationOwner.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationOwner.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationProtectAccessToRepositories.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationStageChooser.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationStageChooser.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationStorageUsage.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationStorageUsage.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoProjectCollectionAdministrators.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoProjectCollectionAdministrators.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoPublicProjects.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoPublicProjects.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoResourceUsageProjects.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoResourceUsageProjects.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoResourceUsageWorkItemTags.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoSSHAuthentication.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoSSHAuthentication.ps1 (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoThirdPartyAccessViaOauth.md (100%) rename powershell/public/{ => maester}/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 (100%) rename tests/{ => Maester}/Azdo/README.md (100%) rename tests/{ => Maester}/Azdo/Test-Azdo.Tests.ps1 (100%) diff --git a/powershell/public/azdo/Test-AzdoAllowRequestAccessToken.md b/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.md similarity index 100% rename from powershell/public/azdo/Test-AzdoAllowRequestAccessToken.md rename to powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.md diff --git a/powershell/public/azdo/Test-AzdoAllowRequestAccessToken.ps1 b/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoAllowRequestAccessToken.ps1 rename to powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.ps1 diff --git a/powershell/public/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md b/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md similarity index 100% rename from powershell/public/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md rename to powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md diff --git a/powershell/public/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 b/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 rename to powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 diff --git a/powershell/public/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md b/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md similarity index 100% rename from powershell/public/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md rename to powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md diff --git a/powershell/public/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 b/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 rename to powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 diff --git a/powershell/public/azdo/Test-AzdoAuditStreams.md b/powershell/public/maester/azdo/Test-AzdoAuditStreams.md similarity index 100% rename from powershell/public/azdo/Test-AzdoAuditStreams.md rename to powershell/public/maester/azdo/Test-AzdoAuditStreams.md diff --git a/powershell/public/azdo/Test-AzdoAuditStreams.ps1 b/powershell/public/maester/azdo/Test-AzdoAuditStreams.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoAuditStreams.ps1 rename to powershell/public/maester/azdo/Test-AzdoAuditStreams.ps1 diff --git a/powershell/public/azdo/Test-AzdoEnforceAADConditionalAccess.md b/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.md similarity index 100% rename from powershell/public/azdo/Test-AzdoEnforceAADConditionalAccess.md rename to powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.md diff --git a/powershell/public/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 b/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 rename to powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 diff --git a/powershell/public/azdo/Test-AzdoExternalGuestAccess.md b/powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.md similarity index 100% rename from powershell/public/azdo/Test-AzdoExternalGuestAccess.md rename to powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.md diff --git a/powershell/public/azdo/Test-AzdoExternalGuestAccess.ps1 b/powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoExternalGuestAccess.ps1 rename to powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.ps1 diff --git a/powershell/public/azdo/Test-AzdoFeedbackCollection.md b/powershell/public/maester/azdo/Test-AzdoFeedbackCollection.md similarity index 100% rename from powershell/public/azdo/Test-AzdoFeedbackCollection.md rename to powershell/public/maester/azdo/Test-AzdoFeedbackCollection.md diff --git a/powershell/public/azdo/Test-AzdoFeedbackCollection.ps1 b/powershell/public/maester/azdo/Test-AzdoFeedbackCollection.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoFeedbackCollection.ps1 rename to powershell/public/maester/azdo/Test-AzdoFeedbackCollection.ps1 diff --git a/powershell/public/azdo/Test-AzdoLogAuditEvents.md b/powershell/public/maester/azdo/Test-AzdoLogAuditEvents.md similarity index 100% rename from powershell/public/azdo/Test-AzdoLogAuditEvents.md rename to powershell/public/maester/azdo/Test-AzdoLogAuditEvents.md diff --git a/powershell/public/azdo/Test-AzdoLogAuditEvents.ps1 b/powershell/public/maester/azdo/Test-AzdoLogAuditEvents.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoLogAuditEvents.ps1 rename to powershell/public/maester/azdo/Test-AzdoLogAuditEvents.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationArtifactFeed.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationArtifactFeed.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationArtifactFeed.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationArtifactFeed.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md b/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationBadgesArePrivate.md b/powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationBadgesArePrivate.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationOwner.md b/powershell/public/maester/azdo/Test-AzdoOrganizationOwner.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationOwner.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationOwner.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationOwner.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationOwner.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationOwner.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationOwner.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationProtectAccessToRepositories.md b/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationProtectAccessToRepositories.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationStageChooser.md b/powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationStageChooser.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationStageChooser.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationStageChooser.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationStorageUsage.md b/powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationStorageUsage.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationStorageUsage.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationStorageUsage.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 diff --git a/powershell/public/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md b/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md diff --git a/powershell/public/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 diff --git a/powershell/public/azdo/Test-AzdoProjectCollectionAdministrators.md b/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrators.md similarity index 100% rename from powershell/public/azdo/Test-AzdoProjectCollectionAdministrators.md rename to powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrators.md diff --git a/powershell/public/azdo/Test-AzdoProjectCollectionAdministrators.ps1 b/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrators.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoProjectCollectionAdministrators.ps1 rename to powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrators.ps1 diff --git a/powershell/public/azdo/Test-AzdoPublicProjects.md b/powershell/public/maester/azdo/Test-AzdoPublicProjects.md similarity index 100% rename from powershell/public/azdo/Test-AzdoPublicProjects.md rename to powershell/public/maester/azdo/Test-AzdoPublicProjects.md diff --git a/powershell/public/azdo/Test-AzdoPublicProjects.ps1 b/powershell/public/maester/azdo/Test-AzdoPublicProjects.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoPublicProjects.ps1 rename to powershell/public/maester/azdo/Test-AzdoPublicProjects.ps1 diff --git a/powershell/public/azdo/Test-AzdoResourceUsageProjects.md b/powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.md similarity index 100% rename from powershell/public/azdo/Test-AzdoResourceUsageProjects.md rename to powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.md diff --git a/powershell/public/azdo/Test-AzdoResourceUsageProjects.ps1 b/powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoResourceUsageProjects.ps1 rename to powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.ps1 diff --git a/powershell/public/azdo/Test-AzdoResourceUsageWorkItemTags.md b/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.md similarity index 100% rename from powershell/public/azdo/Test-AzdoResourceUsageWorkItemTags.md rename to powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.md diff --git a/powershell/public/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 b/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 rename to powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 diff --git a/powershell/public/azdo/Test-AzdoSSHAuthentication.md b/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.md similarity index 100% rename from powershell/public/azdo/Test-AzdoSSHAuthentication.md rename to powershell/public/maester/azdo/Test-AzdoSSHAuthentication.md diff --git a/powershell/public/azdo/Test-AzdoSSHAuthentication.ps1 b/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoSSHAuthentication.ps1 rename to powershell/public/maester/azdo/Test-AzdoSSHAuthentication.ps1 diff --git a/powershell/public/azdo/Test-AzdoThirdPartyAccessViaOauth.md b/powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.md similarity index 100% rename from powershell/public/azdo/Test-AzdoThirdPartyAccessViaOauth.md rename to powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.md diff --git a/powershell/public/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 b/powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 similarity index 100% rename from powershell/public/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 rename to powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 diff --git a/tests/Azdo/README.md b/tests/Maester/Azdo/README.md similarity index 100% rename from tests/Azdo/README.md rename to tests/Maester/Azdo/README.md diff --git a/tests/Azdo/Test-Azdo.Tests.ps1 b/tests/Maester/Azdo/Test-Azdo.Tests.ps1 similarity index 100% rename from tests/Azdo/Test-Azdo.Tests.ps1 rename to tests/Maester/Azdo/Test-Azdo.Tests.ps1 From 86125a898b0fa6dc604a76e1f7fe6f89c461b793 Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Date: Thu, 11 Dec 2025 08:53:01 +0100 Subject: [PATCH 07/22] updating readmes and powershell scripts --- powershell/Maester.psd1 | 33 +++++++++- .../azdo/Test-AzdoAllowRequestAccessToken.ps1 | 25 +++++++- ...oAllowTeamAdminsInvitationsAccessToken.ps1 | 26 +++++++- ...rtifactsExternalPackageProtectionToken.ps1 | 25 +++++++- ...uditStreams.md => Test-AzdoAuditStream.md} | 0 ...itStreams.ps1 => Test-AzdoAuditStream.ps1} | 30 ++++++++- .../Test-AzdoEnforceAADConditionalAccess.ps1 | 28 +++++++-- .../azdo/Test-AzdoExternalGuestAccess.ps1 | 22 ++++++- .../azdo/Test-AzdoFeedbackCollection.ps1 | 28 +++++++-- ...ditEvents.md => Test-AzdoLogAuditEvent.md} | 0 .../maester/azdo/Test-AzdoLogAuditEvent.ps1 | 45 ++++++++++++++ .../maester/azdo/Test-AzdoLogAuditEvents.ps1 | 26 -------- .../Test-AzdoOrganizationArtifactFeed.ps1 | 27 -------- ...icEnrollmentAdvancedSecurityNewProject.md} | 0 ...icEnrollmentAdvancedSecurityNewProject.ps1 | 42 +++++++++++++ ...cEnrollmentAdvancedSecurityNewProjects.ps1 | 25 -------- .../Test-AzdoOrganizationBadgesArePrivate.ps1 | 27 ++++++-- ...ganizationCreationClassicBuildPipeline.md} | 0 ...anizationCreationClassicBuildPipeline.ps1} | 29 +++++++-- ...nizationCreationClassicReleasePipeline.md} | 0 ...izationCreationClassicReleasePipeline.ps1} | 28 +++++++-- ...obAuthorizationScopeNonReleasePipeline.md} | 0 ...obAuthorizationScopeNonReleasePipeline.ps1 | 41 +++++++++++++ ...bAuthorizationScopeNonReleasePipelines.ps1 | 25 -------- ...itJobAuthorizationScopeReleasePipeline.md} | 0 ...itJobAuthorizationScopeReleasePipeline.ps1 | 41 +++++++++++++ ...tJobAuthorizationScopeReleasePipelines.ps1 | 25 -------- ...oOrganizationLimitVariablesAtQueueTime.ps1 | 26 ++++++-- .../azdo/Test-AzdoOrganizationOwner.ps1 | 39 ++++++++---- ...rganizationProtectAccessToRepositories.ps1 | 25 -------- ...oOrganizationProtectAccessToRepository.md} | 0 ...oOrganizationProtectAccessToRepository.ps1 | 42 +++++++++++++ ...ositorySettingsDisableCreationTFVCRepo.md} | 0 ...ositorySettingsDisableCreationTFVCRepo.ps1 | 41 +++++++++++++ ...sitorySettingsDisableCreationTFVCRepos.ps1 | 25 -------- ...izationRepositorySettingsGravatarImage.md} | 0 ...izationRepositorySettingsGravatarImage.ps1 | 41 +++++++++++++ ...zationRepositorySettingsGravatarImages.ps1 | 25 -------- .../Test-AzdoOrganizationStageChooser.ps1 | 29 +++++++-- .../Test-AzdoOrganizationStorageUsage.ps1 | 37 ++++++++--- ...TaskRestrictionsDisableMarketplaceTask.md} | 0 ...TaskRestrictionsDisableMarketplaceTask.ps1 | 41 +++++++++++++ ...askRestrictionsDisableMarketplaceTasks.ps1 | 25 -------- ...zationTaskRestrictionsDisableNode6Task.md} | 0 ...zationTaskRestrictionsDisableNode6Task.ps1 | 61 +++++++++++++++++++ ...ationTaskRestrictionsDisableNode6Tasks.ps1 | 25 -------- ...estrictionsShellTaskArgumentValidation.ps1 | 27 ++++++-- ...tionTriggerPullRequestGitHubRepository.md} | 0 ...ionTriggerPullRequestGitHubRepository.ps1} | 41 ++++++++----- ...est-AzdoProjectCollectionAdministrator.md} | 0 ...st-AzdoProjectCollectionAdministrator.ps1} | 46 ++++++++------ ...cProjects.md => Test-AzdoPublicProject.md} | 0 .../maester/azdo/Test-AzdoPublicProject.ps1 | 40 ++++++++++++ .../maester/azdo/Test-AzdoPublicProjects.ps1 | 26 -------- ...ts.md => Test-AzdoResourceUsageProject.md} | 0 .../azdo/Test-AzdoResourceUsageProject.ps1 | 42 +++++++++++++ .../azdo/Test-AzdoResourceUsageProjects.ps1 | 29 --------- ...d => Test-AzdoResourceUsageWorkItemTag.md} | 0 .../Test-AzdoResourceUsageWorkItemTag.ps1 | 44 +++++++++++++ .../Test-AzdoResourceUsageWorkItemTags.ps1 | 29 --------- .../azdo/Test-AzdoSSHAuthentication.md | 1 + .../azdo/Test-AzdoSSHAuthentication.ps1 | 32 +++++++--- .../Test-AzdoThirdPartyAccessViaOauth.ps1 | 33 +++++++--- tests/Maester/Azdo/Test-Azdo.Tests.ps1 | 38 ++++++------ 64 files changed, 1030 insertions(+), 478 deletions(-) rename powershell/public/maester/azdo/{Test-AzdoAuditStreams.md => Test-AzdoAuditStream.md} (100%) rename powershell/public/maester/azdo/{Test-AzdoAuditStreams.ps1 => Test-AzdoAuditStream.ps1} (52%) rename powershell/public/maester/azdo/{Test-AzdoLogAuditEvents.md => Test-AzdoLogAuditEvent.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoLogAuditEvent.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoLogAuditEvents.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationArtifactFeed.ps1 rename powershell/public/maester/azdo/{Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md => Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 rename powershell/public/maester/azdo/{Test-AzdoOrganizationCreationClassicBuildPipelines.md => Test-AzdoOrganizationCreationClassicBuildPipeline.md} (100%) rename powershell/public/maester/azdo/{Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 => Test-AzdoOrganizationCreationClassicBuildPipeline.ps1} (50%) rename powershell/public/maester/azdo/{Test-AzdoOrganizationCreationClassicReleasePipelines.md => Test-AzdoOrganizationCreationClassicReleasePipeline.md} (100%) rename powershell/public/maester/azdo/{Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 => Test-AzdoOrganizationCreationClassicReleasePipeline.ps1} (53%) rename powershell/public/maester/azdo/{Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md => Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 rename powershell/public/maester/azdo/{Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md => Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 rename powershell/public/maester/azdo/{Test-AzdoOrganizationProtectAccessToRepositories.md => Test-AzdoOrganizationProtectAccessToRepository.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.ps1 rename powershell/public/maester/azdo/{Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md => Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 rename powershell/public/maester/azdo/{Test-AzdoOrganizationRepositorySettingsGravatarImages.md => Test-AzdoOrganizationRepositorySettingsGravatarImage.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 rename powershell/public/maester/azdo/{Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md => Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 rename powershell/public/maester/azdo/{Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md => Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 rename powershell/public/maester/azdo/{Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md => Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md} (100%) rename powershell/public/maester/azdo/{Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 => Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1} (58%) rename powershell/public/maester/azdo/{Test-AzdoProjectCollectionAdministrators.md => Test-AzdoProjectCollectionAdministrator.md} (100%) rename powershell/public/maester/azdo/{Test-AzdoProjectCollectionAdministrators.ps1 => Test-AzdoProjectCollectionAdministrator.ps1} (73%) rename powershell/public/maester/azdo/{Test-AzdoPublicProjects.md => Test-AzdoPublicProject.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoPublicProject.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoPublicProjects.ps1 rename powershell/public/maester/azdo/{Test-AzdoResourceUsageProjects.md => Test-AzdoResourceUsageProject.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoResourceUsageProject.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.ps1 rename powershell/public/maester/azdo/{Test-AzdoResourceUsageWorkItemTags.md => Test-AzdoResourceUsageWorkItemTag.md} (100%) create mode 100644 powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.ps1 delete mode 100644 powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 diff --git a/powershell/Maester.psd1 b/powershell/Maester.psd1 index d00448cf9..068f9e708 100644 --- a/powershell/Maester.psd1 +++ b/powershell/Maester.psd1 @@ -184,7 +184,38 @@ 'Test-MtXspmCriticalCredsOnDevicesWithNonCriticalAccounts', 'Test-MtXspmPublicRemotelyExploitableHighExposureDevices', 'Test-MtXspmCriticalCredentialsOnNonTpmProtectedDevices', - 'Test-MtXspmCriticalCredentialsOnNonCredGuardProtectedDevices' + 'Test-MtXspmCriticalCredentialsOnNonCredGuardProtectedDevices', + 'Test-AzdoAllowRequestAccessToken', + 'Test-AzdoAllowTeamAdminsInvitationsAccessToken', + 'Test-AzdoArtifactsExternalPackageProtectionToken', + 'Test-AzdoAuditStream', + 'Test-AzdoEnforceAADConditionalAccess', + 'Test-AzdoExternalGuestAccess', + 'Test-AzdoFeedbackCollection', + 'Test-AzdoLogAuditEvent', + 'Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject', + 'Test-AzdoOrganizationBadgesArePrivate', + 'Test-AzdoOrganizationCreationClassicBuildPipeline', + 'Test-AzdoOrganizationCreationClassicReleasePipeline', + 'Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline', + 'Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline', + 'Test-AzdoOrganizationLimitVariablesAtQueueTime', + 'Test-AzdoOrganizationOwner', + 'Test-AzdoOrganizationProtectAccessToRepository', + 'Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo', + 'Test-AzdoOrganizationRepositorySettingsGravatarImage', + 'Test-AzdoOrganizationStageChooser', + 'Test-AzdoOrganizationStorageUsage', + 'Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask', + 'Test-AzdoOrganizationTaskRestrictionsDisableNode6Task', + 'Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation', + 'Test-AzdoOrganizationTriggerPullRequestGitHubRepository', + 'Test-AzdoProjectCollectionAdministrator', + 'Test-AzdoPublicProject', + 'Test-AzdoResourceUsageProject', + 'Test-AzdoResourceUsageWorkItemTag', + 'Test-AzdoSSHAuthentication', + 'Test-AzdoThirdPartyAccessViaOauth' # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @() diff --git a/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.ps1 b/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.ps1 index 1fa7b9312..9e1561f82 100644 --- a/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.ps1 @@ -1,9 +1,32 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of the 'Request Access' policy in Azure DevOps to prevent users from requesting access to your organization or projects. + When this policy is enabled, users can request access, and administrators receive email notifications for review and approval. + Disabling the policy stops these requests and notifications, helping you control access more tightly. + + https://go.microsoft.com/fwlink/?linkid=2113172 + +.EXAMPLE + ``` + Test-AzdoAllowRequestAccessToken + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoAllowRequestAccessToken +#> + function Test-AzdoAllowRequestAccessToken { [CmdletBinding()] [OutputType([bool])] param() if ($null -eq (Get-ADOPSConnection)['Organization']) { + Write-verbose 'Not connected to Azure DevOps' Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' return $null } @@ -18,8 +41,6 @@ function Test-AzdoAllowRequestAccessToken { $resultMarkdown = "Well done. Disabling the policy stops these requests and notifications." } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' return $result diff --git a/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 b/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 index 782f8d342..c1bebe754 100644 --- a/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 @@ -1,9 +1,33 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + By default, all administrators can invite new users to their Azure DevOps organization. + Disabling this policy prevents Team and Project Administrators from inviting new users or adding Entra groups. + However, Project Collection Administrators (PCAs) can still add new users and Entra groups to the organization regardless of the policy status. + Additionally, if a user is already a member of the organization, Project and Team Administrators can add that user to specific projects. + + https://aka.ms/azure-devops-invitations-policy + +.EXAMPLE + ``` + Test-AzdoAllowTeamAdminsInvitationsAccessToken + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoAllowTeamAdminsInvitationsAccessToken +#> + function Test-AzdoAllowTeamAdminsInvitationsAccessToken { [CmdletBinding()] [OutputType([bool])] param() if ($null -eq (Get-ADOPSConnection)['Organization']) { + Write-verbose 'Not connected to Azure DevOps' Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' return $null } @@ -18,8 +42,6 @@ function Test-AzdoAllowTeamAdminsInvitationsAccessToken { $resultMarkdown = "Well done. Enrolling to your Azure DevOps organization should be a controlled process." } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' return $result diff --git a/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 b/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 index c48730b4d..6798be7b4 100644 --- a/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 @@ -1,9 +1,32 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the policy for additional security for your private feeds by limiting access to externally sourced packages when internally sources packages are already present. + This provides a new layer of security, which prevents malicious packages from a public registry being inadvertently consumed. + These changes will not affect any package versions that are already in use or cached in your feed. + + https://devblogs.microsoft.com/devops/changes-to-azure-artifact-upstream-behavior + +.EXAMPLE + ``` + Test-AzdoArtifactsExternalPackageProtectionToken + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoArtifactsExternalPackageProtectionToken +#> + function Test-AzdoArtifactsExternalPackageProtectionToken { [CmdletBinding()] [OutputType([bool])] param() if ($null -eq (Get-ADOPSConnection)['Organization']) { + Write-verbose 'Not connected to Azure DevOps' Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' return $null } @@ -18,7 +41,7 @@ function Test-AzdoArtifactsExternalPackageProtectionToken { $resultMarkdown = "Your tenant should prefer to use internal source packages when present" } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' diff --git a/powershell/public/maester/azdo/Test-AzdoAuditStreams.md b/powershell/public/maester/azdo/Test-AzdoAuditStream.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoAuditStreams.md rename to powershell/public/maester/azdo/Test-AzdoAuditStream.md diff --git a/powershell/public/maester/azdo/Test-AzdoAuditStreams.ps1 b/powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 similarity index 52% rename from powershell/public/maester/azdo/Test-AzdoAuditStreams.ps1 rename to powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 index bf586ca23..093804ff7 100644 --- a/powershell/public/maester/azdo/Test-AzdoAuditStreams.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 @@ -1,15 +1,39 @@ -function Test-AzdoAuditStreams { +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks auditing data to other Security Incident and Event Management (SIEM) tools and open new possibilities, + such as the ability to trigger alerts for specific events, create views on auditing data, + and perform anomaly detection. Setting up a stream also allows you to store more than 90-days worth of auditing data, + which is the maximum amount of data that Azure DevOps keeps for your organizations. + + https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops + +.EXAMPLE + ``` + Test-AzdoAuditStream + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoAuditStream +#> + +function Test-AzdoAuditStream { [CmdletBinding()] [OutputType([bool])] param() if ($null -eq (Get-ADOPSConnection)['Organization']) { + Write-verbose 'Not connected to Azure DevOps' Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' return $null } $AuditStreams = Get-ADOPSAuditStreams - + if ($AuditStreams) { if ('Enabled' -in $AuditStreams.status) { $resultMarkdown = "Well done. Audit logs have been configured for long-term storage and purge protection." @@ -25,7 +49,7 @@ function Test-AzdoAuditStreams { $result = $false } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' diff --git a/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 b/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 index c64a09844..6d4431c52 100644 --- a/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 @@ -1,9 +1,30 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of when you sign in to the web portal of a Microsoft Entra ID-backed organization, + Microsoft Entra ID always performs validation for any Conditional Access Policies (CAPs) set by tenant administrators. + + https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops + +.EXAMPLE + ``` + Test-AzdoEnforceAADConditionalAccess + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoEnforceAADConditionalAccess +#> function Test-AzdoEnforceAADConditionalAccess { [CmdletBinding()] [OutputType([bool])] param() if ($null -eq (Get-ADOPSConnection)['Organization']) { + Write-verbose 'Not connected to Azure DevOps' Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' return $null } @@ -13,14 +34,13 @@ function Test-AzdoEnforceAADConditionalAccess { $result = $Policy.effectiveValue if ($result) { $resultMarkdown = "Well done. Microsoft Entra ID always performs validation for any Conditional Access Policies (CAPs) set by tenant administrators." - } - else { + } else { $resultMarkdown = "Your tenant should always perform validation for any Conditional Access Policies (CAPs) set by tenant administrators. " } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' return $result } \ No newline at end of file diff --git a/powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.ps1 b/powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.ps1 index 2fdee34eb..86e952dd9 100644 --- a/powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.ps1 @@ -1,9 +1,29 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the configuration of external guest access to Azure DevOps. + + https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-overview?view=azure-devops#manage-external-guest-access + +.EXAMPLE + ``` + Test-AzdoExternalGuestAccess + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoExternalGuestAccess +#> function Test-AzdoExternalGuestAccess { [CmdletBinding()] [OutputType([bool])] param() if ($null -eq (Get-ADOPSConnection)['Organization']) { + Write-verbose 'Not connected to Azure DevOps' Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' return $null } @@ -18,7 +38,7 @@ function Test-AzdoExternalGuestAccess { $resultMarkdown = "Well done. External users should not be allowed access to your Azure DevOps organization" } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' diff --git a/powershell/public/maester/azdo/Test-AzdoFeedbackCollection.ps1 b/powershell/public/maester/azdo/Test-AzdoFeedbackCollection.ps1 index b669e5bcf..c225ab349 100644 --- a/powershell/public/maester/azdo/Test-AzdoFeedbackCollection.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoFeedbackCollection.ps1 @@ -1,12 +1,30 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status if Azure DevOps is collecting customer feedback to the product team. + + https://aka.ms/ADOPrivacyPolicy + https://learn.microsoft.com/en-us/azure/devops/organizations/security/data-protection?view=azure-devops#managing-privacy-policies-for-admins-to-control-user-feedback-collection + +.EXAMPLE + ``` + Test-AzdoFeedbackCollection + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoFeedbackCollection +#> + function Test-AzdoFeedbackCollection { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } +Write-verbose 'Not connected to Azure DevOps' $PrivacyPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Privacy' $Policy = $PrivacyPolicies.policy | where-object -property name -eq 'Policy.AllowFeedbackCollection' @@ -18,7 +36,7 @@ function Test-AzdoFeedbackCollection { $resultMarkdown = "You should have confidence that we're handling your data appropriately and for legitimate uses. Part of that assurance involves carefully restricting usage." } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Info' diff --git a/powershell/public/maester/azdo/Test-AzdoLogAuditEvents.md b/powershell/public/maester/azdo/Test-AzdoLogAuditEvent.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoLogAuditEvents.md rename to powershell/public/maester/azdo/Test-AzdoLogAuditEvent.md diff --git a/powershell/public/maester/azdo/Test-AzdoLogAuditEvent.ps1 b/powershell/public/maester/azdo/Test-AzdoLogAuditEvent.ps1 new file mode 100644 index 000000000..feb68bda3 --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoLogAuditEvent.ps1 @@ -0,0 +1,45 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks if auditing of events is configured. + + https://learn.microsoft.com/en-us/azure/devops/organizations/audit/azure-devops-auditing?view=azure-devops&tabs=preview-page#enable-and-disable-auditing + https://learn.microsoft.com/en-us/azure/devops/organizations/audit/azure-devops-auditing?view=azure-devops&tabs=preview-page#review-audit-log + + +.EXAMPLE + ``` + Test-AzdoLogAuditEvent + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoLogAuditEvent +#> + +function Test-AzdoLogAuditEvent { + [CmdletBinding()] + [OutputType([bool])] + param() + +Write-verbose 'Not connected to Azure DevOps' + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.LogAuditEvents' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Well done. Your tenant has auditing enabled, tracking events such as permission changes, deleted resources, log access and downloads with many other types of changes." + } + else { + $resultMarkdown = "Your tenant do not have logging enabled for Azure DevOps" + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} \ No newline at end of file diff --git a/powershell/public/maester/azdo/Test-AzdoLogAuditEvents.ps1 b/powershell/public/maester/azdo/Test-AzdoLogAuditEvents.ps1 deleted file mode 100644 index 0ad18bc5b..000000000 --- a/powershell/public/maester/azdo/Test-AzdoLogAuditEvents.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -function Test-AzdoLogAuditEvents { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' - $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.LogAuditEvents' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Well done. Your tenant has auditing enabled, tracking events such as permission changes, deleted resources, log access and downloads with many other types of changes." - } - else { - $resultMarkdown = "Your tenant do not have logging enabled for Azure DevOps" - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' - - return $result -} \ No newline at end of file diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationArtifactFeed.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationArtifactFeed.ps1 deleted file mode 100644 index 15f98f3db..000000000 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationArtifactFeed.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -function Test-AzdoOrganizationProtectAccessToRepositories { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).enforceReferencedRepoScopedToken - - if ($result) { - $resultMarkdown = "Well done. Checks and approvals are applied when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." - } - else { - $resultMarkdown = "Checks and approvals are not applied when accessing repositories from YAML pipelines. Also, a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline is not generated." - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' - - return $result - - ## https://learn.microsoft.com/en-us/azure/devops/artifacts/feeds/feed-permissions?view=azure-devops&tabs=nuget%2Cnugetserver22%2Cnugetserver -} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md b/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 new file mode 100644 index 000000000..e4b67fd14 --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 @@ -0,0 +1,42 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks if GitHub advanced Security for Azure DevOps is automatically enabled for new projects. + + https://learn.microsoft.com/en-us/azure/devops/repos/security/configure-github-advanced-security-features?view=azure-devops&tabs=yaml#organization-level-onboarding + +.EXAMPLE + ``` + Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject +#> + +function Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject { + [CmdletBinding()] + [OutputType([bool])] + param() + +Write-verbose 'Not connected to Azure DevOps' + + $result = (Get-ADOPSOrganizationAdvancedSecurity).enableOnCreate + + if ($result) { + $resultMarkdown = "Well done. New projects will by default have Advanced Security enabled." + } + else { + $resultMarkdown = "New projects must be manually enrolled in Advanced Security." + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 deleted file mode 100644 index ac669dc7e..000000000 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationAdvancedSecurity).enableOnCreate - - if ($result) { - $resultMarkdown = "Well done. New projects will by default have Advanced Security enabled." - } - else { - $resultMarkdown = "New projects must be manually enrolled in Advanced Security." - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 index fe44b2537..de0a27a70 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 @@ -1,12 +1,29 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of anonymous status badges in Azure DevOps. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops&tabs=net%2Cbrowser#add-a-status-badge-to-your-repository + +.EXAMPLE + ``` + Test-AzdoOrganizationBadgesArePrivate + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationBadgesArePrivate +#> + function Test-AzdoOrganizationBadgesArePrivate { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } +Write-verbose 'Not connected to Azure DevOps' $result = (Get-ADOPSOrganizationPipelineSettings).statusBadgesArePrivate @@ -17,7 +34,7 @@ function Test-AzdoOrganizationBadgesArePrivate { $resultMarkdown = "Anonymous users can access the status badge API for all pipelines." } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipeline.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipeline.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 similarity index 50% rename from powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 index e9a398d08..4a052ad6b 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 @@ -1,12 +1,29 @@ -function Test-AzdoOrganizationCreationClassicBuildPipelines { +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks if classic build pipelines can be created. + + https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines + +.EXAMPLE + ``` + Test-AzdoOrganizationCreationClassicBuildPipeline + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationCreationClassicBuildPipeline +#> + +function Test-AzdoOrganizationCreationClassicBuildPipeline { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } +Write-verbose 'Not connected to Azure DevOps' $PipelineCreation = (Get-ADOPSOrganizationPipelineSettings).disableClassicBuildPipelineCreation @@ -19,7 +36,7 @@ function Test-AzdoOrganizationCreationClassicBuildPipelines { $result = $true } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipeline.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipeline.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 similarity index 53% rename from powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 index b136519f6..caf1d3f70 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 @@ -1,12 +1,28 @@ -function Test-AzdoOrganizationCreationClassicReleasePipelines { +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks if classic release pipelines can be created. + + https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines + +.EXAMPLE + ``` + Test-AzdoOrganizationCreationClassicReleasePipeline + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationCreationClassicReleasePipeline +#> +function Test-AzdoOrganizationCreationClassicReleasePipeline { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } +Write-verbose 'Not connected to Azure DevOps' $PipelineCreation = (Get-ADOPSOrganizationPipelineSettings).disableClassicReleasePipelineCreation @@ -19,7 +35,7 @@ function Test-AzdoOrganizationCreationClassicReleasePipelines { $result = $true } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 new file mode 100644 index 000000000..ef9480db5 --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 @@ -0,0 +1,41 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks IF YAML & build pipelines have restricted access to only those repositories that are in the same project as the pipeline.. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope + +.EXAMPLE + ``` + Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline +#> +function Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline { + [CmdletBinding()] + [OutputType([bool])] + param() + +Write-verbose 'Not connected to Azure DevOps' + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScope + + if ($result) { + $resultMarkdown = "Well done. Access tokens have reduced scope of access for all non-release pipelines." + } + else { + $resultMarkdown = "Non-Release Pipelines can run with collection scoped access tokens" + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 deleted file mode 100644 index 878d70e75..000000000 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScope - - if ($result) { - $resultMarkdown = "Well done. Access tokens have reduced scope of access for all non-release pipelines." - } - else { - $resultMarkdown = "Non-Release Pipelines can run with collection scoped access tokens" - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 new file mode 100644 index 000000000..7d9d3717e --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 @@ -0,0 +1,41 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks if release pipelines have restricted access to only those repositories that are in the same project as the pipeline. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope + +.EXAMPLE + ``` + Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline +#> +function Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline { + [CmdletBinding()] + [OutputType([bool])] + param() + +Write-verbose 'Not connected to Azure DevOps' + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScopeForReleases + + if ($result) { + $resultMarkdown = "Well done. Access tokens have reduced scope of access for all classic release pipelines." + } + else { + $resultMarkdown = "Classic Release Pipelines can run with collection scoped access tokens" + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 deleted file mode 100644 index e3890d66b..000000000 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).enforceJobAuthScopeForReleases - - if ($result) { - $resultMarkdown = "Well done. Access tokens have reduced scope of access for all classic release pipelines." - } - else { - $resultMarkdown = "Classic Release Pipelines can run with collection scoped access tokens" - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 index 021ceffd4..f9f27de4f 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 @@ -1,12 +1,28 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks if user defined variables are able to override system variables or variables not defined by the pipeline author. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#limit-variables-that-can-be-set-at-queue-time + +.EXAMPLE + ``` + Test-AzdoOrganizationLimitVariablesAtQueueTime + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationLimitVariablesAtQueueTime +#> function Test-AzdoOrganizationLimitVariablesAtQueueTime { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } +Write-verbose 'Not connected to Azure DevOps' $result = (Get-ADOPSOrganizationPipelineSettings).enforceSettableVar @@ -23,7 +39,7 @@ function Test-AzdoOrganizationLimitVariablesAtQueueTime { } } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationOwner.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationOwner.ps1 index 8de4b5c64..2b5c56c86 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationOwner.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationOwner.ps1 @@ -1,28 +1,43 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks if the Azure DevOps Organization owner is a indiviual or a service/admin account. + Returns a true boolean if the users matches adm|admin|btg|svc|service. + + https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/change-organization-ownership?view=azure-devops + +.EXAMPLE + ``` + Test-AzdoOrganizationOwner + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationOwner +#> function Test-AzdoOrganizationOwner { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } + Write-verbose 'Not connected to Azure DevOps' + + - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - $Data = Get-ADOPSOrganizationAdminOverview if ($data.'ms.vss-admin-web.organization-admin-overview-delay-load-data-provider'.exceptionType -eq 'AadGraphException') { $resultMarkdown = "Workload identities cannot fetch Organization Owner." - Add-MtTestResultDetail -Result "BUG: Workload identities cannot fetch Organization Owner." -SkippedCustomReason "Workload identities cannot fetch Organization Owner." -SkippedBecause Custom + Add-MtTestResultDetail -Result "BUG: Workload identities cannot fetch Organization Owner." -SkippedCustomReason "Workload identities cannot fetch Organization Owner." -SkippedBecause Custom $result = $false - } - else { + } else { $currentOwner = $data.'ms.vss-admin-web.organization-admin-overview-delay-load-data-provider'.currentOwner if ($currentOwner.email -match '(?i)(adm|admin|btg|svc|service)') { $resultMarkdown = "Well done. Azure DevOps organization owner should be a service account and not an individual.`n`n%TestResult%" $result = $true - } - else { + } else { $resultMarkdown = "Azure DevOps organization owner should not be an individual ($($currentOwner.name)). Note: This might be a false positive.`n`n%TestResult%" $result = $false } @@ -30,7 +45,7 @@ function Test-AzdoOrganizationOwner { $markdown += "| --- | --- | --- |`n" $markdown += "| $($currentOwner.name) | $($currentOwner.id) | $($currentOwner.email) |`n" $resultMarkdown = $resultMarkdown -replace '%TestResult%', $markdown - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' } return $result } \ No newline at end of file diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 deleted file mode 100644 index 4bc1188da..000000000 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Test-AzdoOrganizationProtectAccessToRepositories { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).enforceReferencedRepoScopedToken - - if ($result) { - $resultMarkdown = "Well done. Checks and approvals are applied when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." - } - else { - $resultMarkdown = "Checks and approvals are not applied when accessing repositories from YAML pipelines. Also, a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline is not generated." - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.md b/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepositories.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.ps1 new file mode 100644 index 000000000..01c7658ea --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.ps1 @@ -0,0 +1,42 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks if checks and approvals are applied when accessing repositories from YAML pipelines. + Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#restrict-project-repository-and-service-connection-access + +.EXAMPLE + ``` + Test-AzdoOrganizationProtectAccessToRepository + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationProtectAccessToRepository +#> +function Test-AzdoOrganizationProtectAccessToRepository { + [CmdletBinding()] + [OutputType([bool])] + param() + +Write-verbose 'Not connected to Azure DevOps' + + $result = (Get-ADOPSOrganizationPipelineSettings).enforceReferencedRepoScopedToken + + if ($result) { + $resultMarkdown = "Well done. Checks and approvals are applied when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." + } + else { + $resultMarkdown = "Checks and approvals are not applied when accessing repositories from YAML pipelines. Also, a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline is not generated." + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 new file mode 100644 index 000000000..0e1d0a869 --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 @@ -0,0 +1,41 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status if creation of Team Foundation Version Control (TFVC) repositories is disabled. + + https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2024/no-tfvc-in-new-projects + +.EXAMPLE + ``` + Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo +#> +function Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo { + [CmdletBinding()] + [OutputType([bool])] + param() + +Write-verbose 'Not connected to Azure DevOps' + + $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "DisableTfvcRepositories").value + + if ($result) { + $resultMarkdown = "Well done. Team Foundation Version Control (TFVC) repositories cannot be created." + } + else { + $resultMarkdown = "Team Foundation Version Control (TFVC) can be created." + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 deleted file mode 100644 index ac5b472c2..000000000 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "DisableTfvcRepositories").value - - if ($result) { - $resultMarkdown = "Well done. Team Foundation Version Control (TFVC) repositories cannot be created." - } - else { - $resultMarkdown = "Team Foundation Version Control (TFVC) can be created." - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 new file mode 100644 index 000000000..96cb54ab6 --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 @@ -0,0 +1,41 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status if Gravatar images are shown for users outside of your enterprise. + + https://learn.microsoft.com/en-us/azure/devops/repos/git/repository-settings?view=azure-devops&tabs=browser#gravatar-images + +.EXAMPLE + ``` + Test-AzdoOrganizationRepositorySettingsGravatarImage + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationRepositorySettingsGravatarImage +#> +function Test-AzdoOrganizationRepositorySettingsGravatarImage { + [CmdletBinding()] + [OutputType([bool])] + param() + +Write-verbose 'Not connected to Azure DevOps' + + $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "GravatarEnabled").value + + if ($result) { + $resultMarkdown = "Gravatar images are exposed for users outside of your enterprise." + } + else { + $resultMarkdown = "Well done. Gravatar images are not exposed outside of your enterprise." + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Medium' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 deleted file mode 100644 index 001f5f374..000000000 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Test-AzdoOrganizationRepositorySettingsGravatarImages { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationRepositorySettings | Where-object key -eq "GravatarEnabled").value - - if ($result) { - $resultMarkdown = "Gravatar images are exposed for users outside of your enterprise." - } - else { - $resultMarkdown = "Well done. Gravatar images are not exposed outside of your enterprise." - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Medium' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.ps1 index 7864fb89f..1072cecc1 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.ps1 @@ -1,14 +1,31 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status if users are able to choose what stages to run or skip. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/process/stages?view=azure-devops&tabs=yaml + https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops + +.EXAMPLE + ``` + Test-AzdoOrganizationStageChooser + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationStageChooser +#> function Test-AzdoOrganizationStageChooser { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } +Write-verbose 'Not connected to Azure DevOps' - $StageChooser = (Get-ADOPSOrganizationPipelineSettings).disableStageChooser + $result = (Get-ADOPSOrganizationPipelineSettings).disableStageChooser if ($result) { $resultMarkdown = "Well done. Users will not be able to select stages to skip from the Queue Pipeline panel." @@ -19,7 +36,7 @@ function Test-AzdoOrganizationStageChooser { $result = $true } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.ps1 index 1098bcc84..367479877 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.ps1 @@ -1,12 +1,32 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of Azure Artifacts storage, Azure DevOps provides 2 GiB of free storage for each organization. + Once your organization reaches the maximum storage limit, you won't be able to publish new artifacts. + To continue, you can either delete some of your existing artifacts or increase your storage limit. + + https://learn.microsoft.com/en-us/azure/devops/artifacts/how-to/delete-and-recover-packages?view=azure-devops&tabs=nuget#delete-packages-automatically-with-retention-policies + https://learn.microsoft.com/en-us/azure/devops/organizations/billing/set-up-billing-for-your-organization-vs?view=azure-devops#set-up-billing + https://learn.microsoft.com/en-us/azure/devops/artifacts/reference/limits?view=azure-devops + +.EXAMPLE + ``` + Test-AzdoOrganizationStorageUsage + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationStorageUsage +#> function Test-AzdoOrganizationStorageUsage { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } + Write-verbose 'Not connected to Azure DevOps' $StorageUsage = Get-ADOPSOrganizationCommerceMeterUsage -MeterId '3efc2e47-d73e-4213-8368-3a8723ceb1cc' $availableQuantity = $StorageUsage.availableQuantity @@ -14,9 +34,8 @@ function Test-AzdoOrganizationStorageUsage { if ($availableQuantity -lt [double]::Parse('0.1')) { $resultMarkdown = "Your storage is exceeding the usage limit or close to. '$availableQuantity' GB available." $result = $false - } - else { - $resultMarkdown = + } else { + $resultMarkdown = @' Well done. You are not exceeding or approaching your storage usage limit. Current usage: {0} GB @@ -25,9 +44,9 @@ function Test-AzdoOrganizationStorageUsage { $result = $true } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' return $result } \ No newline at end of file diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 new file mode 100644 index 000000000..15de35985 --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 @@ -0,0 +1,41 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the ability to install and run tasks from the Marketplace, which gives you greater control over the code that executes in a pipeline. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#prevent-malicious-code-execution + +.EXAMPLE + ``` + Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask +#> +function Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask { + [CmdletBinding()] + [OutputType([bool])] + param() + +Write-verbose 'Not connected to Azure DevOps' + + $result = (Get-ADOPSOrganizationPipelineSettings).disableMarketplaceTasksVar + + if ($result) { + $resultMarkdown = "Well done. The ability to install and run tasks from the Marketplace has been restricted." + } + else { + $resultMarkdown = "It is allowed to install and run tasks from the Marketplace." + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 deleted file mode 100644 index 5f591043c..000000000 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).disableMarketplaceTasksVar - - if ($result) { - $resultMarkdown = "Well done. The ability to install and run tasks from the Marketplace has been restricted." - } - else { - $resultMarkdown = "It is allowed to install and run tasks from the Marketplace." - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 new file mode 100644 index 000000000..214fe6398 --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 @@ -0,0 +1,61 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status if Node 6 is allowed on hosted agents. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#prevent-malicious-code-execution + https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2022/no-node-6-on-hosted-agents + +.EXAMPLE + ``` + Test-AzdoOrganizationTaskRestrictionsDisableNode6Task + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task +#> +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of when you sign in to the web portal of a Microsoft Entra ID-backed organization, + Microsoft Entra ID always performs validation for any Conditional Access Policies (CAPs) set by tenant administrators. + + https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops + +.EXAMPLE + ``` + Test-AzdoExternalGuestAccess + ``` + + Returns a boolean depending on the configuration. + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw +.LINK +#> +function Test-AzdoOrganizationTaskRestrictionsDisableNode6Task { + [CmdletBinding()] + [OutputType([bool])] + param() + +Write-verbose 'Not connected to Azure DevOps' + + $result = (Get-ADOPSOrganizationPipelineSettings).disableNode6TasksVar + + if ($result) { + $resultMarkdown = "Well done. Pipelines will fail if they utilize a task with a Node 6 execution handler." + } + else { + $resultMarkdown = "Pipeliens may utilize a task with Node 6 execution handler." + } + + # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 deleted file mode 100644 index 1aeee8cb7..000000000 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $result = (Get-ADOPSOrganizationPipelineSettings).disableNode6TasksVar - - if ($result) { - $resultMarkdown = "Well done. Pipelines will fail if they utilize a task with a Node 6 execution handler." - } - else { - $resultMarkdown = "Pipeliens may utilize a task with Node 6 execution handler." - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 index 50429e4bf..50827e698 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 @@ -1,12 +1,29 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status if the Enable shell tasks arguments validation setting that validates argument parameters for built-in shell tasks to check for inputs that can inject commands into scripts. + The check ensures that the shell correctly executes characters like semicolons, quotes, and parentheses. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#shellTasksValidation + +.EXAMPLE + ``` + Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation +#> function Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } +Write-verbose 'Not connected to Azure DevOps' $result = (Get-ADOPSOrganizationPipelineSettings).enableShellTasksArgsSanitizing @@ -17,7 +34,7 @@ function Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation { $resultMarkdown = "Argument parameters for built-in shell tasks may inject commands into scripts." } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md b/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.md rename to powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 similarity index 58% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 rename to powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 index ae9f5bd50..100b244d8 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 @@ -1,12 +1,28 @@ -function Test-AzdoOrganizationTriggerPullRequestGitHubRepositories { +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status if Azure Pipelines can automatically build and validate every pull request and commit to your GitHub repository. + + https://learn.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#validate-contributions-from-forks + +.EXAMPLE + ``` + Test-AzdoOrganizationTriggerPullRequestGitHubRepository + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoOrganizationTriggerPullRequestGitHubRepository +#> +function Test-AzdoOrganizationTriggerPullRequestGitHubRepository { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } + Write-verbose 'Not connected to Azure DevOps' $settings = Get-ADOPSOrganizationPipelineSettings $result = $settings.forkProtectionEnabled @@ -14,14 +30,12 @@ function Test-AzdoOrganizationTriggerPullRequestGitHubRepositories { if ($result) { if ($settings.requireCommentsForNonTeamMemberAndNonContributors) { $AdditionalInfo = 'Only on pull requests from non-team members and contributors' - } - elseif ($settings.requireCommentsForNonTeamMembersOnly) { + } elseif ($settings.requireCommentsForNonTeamMembersOnly) { $AdditionalInfo = 'Only on pull requests from non-team members' - } - else { + } else { $AdditionalInfo = 'On all pull requests' } - + $data = @' Prevent pipelines from making secrets available to fork builds is set to '{0}'\ Prevent pipelines from making fork builds have the same permissions as regular builds is set to '{1}'\ @@ -29,14 +43,11 @@ function Test-AzdoOrganizationTriggerPullRequestGitHubRepositories { '@ -f $settings.enforceNoAccessToSecretsFromForks, $settings.enforceJobAuthScopeForForks, $settings.isCommentRequiredForPullRequest, $AdditionalInfo $resultMarkdown = "Well done. You have configured building pull requests from forked GitHub repositories according to your requirements. $data" - } - else { + } else { $resultMarkdown = "No limits building pull requests from forked GitHub repositories have been configured." } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' return $result } diff --git a/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrators.md b/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrator.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrators.md rename to powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrator.md diff --git a/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrators.ps1 b/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrator.ps1 similarity index 73% rename from powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrators.ps1 rename to powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrator.ps1 index af76c4b0f..57ea937f5 100644 --- a/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrators.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrator.ps1 @@ -1,12 +1,28 @@ -function Test-AzdoProjectCollectionAdministrators { +<# +.SYNOPSIS + Returns a list of all Project Collection Administrators. + +.DESCRIPTION + Checks the status of how many Project Collection Administrators that are assigned to your Azure DevOps organisation. + + https://learn.microsoft.com/en-us/azure/devops/organizations/security/about-permissions?view=azure-devops&tabs=preview-page#permissions + +.EXAMPLE + ``` + Test-AzdoProjectCollectionAdministrator + ``` + + Returns a list of all Project Collection Administrators. + +.LINK + https://maester.dev/docs/commands/Test-AzdoProjectCollectionAdministrator +#> +function Test-AzdoProjectCollectionAdministrator { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } + Write-verbose 'Not connected to Azure DevOps' function Get-NestedAdoMembership { param ( @@ -20,8 +36,7 @@ function Test-AzdoProjectCollectionAdministrators { Write-Verbose "Processing member '$($_.DisplayName)' - Descriptor '$($_.Descriptor)'" Get-NestedAdoMembership -Member $_ } - } - else { + } else { Write-output $Member } } @@ -45,8 +60,7 @@ function Test-AzdoProjectCollectionAdministrators { Get-NestedAdoMembership -Member $_ | Foreach-object { if ($_.descriptor -notin $UniqueUsersWithPCA.descriptor) { $UniqueUsersWithPCA.Add($_) | Out-Null - } - else { + } else { Write-Verbose "$($_.subjectKind) - $($_.displayname) - $($_.descriptor) - has already been added." } } @@ -56,24 +70,20 @@ function Test-AzdoProjectCollectionAdministrators { if ($UniqueUsersWithPCA.Count -ge 4) { $result = $false $resultMarkdown = "Restrict direct user access ('$($UniqueUsersWithPCA.Count)') to Project Collection Administrators role. The role holds the highest authority within an organization or project collection. Members can Perform all operations for the entire collection, Manage settings, policies, and processes for the organization, create and manage all projects and extensions.`n`n%TestResult%" - } - else { + } else { $result = $true $resultMarkdown = "Well done. Less than 4 users/service accounts are directly assigned to the Project Collection Administrators role.`n`n%TestResult%" } - $UniqueUsersWithPCA | ForEach-Object -Begin { + $UniqueUsersWithPCA | ForEach-Object -Begin { $markdown = "| DisplayName | Alias | E-mail |`n" - $markdown += "| --- | --- | --- |`n" + $markdown += "| --- | --- | --- |`n" } -Process { - $markdown += "| $($_.displayName) | $($_.directoryAlias) | $($_.mailAddress) |`n" + $markdown += "| $($_.displayName) | $($_.directoryAlias) | $($_.mailAddress) |`n" } -end { $resultMarkdown = $resultMarkdown -replace '%TestResult%', $markdown } - - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' return $result } \ No newline at end of file diff --git a/powershell/public/maester/azdo/Test-AzdoPublicProjects.md b/powershell/public/maester/azdo/Test-AzdoPublicProject.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoPublicProjects.md rename to powershell/public/maester/azdo/Test-AzdoPublicProject.md diff --git a/powershell/public/maester/azdo/Test-AzdoPublicProject.ps1 b/powershell/public/maester/azdo/Test-AzdoPublicProject.ps1 new file mode 100644 index 000000000..8a4f5966c --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoPublicProject.ps1 @@ -0,0 +1,40 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of public projects within your Azure DevOps Organisation. + + https://aka.ms/vsts-anon-access + https://learn.microsoft.com/en-us/azure/devops/organizations/projects/make-project-public?view=azure-devops + +.EXAMPLE + ``` + Test-AzdoPublicProject + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoPublicProject +#> +function Test-AzdoPublicProject { + [CmdletBinding()] + [OutputType([bool])] + param() + + Write-verbose 'Not connected to Azure DevOps' + + $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' + $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.AllowAnonymousAccess' + $result = $Policy.effectiveValue + if ($result) { + $resultMarkdown = "Your Azure DevOps tenant allows the creation and use of public projects" + } else { + $resultMarkdown = "Well done. Your tenant has disabled the use of public projects" + } + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} \ No newline at end of file diff --git a/powershell/public/maester/azdo/Test-AzdoPublicProjects.ps1 b/powershell/public/maester/azdo/Test-AzdoPublicProjects.ps1 deleted file mode 100644 index d677b1f7f..000000000 --- a/powershell/public/maester/azdo/Test-AzdoPublicProjects.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -function Test-AzdoPublicProjects { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $SecurityPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'Security' - $Policy = $SecurityPolicies.policy | where-object -property name -eq 'Policy.AllowAnonymousAccess' - $result = $Policy.effectiveValue - if ($result) { - $resultMarkdown = "Your Azure DevOps tenant allows the creation and use of public projects" - } - else { - $resultMarkdown = "Well done. Your tenant has disabled the use of public projects" - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' - - return $result -} \ No newline at end of file diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.md b/powershell/public/maester/azdo/Test-AzdoResourceUsageProject.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.md rename to powershell/public/maester/azdo/Test-AzdoResourceUsageProject.md diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageProject.ps1 b/powershell/public/maester/azdo/Test-AzdoResourceUsageProject.ps1 new file mode 100644 index 000000000..1d6089872 --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoResourceUsageProject.ps1 @@ -0,0 +1,42 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of limitation regarding projects in Azure DevOps, As it supports up to 1,000 projects within an organization + + https://learn.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops + +.EXAMPLE + ``` + Test-AzdoResourceUsageProject + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoResourceUsageProject +#> +function Test-AzdoResourceUsageProject { + [CmdletBinding()] + [OutputType([bool])] + param() + + Write-verbose 'Not connected to Azure DevOps' + + $Projects = (Get-ADOPSResourceUsage).Projects + + $CurrentUsage = $($Projects.count / $Projects.limit).Tostring("P") + + if ($($Projects.count / $Projects.limit) -gt 0.9) { + $resultMarkdown = "Project Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" + $result = $false + } else { + $resultMarkdown = "Well done. Project Resource Usage limit is at $CurrentUsage" + $result = $true + } + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.ps1 b/powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.ps1 deleted file mode 100644 index 0c7304e3e..000000000 --- a/powershell/public/maester/azdo/Test-AzdoResourceUsageProjects.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -function Test-AzdoResourceUsageProjects { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $Projects = (Get-ADOPSResourceUsage).Projects - - $CurrentUsage = $($Projects.count / $Projects.limit).Tostring("P") - - if ($($Projects.count / $Projects.limit) -gt 0.9) { - $resultMarkdown = "Project Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" - $result = $false - } - else { - $resultMarkdown = "Well done. Project Resource Usage limit is at $CurrentUsage" - $result = $true - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.md b/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.md rename to powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.md diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.ps1 b/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.ps1 new file mode 100644 index 000000000..bad04564f --- /dev/null +++ b/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.ps1 @@ -0,0 +1,44 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of the usage of tag definitions in Azure DevOps, as Azure DevOps supports up to 150,000 tag definitions per organization or collection. + + https://learn.microsoft.com/en-us/azure/devops/organizations/settings/work/object-limits?view=azure-devops + +.EXAMPLE + ``` + Test-AzdoResourceUsageWorkItemTag + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoResourceUsageWorkItemTag +#> +function Test-AzdoResourceUsageWorkItemTag { + [CmdletBinding()] + [OutputType([bool])] + param() + + Write-verbose 'Not connected to Azure DevOps' + + $WorkItemTags = (Get-ADOPSResourceUsage).'Work Item Tags' + + $CurrentUsage = $($WorkItemTags.count / $WorkItemTags.limit).Tostring("P") + + if ($($WorkItemTags.count / $WorkItemTags.limit) -gt 0.9) { + $resultMarkdown = "Work Item Tags Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" + $result = $false + } else { + $resultMarkdown = "Well done. Work Item Tags Resource Usage limit is at $CurrentUsage" + $result = $true + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 b/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 deleted file mode 100644 index 3934ebb37..000000000 --- a/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTags.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -function Test-AzdoResourceUsageWorkItemTags { - [CmdletBinding()] - [OutputType([bool])] - param() - - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } - - $WorkItemTags = (Get-ADOPSResourceUsage).'Work Item Tags' - - $CurrentUsage = $($WorkItemTags.count / $WorkItemTags.limit).Tostring("P") - - if ($($WorkItemTags.count / $WorkItemTags.limit) -gt 0.9) { - $resultMarkdown = "Work Item Tags Resource Usage limit is greater than 90% - Current usage: $CurrentUsage" - $result = $false - } - else { - $resultMarkdown = "Well done. Work Item Tags Resource Usage limit is at $CurrentUsage" - $result = $true - } - - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' - - return $result -} diff --git a/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.md b/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.md index faad2de75..bd9bf2ea7 100644 --- a/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.md +++ b/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.md @@ -15,3 +15,4 @@ Users can no longer user SSH to connect to Azure DevOps. #### Related links * [Learn - Use SSH key authentication](https://aka.ms/vstspolicyssh) +* [Learn - Authentication with Azure Repos](https://learn.microsoft.com/en-us/azure/devops/repos/git/auth-overview?view=azure-devops&source=recommendations&tabs=Windows) diff --git a/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.ps1 b/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.ps1 index 6e1d9995a..6f55b23ae 100644 --- a/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.ps1 @@ -1,26 +1,42 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of the possibility to use SSH keys to connect to Azure DevOps. + + https://aka.ms/vstspolicyssh + https://learn.microsoft.com/en-us/azure/devops/repos/git/auth-overview?view=azure-devops&source=recommendations&tabs=Windows + +.EXAMPLE + ``` + Test-AzdoSSHAuthentication + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoSSHAuthentication +#> function Test-AzdoSSHAuthentication { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } + Write-verbose 'Not connected to Azure DevOps' $ApplicationPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'ApplicationConnection' $Policy = $ApplicationPolicies.policy | where-object -property name -eq 'Policy.DisallowSecureShell' $result = $Policy.effectiveValue if ($result) { $resultMarkdown = "Your tenant allows developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" - } - else { + } else { $resultMarkdown = "Well done. Your tenant do not allow developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' return $result } \ No newline at end of file diff --git a/powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 b/powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 index df2b8e46c..fc53535de 100644 --- a/powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 @@ -1,26 +1,43 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + Checks the status of Third-party application access via OAuth. + + https://aka.ms/vstspolicyoauth + https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/azure-devops-oauth?view=azure-devops + + +.EXAMPLE + ``` + Test-AzdoThirdPartyAccessViaOauth + ``` + + Returns a boolean depending on the configuration. + +.LINK + https://maester.dev/docs/commands/Test-AzdoThirdPartyAccessViaOauth +#> function Test-AzdoThirdPartyAccessViaOauth { [CmdletBinding()] [OutputType([bool])] param() - if ($null -eq (Get-ADOPSConnection)['Organization']) { - Add-MtTestResultDetail -SkippedBecause Custom -SkippedCustomReason 'Not connected to Azure DevOps' - return $null - } + Write-verbose 'Not connected to Azure DevOps' $ApplicationPolicies = Get-ADOPSOrganizationPolicy -PolicyCategory 'ApplicationConnection' $Policy = $ApplicationPolicies.policy | where-object -property name -eq 'Policy.DisallowOAuthAuthentication' $result = $Policy.effectiveValue if ($result) { $resultMarkdown = "Your tenant have not restricted Azure DevOps OAuth apps to access resources in your organization through OAuth." - } - else { + } else { $resultMarkdown = "Well done. Your tenant has restricted Azure DevOps OAuth apps to access resources in your organization through OAuth." } - # $Description = Get-Content $PSScriptRoot\$($MyInvocation.MyCommand.Name).md -Raw - Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' return $result } \ No newline at end of file diff --git a/tests/Maester/Azdo/Test-Azdo.Tests.ps1 b/tests/Maester/Azdo/Test-Azdo.Tests.ps1 index fca6f182a..be1fcd042 100644 --- a/tests/Maester/Azdo/Test-Azdo.Tests.ps1 +++ b/tests/Maester/Azdo/Test-Azdo.Tests.ps1 @@ -2,33 +2,32 @@ BeforeAll { . $PSScriptRoot/Test-AzdoAllowRequestAccessToken.ps1 . $PSScriptRoot/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 . $PSScriptRoot/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 - . $PSScriptRoot/Test-AzdoAuditStreams.ps1 + . $PSScriptRoot/Test-AzdoAuditStream.ps1 . $PSScriptRoot/Test-AzdoEnforceAADConditionalAccess.ps1 . $PSScriptRoot/Test-AzdoExternalGuestAccess.ps1 . $PSScriptRoot/Test-AzdoFeedbackCollection.ps1 - . $PSScriptRoot/Test-AzdoLogAuditEvents.ps1 - . $PSScriptRoot/Test-AzdoOrganizationArtifactFeed.ps1 - . $PSScriptRoot/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects.ps1 + . $PSScriptRoot/Test-AzdoLogAuditEvent.ps1 + . $PSScriptRoot/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 . $PSScriptRoot/Test-AzdoOrganizationBadgesArePrivate.ps1 - . $PSScriptRoot/Test-AzdoOrganizationCreationClassicBuildPipelines.ps1 - . $PSScriptRoot/Test-AzdoOrganizationCreationClassicReleasePipelines.ps1 - . $PSScriptRoot/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines.ps1 - . $PSScriptRoot/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines.ps1 + . $PSScriptRoot/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 + . $PSScriptRoot/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 + . $PSScriptRoot/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 + . $PSScriptRoot/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 . $PSScriptRoot/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 . $PSScriptRoot/Test-AzdoOrganizationOwner.ps1 - . $PSScriptRoot/Test-AzdoOrganizationProtectAccessToRepositories.ps1 - . $PSScriptRoot/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos.ps1 - . $PSScriptRoot/Test-AzdoOrganizationRepositorySettingsGravatarImages.ps1 + . $PSScriptRoot/Test-AzdoOrganizationProtectAccessToRepository.ps1 + . $PSScriptRoot/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 + . $PSScriptRoot/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 . $PSScriptRoot/Test-AzdoOrganizationStageChooser.ps1 . $PSScriptRoot/Test-AzdoOrganizationStorageUsage.ps1 - . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks.ps1 - . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks.ps1 + . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 + . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 - . $PSScriptRoot/Test-AzdoOrganizationTriggerPullRequestGitHubRepositories.ps1 - . $PSScriptRoot/Test-AzdoProjectCollectionAdministrators.ps1 - . $PSScriptRoot/Test-AzdoPublicProjects.ps1 - . $PSScriptRoot/Test-AzdoResourceUsageProjects.ps1 - . $PSScriptRoot/Test-AzdoResourceUsageWorkItemTags.ps1 + . $PSScriptRoot/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 + . $PSScriptRoot/Test-AzdoProjectCollectionAdministrator.ps1 + . $PSScriptRoot/Test-AzdoPublicProject.ps1 + . $PSScriptRoot/Test-AzdoResourceUsageProject.ps1 + . $PSScriptRoot/Test-AzdoResourceUsageWorkItemTag.ps1 . $PSScriptRoot/Test-AzdoSSHAuthentication.ps1 . $PSScriptRoot/Test-AzdoThirdPartyAccessViaOauth.ps1 } @@ -37,6 +36,7 @@ Describe "Azure DevOps" -Tag "Azure DevOps Security" { It "AZDO.1000: Azure DevOps OAuth apps can access resources in your organization through OAuth. See https://aka.ms/vstspolicyoauth" -Tag "AZDO.1000" { Test-AzdoThirdPartyAccessViaOauth | Should -Be $false -Because "Your tenant should restrict Azure DevOps OAuth apps to access resources in your organization through OAuth." + } It "AZDO.1001: Identities can connect to your organization's Git repos through SSH. See https://aka.ms/vstspolicyssh" -Tag "AZDO.1001" { @@ -153,7 +153,7 @@ Describe "Azure DevOps" -Tag "Azure DevOps Security" { Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks | Should -Be $false -Because "Disable the ability to install and run tasks from the Marketplace, which gives you greater control over the code that executes in a pipeline." } - + It "AZDO.1024: Disable Node 6 tasks. See https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2022/no-node-6-on-hosted-agents" -Tag "AZDO.1024" { Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks | Should -Be $true -Because "With this enabled, pipelines will fail if they utilize a task with a Node 6 execution handler." From 0ba07d87e0f78d23546326e8a404c6c3124a9dab Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:29:40 -0500 Subject: [PATCH 08/22] Fix typos and improve formatting in documentation Corrected typos and formatting in the documentation. --- .../azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md b/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md index 37ec1b6f4..6996eb5c3 100644 --- a/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md +++ b/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md @@ -6,10 +6,10 @@ However, Project Collection Administrators (PCAs) can still add new users to the Additionally, if a user is already a member of the organization, Project and Team Administrators can add that user to specific projects. #### Remediation action: -Disable the policy to stops these invitations. +Disable the policy to stop these invitations. 1. Sign in to your organization 2. Choose Organization settings. -3. Select Policies, locate the Allow team and project administrators to invite new users policy and toggle it to off. +3. Select Policies, locate the **Allow team and project administrators to invite new users** policy and toggle it to off. 4. Now, only Project Collection Administrators can invite new users to Azure DevOps. > Project and Team Administrators can directly add users to their projects through the permissions blade. However, if they attempt to add users through the Add Users button located in the Organization settings > Users section, it's not visible to them. Adding a user directly through Project settings > Permissions doesn't result in the user appearing automatically in the Organization settings > Users list. For the user to be reflected in the Users list, they must sign in to the system. From c12ab8b0b6816fce148ef2ae42f5b1a6bb1b4c06 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:34:06 -0500 Subject: [PATCH 09/22] Refine grammar in Test-AzdoArtifactsExternalPackageProtectionToken.md Removed unnecessary commas and spaces. Added line breaks for MD linting. --- .../Test-AzdoArtifactsExternalPackageProtectionToken.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md b/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md index d2a0a3281..e2c05112b 100644 --- a/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md +++ b/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md @@ -1,11 +1,13 @@ -Externally sourced package versions SHOULD BE manually approved for internal use, to prevent malicious packages from a public registry being inadvertently consumed. +Externally sourced package versions SHOULD BE manually approved for internal use to prevent malicious packages from a public registry being inadvertently consumed. -Rationale: Previously, Azure Artifacts feeds presented package versions from all of its upstream sources. This includes package versions that were originally pushed to an Azure Artifacts feed (internally sourced) and package versions from common public repositories like npmjs.com , NuGet.org , Maven Central, and PyPI (externally sourced). +Rationale: Previously, Azure Artifacts feeds presented package versions from all of its upstream sources. This includes package versions that were originally pushed to an Azure Artifacts feed (internally sourced) and package versions from common public repositories like npmjs.com, NuGet.org, Maven Central, and PyPI (externally sourced). Configure a policy for additional security for your private feeds by limiting access to externally sourced packages when internally sources packages are already present. This provides a new layer of security, which prevents malicious packages from a public registry being inadvertently consumed. These changes will not affect any package versions that are already in use or cached in your feed. #### Remediation action: + Enable the policy to opt-in for additional protective behavior. + 1. Sign in to your organization 2. Choose Organization settings. 3. Click on policies under the security section From bdfa2ff3e9beb2d38e16081af160c9597d9e3fc4 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:35:09 -0500 Subject: [PATCH 10/22] Fix typo in description of security policy check --- .../azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 b/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 index 6798be7b4..f29733aa4 100644 --- a/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 @@ -3,7 +3,7 @@ Returns a boolean depending on the configuration. .DESCRIPTION - Checks the policy for additional security for your private feeds by limiting access to externally sourced packages when internally sources packages are already present. + Checks the policy for additional security for your private feeds by limiting access to externally sourced packages when internally sourced packages are already present. This provides a new layer of security, which prevents malicious packages from a public registry being inadvertently consumed. These changes will not affect any package versions that are already in use or cached in your feed. From f4f68c7149f2953017a21648b73c2e908772c9df Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:37:40 -0500 Subject: [PATCH 11/22] Fix grammar and punctuation in Test-AzdoAuditStream.md Corrected grammatical errors and added punctuation for clarity. --- powershell/public/maester/azdo/Test-AzdoAuditStream.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powershell/public/maester/azdo/Test-AzdoAuditStream.md b/powershell/public/maester/azdo/Test-AzdoAuditStream.md index 5443440c1..567f47f10 100644 --- a/powershell/public/maester/azdo/Test-AzdoAuditStream.md +++ b/powershell/public/maester/azdo/Test-AzdoAuditStream.md @@ -1,12 +1,12 @@ -Audit logs SHOULD BE retained according to your organizations needs and protected from purging. +Audit logs SHOULD BE retained according to your organization's needs, and protected from purging. -Rationale: Send auditing data to other Security Incident and Event Management (SIEM) tools and open new possibilities, such as the ability to trigger alerts for specific events, create views on auditing data, and perform anomaly detection. Setting up a stream also allows you to store more than 90-days worth of auditing data, which is the maximum amount of data that Azure DevOps keeps for your organizations. +Rationale: Send auditing data to other Security Incident and Event Management (SIEM) tools and open new possibilities, such as the ability to trigger alerts for specific events, create views on auditing data, and perform anomaly detection. Setting up a stream also allows you to store more than 90-days of auditing data, which is the maximum amount of data that Azure DevOps keeps for your organizations. #### Remediation action: -Create an audit stream, which sends data to other locations for further processing. +Create an audit stream, which sends data to other locations for further processing. -1. Sign in to your organization +1. Sign in to your organization. 2. Choose Organization settings. 3. Select Auditing. > If you don't see Auditing in Organization Settings, then auditing is not currently enabled for your organization. Someone in the organization owner or Project Collection Administrators (PCAs) group must enable Auditing in Organization Policies. You will then be able to see events on the Auditing page if you have the appropriate permissions. @@ -21,4 +21,4 @@ Audit streams represent a pipeline that flows audit events from your Azure DevOp #### Related links -* [Azure DevOps Security - Create audit streaming](https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops) \ No newline at end of file +* [Azure DevOps Security - Create audit streaming](https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops) From ca1d7dad0dc1a17f6aa56b00d868b7e05ccaf2bc Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:39:16 -0500 Subject: [PATCH 12/22] Refine description for Test-AzdoAuditStream function Updated description to clarify the function's purpose and improve readability. --- powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 b/powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 index 093804ff7..e2f794b25 100644 --- a/powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 @@ -3,9 +3,9 @@ Returns a boolean depending on the configuration. .DESCRIPTION - Checks auditing data to other Security Incident and Event Management (SIEM) tools and open new possibilities, - such as the ability to trigger alerts for specific events, create views on auditing data, - and perform anomaly detection. Setting up a stream also allows you to store more than 90-days worth of auditing data, + Sends auditing data to Security Incident and Event Management (SIEM) tools and open new possibilities, + such as the ability to trigger alerts for specific events, create views on auditing data, and perform + anomaly detection. Setting up a stream also allows you to store more than 90-days of auditing data, which is the maximum amount of data that Azure DevOps keeps for your organizations. https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops From 6005abdabb87fa8538beda0cdeeb5d92de42570b Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:41:24 -0500 Subject: [PATCH 13/22] Fix grammar and clarity in Test-AzdoEnforceAADConditionalAccess.md Corrected grammatical errors and improved clarity in remediation instructions. --- .../maester/azdo/Test-AzdoEnforceAADConditionalAccess.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.md b/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.md index 733377b63..83c7551e9 100644 --- a/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.md +++ b/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.md @@ -3,11 +3,11 @@ Conditional Access Policies SHOULD BE configured for Microsoft Entra ID-backed o Rationale: When you sign in to the web portal of a Microsoft Entra ID-backed organization, Microsoft Entra ID always performs validation for any Conditional Access Policies (CAPs) set by tenant administrators. #### Remediation action: -Disable the policy to stops these requests and notifications. +Disable the policy to stop these requests and notifications. 1. Sign in to your organization 2. Choose Organization settings. 3. Select Policies, and then toggle your policy to on or off as needed. - 1. If the “Enable IP Conditional Access policy Validation” organization policy is enabled, we check IP fencing policies on both web and non-interactive flows, such as non-Microsoft client flows like using a PAT with git operations. + 1. If the “Enable IP Conditional Access policy Validation” organization policy is enabled, we check IP fencing policies on both web and non-interactive flows, such as non-Microsoft client flows (e.g., using a PAT with git operations). 2. Sign-in policies might be enforced for PATs as well. Using PATs to make Microsoft Entra ID calls requires adherence to any sign-in policies that are set. For example, if a sign-in policy requires that a user sign in every seven days, you must also sign in every seven days to continue using PATs for Microsoft Entra ID requests. > We support MFA policies on web flows only. For non-interactive flows, if they don't satisfy the conditional access policy, the user isn't prompted for MFA and gets blocked instead. > We support IP-fencing conditional access policies (CAPs) for both IPv4 and IPv6 addresses. If your IPv6 address is being blocked, ensure that the tenant administrator configured CAPs to allow your IPv6 address. Additionally, consider including the IPv4-mapped address for any default IPv6 address in all CAP conditions. From 0c4b342a9b46d16754325cd629a2fefd4b746e83 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:49:17 -0500 Subject: [PATCH 14/22] Update README.md with clearer test descriptions Clarified the purpose of the Azure DevOps tests and updated the reference link. --- tests/Maester/Azdo/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Maester/Azdo/README.md b/tests/Maester/Azdo/README.md index 8ac4cf643..be8bb525c 100644 --- a/tests/Maester/Azdo/README.md +++ b/tests/Maester/Azdo/README.md @@ -1,4 +1,5 @@ # Azure DevOps Tests - Preview -Azure DevOps tests we will perform a healthcheck towards your Azure DevOps organization. -The base of the tests will be from https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-overview?view=azure-devops and recommendations/considerations from professionals in the field. \ No newline at end of file +These tests will perform a healthcheck towards your Azure DevOps organization. + +The base of the tests will be from [Make your Azure DevOps secure](https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-overview?view=azure-devops) (Microsoft Learn) and recommendations/considerations from professionals in the field. From 25dcaffab7d3c99b9739e9b5bc6efc32f362159c Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:56:35 -0500 Subject: [PATCH 15/22] Update powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ...OrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 index ef9480db5..e76914eff 100644 --- a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 +++ b/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 @@ -3,7 +3,7 @@ Returns a boolean depending on the configuration. .DESCRIPTION - Checks IF YAML & build pipelines have restricted access to only those repositories that are in the same project as the pipeline.. + Checks IF YAML & build pipelines have restricted access to only those repositories that are in the same project as the pipeline. https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope From acd7ecc718c3212fa1833941c21029253c4e0dbf Mon Sep 17 00:00:00 2001 From: sebastian claesson Date: Fri, 23 Jan 2026 12:17:10 +0100 Subject: [PATCH 16/22] Renaming Azdo to AzureDevOps --- .../azdo/Test-AzdoAllowRequestAccessToken.md | 18 -- ...doAllowTeamAdminsInvitationsAccessToken.md | 19 -- .../Test-AzdoAllowRequestAccessToken.ps1 | 0 ...oAllowTeamAdminsInvitationsAccessToken.ps1 | 0 ...ArtifactsExternalPackageProtectionToken.md | 0 ...rtifactsExternalPackageProtectionToken.ps1 | 0 .../Test-AzdoAuditStream.md | 0 .../Test-AzdoAuditStream.ps1 | 0 .../Test-AzdoEnforceAADConditionalAccess.md | 0 .../Test-AzdoEnforceAADConditionalAccess.ps1 | 0 .../Test-AzdoExternalGuestAccess.md | 0 .../Test-AzdoExternalGuestAccess.ps1 | 0 .../Test-AzdoFeedbackCollection.md | 0 .../Test-AzdoFeedbackCollection.ps1 | 0 .../Test-AzdoLogAuditEvent.md | 0 .../Test-AzdoLogAuditEvent.ps1 | 0 ...ticEnrollmentAdvancedSecurityNewProject.md | 0 ...icEnrollmentAdvancedSecurityNewProject.ps1 | 0 .../Test-AzdoOrganizationBadgesArePrivate.md | 0 .../Test-AzdoOrganizationBadgesArePrivate.ps1 | 0 ...rganizationCreationClassicBuildPipeline.md | 0 ...ganizationCreationClassicBuildPipeline.ps1 | 0 ...anizationCreationClassicReleasePipeline.md | 0 ...nizationCreationClassicReleasePipeline.ps1 | 0 ...JobAuthorizationScopeNonReleasePipeline.md | 0 ...obAuthorizationScopeNonReleasePipeline.ps1 | 0 ...mitJobAuthorizationScopeReleasePipeline.md | 0 ...itJobAuthorizationScopeReleasePipeline.ps1 | 0 ...doOrganizationLimitVariablesAtQueueTime.md | 0 ...oOrganizationLimitVariablesAtQueueTime.ps1 | 0 .../Test-AzdoOrganizationOwner.md | 0 .../Test-AzdoOrganizationOwner.ps1 | 0 ...doOrganizationProtectAccessToRepository.md | 0 ...oOrganizationProtectAccessToRepository.ps1 | 0 ...positorySettingsDisableCreationTFVCRepo.md | 0 ...ositorySettingsDisableCreationTFVCRepo.ps1 | 0 ...nizationRepositorySettingsGravatarImage.md | 0 ...izationRepositorySettingsGravatarImage.ps1 | 0 .../Test-AzdoOrganizationStageChooser.md | 0 .../Test-AzdoOrganizationStageChooser.ps1 | 0 .../Test-AzdoOrganizationStorageUsage.md | 0 .../Test-AzdoOrganizationStorageUsage.ps1 | 0 ...nTaskRestrictionsDisableMarketplaceTask.md | 0 ...TaskRestrictionsDisableMarketplaceTask.ps1 | 0 ...izationTaskRestrictionsDisableNode6Task.md | 0 ...zationTaskRestrictionsDisableNode6Task.ps1 | 0 ...RestrictionsShellTaskArgumentValidation.md | 0 ...estrictionsShellTaskArgumentValidation.ps1 | 0 ...ationTriggerPullRequestGitHubRepository.md | 0 ...tionTriggerPullRequestGitHubRepository.ps1 | 0 ...Test-AzdoProjectCollectionAdministrator.md | 0 ...est-AzdoProjectCollectionAdministrator.ps1 | 0 .../Test-AzdoPublicProject.md | 0 .../Test-AzdoPublicProject.ps1 | 0 .../Test-AzdoResourceUsageProject.md | 0 .../Test-AzdoResourceUsageProject.ps1 | 0 .../Test-AzdoResourceUsageWorkItemTag.md | 0 .../Test-AzdoResourceUsageWorkItemTag.ps1 | 0 .../Test-AzdoSSHAuthentication.md | 0 .../Test-AzdoSSHAuthentication.ps1 | 0 .../Test-AzdoThirdPartyAccessViaOauth.md | 0 .../Test-AzdoThirdPartyAccessViaOauth.ps1 | 0 tests/Maester/Azdo/Test-Azdo.Tests.ps1 | 195 ------------------ tests/Maester/{Azdo => AzureDevOps}/README.md | 0 64 files changed, 232 deletions(-) delete mode 100644 powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.md delete mode 100644 powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoAllowRequestAccessToken.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoArtifactsExternalPackageProtectionToken.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoAuditStream.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoAuditStream.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoEnforceAADConditionalAccess.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoEnforceAADConditionalAccess.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoExternalGuestAccess.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoExternalGuestAccess.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoFeedbackCollection.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoFeedbackCollection.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoLogAuditEvent.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoLogAuditEvent.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationBadgesArePrivate.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationBadgesArePrivate.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationCreationClassicBuildPipeline.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationCreationClassicReleasePipeline.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationLimitVariablesAtQueueTime.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationOwner.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationOwner.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationProtectAccessToRepository.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationProtectAccessToRepository.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationRepositorySettingsGravatarImage.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationStageChooser.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationStageChooser.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationStorageUsage.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationStorageUsage.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoProjectCollectionAdministrator.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoProjectCollectionAdministrator.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoPublicProject.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoPublicProject.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoResourceUsageProject.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoResourceUsageProject.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoResourceUsageWorkItemTag.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoResourceUsageWorkItemTag.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoSSHAuthentication.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoSSHAuthentication.ps1 (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoThirdPartyAccessViaOauth.md (100%) rename powershell/public/maester/{azdo => azuredevops}/Test-AzdoThirdPartyAccessViaOauth.ps1 (100%) delete mode 100644 tests/Maester/Azdo/Test-Azdo.Tests.ps1 rename tests/Maester/{Azdo => AzureDevOps}/README.md (100%) diff --git a/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.md b/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.md deleted file mode 100644 index 0ea27e02f..000000000 --- a/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.md +++ /dev/null @@ -1,18 +0,0 @@ -Request access to Azure DevOps by e-mail notifications to administrators SHOULD BE disabled. - -Rationale: Access control to Azure DevOps is to be a controlled process where access is granted and tracked. - -#### Remediation action: -Disable the policy to stops these requests and notifications. -1. Sign in to your organization -2. Choose Organization settings. -3. Select Policies, locate the Request Access policy and toggle it to off. -4. Provide the URL to your internal process for gaining access. Users see this URL in the error report when they try to access the organization or a project within the organization that they don't have permission to access. - -**Results:** -Users already part of the organization: If they lack permission to access a specific project, they get a 404 error. To maintain confidentiality, the 404 error doesn’t reveal whether the project exists and so doesn't provide a link to request access. -Users not part of the organization: If they attempt to access a resource, they get a 401 error, which includes a link to the configured custom URL for requesting access. - -#### Related links - -* [Azure DevOps Security - Disable your organization's Request Access policy](https://go.microsoft.com/fwlink/?linkid=2113172) diff --git a/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md b/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md deleted file mode 100644 index 6996eb5c3..000000000 --- a/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md +++ /dev/null @@ -1,19 +0,0 @@ -Access to Azure DevOps SHOULD BE a controlled process provided by the IAM team. - -Rationale: By default, all administrators can invite new users to their Azure DevOps organization. -Disabling this policy prevents Team and Project Administrators from inviting new users. -However, Project Collection Administrators (PCAs) can still add new users to the organization regardless of the policy status. -Additionally, if a user is already a member of the organization, Project and Team Administrators can add that user to specific projects. - -#### Remediation action: -Disable the policy to stop these invitations. -1. Sign in to your organization -2. Choose Organization settings. -3. Select Policies, locate the **Allow team and project administrators to invite new users** policy and toggle it to off. -4. Now, only Project Collection Administrators can invite new users to Azure DevOps. - -> Project and Team Administrators can directly add users to their projects through the permissions blade. However, if they attempt to add users through the Add Users button located in the Organization settings > Users section, it's not visible to them. Adding a user directly through Project settings > Permissions doesn't result in the user appearing automatically in the Organization settings > Users list. For the user to be reflected in the Users list, they must sign in to the system. - -#### Related links - -* [Azure DevOps Security - Restrict administrators from inviting new users](https://aka.ms/azure-devops-invitations-policy) diff --git a/powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.ps1 b/powershell/public/maester/azuredevops/Test-AzdoAllowRequestAccessToken.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoAllowRequestAccessToken.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoAllowRequestAccessToken.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 b/powershell/public/maester/azuredevops/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md b/powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.md rename to powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.md diff --git a/powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 b/powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoAuditStream.md b/powershell/public/maester/azuredevops/Test-AzdoAuditStream.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoAuditStream.md rename to powershell/public/maester/azuredevops/Test-AzdoAuditStream.md diff --git a/powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 b/powershell/public/maester/azuredevops/Test-AzdoAuditStream.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoAuditStream.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoAuditStream.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.md b/powershell/public/maester/azuredevops/Test-AzdoEnforceAADConditionalAccess.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.md rename to powershell/public/maester/azuredevops/Test-AzdoEnforceAADConditionalAccess.md diff --git a/powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 b/powershell/public/maester/azuredevops/Test-AzdoEnforceAADConditionalAccess.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoEnforceAADConditionalAccess.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoEnforceAADConditionalAccess.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.md b/powershell/public/maester/azuredevops/Test-AzdoExternalGuestAccess.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.md rename to powershell/public/maester/azuredevops/Test-AzdoExternalGuestAccess.md diff --git a/powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.ps1 b/powershell/public/maester/azuredevops/Test-AzdoExternalGuestAccess.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoExternalGuestAccess.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoExternalGuestAccess.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoFeedbackCollection.md b/powershell/public/maester/azuredevops/Test-AzdoFeedbackCollection.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoFeedbackCollection.md rename to powershell/public/maester/azuredevops/Test-AzdoFeedbackCollection.md diff --git a/powershell/public/maester/azdo/Test-AzdoFeedbackCollection.ps1 b/powershell/public/maester/azuredevops/Test-AzdoFeedbackCollection.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoFeedbackCollection.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoFeedbackCollection.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoLogAuditEvent.md b/powershell/public/maester/azuredevops/Test-AzdoLogAuditEvent.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoLogAuditEvent.md rename to powershell/public/maester/azuredevops/Test-AzdoLogAuditEvent.md diff --git a/powershell/public/maester/azdo/Test-AzdoLogAuditEvent.ps1 b/powershell/public/maester/azuredevops/Test-AzdoLogAuditEvent.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoLogAuditEvent.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoLogAuditEvent.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationBadgesArePrivate.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationBadgesArePrivate.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationBadgesArePrivate.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationBadgesArePrivate.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationBadgesArePrivate.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipeline.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipeline.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipeline.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipeline.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitVariablesAtQueueTime.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitVariablesAtQueueTime.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationOwner.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationOwner.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationOwner.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationOwner.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationOwner.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationOwner.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationOwner.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationOwner.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationProtectAccessToRepository.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationProtectAccessToRepository.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationProtectAccessToRepository.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationProtectAccessToRepository.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationProtectAccessToRepository.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsGravatarImage.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsGravatarImage.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationStageChooser.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStorageUsage.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationStorageUsage.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStorageUsage.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationStorageUsage.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationStorageUsage.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md diff --git a/powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrator.md b/powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrator.md rename to powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.md diff --git a/powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrator.ps1 b/powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoProjectCollectionAdministrator.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoPublicProject.md b/powershell/public/maester/azuredevops/Test-AzdoPublicProject.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoPublicProject.md rename to powershell/public/maester/azuredevops/Test-AzdoPublicProject.md diff --git a/powershell/public/maester/azdo/Test-AzdoPublicProject.ps1 b/powershell/public/maester/azuredevops/Test-AzdoPublicProject.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoPublicProject.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoPublicProject.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageProject.md b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageProject.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoResourceUsageProject.md rename to powershell/public/maester/azuredevops/Test-AzdoResourceUsageProject.md diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageProject.ps1 b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageProject.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoResourceUsageProject.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoResourceUsageProject.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.md b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageWorkItemTag.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.md rename to powershell/public/maester/azuredevops/Test-AzdoResourceUsageWorkItemTag.md diff --git a/powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.ps1 b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageWorkItemTag.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoResourceUsageWorkItemTag.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoResourceUsageWorkItemTag.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.md b/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoSSHAuthentication.md rename to powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.md diff --git a/powershell/public/maester/azdo/Test-AzdoSSHAuthentication.ps1 b/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoSSHAuthentication.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.ps1 diff --git a/powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.md b/powershell/public/maester/azuredevops/Test-AzdoThirdPartyAccessViaOauth.md similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.md rename to powershell/public/maester/azuredevops/Test-AzdoThirdPartyAccessViaOauth.md diff --git a/powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 b/powershell/public/maester/azuredevops/Test-AzdoThirdPartyAccessViaOauth.ps1 similarity index 100% rename from powershell/public/maester/azdo/Test-AzdoThirdPartyAccessViaOauth.ps1 rename to powershell/public/maester/azuredevops/Test-AzdoThirdPartyAccessViaOauth.ps1 diff --git a/tests/Maester/Azdo/Test-Azdo.Tests.ps1 b/tests/Maester/Azdo/Test-Azdo.Tests.ps1 deleted file mode 100644 index be1fcd042..000000000 --- a/tests/Maester/Azdo/Test-Azdo.Tests.ps1 +++ /dev/null @@ -1,195 +0,0 @@ -BeforeAll { - . $PSScriptRoot/Test-AzdoAllowRequestAccessToken.ps1 - . $PSScriptRoot/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 - . $PSScriptRoot/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 - . $PSScriptRoot/Test-AzdoAuditStream.ps1 - . $PSScriptRoot/Test-AzdoEnforceAADConditionalAccess.ps1 - . $PSScriptRoot/Test-AzdoExternalGuestAccess.ps1 - . $PSScriptRoot/Test-AzdoFeedbackCollection.ps1 - . $PSScriptRoot/Test-AzdoLogAuditEvent.ps1 - . $PSScriptRoot/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 - . $PSScriptRoot/Test-AzdoOrganizationBadgesArePrivate.ps1 - . $PSScriptRoot/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 - . $PSScriptRoot/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 - . $PSScriptRoot/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 - . $PSScriptRoot/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 - . $PSScriptRoot/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 - . $PSScriptRoot/Test-AzdoOrganizationOwner.ps1 - . $PSScriptRoot/Test-AzdoOrganizationProtectAccessToRepository.ps1 - . $PSScriptRoot/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 - . $PSScriptRoot/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 - . $PSScriptRoot/Test-AzdoOrganizationStageChooser.ps1 - . $PSScriptRoot/Test-AzdoOrganizationStorageUsage.ps1 - . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 - . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 - . $PSScriptRoot/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 - . $PSScriptRoot/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 - . $PSScriptRoot/Test-AzdoProjectCollectionAdministrator.ps1 - . $PSScriptRoot/Test-AzdoPublicProject.ps1 - . $PSScriptRoot/Test-AzdoResourceUsageProject.ps1 - . $PSScriptRoot/Test-AzdoResourceUsageWorkItemTag.ps1 - . $PSScriptRoot/Test-AzdoSSHAuthentication.ps1 - . $PSScriptRoot/Test-AzdoThirdPartyAccessViaOauth.ps1 -} - -Describe "Azure DevOps" -Tag "Azure DevOps Security" { - It "AZDO.1000: Azure DevOps OAuth apps can access resources in your organization through OAuth. See https://aka.ms/vstspolicyoauth" -Tag "AZDO.1000" { - - Test-AzdoThirdPartyAccessViaOauth | Should -Be $false -Because "Your tenant should restrict Azure DevOps OAuth apps to access resources in your organization through OAuth." - - } - - It "AZDO.1001: Identities can connect to your organization's Git repos through SSH. See https://aka.ms/vstspolicyssh" -Tag "AZDO.1001" { - - Test-AzdoSSHAuthentication | Should -Be $false -Because "Authentication towards your tenant should only be by Entra, do not allow developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" - } - - It "AZDO.1002: Log Audit Events. See https://aka.ms/log-audit-events" -Tag "AZDO.1002" { - - Test-AzdoLogAuditEvents | Should -Be $true -Because "Auditing should be enabled for Azure DevOps" - } - - It "AZDO.1003: Restricting public projects. See https://aka.ms/vsts-anon-access" -Tag "AZDO.1003" { - - Test-AzdoPublicProjects | Should -Be $false -Because "Public projects should be disabled for Azure DevOps" - } - - It "AZDO.1004: Additional protections when using public package registries. See https://aka.ms/upstreamBehaviorBlog" -Tag "AZDO.1004" { - - Test-AzdoArtifactsExternalPackageProtectionToken | Should -Be $true -Because "Limiting access to externally sourced packages when internally sources packages are already present in Azure DevOps" - } - - It "AZDO.1005: IP Conditional Access policy validation. See https://aka.ms/visual-studio-conditional-access-policy" -Tag "AZDO.1005" { - - Test-AzdoEnforceAADConditionalAccess | Should -Be $true -Because "Microsoft Entra ID should always perform validation for any Conditional Access Policies (CAPs) set by tenant administrators." - } - - It "AZDO.1006: External Users access. See https://aka.ms/vstspolicyguest" -Tag "AZDO.1006" { - - Test-AzdoExternalGuestAccess | Should -Be $false -Because "External users should not be allowed access to your Azure DevOps organization" - } - - It "AZDO.1007: Team and project administrator are allowed to invite new users. See https://aka.ms/azure-devops-invitations-policy" -Tag "AZDO.1007" { - - Test-AzdoAllowTeamAdminsInvitationsAccessToken | Should -Be $false -Because "Enrolling to your Azure DevOps organization should be a controlled process." - } - - It "AZDO.1008: Request access to Azure DevOps by e-mail notifications to administrators. See https://go.microsoft.com/fwlink/?linkid=2113172" -Tag "AZDO.1008" { - - Test-AzdoAllowRequestAccessToken | Should -Be $false -Because "You should prevent users from requesting access to your organization or projects" - } - - It "AZDO.1009: Feedback Collection. See https://aka.ms/ADOPrivacyPolicy" -Tag "AZDO.1009" { - - Test-AzdoFeedbackCollection | Should -Be $true -Because "You should have confidence that we're handling your data appropriately and for legitimate uses. Part of that assurance involves carefully restricting usage." - } - - It "AZDO.1010: Audit streaming. See https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops" -Tag "AZDO.1010" { - - Test-AzdoAuditStreams | Should -Be $true -Because "Setting up a stream also allows you to store more than 90-days worth of auditing data." - } - - It "AZDO.1011: Project Resource Limits. See https://learn.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops" -Tag "AZDO.1011" { - - Test-AzdoResourceUsageProjects | Should -Be $true -Because "Azure DevOps supports up to 1,000 projects within an organization." - } - - It "AZDO.1012: Work Items Tags Limits. See https://learn.microsoft.com/en-us/azure/devops/organizations/settings/work/object-limits?view=azure-devops" -Tag "AZDO.1012" { - - Test-AzdoResourceUsageWorkItemTags | Should -Be $true -Because "Azure DevOps supports up to 150,000 tag definitions per organization or collection." - } - - It "AZDO.1013: Organization Owner should not be an individual. See https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/change-organization-ownership?view=azure-devops" -Tag "AZDO.1013" { - - Test-AzdoOrganizationOwner | Should -Be $true -Because "Organization owners are automatically members of the 'Project Collection Administrators' group. As roles and responsibilities change, you can change the owner for your organization." - } - - It "AZDO.1014: Anonymous access to pipeline badges. See https://learn.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops&tabs=net%2Cbrowser#add-a-status-badge-to-your-repository" -Tag "AZDO.1014" { - - Test-AzdoOrganizationBadgesArePrivate | Should -Be $true -Because "Even in a private project, anonymous badge access is enabled by default. With anonymous badge access enabled, users outside your organization might be able to query information such as project names, branch names, job names, and build status through the badge status API." - } - - It "AZDO.1015: Limit variables that can be set at queue time. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#limit-variables-that-can-be-set-at-queue-time" -Tag "AZDO.1015" { - - Test-AzdoOrganizationLimitVariablesAtQueueTime | Should -Be $true -Because "Only those variables explicitly marked as 'Settable at queue time' can be set. In other words, you can set any variables at queue time unless this setting is turned on." - } - - It "AZDO.1016: Limit job authorization scope to current project for non-release pipelines. See https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope" -Tag "AZDO.1016" { - - Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines | Should -Be $true -Because "With this option enabled, you can reduce the scope of access for all classic release pipelines to the current project." - } - - It "AZDO.1017: Limit job authorization scope to current project for classic release pipelines. See https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope" -Tag "AZDO.1017" { - - Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines | Should -Be $true -Because "With this option enabled, you can reduce the scope of access for all non-release pipelines to the current project." - } - - It "AZDO.1018: Protect access to repositories in YAML pipelines. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#restrict-project-repository-and-service-connection-access" -Tag "AZDO.1018" { - - Test-AzdoOrganizationProtectAccessToRepositories | Should -Be $true -Because "Apply checks and approvals when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." - } - - It "AZDO.1019: Stage chooser. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops" -Tag "AZDO.1019" { - - Test-AzdoOrganizationStageChooser | Should -Be $false -Because "Users should not be able to select stages to skip from the Queue Pipeline panel" - } - - It "AZDO.1020: Creation of classic build pipelines. See https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines/" -Tag "AZDO.1020" { - - Test-AzdoOrganizationCreationClassicBuildPipelines | Should -Be $false -Because "Creating classic build pipelines should not be allowed." - } - - It "AZDO.1021: Creation of classic release pipelines. See https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines/" -Tag "AZDO.1021" { - - Test-AzdoOrganizationCreationClassicReleasePipelines | Should -Be $false -Because "Creating classic release pipelines should not be allowed." - } - - It "AZDO.1022: Limit building pull requests from forked GitHub repositories. See https://learn.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#validate-contributions-from-forks" -Tag "AZDO.1022" { - - Test-AzdoOrganizationTriggerPullRequestGitHubRepositories | Should -Be $true -Because "Azure Pipelines can automatically build and validate every pull request and commit to your GitHub repository. This should be configured according to your organizations requirements." - } - - It "AZDO.1023: Disable Marketplace tasks. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#prevent-malicious-code-execution" -Tag "AZDO.1023" { - - Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks | Should -Be $false -Because "Disable the ability to install and run tasks from the Marketplace, which gives you greater control over the code that executes in a pipeline." - } - - It "AZDO.1024: Disable Node 6 tasks. See https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2022/no-node-6-on-hosted-agents" -Tag "AZDO.1024" { - - Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks | Should -Be $true -Because "With this enabled, pipelines will fail if they utilize a task with a Node 6 execution handler." - } - - It "AZDO.1025: Enable shell tasks arguments validation. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#shellTasksValidation" -Tag "AZDO.1025" { - - Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation | Should -Be $true -Because "When this is enabled, argument parameters for built-in shell tasks are validated to check for inputs that can inject commands into scripts." - } - - It "AZDO.1026: Enable automatic enrollment to Advanced Security for Azure DevOps. See https://learn.microsoft.com/en-us/azure/devops/repos/security/configure-github-advanced-security-features?view=azure-devops&tabs=yaml#organization-level-onboarding" -Tag "AZDO.1026" { - - Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects | Should -Be $true -Because "Enable automatic enrollment for new git repositories to use GitHub Advanced Security for Azure DevOps. It adds GitHub Advanced Security's suite of security features to Azure Repos." - } - - It "AZDO.1027: Disable showing Gravatar images for users outside of your enterprise. See https://learn.microsoft.com/en-us/azure/devops/repos/git/repository-settings?view=azure-devops&tabs=browser#gravatar-images" -Tag "AZDO.1027" { - - Test-AzdoOrganizationRepositorySettingsGravatarImages | Should -Be $false -Because "Gravatar images should not be exposed outside of your enterprise." - } - - It "AZDO.1028: Disable creation of TFVC repositories. See https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2024/no-tfvc-in-new-projects" -Tag "AZDO.1028" { - - Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos | Should -Be $true -Because "Team Foundation Version Control (TFVC) has been deprecated." - } - - It "AZDO.1029: Storage Usage Limit. See https://learn.microsoft.com/en-us/azure/devops/artifacts/reference/limits?view=azure-devops" -Tag "AZDO.1029" { - - Test-AzdoOrganizationStorageUsage | Should -Be $true -Because "Storage Usage Limit should not be reached." - } - - # Excluding test due to function missing in ADOPS. - It "AZDO.1030: Project Collection Administrators. See https://learn.microsoft.com/en-us/azure/devops/organizations/security/about-permissions?view=azure-devops&tabs=preview-page#permissions" -Tag "AZDO.1030" { - - Test-AzdoProjectCollectionAdministrators | Should -Be $true -Because "Users should not be directly assigned to 'Project Collection Administrator' as it is the most privileged role within Azure DevOps." - } - -} - - diff --git a/tests/Maester/Azdo/README.md b/tests/Maester/AzureDevOps/README.md similarity index 100% rename from tests/Maester/Azdo/README.md rename to tests/Maester/AzureDevOps/README.md From 2fa5cb3a243e24f0f923c8cf4c044ba850e84fd2 Mon Sep 17 00:00:00 2001 From: sebastian claesson Date: Fri, 23 Jan 2026 12:17:39 +0100 Subject: [PATCH 17/22] Fixed typo and information --- .../Test-AzdoAllowRequestAccessToken.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 powershell/public/maester/azuredevops/Test-AzdoAllowRequestAccessToken.md diff --git a/powershell/public/maester/azuredevops/Test-AzdoAllowRequestAccessToken.md b/powershell/public/maester/azuredevops/Test-AzdoAllowRequestAccessToken.md new file mode 100644 index 000000000..b052c7bec --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoAllowRequestAccessToken.md @@ -0,0 +1,17 @@ +Request access to Azure DevOps by e-mail notifications to administrators SHOULD BE disabled. + +Rationale: Access control to Azure DevOps is to be a controlled process where access is granted and tracked. + +#### Remediation action: +Disable the policy to stop these requests and notifications. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies, locate the Request Access policy and toggle it to off. +4. Provide the URL to your internal process for gaining access. Users see this URL in the error report when they try to access the organization or a project within the organization that they don't have permission to access. + +**Results:** +When users try to access a project without the required permissions, the error message includes the request access URL. This link is shown on the error page to maintain confidentiality, regardless of whether the project exists. + +#### Related links + +* [Azure DevOps Security - Disable your organization's Request Access policy](https://go.microsoft.com/fwlink/?linkid=2113172) From 046bfea1a6367fcf6d0ea7ba671fdf6b4aae6a3b Mon Sep 17 00:00:00 2001 From: sebastian claesson Date: Fri, 23 Jan 2026 12:18:02 +0100 Subject: [PATCH 18/22] Updated description --- ...doAllowTeamAdminsInvitationsAccessToken.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 powershell/public/maester/azuredevops/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md diff --git a/powershell/public/maester/azuredevops/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md b/powershell/public/maester/azuredevops/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md new file mode 100644 index 000000000..97a1a67d4 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoAllowTeamAdminsInvitationsAccessToken.md @@ -0,0 +1,19 @@ +Access to Azure DevOps SHOULD BE a controlled process provided by the IAM team or relevant Azure DevOps administrators roles. + +Rationale: By default, all administrators can invite new users to their Azure DevOps organization. +Disabling this policy prevents Team and Project Administrators from inviting new users. +However, Project Collection Administrators (PCAs) can still add new users to the organization regardless of the policy status. +Additionally, if a user is already a member of the organization, Project and Team Administrators can add that user to specific projects. + +#### Remediation action: +Disable the policy to stop these invitations. +1. Sign in to your organization +2. Choose Organization settings. +3. Select Policies, locate the **Allow team and project administrators to invite new users** policy and toggle it to off. +4. Now, only Project Collection Administrators can invite new users to Azure DevOps. + +> Project and Team Administrators can directly add users to their projects through the permissions blade. However, if they attempt to add users through the Add Users button located in the Organization settings > Users section, it's not visible to them. Adding a user directly through Project settings > Permissions doesn't result in the user appearing automatically in the Organization settings > Users list. For the user to be reflected in the Users list, they must sign in to the system. + +#### Related links + +* [Azure DevOps Security - Restrict administrators from inviting new users](https://aka.ms/azure-devops-invitations-policy) From 223fa568a5135f91e2d888131a03b8e52cb11f7a Mon Sep 17 00:00:00 2001 From: sebastian claesson Date: Fri, 23 Jan 2026 12:18:34 +0100 Subject: [PATCH 19/22] Removing dot sourcing as the test cmdlets are to be part of the module --- tests/Maester/AzureDevOps/Test-Azdo.Tests.ps1 | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 tests/Maester/AzureDevOps/Test-Azdo.Tests.ps1 diff --git a/tests/Maester/AzureDevOps/Test-Azdo.Tests.ps1 b/tests/Maester/AzureDevOps/Test-Azdo.Tests.ps1 new file mode 100644 index 000000000..ef9719343 --- /dev/null +++ b/tests/Maester/AzureDevOps/Test-Azdo.Tests.ps1 @@ -0,0 +1,160 @@ +Describe "Azure DevOps" -Tag "Azure DevOps" { + It "AZDO.1000: Azure DevOps OAuth apps can access resources in your organization through OAuth. See https://aka.ms/vstspolicyoauth" -Tag "AZDO.1000" { + + Test-AzdoThirdPartyAccessViaOauth | Should -Be $false -Because "Your tenant should restrict Azure DevOps OAuth apps to access resources in your organization through OAuth." + + } + + It "AZDO.1001: Identities can connect to your organization's Git repos through SSH. See https://aka.ms/vstspolicyssh" -Tag "AZDO.1001" { + + Test-AzdoSSHAuthentication | Should -Be $false -Because "Authentication towards your tenant should only be by Entra, do not allow developers to connect to your Git repos through SSH on macOS, Linux, or Windows to connect with Azure DevOps" + } + + It "AZDO.1002: Log Audit Events. See https://aka.ms/log-audit-events" -Tag "AZDO.1002" { + + Test-AzdoLogAuditEvents | Should -Be $true -Because "Auditing should be enabled for Azure DevOps" + } + + It "AZDO.1003: Restricting public projects. See https://aka.ms/vsts-anon-access" -Tag "AZDO.1003" { + + Test-AzdoPublicProjects | Should -Be $false -Because "Public projects should be disabled for Azure DevOps" + } + + It "AZDO.1004: Additional protections when using public package registries. See https://aka.ms/upstreamBehaviorBlog" -Tag "AZDO.1004" { + + Test-AzdoArtifactsExternalPackageProtectionToken | Should -Be $true -Because "Limiting access to externally sourced packages when internally sources packages are already present in Azure DevOps" + } + + It "AZDO.1005: IP Conditional Access policy validation. See https://aka.ms/visual-studio-conditional-access-policy" -Tag "AZDO.1005" { + + Test-AzdoEnforceAADConditionalAccess | Should -Be $true -Because "Microsoft Entra ID should always perform validation for any Conditional Access Policies (CAPs) set by tenant administrators." + } + + It "AZDO.1006: External Users access. See https://aka.ms/vstspolicyguest" -Tag "AZDO.1006" { + + Test-AzdoExternalGuestAccess | Should -Be $false -Because "External users should not be allowed access to your Azure DevOps organization" + } + + It "AZDO.1007: Team and project administrator are allowed to invite new users. See https://aka.ms/azure-devops-invitations-policy" -Tag "AZDO.1007" { + + Test-AzdoAllowTeamAdminsInvitationsAccessToken | Should -Be $false -Because "Enrolling to your Azure DevOps organization should be a controlled process." + } + + It "AZDO.1008: Request access to Azure DevOps by e-mail notifications to administrators. See https://go.microsoft.com/fwlink/?linkid=2113172" -Tag "AZDO.1008" { + + Test-AzdoAllowRequestAccessToken | Should -Be $false -Because "You should prevent users from requesting access to your organization or projects" + } + + It "AZDO.1009: Feedback Collection. See https://aka.ms/ADOPrivacyPolicy" -Tag "AZDO.1009" { + + Test-AzdoFeedbackCollection | Should -Be $true -Because "You should have confidence that we're handling your data appropriately and for legitimate uses. Part of that assurance involves carefully restricting usage." + } + + It "AZDO.1010: Audit streaming. See https://learn.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops" -Tag "AZDO.1010" { + + Test-AzdoAuditStreams | Should -Be $true -Because "Setting up a stream also allows you to store more than 90-days worth of auditing data." + } + + It "AZDO.1011: Project Resource Limits. See https://learn.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops" -Tag "AZDO.1011" { + + Test-AzdoResourceUsageProjects | Should -Be $true -Because "Azure DevOps supports up to 1,000 projects within an organization." + } + + It "AZDO.1012: Work Items Tags Limits. See https://learn.microsoft.com/en-us/azure/devops/organizations/settings/work/object-limits?view=azure-devops" -Tag "AZDO.1012" { + + Test-AzdoResourceUsageWorkItemTags | Should -Be $true -Because "Azure DevOps supports up to 150,000 tag definitions per organization or collection." + } + + It "AZDO.1013: Organization Owner should not be an individual. See https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/change-organization-ownership?view=azure-devops" -Tag "AZDO.1013" { + + Test-AzdoOrganizationOwner | Should -Be $true -Because "Organization owners are automatically members of the 'Project Collection Administrators' group. As roles and responsibilities change, you can change the owner for your organization." + } + + It "AZDO.1014: Anonymous access to pipeline badges. See https://learn.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops&tabs=net%2Cbrowser#add-a-status-badge-to-your-repository" -Tag "AZDO.1014" { + + Test-AzdoOrganizationBadgesArePrivate | Should -Be $true -Because "Even in a private project, anonymous badge access is enabled by default. With anonymous badge access enabled, users outside your organization might be able to query information such as project names, branch names, job names, and build status through the badge status API." + } + + It "AZDO.1015: Limit variables that can be set at queue time. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#limit-variables-that-can-be-set-at-queue-time" -Tag "AZDO.1015" { + + Test-AzdoOrganizationLimitVariablesAtQueueTime | Should -Be $true -Because "Only those variables explicitly marked as 'Settable at queue time' can be set. In other words, you can set any variables at queue time unless this setting is turned on." + } + + It "AZDO.1016: Limit job authorization scope to current project for non-release pipelines. See https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope" -Tag "AZDO.1016" { + + Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipelines | Should -Be $true -Because "With this option enabled, you can reduce the scope of access for all classic release pipelines to the current project." + } + + It "AZDO.1017: Limit job authorization scope to current project for classic release pipelines. See https://learn.microsoft.com/en-us/azure/devops/pipelines/process/access-tokens?view=azure-devops&tabs=yaml#job-authorization-scope" -Tag "AZDO.1017" { + + Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipelines | Should -Be $true -Because "With this option enabled, you can reduce the scope of access for all non-release pipelines to the current project." + } + + It "AZDO.1018: Protect access to repositories in YAML pipelines. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#restrict-project-repository-and-service-connection-access" -Tag "AZDO.1018" { + + Test-AzdoOrganizationProtectAccessToRepositories | Should -Be $true -Because "Apply checks and approvals when accessing repositories from YAML pipelines. Also, generate a job access token that is scoped to repositories that are explicitly referenced in the YAML pipeline." + } + + It "AZDO.1019: Stage chooser. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops" -Tag "AZDO.1019" { + + Test-AzdoOrganizationStageChooser | Should -Be $false -Because "Users should not be able to select stages to skip from the Queue Pipeline panel" + } + + It "AZDO.1020: Creation of classic build pipelines. See https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines/" -Tag "AZDO.1020" { + + Test-AzdoOrganizationCreationClassicBuildPipelines | Should -Be $false -Because "Creating classic build pipelines should not be allowed." + } + + It "AZDO.1021: Creation of classic release pipelines. See https://devblogs.microsoft.com/devops/disable-creation-of-classic-pipelines/" -Tag "AZDO.1021" { + + Test-AzdoOrganizationCreationClassicReleasePipelines | Should -Be $false -Because "Creating classic release pipelines should not be allowed." + } + + It "AZDO.1022: Limit building pull requests from forked GitHub repositories. See https://learn.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#validate-contributions-from-forks" -Tag "AZDO.1022" { + + Test-AzdoOrganizationTriggerPullRequestGitHubRepositories | Should -Be $true -Because "Azure Pipelines can automatically build and validate every pull request and commit to your GitHub repository. This should be configured according to your organizations requirements." + } + + It "AZDO.1023: Disable Marketplace tasks. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/overview?view=azure-devops#prevent-malicious-code-execution" -Tag "AZDO.1023" { + + Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTasks | Should -Be $false -Because "Disable the ability to install and run tasks from the Marketplace, which gives you greater control over the code that executes in a pipeline." + } + + It "AZDO.1024: Disable Node 6 tasks. See https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2022/no-node-6-on-hosted-agents" -Tag "AZDO.1024" { + + Test-AzdoOrganizationTaskRestrictionsDisableNode6Tasks | Should -Be $true -Because "With this enabled, pipelines will fail if they utilize a task with a Node 6 execution handler." + } + + It "AZDO.1025: Enable shell tasks arguments validation. See https://learn.microsoft.com/en-us/azure/devops/pipelines/security/inputs?view=azure-devops#shellTasksValidation" -Tag "AZDO.1025" { + + Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation | Should -Be $true -Because "When this is enabled, argument parameters for built-in shell tasks are validated to check for inputs that can inject commands into scripts." + } + + It "AZDO.1026: Enable automatic enrollment to Advanced Security for Azure DevOps. See https://learn.microsoft.com/en-us/azure/devops/repos/security/configure-github-advanced-security-features?view=azure-devops&tabs=yaml#organization-level-onboarding" -Tag "AZDO.1026" { + + Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProjects | Should -Be $true -Because "Enable automatic enrollment for new git repositories to use GitHub Advanced Security for Azure DevOps. It adds GitHub Advanced Security's suite of security features to Azure Repos." + } + + It "AZDO.1027: Disable showing Gravatar images for users outside of your enterprise. See https://learn.microsoft.com/en-us/azure/devops/repos/git/repository-settings?view=azure-devops&tabs=browser#gravatar-images" -Tag "AZDO.1027" { + + Test-AzdoOrganizationRepositorySettingsGravatarImages | Should -Be $false -Because "Gravatar images should not be exposed outside of your enterprise." + } + + It "AZDO.1028: Disable creation of TFVC repositories. See https://learn.microsoft.com/en-us/azure/devops/release-notes/roadmap/2024/no-tfvc-in-new-projects" -Tag "AZDO.1028" { + + Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepos | Should -Be $true -Because "Team Foundation Version Control (TFVC) has been deprecated." + } + + It "AZDO.1029: Storage Usage Limit. See https://learn.microsoft.com/en-us/azure/devops/artifacts/reference/limits?view=azure-devops" -Tag "AZDO.1029" { + + Test-AzdoOrganizationStorageUsage | Should -Be $true -Because "Storage Usage Limit should not be reached." + } + + It "AZDO.1030: Project Collection Administrators. See https://learn.microsoft.com/en-us/azure/devops/organizations/security/about-permissions?view=azure-devops&tabs=preview-page#permissions" -Tag "AZDO.1030" { + + Test-AzdoProjectCollectionAdministrators | Should -Be $true -Because "Users should not be directly assigned to 'Project Collection Administrator' as it is the most privileged role within Azure DevOps." + } + +} + + From 0a4eb89cb4b48067ca2c06aeed1b54a90a551e20 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Sat, 31 Jan 2026 17:49:08 -0500 Subject: [PATCH 20/22] Fix typo in SSH authentication documentation --- .../public/maester/azuredevops/Test-AzdoSSHAuthentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.md b/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.md index bd9bf2ea7..a823d6d4e 100644 --- a/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.md +++ b/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.md @@ -10,7 +10,7 @@ Disable the policy to stops these requests and notifications. 4. Locate the "SSH authentication" policy and toggle it to off. **Results:** -Users can no longer user SSH to connect to Azure DevOps. +Users can no longer use SSH to connect to Azure DevOps. #### Related links From 09caf7fb1cab0b72a64fb180505c90bd28f0f632 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Sat, 31 Jan 2026 17:49:59 -0500 Subject: [PATCH 21/22] Fix typo in Test-AzdoOrganizationStageChooser.md --- .../maester/azuredevops/Test-AzdoOrganizationStageChooser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.md index dbfe6c3b4..129065d38 100644 --- a/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.md +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.md @@ -6,7 +6,7 @@ Rationale: Users should not be able to select stages to skip from the Queue Pipe Disable the policy to stops these requests and notifications. 1. Sign in to your organization 2. Choose Organization settings. -3. Select Settings under Pieplines, locate the "Disable stage chooser" policy and toggle it to on. +3. Select Settings under Pipelines, locate the "Disable stage chooser" policy and toggle it to on. #### Related links From 04fd3340a7d08425ae40fd26e98a1bd053dee43f Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Sat, 31 Jan 2026 18:01:50 -0500 Subject: [PATCH 22/22] Update powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ...AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md index d91e16f3c..a5fe5a98f 100644 --- a/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md @@ -7,7 +7,7 @@ Enable the policy to restrict the job authorization scope. 1. Sign in to your organization 2. Choose Organization settings. 3. Under the Pipelines section choose Settings. -4. In the General section, toggle on "Limit job authorization scope to current project for non-release pipelines". +4. In the General section, toggle on "Limit job authorization scope to current project for release pipelines". #### Related links