Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
When you're handling information and data, especially in a cloud-based solution like Azure DevOps Services, security should be your top priority. While Microsoft ensures the security of the underlying cloud infrastructure, configuring security within Azure DevOps is your responsibility.
While not mandatory, incorporating best practices can significantly enhance your experience and bolster security. The following recommendations are designed to help you maintain a secure Azure DevOps environment.
Disable or delete Microsoft Entra user accounts: If connected to Microsoft Entra ID, disable or delete the Microsoft Entra user account while keeping the Azure DevOps user account active. This action allows you to continue querying work item history using your Azure DevOps user ID.
Revoke user PATs: Regularly review and revoke any existing user PATs to ensure secure management of these critical authentication tokens.
Revoke special permissions granted to individual users: Audit and revoke any special permissions granted to individual users to ensure alignment with the principle of least privilege.
Reassign work from removed users: Before removing users, reassign their work items to current team members to distribute the load effectively.
Use Microsoft Entra ID
Establish a unified identity system: Connect Azure DevOps to Microsoft Entra ID to create a single plane for identity. This consistency reduces confusion and minimizes security risks from manual configuration errors.
Implement fine-grained governance: Use Microsoft Entra ID to assign different roles and permissions to specific groups across various resource scopes. This action ensures controlled access and aligns with security best practices.
Enhance security features: Enable other security features with Microsoft Entra ID, such as:
Multifactor Authentication (MFA): Require multiple factors like password and phone verification for user authentication.
Conditional access policies: Define access rules based on conditions like location, device, or risk level.
The following functions are effective ways to enhance the security of your network when you're working with Azure DevOps.
Set up IP allowlisting: Restrict access to specific IP addresses to allow traffic only from trusted sources, reducing the attack surface.
Use encryption: Always encrypt data in transit and at rest. Secure communication channels using protocols like HTTPS.
Validate certificates: Ensure certificates are valid and issued by trusted authorities when establishing connections.
Implement web application firewalls (WAFs): Filter, monitor, and block malicious web-based traffic with WAFs for an extra layer of protection against common attacks.
The system handles permissions at various levels—individual, collection, project, and object—and assigns them to one or more built-in groups by default. To enhance security, do the following actions:
Provide least privilege access: Give users and services the minimum necessary access to perform their business functions.
Disable inheritance: Whenever possible, disable inheritance. Inheritance can inadvertently grant access or permissions to unexpected users due to its allow-by-default nature. For more information, see the section on permission inheritance
For more information about permissions, see the following articles:
Limit access to projects and repos: Reduce the risk of leaking sensitive information and deploying insecure code by limiting access to projects and repositories. Use built-in or custom security groups manage permissions.
Disable “Allow public projects”: In your organization’s policy settings, disable the option to create public projects. Switch project visibility from public to private as needed. Users who haven’t signed in have read-only access to public projects, while signed-in users can be granted access to private projects and make permitted changes.
Restrict project creation: Prevent users from creating new projects to maintain control over your environment.
Use distinct emails or UPNs: Use different email addresses or user principal names (UPNs) for personal and business accounts to eliminate ambiguity between personal and work-related accounts.
Group external guest users: Place all external guest users in a single Microsoft Entra group and manage permissions for this group appropriately. Remove direct assignments to ensure group rules apply to these users.
Reevaluate rules regularly: Regularly review rules on the Group rules tab of the Users page. Consider any group membership changes in Microsoft Entra ID that might affect your organization. Microsoft Entra ID can take up to 24 hours to update dynamic group membership, and rules are automatically reevaluated every 24 hours and whenever a group rule changes.
The following table shows recommendations for assigning permissions to security groups and users groups.
Do
Don't
Use Microsoft Entra ID, Active Directory, or Windows security groups when you're managing lots of users.
Don’t change the default permissions for the Project Valid Users group. This group can access and view project information. For more information, see Valid user groups.
When you're adding teams, consider what permissions you want to assign to team members who need to create and modify area paths, iteration paths, and queries.
Don't add users to multiple security groups that contain different permission levels. In certain cases, a Deny permission level might override an Allow permission level. For example, imagine you have two security groups in your Azure DevOps project: Developers and Testers. The Developers group has the permission to edit work items set to Allow. But, ensure that certain sensitive work items aren't edited by anyone except a few key individuals. To do so, create a new security group called Sensitive Items Editors and set the permission to edit work items to Deny for this group. If a user is a member of both the Developers group and the Sensitive Items Editors group, the Deny permission from the Sensitive Items Editors group takes precedence over the Allow permission from the Developers group. As a result, this user can't edit the sensitive work items, even though they have the Allow permission in the Developers group. This behavior ensures that Deny permissions are enforced strictly, providing a higher level of security and control over sensitive actions within your Azure DevOps environment.
When you're adding many teams, consider creating a Team Administrators custom group where you allocate a subset of the permissions available to Project Administrators.
Don't change the default assignments made to the Project Valid Users groups. If you remove or set View instance-level information to Deny for one of the Project Valid Users groups, no users in the group can access whatever project, collection, or deployment you set the permission on.
Consider granting the work item query folders Contribute permission to users or groups who require the ability to create and share work item queries for the project.
Don't assign permissions that are noted as Assign only to service accounts to user accounts.
Keep groups as small as possible. Access should be restricted, and the groups should be frequently audited.
Take advantage of built-in roles and default to Contributor for developers. Admins get assigned to the Project Administrator security group for elevated permissions, allowing them to configure security permissions.
Just-in-time access for admin groups
If you have Project Collection Administrator and Project Administrator access, you can modify the configuration of your organization or project. To enhance security for these built-in administrator groups, consider implementing just-in-time access using a Microsoft Entra Privileged Identity Management (PIM) group. This approach allows you to grant elevated permissions only when needed, reducing the risk associated with permanent access.
When you configure just-in-time access using a Microsoft Entra Privileged Identity Management (PIM) group, ensure that any user with elevated access also retains standard access to the organization. This way, they can view the necessary pages and refresh their permissions as needed.
Users have elevated access in Azure DevOps for up to 1 hour after their PIM group access gets deactivated.
Scope service accounts
Create single-purpose service accounts: Each service should have its dedicated account to minimize risk. Avoid using regular user accounts as service accounts.
Follow naming and documentation conventions: Use clear, descriptive names for service accounts and document their purpose and associated services.
Identify and disable unused service accounts: Regularly review and identify accounts no longer in use. Disable unused accounts before considering deletion.
Restrict privileges: Limit service account privileges to the minimum necessary. Avoid interactive sign-in rights for service accounts.
Use local accounts for workgroup installations: When installing components in a workgroup, use local accounts for user accounts. Avoid domain accounts in this scenario.
Leverage service connections: Use service connections whenever possible to securely connect to services without passing secret variables directly to builds. Restrict connections to specific use cases.
Monitor service account activity: Implement auditing and create audit streams to monitor service account activity.
Scope service connections: Limit access by scoping your Azure Resource Manager service connections to specific resources and groups. Avoid granting broad contributor rights across the entire Azure subscription.
Use workload identity federation: Authenticate with Azure resources using OpenID Connect (OIDC) without secrets. Create workload identity federation automatically or manually if you have the Owner role for your Azure subscription, aren't connecting to Azure Stack or Azure US Government environments, and any Marketplace extensions tasks you use support it.
Scope resource groups: Ensure resource groups contain only the Virtual Machines (VMs) or resources needed for the build process.
Avoid classic service connections: Opt for modern Azure Resource Manager service connections instead of classic one, which lack scoping options.
Use purpose-specific team service accounts: Authenticate service connections using purpose-specific team service accounts to maintain security and control.
Use service principals: Represent security objects within a Microsoft Entra application. Define what an application can do in a given tenant. Set up during application registration in the Azure portal. Configure to access Azure resources, including Azure DevOps. Useful for apps needing specific access and control.
Use managed identities: Similar to an application’s service principal. Provide identities for Azure resources. Allow services supporting Microsoft Entra authentication to share credentials. Azure handles credential management and rotation automatically. Ideal for seamless sign-in details management.
Use PATs sparingly
Scope PATs to specific roles: Assign PATs only the necessary permissions for specific tasks. Avoid granting global access to multiple organizations or repositories to minimize the risk of accidental misuse.
Avoid write or manage permissions on builds and releases: PATs shouldn't have write or manage permissions on builds, releases, or other critical resources. Restricting these permissions helps prevent unintended actions that could affect your pipelines or deployments.
Set expiration dates and keep PATs secret: Always set an expiration date for PATs. Regularly review and renew them as needed. Treat PATs as critical as passwords, keeping them confidential and avoiding public sharing or hardcoding in application code.
Avoid hardcoding PATs in application code: Instead of embedding PATs directly in your code, use secure configuration files or environment variables to store and retrieve PATs dynamically. dynamically.
Audit and revoke unused PATs regularly: Administrators should periodically review all PATs using the REST APIs. Revoke any PATs that are no longer needed or don’t meet the recommended criteria.
Ensure that you understand the difference between feeds, project, and project collection administrators. For more information, see Configure Azure Artifacts settings.
Require external reviewers: Ensure at least one reviewer outside the original requester for a thorough review process. The approver shares co-ownership of the changes and accountability for any potential effects.
Require CI build to pass: Establish a baseline for code quality by requiring the Continuous Integration (CI) build to pass before merging a PR. CI checks include code linting, unit tests, and security scans.
Disallow self-approval: Prevent the original PR requester from approving their own changes to ensure an unbiased review process and avoid conflicts of interest.
Disallow PR completion with “wait” or “reject” votes: Prevent PR completion even if some reviewers vote to wait or reject, encouraging addressing all feedback before merging.
Reset reviewer votes on changes: Reset reviewer votes when recent changes are pushed to the PR to ensure reviewers reevaluate the updated code.
Lock down release pipelines: Limit release pipelines to specific branches (usually production or main) to avoid accidental deployments from other branches.
Enforce settable variables at queue time: Enable the "Enforce settable at queue time" option for pipeline variables to prevent users from overriding variable values during pipeline execution.
Disallow variable overrides in the editor: For variables set in the pipeline editor, disallow user overrides to maintain consistency and prevent unintended changes.
Agents
Grant permissions sparingly: Limit permissions to the smallest necessary set of accounts to reduce the attack surface.
Configure restrictive firewalls for agents: Set up firewalls to be as restrictive as possible while still allowing agents to function, balancing security and usability.
Regularly update agent pools: Keep your agent fleet up-to-date by regularly updating agents to ensure vulnerable code isn’t running, reducing the risk of exploitation.
Use a separate agent pool for production artifacts: Isolate production artifacts by using a distinct agent pool, preventing accidental deployments from nonproduction branches.
Segment sensitive pools: Create separate pools for sensitive and nonsensitive workloads. Only allow credentials in build definitions associated with the appropriate pool.
Definitions
Use YAML for pipeline definitions: Define pipelines using YAML (Yet Another Markup Language) for better traceability and adherence to approval guidelines and version control practices.
Restrict edit access to pipeline definitions: Limit access to editing pipeline definitions to the minimum necessary accounts to reduce the risk of accidental or unauthorized changes.
Input
Include checks for variables in build scripts: Implement checks within your build scripts to mitigate potential command injection attacks through settable variables. Validate input values and prevent unintended or malicious commands.
Limit “settable at release time” build variables: Minimize the number of build variables settable at release time to reduce the attack surface and simplify configuration management.
Tasks
Avoid remotely fetched resources: Whenever possible, avoid fetching resources from external URLs during your build process. If remote resources are necessary, use versioning and hash checking to ensure integrity.
Avoid logging secrets: Never log sensitive information, such as secrets or credentials, in your build logs to prevent unintentional exposure and compromise of security.
Use Azure Key Vault for secrets: Instead of storing secrets directly in pipeline variables, use Azure Key Vault to manage and retrieve secrets securely.
Restrict running builds against arbitrary branches or tags: For security-critical pipelines, limit users from running builds against any branch or tag. Define specific authorized branches or tags to prevent accidental or unauthorized executions.
Disable inheritance for pipeline permissions: Inherited permissions can be overly broad and might not accurately reflect your needs. Disable inheritance and set permissions explicitly to align with your security requirements.
Limit job authorization scopes: Always restrict job authorization scopes to the minimum necessary. Fine-tune permissions based on the specific tasks performed by each job.
Repositories and branches
Require a minimum number of reviewers: Enable the policy to ensure every pull request receives reviews from at least two approvers, promoting thorough code review and accountability.
Configure repository-specific security policies: Tailor security policies to each repository or branch instead of project-wide policies to reduce risk, enforce change management standards, and enhance code quality.
Isolate production secrets in a separate key vault: Store production secrets separately in an Azure Key Vault and limit access to a need-to-know basis to maintain separation from nonproduction builds.
Segregate test environments from production: Avoid mixing test environments with production to ensure credentials and secrets aren't used in nonproduction contexts.
Disable forking: Disabling forking helps manage security by preventing the proliferation of forks, making it easier to track security across all copies.
Consider manually triggering fork builds: Manually trigger builds for forks rather than allowing automatic triggers to provide better control over security checks.
Scan production build definitions in Git repositories: Regularly check production build definitions stored in your project’s Git repository for any credentials or sensitive information.
Configure branch control checks for production context: Set up branch control checks to restrict the use of sensitive connections (for example, prod-connection) to pipelines running in the context of the production branch, ensuring proper authorization and preventing accidental misuse.
Use OAuth flow instead of PATs: Disable PAT-based authentication for GitHub service connections and opt for OAuth flow for better security and integration.
Avoid admin or owner identities: Never authenticate GitHub service connections using an identity that's an administrator or owner of any repositories. Limit permissions to the necessary level.
Avoid full-scope GitHub PATs: Refrain from using a full-scope GitHub PAT to authenticate service connections. Use tokens with the minimum required permissions.
Avoid personal GitHub accounts as service connections: Don’t use personal GitHub accounts as service connections in Azure DevOps. Instead, create dedicated service accounts or use organization-level accounts.
Note: The author created this article with assistance from AI. Learn more