Configure Azure DevOps Security

The azure-native:security:DevOpsConfiguration resource, part of the Pulumi Azure Native provider, configures how Microsoft Defender for Cloud connects to and scans DevOps platforms such as GitHub, Azure DevOps, and GitLab. This guide focuses on two capabilities: automatic discovery of organizations and manual organization selection.

DevOps configurations require an existing security connector and OAuth authorization with your DevOps platform. The examples are intentionally small. Combine them with your own security connector setup and authorization flow.

Enable automatic discovery for current and future organizations

Teams connecting DevOps platforms to Defender for Cloud often want security scanning to apply automatically as new organizations or projects are created.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const devOpsConfiguration = new azure_native.security.DevOpsConfiguration("devOpsConfiguration", {
    properties: {
        authorization: {
            code: "00000000000000000000",
        },
        autoDiscovery: azure_native.security.AutoDiscovery.Enabled,
    },
    resourceGroupName: "myRg",
    securityConnectorName: "mySecurityConnectorName",
});
import pulumi
import pulumi_azure_native as azure_native

dev_ops_configuration = azure_native.security.DevOpsConfiguration("devOpsConfiguration",
    properties={
        "authorization": {
            "code": "00000000000000000000",
        },
        "auto_discovery": azure_native.security.AutoDiscovery.ENABLED,
    },
    resource_group_name="myRg",
    security_connector_name="mySecurityConnectorName")
package main

