diff --git a/powershell/Maester.psd1 b/powershell/Maester.psd1 index 2f2cac855..cd4c84a93 100644 --- a/powershell/Maester.psd1 +++ b/powershell/Maester.psd1 @@ -186,7 +186,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/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) diff --git a/powershell/public/maester/azuredevops/Test-AzdoAllowRequestAccessToken.ps1 b/powershell/public/maester/azuredevops/Test-AzdoAllowRequestAccessToken.ps1 new file mode 100644 index 000000000..9e1561f82 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoAllowRequestAccessToken.ps1 @@ -0,0 +1,47 @@ +<# +.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 + } + + $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." + } + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} 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) diff --git a/powershell/public/maester/azuredevops/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 b/powershell/public/maester/azuredevops/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 new file mode 100644 index 000000000..c1bebe754 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoAllowTeamAdminsInvitationsAccessToken.ps1 @@ -0,0 +1,48 @@ +<# +.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 + } + + $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." + } + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.md b/powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.md new file mode 100644 index 000000000..e2c05112b --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.md @@ -0,0 +1,24 @@ +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/powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 b/powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 new file mode 100644 index 000000000..f29733aa4 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoArtifactsExternalPackageProtectionToken.ps1 @@ -0,0 +1,49 @@ +<# +.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 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. + + 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 + } + + $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" + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoAuditStream.md b/powershell/public/maester/azuredevops/Test-AzdoAuditStream.md new file mode 100644 index 000000000..567f47f10 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoAuditStream.md @@ -0,0 +1,24 @@ +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 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) diff --git a/powershell/public/maester/azuredevops/Test-AzdoAuditStream.ps1 b/powershell/public/maester/azuredevops/Test-AzdoAuditStream.ps1 new file mode 100644 index 000000000..e2f794b25 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoAuditStream.ps1 @@ -0,0 +1,57 @@ +<# +.SYNOPSIS + Returns a boolean depending on the configuration. + +.DESCRIPTION + 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 + +.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." + $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 + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoEnforceAADConditionalAccess.md b/powershell/public/maester/azuredevops/Test-AzdoEnforceAADConditionalAccess.md new file mode 100644 index 000000000..83c7551e9 --- /dev/null +++ b/powershell/public/maester/azuredevops/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 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 (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. + +**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/powershell/public/maester/azuredevops/Test-AzdoEnforceAADConditionalAccess.ps1 b/powershell/public/maester/azuredevops/Test-AzdoEnforceAADConditionalAccess.ps1 new file mode 100644 index 000000000..6d4431c52 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoEnforceAADConditionalAccess.ps1 @@ -0,0 +1,46 @@ +<# +.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 + } + + $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. " + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} \ No newline at end of file diff --git a/powershell/public/maester/azuredevops/Test-AzdoExternalGuestAccess.md b/powershell/public/maester/azuredevops/Test-AzdoExternalGuestAccess.md new file mode 100644 index 000000000..0e2a47fc8 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/powershell/public/maester/azuredevops/Test-AzdoExternalGuestAccess.ps1 b/powershell/public/maester/azuredevops/Test-AzdoExternalGuestAccess.ps1 new file mode 100644 index 000000000..86e952dd9 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoExternalGuestAccess.ps1 @@ -0,0 +1,46 @@ +<# +.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 + } + + $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" + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoFeedbackCollection.md b/powershell/public/maester/azuredevops/Test-AzdoFeedbackCollection.md new file mode 100644 index 000000000..c194b0d1d --- /dev/null +++ b/powershell/public/maester/azuredevops/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/powershell/public/maester/azuredevops/Test-AzdoFeedbackCollection.ps1 b/powershell/public/maester/azuredevops/Test-AzdoFeedbackCollection.ps1 new file mode 100644 index 000000000..c225ab349 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoFeedbackCollection.ps1 @@ -0,0 +1,44 @@ +<# +.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() + +Write-verbose 'Not connected to Azure DevOps' + + $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." + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Info' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoLogAuditEvent.md b/powershell/public/maester/azuredevops/Test-AzdoLogAuditEvent.md new file mode 100644 index 000000000..23936caf1 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoLogAuditEvent.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/powershell/public/maester/azuredevops/Test-AzdoLogAuditEvent.ps1 b/powershell/public/maester/azuredevops/Test-AzdoLogAuditEvent.ps1 new file mode 100644 index 000000000..feb68bda3 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.md new file mode 100644 index 000000000..70c86d2b0 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationAutomaticEnrollmentAdvancedSecurityNewProject.ps1 new file mode 100644 index 000000000..e4b67fd14 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoOrganizationBadgesArePrivate.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationBadgesArePrivate.md new file mode 100644 index 000000000..5014067ef --- /dev/null +++ b/powershell/public/maester/azuredevops/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/powershell/public/maester/azuredevops/Test-AzdoOrganizationBadgesArePrivate.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationBadgesArePrivate.ps1 new file mode 100644 index 000000000..de0a27a70 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationBadgesArePrivate.ps1 @@ -0,0 +1,42 @@ +<# +.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() + +Write-verbose 'Not connected to Azure DevOps' + + $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." + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.md new file mode 100644 index 000000000..fba250f3c --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 new file mode 100644 index 000000000..4a052ad6b --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicBuildPipeline.ps1 @@ -0,0 +1,44 @@ +<# +.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() + +Write-verbose 'Not connected to Azure DevOps' + + $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 + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.md new file mode 100644 index 000000000..6ccb91f67 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 new file mode 100644 index 000000000..caf1d3f70 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationCreationClassicReleasePipeline.ps1 @@ -0,0 +1,43 @@ +<# +.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() + +Write-verbose 'Not connected to Azure DevOps' + + $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 + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.md new file mode 100644 index 000000000..0a7ba924f --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeNonReleasePipeline.ps1 new file mode 100644 index 000000000..e76914eff --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.md new file mode 100644 index 000000000..a5fe5a98f --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.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 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/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitJobAuthorizationScopeReleasePipeline.ps1 new file mode 100644 index 000000000..7d9d3717e --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoOrganizationLimitVariablesAtQueueTime.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitVariablesAtQueueTime.md new file mode 100644 index 000000000..e74bc3616 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 new file mode 100644 index 000000000..f9f27de4f --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationLimitVariablesAtQueueTime.ps1 @@ -0,0 +1,47 @@ +<# +.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() + +Write-verbose 'Not connected to Azure DevOps' + + $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." + } + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoOrganizationOwner.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationOwner.md new file mode 100644 index 000000000..d36eb3092 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/powershell/public/maester/azuredevops/Test-AzdoOrganizationOwner.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationOwner.ps1 new file mode 100644 index 000000000..2b5c56c86 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationOwner.ps1 @@ -0,0 +1,51 @@ +<# +.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() + + Write-verbose 'Not connected to Azure DevOps' + + + + $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/powershell/public/maester/azuredevops/Test-AzdoOrganizationProtectAccessToRepository.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationProtectAccessToRepository.md new file mode 100644 index 000000000..c337c87e5 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationProtectAccessToRepository.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationProtectAccessToRepository.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationProtectAccessToRepository.ps1 new file mode 100644 index 000000000..01c7658ea --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.md new file mode 100644 index 000000000..78d9793ff --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsDisableCreationTFVCRepo.ps1 new file mode 100644 index 000000000..0e1d0a869 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoOrganizationRepositorySettingsGravatarImage.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsGravatarImage.md new file mode 100644 index 000000000..e0ea1828b --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsGravatarImage.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationRepositorySettingsGravatarImage.ps1 new file mode 100644 index 000000000..96cb54ab6 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoOrganizationStageChooser.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.md new file mode 100644 index 000000000..129065d38 --- /dev/null +++ b/powershell/public/maester/azuredevops/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 Pipelines, 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/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.ps1 new file mode 100644 index 000000000..1072cecc1 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStageChooser.ps1 @@ -0,0 +1,44 @@ +<# +.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() + +Write-verbose 'Not connected to Azure DevOps' + + $result = (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 + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoOrganizationStorageUsage.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStorageUsage.md new file mode 100644 index 000000000..444346af8 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/powershell/public/maester/azuredevops/Test-AzdoOrganizationStorageUsage.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStorageUsage.ps1 new file mode 100644 index 000000000..367479877 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationStorageUsage.ps1 @@ -0,0 +1,52 @@ +<# +.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() + + Write-verbose 'Not connected to Azure DevOps' + + $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 + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} \ No newline at end of file diff --git a/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.md new file mode 100644 index 000000000..9774f1cdf --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableMarketplaceTask.ps1 new file mode 100644 index 000000000..15de35985 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.md new file mode 100644 index 000000000..7960705db --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsDisableNode6Task.ps1 new file mode 100644 index 000000000..214fe6398 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.md new file mode 100644 index 000000000..b4aa4942c --- /dev/null +++ b/powershell/public/maester/azuredevops/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/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 new file mode 100644 index 000000000..50827e698 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTaskRestrictionsShellTaskArgumentValidation.ps1 @@ -0,0 +1,42 @@ +<# +.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() + +Write-verbose 'Not connected to Azure DevOps' + + $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." + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'Critical' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.md new file mode 100644 index 000000000..f4745c129 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.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/powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 new file mode 100644 index 000000000..100b244d8 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoOrganizationTriggerPullRequestGitHubRepository.ps1 @@ -0,0 +1,53 @@ +<# +.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() + + Write-verbose 'Not connected to Azure DevOps' + + $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." + } + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} diff --git a/powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.md b/powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.md new file mode 100644 index 000000000..e7806b138 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.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/powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.ps1 b/powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.ps1 new file mode 100644 index 000000000..57ea937f5 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoProjectCollectionAdministrator.ps1 @@ -0,0 +1,89 @@ +<# +.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() + + Write-verbose 'Not connected to Azure DevOps' + + 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 + } + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} \ No newline at end of file diff --git a/powershell/public/maester/azuredevops/Test-AzdoPublicProject.md b/powershell/public/maester/azuredevops/Test-AzdoPublicProject.md new file mode 100644 index 000000000..3e2d5e662 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoPublicProject.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/powershell/public/maester/azuredevops/Test-AzdoPublicProject.ps1 b/powershell/public/maester/azuredevops/Test-AzdoPublicProject.ps1 new file mode 100644 index 000000000..8a4f5966c --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoResourceUsageProject.md b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageProject.md new file mode 100644 index 000000000..2874dbd37 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageProject.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/powershell/public/maester/azuredevops/Test-AzdoResourceUsageProject.ps1 b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageProject.ps1 new file mode 100644 index 000000000..1d6089872 --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoResourceUsageWorkItemTag.md b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageWorkItemTag.md new file mode 100644 index 000000000..b89296bf1 --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageWorkItemTag.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/powershell/public/maester/azuredevops/Test-AzdoResourceUsageWorkItemTag.ps1 b/powershell/public/maester/azuredevops/Test-AzdoResourceUsageWorkItemTag.ps1 new file mode 100644 index 000000000..bad04564f --- /dev/null +++ b/powershell/public/maester/azuredevops/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/azuredevops/Test-AzdoSSHAuthentication.md b/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.md new file mode 100644 index 000000000..a823d6d4e --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.md @@ -0,0 +1,18 @@ +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 use 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/azuredevops/Test-AzdoSSHAuthentication.ps1 b/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.ps1 new file mode 100644 index 000000000..6f55b23ae --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoSSHAuthentication.ps1 @@ -0,0 +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() + + 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 { + $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" + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} \ No newline at end of file diff --git a/powershell/public/maester/azuredevops/Test-AzdoThirdPartyAccessViaOauth.md b/powershell/public/maester/azuredevops/Test-AzdoThirdPartyAccessViaOauth.md new file mode 100644 index 000000000..972e0b69e --- /dev/null +++ b/powershell/public/maester/azuredevops/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) diff --git a/powershell/public/maester/azuredevops/Test-AzdoThirdPartyAccessViaOauth.ps1 b/powershell/public/maester/azuredevops/Test-AzdoThirdPartyAccessViaOauth.ps1 new file mode 100644 index 000000000..fc53535de --- /dev/null +++ b/powershell/public/maester/azuredevops/Test-AzdoThirdPartyAccessViaOauth.ps1 @@ -0,0 +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() + + 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 { + $resultMarkdown = "Well done. Your tenant has restricted Azure DevOps OAuth apps to access resources in your organization through OAuth." + } + + + + Add-MtTestResultDetail -Result $resultMarkdown -Severity 'High' + + return $result +} \ No newline at end of file diff --git a/tests/Maester/AzureDevOps/README.md b/tests/Maester/AzureDevOps/README.md new file mode 100644 index 000000000..be8bb525c --- /dev/null +++ b/tests/Maester/AzureDevOps/README.md @@ -0,0 +1,5 @@ +# Azure DevOps Tests - Preview + +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. 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." + } + +} + +