Configure Azure SQL Database Vulnerability Assessment

The azure-native:sql:DatabaseVulnerabilityAssessment resource, part of the Pulumi Azure Native provider, configures vulnerability scanning for an Azure SQL Database: where scan results are stored, how often scans run, and who receives findings. This guide focuses on three capabilities: storage authentication methods, recurring scan scheduling, and email notification configuration.

Vulnerability assessments require an existing Azure SQL Database, server, and storage account with a blob container for scan results. The examples are intentionally small. Combine them with your own database infrastructure and notification preferences.

Enable scanning with storage account access key

Most vulnerability assessment deployments start with the minimum required configuration: a storage location for scan results and credentials to write to that location.

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

const databaseVulnerabilityAssessment = new azure_native.sql.DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment", {
    databaseName: "testdb",
    resourceGroupName: "vulnerabilityaseessmenttest-4799",
    serverName: "vulnerabilityaseessmenttest-6440",
    storageAccountAccessKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    storageContainerPath: "https://myStorage.blob.core.windows.net/vulnerability-assessment/",
    vulnerabilityAssessmentName: "default",
});
import pulumi
import pulumi_azure_native as azure_native

database_vulnerability_assessment = azure_native.sql.DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment",
    database_name="testdb",
    resource_group_name="vulnerabilityaseessmenttest-4799",
    server_name="vulnerabilityaseessmenttest-6440",
    storage_account_access_key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    storage_container_path="https://myStorage.blob.core.windows.net/vulnerability-assessment/",
    vulnerability_assessment_name="default")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := sql.NewDatabaseVulnerabilityAssessment(ctx, "databaseVulnerabilityAssessment", &sql.DatabaseVulnerabilityAssessmentArgs{
			DatabaseName:                pulumi.String("testdb"),
			ResourceGroupName:           pulumi.String("vulnerabilityaseessmenttest-4799"),
			ServerName:                  pulumi.String("vulnerabilityaseessmenttest-6440"),
			StorageAccountAccessKey:     pulumi.String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"),
			StorageContainerPath:        pulumi.String("https://myStorage.blob.core.windows.net/vulnerability-assessment/"),
			VulnerabilityAssessmentName: pulumi.String("default"),
		})
		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 databaseVulnerabilityAssessment = new AzureNative.Sql.DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment", new()
    {
        DatabaseName = "testdb",
        ResourceGroupName = "vulnerabilityaseessmenttest-4799",
        ServerName = "vulnerabilityaseessmenttest-6440",
        StorageAccountAccessKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        StorageContainerPath = "https://myStorage.blob.core.windows.net/vulnerability-assessment/",
        VulnerabilityAssessmentName = "default",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.sql.DatabaseVulnerabilityAssessment;
import com.pulumi.azurenative.sql.DatabaseVulnerabilityAssessmentArgs;
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 databaseVulnerabilityAssessment = new DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment", DatabaseVulnerabilityAssessmentArgs.builder()
            .databaseName("testdb")
            .resourceGroupName("vulnerabilityaseessmenttest-4799")
            .serverName("vulnerabilityaseessmenttest-6440")
            .storageAccountAccessKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
            .storageContainerPath("https://myStorage.blob.core.windows.net/vulnerability-assessment/")
            .vulnerabilityAssessmentName("default")
            .build());

    }
}
resources:
  databaseVulnerabilityAssessment:
    type: azure-native:sql:DatabaseVulnerabilityAssessment
    properties:
      databaseName: testdb
      resourceGroupName: vulnerabilityaseessmenttest-4799
      serverName: vulnerabilityaseessmenttest-6440
      storageAccountAccessKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      storageContainerPath: https://myStorage.blob.core.windows.net/vulnerability-assessment/
      vulnerabilityAssessmentName: default

The storageContainerPath property points to a blob container where Azure SQL writes scan results. The storageAccountAccessKey provides write access to that container. This authentication method works when the storage account is publicly accessible or when the SQL server has network access to it.

Enable scanning with SAS token authentication

When storage accounts sit behind VNets or firewalls, SAS tokens provide scoped access without exposing full account keys.

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

const databaseVulnerabilityAssessment = new azure_native.sql.DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment", {
    databaseName: "testdb",
    resourceGroupName: "vulnerabilityaseessmenttest-4799",
    serverName: "vulnerabilityaseessmenttest-6440",
    storageContainerPath: "https://myStorage.blob.core.windows.net/vulnerability-assessment/",
    storageContainerSasKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    vulnerabilityAssessmentName: "default",
});
import pulumi
import pulumi_azure_native as azure_native