import (
	security "github.com/pulumi/pulumi-azure-native-sdk/security/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := security.NewDevOpsConfiguration(ctx, "devOpsConfiguration", &security.DevOpsConfigurationArgs{
			Properties: &security.DevOpsConfigurationPropertiesArgs{
				Authorization: &security.AuthorizationArgs{
					Code: pulumi.String("00000000000000000000"),
				},
				AutoDiscovery: pulumi.String(security.AutoDiscoveryEnabled),
			},
			ResourceGroupName:     pulumi.String("myRg"),
			SecurityConnectorName: pulumi.String("mySecurityConnectorName"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var devOpsConfiguration = new AzureNative.Security.DevOpsConfiguration("devOpsConfiguration", new()
    {
        Properties = new AzureNative.Security.Inputs.DevOpsConfigurationPropertiesArgs
        {
            Authorization = new AzureNative.Security.Inputs.AuthorizationArgs
            {
                Code = "00000000000000000000",
            },
            AutoDiscovery = AzureNative.Security.AutoDiscovery.Enabled,
        },
        ResourceGroupName = "myRg",
        SecurityConnectorName = "mySecurityConnectorName",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.security.DevOpsConfiguration;
import com.pulumi.azurenative.security.DevOpsConfigurationArgs;
import com.pulumi.azurenative.security.inputs.DevOpsConfigurationPropertiesArgs;
import com.pulumi.azurenative.security.inputs.AuthorizationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var devOpsConfiguration = new DevOpsConfiguration("devOpsConfiguration", DevOpsConfigurationArgs.builder()
            .properties(DevOpsConfigurationPropertiesArgs.builder()
                .authorization(AuthorizationArgs.builder()
                    .code("00000000000000000000")
                    .build())
                .autoDiscovery("Enabled")
                .build())
            .resourceGroupName("myRg")
            .securityConnectorName("mySecurityConnectorName")
            .build());

    }
}
resources:
  devOpsConfiguration:
    type: azure-native:security:DevOpsConfiguration
    properties:
      properties:
        authorization:
          code: '00000000000000000000'
        autoDiscovery: Enabled
      resourceGroupName: myRg
      securityConnectorName: mySecurityConnectorName

When autoDiscovery is set to Enabled, Defender for Cloud automatically scans all organizations accessible through the authorization code, including any created in the future. The authorization.code property contains the OAuth token from your DevOps platform. The securityConnectorName links this configuration to an existing security connector that defines the connection parameters.

Onboard specific organizations with manual selection

Organizations with strict governance may need to control exactly which DevOps organizations are scanned.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const devOpsConfiguration = new azure_native.security.DevOpsConfiguration("devOpsConfiguration", {
    properties: {
        authorization: {
            code: "00000000000000000000",
        },
        autoDiscovery: azure_native.security.AutoDiscovery.Disabled,
        topLevelInventoryList: [
            "org1",
            "org2",
        ],
    },
    resourceGroupName: "myRg",
    securityConnectorName: "mySecurityConnectorName",
});
import pulumi
import pulumi_azure_native as azure_native

dev_ops_configuration = azure_native.security.DevOpsConfiguration("devOpsConfiguration",
    properties={
        "authorization": {
            "code": "00000000000000000000",
        },
        "auto_discovery": azure_native.security.AutoDiscovery.DISABLED,
        "top_level_inventory_list": [
            "org1",
            "org2",
        ],
    },
    resource_group_name="myRg",
    security_connector_name="mySecurityConnectorName")
package main

import (
	security "github.com/pulumi/pulumi-azure-native-sdk/security/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := security.NewDevOpsConfiguration(ctx, "devOpsConfiguration", &security.DevOpsConfigurationArgs{
			Properties: &security.DevOpsConfigurationPropertiesArgs{
				Authorization: &security.AuthorizationArgs{
					Code: pulumi.String("00000000000000000000"),
				},
				AutoDiscovery: pulumi.String(security.AutoDiscoveryDisabled),
				TopLevelInventoryList: pulumi.StringArray{
					pulumi.String("org1"),
					pulumi.String("org2"),
				},
			},
			ResourceGroupName:     pulumi.String("myRg"),
			SecurityConnectorName: pulumi.String("mySecurityConnectorName"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var devOpsConfiguration = new AzureNative.Security.DevOpsConfiguration("devOpsConfiguration", new()
    {
        Properties = new AzureNative.Security.Inputs.DevOpsConfigurationPropertiesArgs
        {
            Authorization = new AzureNative.Security.Inputs.AuthorizationArgs
            {
                Code = "00000000000000000000",
            },
            AutoDiscovery = AzureNative.Security.AutoDiscovery.Disabled,
            TopLevelInventoryList = new[]
            {
                "org1",
                "org2",
            },
        },
        ResourceGroupName = "myRg",
        SecurityConnectorName = "mySecurityConnectorName",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.security.DevOpsConfiguration;
import com.pulumi.azurenative.security.DevOpsConfigurationArgs;
import com.pulumi.azurenative.security.inputs.DevOpsConfigurationPropertiesArgs;
import com.pulumi.azurenative.security.inputs.AuthorizationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var devOpsConfiguration = new DevOpsConfiguration("devOpsConfiguration", DevOpsConfigurationArgs.builder()
            .properties(DevOpsConfigurationPropertiesArgs.builder()
                .authorization(AuthorizationArgs.builder()
                    .code("00000000000000000000")
                    .build())
                .autoDiscovery("Disabled")
                .topLevelInventoryList(                
                    "org1",
                    "org2")
                .build())
            .resourceGroupName("myRg")
            .securityConnectorName("mySecurityConnectorName")
            .build());

    }
}
resources:
  devOpsConfiguration:
    type: azure-native:security:DevOpsConfiguration
    properties:
      properties:
        authorization:
          code: '00000000000000000000'
        autoDiscovery: Disabled
        topLevelInventoryList:
          - org1
          - org2
      resourceGroupName: myRg
      securityConnectorName: mySecurityConnectorName

Setting autoDiscovery to Disabled prevents automatic scanning. The topLevelInventoryList property explicitly names which organizations to include. This configuration extends the basic authorization setup by adding organization-level filtering, giving you precise control over the scan scope.

Beyond these examples

These snippets focus on specific DevOps configuration features: automatic discovery controls and organization-level filtering. They’re intentionally minimal rather than complete security integrations.

The examples require pre-existing infrastructure such as a Microsoft Defender for Cloud security connector and OAuth authorization with your DevOps platform (GitHub, Azure DevOps, or GitLab). They focus on configuring discovery behavior rather than provisioning the security connector itself.

To keep things focused, common DevOps security patterns are omitted, including:

  • Security connector creation and configuration
  • OAuth authorization flow and code generation
  • Repository-level filtering within organizations
  • Security policy configuration and scanning rules

These omissions are intentional: the goal is to illustrate how discovery and filtering are wired, not provide drop-in security modules. See the DevOpsConfiguration resource reference for all available configuration options.

Let's configure Azure DevOps Security

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Onboarding
What's the difference between Enabled and Disabled autoDiscovery?
When autoDiscovery is Enabled, the configuration onboards current and future resources automatically. When Disabled, it onboards only current resources.
How do I onboard specific organizations instead of all resources?
Set autoDiscovery to Disabled and provide a topLevelInventoryList array with the organization names you want to onboard (e.g., ["org1", "org2"]).
Resource Management
What properties can't be changed after creating the DevOps configuration?
Both resourceGroupName and securityConnectorName are immutable and cannot be changed after creation.
How do I use a different API version for this resource?
Generate a local SDK package using the CLI command pulumi package add azure-native security [ApiVersion]. Available versions include 2023-09-01-preview, 2024-05-15-preview, 2025-03-01, and 2025-11-01-preview.

Using a different cloud?

Explore security guides for other cloud providers: