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:
| Finding | Development | Production |
|---|
| Informational | Allow | Allow |
| Low severity | Allow | Review |
| Medium severity | Review | Review |
| High severity | Review | Block |
| Critical severity | Block | Block |
| Hardcoded secret | Block | Block |
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.
| Area | Deployment Security Gate | Azure Policy |
|---|
| Primary focus | Deployment workflow | Azure resource governance |
| Timing | Before/during deployment | Resource governance |
| Source scanning | Yes | No |
| Dependency scanning | Yes | No |
| Infrastructure validation | Yes | Yes, depending on policy |
| Runtime governance | Limited | Stronger |
| Application-specific checks | Strong | Limited |
| Central Azure governance | Limited | Strong |
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
Run security checks before production deployment.
Reuse established security scanners instead of implementing custom scanners from scratch.
Define severity-based blocking policies.
Scan source, dependencies, infrastructure, and container images where applicable.
Keep secrets outside source code and deployment logs.
Make security failures return non-zero exit codes.
Use the same gate in local and CI/CD workflows.
Keep security exceptions explicit and time-bound.
Avoid a simple permanent bypass mechanism.
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.