database_vulnerability_assessment = azure_native.sql.DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment",
    database_name="testdb",
    resource_group_name="vulnerabilityaseessmenttest-4799",
    server_name="vulnerabilityaseessmenttest-6440",
    storage_container_path="https://myStorage.blob.core.windows.net/vulnerability-assessment/",
    storage_container_sas_key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    vulnerability_assessment_name="default")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := sql.NewDatabaseVulnerabilityAssessment(ctx, "databaseVulnerabilityAssessment", &sql.DatabaseVulnerabilityAssessmentArgs{
			DatabaseName:                pulumi.String("testdb"),
			ResourceGroupName:           pulumi.String("vulnerabilityaseessmenttest-4799"),
			ServerName:                  pulumi.String("vulnerabilityaseessmenttest-6440"),
			StorageContainerPath:        pulumi.String("https://myStorage.blob.core.windows.net/vulnerability-assessment/"),
			StorageContainerSasKey:      pulumi.String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"),
			VulnerabilityAssessmentName: pulumi.String("default"),
		})
		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 databaseVulnerabilityAssessment = new AzureNative.Sql.DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment", new()
    {
        DatabaseName = "testdb",
        ResourceGroupName = "vulnerabilityaseessmenttest-4799",
        ServerName = "vulnerabilityaseessmenttest-6440",
        StorageContainerPath = "https://myStorage.blob.core.windows.net/vulnerability-assessment/",
        StorageContainerSasKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        VulnerabilityAssessmentName = "default",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.sql.DatabaseVulnerabilityAssessment;
import com.pulumi.azurenative.sql.DatabaseVulnerabilityAssessmentArgs;
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 databaseVulnerabilityAssessment = new DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment", DatabaseVulnerabilityAssessmentArgs.builder()
            .databaseName("testdb")
            .resourceGroupName("vulnerabilityaseessmenttest-4799")
            .serverName("vulnerabilityaseessmenttest-6440")
            .storageContainerPath("https://myStorage.blob.core.windows.net/vulnerability-assessment/")
            .storageContainerSasKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
            .vulnerabilityAssessmentName("default")
            .build());

    }
}
resources:
  databaseVulnerabilityAssessment:
    type: azure-native:sql:DatabaseVulnerabilityAssessment
    properties:
      databaseName: testdb
      resourceGroupName: vulnerabilityaseessmenttest-4799
      serverName: vulnerabilityaseessmenttest-6440
      storageContainerPath: https://myStorage.blob.core.windows.net/vulnerability-assessment/
      storageContainerSasKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      vulnerabilityAssessmentName: default

The storageContainerSasKey replaces the access key with a time-limited shared access signature. This approach limits the scope of access and allows you to revoke credentials without rotating the entire storage account key. Use this when your storage account restricts access via firewall rules or VNet service endpoints.

Configure recurring scans with email notifications

Production deployments typically enable recurring scans and route findings to security teams via email, ensuring continuous monitoring without manual intervention.

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

const databaseVulnerabilityAssessment = new azure_native.sql.DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment", {
    databaseName: "testdb",
    recurringScans: {
        emailSubscriptionAdmins: true,
        emails: [
            "email1@mail.com",
            "email2@mail.com",
        ],
        isEnabled: true,
    },
    resourceGroupName: "vulnerabilityaseessmenttest-4799",
    serverName: "vulnerabilityaseessmenttest-6440",
    storageAccountAccessKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    storageContainerPath: "https://myStorage.blob.core.windows.net/vulnerability-assessment/",
    storageContainerSasKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    vulnerabilityAssessmentName: "default",
});
import pulumi
import pulumi_azure_native as azure_native

