DevOps  

Azure Developer CLI Extensions: Adding Security Gates to Azure Deployments

Deploying an application to Azure should not end with a successful build. A deployment can technically succeed while introducing a vulnerable dependency, an exposed configuration value, an insecure infrastructure setting, or a container image with known security issues.

For teams using the Azure Developer CLI (azd), custom extensions provide a way to add organization-specific checks around the deployment workflow.

A useful pattern is to introduce security gates before an application is provisioned or deployed. The gate evaluates predefined security requirements and stops the deployment when a critical condition is detected.

For .NET applications, this approach can combine source-code scanning, dependency checks, secret detection, infrastructure validation, and container-image scanning into a repeatable deployment workflow.

What Is a Security Gate?

A security gate is an automated check that must pass before a deployment can continue.

A simplified workflow looks like this:

Developer
    |
    v
azd security deploy
    |
    v
Security Checks
    |
    +-- Dependency scan
    +-- Secret scan
    +-- Infrastructure scan
    +-- Container scan
    |
    v
Security Gate
    |
    +-- FAIL → Stop deployment
    |
    +-- PASS → Continue deployment

The important principle is simple:

A deployment should not proceed when a predefined security requirement fails.

The exact checks should be based on the application's risk profile rather than attempting to scan everything indiscriminately.

Why Add Security Gates to azd?

Without a standardized gate, developers may perform security checks manually:

dotnet test
dotnet build
azd provision
azd deploy

A developer might remember to run a dependency scan, while another developer does not.

A custom deployment command can establish a consistent process:

azd company deploy

The command can execute security validation before invoking the normal Azure deployment workflow.

This is especially useful for teams that have several .NET services using the same deployment standards.

Where Should the Security Gate Run?

A practical deployment pipeline can have several security checkpoints:

Source Code
    |
    v
Dependency / Secret Scan
    |
    v
Build
    |
    v
Container Image Scan
    |
    v
Infrastructure Validation
    |
    v
Azure Provisioning
    |
    v
Application Deployment
    |
    v
Runtime Verification

Not every check needs to happen at the same stage.

For example, source-code secret scanning belongs before deployment, while runtime configuration validation belongs after Azure resources have been provisioned.

Security Gate for .NET Dependencies

.NET applications commonly rely on NuGet packages.

A project might contain:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <ItemGroup>
    <PackageReference Include="Example.Package" Version="1.0.0" />
  </ItemGroup>

</Project>

A security workflow should check dependencies for known vulnerabilities before production deployment.

The .NET CLI provides package auditing capabilities. A basic command is:

dotnet list package --vulnerable

Depending on the installed .NET SDK, newer CLI syntax may differ, so the command should be aligned with the SDK version used by the project.

A custom security gate can interpret the result and decide whether deployment is allowed.

Conceptually:

No critical vulnerabilities
        ↓
PASS

Critical vulnerability detected
        ↓
FAIL

The policy should define what constitutes a blocking vulnerability rather than treating every warning as a deployment blocker.

Secret Detection

Secrets accidentally committed to source control are another major deployment risk.

Examples include:

Connection strings
API keys
Access tokens
Private keys
Cloud credentials

A security gate can scan source files before deployment.

For example, a simple custom validation command could reject known patterns:

public static bool ContainsPotentialSecret(
    string content)
{
    var indicators = new[]
    {
        "password=",
        "api_key",
        "access_token",
        "private_key"
    };

    return indicators.Any(
        indicator =>
            content.Contains(
                indicator,
                StringComparison.OrdinalIgnoreCase));
}

This is only a basic example and should not be treated as a complete secret scanner.

Production environments should use dedicated secret-scanning tools that understand different credential formats and reduce false positives.

The important design principle is to prevent credentials from reaching source control or deployment artifacts in the first place.

Infrastructure Security Validation

Azure infrastructure is commonly defined using Infrastructure as Code.

For example, an application may use Bicep:

resource storage 'Microsoft.Storage/storageAccounts@2023-05-01' = {
  name: storageAccountName
  location: resourceGroup().location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

A security gate can validate infrastructure before provisioning.

Potential checks might include:

  • Public network access

  • Encryption configuration

  • TLS requirements

  • Identity configuration

  • Diagnostic logging

  • Resource exposure

  • Allowed locations

  • Naming conventions

  • Required tags

The exact rules should come from the organization's security baseline.

Container Image Security

If the .NET application is deployed as a container, the image should also be considered part of the deployment security boundary.

For example:

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime

WORKDIR /app

COPY publish/ .

ENTRYPOINT ["dotnet", "Orders.Api.dll"]

The base image and installed operating-system packages can contain vulnerabilities.

A security gate can scan the final image:

Docker Build
     |
     v
Container Image
     |
     v
Vulnerability Scanner
     |
     +-- Critical → FAIL
     |
     +-- Acceptable → PASS

The gate should scan the image that will actually be deployed rather than only scanning the source Dockerfile.

Building the Security Gate

A custom azd extension can orchestrate several existing tools instead of implementing every scanner itself.

A conceptual command could be:

azd company secure-deploy

The extension can perform:

1. Validate configuration
2. Scan dependencies
3. Scan secrets
4. Validate infrastructure
5. Build container
6. Scan image
7. Provision Azure resources
8. Deploy application
9. Verify application health

This keeps specialized security functionality in dedicated tools while the extension coordinates the workflow.

Example Gate Logic

The core decision can be represented with a simple model:

public sealed record SecurityCheckResult(
    string Name,
    bool Passed,
    string Message);

The deployment orchestrator can evaluate the results:

public static void EnforceSecurityGate(
    IEnumerable<SecurityCheckResult> results)
{
    foreach (var result in results)
    {
        Console.WriteLine(
            $"{result.Name}: {result.Message}");
    }

    if (results.Any(result => !result.Passed))
    {
        throw new InvalidOperationException(
            "Security gate failed. Deployment stopped.");
    }
}

The deployment process should terminate with a non-zero exit code when a blocking security condition is detected.

That makes the command usable from both local development and CI/CD.

Defining Security Policies

A security gate is only useful when the policy is clear.

For example:

FindingDevelopmentProduction
InformationalAllowAllow
Low severityAllowReview
Medium severityReviewReview
High severityReviewBlock
Critical severityBlockBlock
Hardcoded secretBlockBlock

These are example policies, not universal security thresholds.

Each organization should define severity thresholds based on its risk model, compliance requirements, application architecture, and exposure.

Avoiding False Positives

Security scanners can produce findings that require human review.

For example:

Potential secret detected

does not always mean a real credential exists.

A string such as:

var example = "api_key";

could be documentation or test data.

Therefore, a mature workflow distinguishes:

Scanner Finding
      ↓
Policy Evaluation
      ↓
Confirmed / Accepted / Blocked

Do not weaken the entire security gate because a small number of findings are noisy.

Instead, create a controlled mechanism for documented exceptions.

Handling Security Exceptions

Sometimes a deployment needs to proceed despite a known finding.

An exception should be explicit and traceable.

For example:

Security finding:
Package X — High severity

Exception:
Approved until 2026-10-15

Reason:
Vendor patch unavailable

Owner:
Security Team

Avoid implementing a generic option such as:

azd company secure-deploy --skip-security

for normal developer use.

A bypass that is too easy eventually becomes the default workflow.

Security Gates in CI/CD

The same command can be used by a CI/CD pipeline:

steps:
  - script: dotnet restore
    displayName: Restore

  - script: dotnet test --configuration Release
    displayName: Test

  - script: azd company secure-deploy
    displayName: Secure Deployment

If the extension returns a non-zero exit code, the pipeline stops.

The important benefit is consistency:

Developer machine
       |
       +--> Security Gate

CI/CD
       |
       +--> Same Security Gate

Using the same validation logic in both places reduces differences between local and automated deployment behavior.

Security Gate vs Azure Policy

Security gates and Azure Policy solve different problems.

AreaDeployment Security GateAzure Policy
Primary focusDeployment workflowAzure resource governance
TimingBefore/during deploymentResource governance
Source scanningYesNo
Dependency scanningYesNo
Infrastructure validationYesYes, depending on policy
Runtime governanceLimitedStronger
Application-specific checksStrongLimited
Central Azure governanceLimitedStrong

A mature Azure environment can use both.

The security gate can prevent problematic deployment artifacts from being submitted, while Azure Policy provides centralized governance for Azure resources.

Common Mistakes

Making Every Finding a Blocking Failure

If every informational warning stops deployment, developers may eventually disable the security gate.

Use meaningful severity thresholds.

Allowing Easy Bypasses

Security controls that can be bypassed with one command provide limited protection.

Exceptions should be deliberate and auditable.

Scanning Only Source Code

Dependencies, container images, infrastructure, and deployment configuration also need consideration.

Hardcoding Security Rules

Security requirements change.

Keep policies configurable where appropriate rather than embedding every rule directly into application code.

Printing Secrets During Diagnostics

A deployment tool should never dump environment variables or connection strings simply because a security check failed.

Troubleshooting Security Gates

When a deployment is blocked:

Step 1: Identify the Failed Check

The command should report something like:

Dependency Scan: PASS
Secret Scan: PASS
Infrastructure Scan: FAIL
Container Scan: NOT RUN

Step 2: Inspect the Finding

Determine whether the finding is:

Real vulnerability
False positive
Accepted risk
Configuration problem

Step 3: Fix or Document

Do not simply rerun the deployment.

Either fix the issue or follow the organization's approved exception process.

Step 4: Run the Gate Again

azd company secure-deploy

The deployment should proceed only when the blocking conditions have been resolved or appropriately approved.

Best Practices

  1. Run security checks before production deployment.

  2. Reuse established security scanners instead of implementing custom scanners from scratch.

  3. Define severity-based blocking policies.

  4. Scan source, dependencies, infrastructure, and container images where applicable.

  5. Keep secrets outside source code and deployment logs.

  6. Make security failures return non-zero exit codes.

  7. Use the same gate in local and CI/CD workflows.

  8. Keep security exceptions explicit and time-bound.

  9. Avoid a simple permanent bypass mechanism.

  10. Review security policies regularly as application and organizational requirements change.

Advantages and Disadvantages

Advantages

  • Creates a consistent security checkpoint.

  • Can prevent vulnerable artifacts from reaching production.

  • Integrates security into existing azd deployment workflows.

  • Can combine multiple specialized security tools.

  • Works well in CI/CD.

  • Makes organization-specific security requirements repeatable.

Disadvantages

  • Adds additional deployment time.

  • Security scanners can generate false positives.

  • Requires ongoing policy maintenance.

  • Poorly configured gates can block legitimate deployments.

  • Custom extensions become another component that must be maintained.

  • Security gates do not replace runtime monitoring or centralized Azure governance.

Conclusion

Azure Developer CLI extensions can provide a practical orchestration layer for adding security gates to Azure deployments.

For .NET applications, the strongest approach is to check the complete deployment path:

Source
  ↓
Dependencies
  ↓
Infrastructure
  ↓
Container
  ↓
Azure Resources
  ↓
Application

Each layer has different security concerns.

The custom extension should not attempt to become a complete security platform. Instead, it should coordinate established security tools, apply the organization's deployment policy, and stop the workflow when a blocking condition is detected.

Combined with Azure Policy, CI/CD controls, secret management, and runtime monitoring, a security-aware azd workflow can move important security checks closer to the point where deployment decisions are made.