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 FREEFrequently Asked Questions
Storage Configuration
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.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/).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
recurringScans with isEnabled: true. Optionally add an emails array for notifications and set emailSubscriptionAdmins: true to notify subscription administrators.Resource Configuration
databaseName, resourceGroupName, serverName, and vulnerabilityAssessmentName properties are immutable and cannot be modified after creation."default" as the assessment name, which is the standard value shown in all examples.