database_vulnerability_assessment = azure_native.sql.DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment",
    database_name="testdb",
    recurring_scans={
        "email_subscription_admins": True,
        "emails": [
            "email1@mail.com",
            "email2@mail.com",
        ],
        "is_enabled": True,
    },
    resource_group_name="vulnerabilityaseessmenttest-4799",
    server_name="vulnerabilityaseessmenttest-6440",
    storage_account_access_key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    storage_container_path="https://myStorage.blob.core.windows.net/vulnerability-assessment/",
    storage_container_sas_key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    vulnerability_assessment_name="default")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := sql.NewDatabaseVulnerabilityAssessment(ctx, "databaseVulnerabilityAssessment", &sql.DatabaseVulnerabilityAssessmentArgs{
			DatabaseName: pulumi.String("testdb"),
			RecurringScans: &sql.VulnerabilityAssessmentRecurringScansPropertiesArgs{
				EmailSubscriptionAdmins: pulumi.Bool(true),
				Emails: pulumi.StringArray{
					pulumi.String("email1@mail.com"),
					pulumi.String("email2@mail.com"),
				},
				IsEnabled: pulumi.Bool(true),
			},
			ResourceGroupName:           pulumi.String("vulnerabilityaseessmenttest-4799"),
			ServerName:                  pulumi.String("vulnerabilityaseessmenttest-6440"),
			StorageAccountAccessKey:     pulumi.String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"),
			StorageContainerPath:        pulumi.String("https://myStorage.blob.core.windows.net/vulnerability-assessment/"),
			StorageContainerSasKey:      pulumi.String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"),
			VulnerabilityAssessmentName: pulumi.String("default"),
		})
		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 databaseVulnerabilityAssessment = new AzureNative.Sql.DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment", new()
    {
        DatabaseName = "testdb",
        RecurringScans = new AzureNative.Sql.Inputs.VulnerabilityAssessmentRecurringScansPropertiesArgs
        {
            EmailSubscriptionAdmins = true,
            Emails = new[]
            {
                "email1@mail.com",
                "email2@mail.com",
            },
            IsEnabled = true,
        },
        ResourceGroupName = "vulnerabilityaseessmenttest-4799",
        ServerName = "vulnerabilityaseessmenttest-6440",
        StorageAccountAccessKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        StorageContainerPath = "https://myStorage.blob.core.windows.net/vulnerability-assessment/",
        StorageContainerSasKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        VulnerabilityAssessmentName = "default",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.sql.DatabaseVulnerabilityAssessment;
import com.pulumi.azurenative.sql.DatabaseVulnerabilityAssessmentArgs;
import com.pulumi.azurenative.sql.inputs.VulnerabilityAssessmentRecurringScansPropertiesArgs;
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 databaseVulnerabilityAssessment = new DatabaseVulnerabilityAssessment("databaseVulnerabilityAssessment", DatabaseVulnerabilityAssessmentArgs.builder()
            .databaseName("testdb")
            .recurringScans(VulnerabilityAssessmentRecurringScansPropertiesArgs.builder()
                .emailSubscriptionAdmins(true)
                .emails(                
                    "email1@mail.com",
                    "email2@mail.com")
                .isEnabled(true)
                .build())
            .resourceGroupName("vulnerabilityaseessmenttest-4799")
            .serverName("vulnerabilityaseessmenttest-6440")
            .storageAccountAccessKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
            .storageContainerPath("https://myStorage.blob.core.windows.net/vulnerability-assessment/")
            .storageContainerSasKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
            .vulnerabilityAssessmentName("default")
            .build());

    }
}
resources:
  databaseVulnerabilityAssessment:
    type: azure-native:sql:DatabaseVulnerabilityAssessment
    properties:
      databaseName: testdb
      recurringScans:
        emailSubscriptionAdmins: true
        emails:
          - email1@mail.com
          - email2@mail.com
        isEnabled: true
      resourceGroupName: vulnerabilityaseessmenttest-4799
      serverName: vulnerabilityaseessmenttest-6440
      storageAccountAccessKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      storageContainerPath: https://myStorage.blob.core.windows.net/vulnerability-assessment/
      storageContainerSasKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      vulnerabilityAssessmentName: default

The recurringScans block enables automated weekly scans. Set isEnabled to true to activate scanning. The emails array specifies recipients for scan results, while emailSubscriptionAdmins sends notifications to subscription administrators. Azure SQL runs scans automatically and delivers findings to the configured addresses.

Beyond these examples

These snippets focus on specific vulnerability assessment features: storage authentication, recurring scan scheduling, and email notification routing. They’re intentionally minimal rather than full security monitoring solutions.

The examples reference pre-existing infrastructure such as Azure SQL Database and server, Azure Storage account with blob container, and resource group. They focus on configuring the assessment rather than provisioning the database or storage infrastructure.

To keep things focused, common assessment patterns are omitted, including:

  • Baseline configuration and custom rules
  • Integration with Azure Security Center
  • Scan result retention policies
  • Role-based access control for assessment management

These omissions are intentional: the goal is to illustrate how each assessment feature is wired, not provide drop-in security modules. See the DatabaseVulnerabilityAssessment resource reference for all available configuration options.

Let's configure Azure SQL Database Vulnerability Assessment

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Storage Configuration
What's the difference between storageAccountAccessKey and storageContainerSasKey?
You must provide one or the other for authentication. Use storageAccountAccessKey for storage account key authentication, or storageContainerSasKey for SAS token authentication. Both only work if the storage account is not behind a VNet or firewall.
When is storageContainerPath required?
The storageContainerPath is required if a server-level vulnerability assessment policy doesn’t already set it. It specifies the blob storage container path for scan results (e.g., https://myStorage.blob.core.windows.net/VaScans/).
Can I use vulnerability assessment with storage behind a VNet or firewall?
No. Both storageAccountAccessKey and storageContainerSasKey only apply if the storage account is not behind a VNet or firewall. For protected storage accounts, use a server-level vulnerability assessment policy instead.
Recurring Scans & Notifications
How do I enable recurring vulnerability scans with email notifications?
Configure recurringScans with isEnabled: true. Optionally add an emails array for notifications and set emailSubscriptionAdmins: true to notify subscription administrators.
Resource Configuration
What properties can't be changed after creating the vulnerability assessment?
The databaseName, resourceGroupName, serverName, and vulnerabilityAssessmentName properties are immutable and cannot be modified after creation.
What should I use for vulnerabilityAssessmentName?
Use "default" as the assessment name, which is the standard value shown in all examples.

Using a different cloud?

Explore security guides for other cloud providers: