The azure-native:storage:BlobServiceProperties resource, part of the Pulumi Azure Native provider, configures service-level settings for Azure Blob Storage: soft delete, versioning, change feed, CORS, and access tracking. This guide focuses on three capabilities: soft delete with permanent delete override, last access time tracking for lifecycle policies, and combined production settings.
This resource configures an existing storage account’s blob service. The storage account and resource group must already exist. The examples are intentionally small. Combine them with your own storage accounts and lifecycle policies.
Enable soft delete with permanent delete option
Teams protecting against accidental deletion enable soft delete to retain deleted blobs for a recovery period, while the allowPermanentDelete flag lets authorized users bypass soft delete for immediate removal of sensitive data.
import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";
const blobServiceProperties = new azure_native.storage.BlobServiceProperties("blobServiceProperties", {
accountName: "sto8607",
blobServicesName: "default",
deleteRetentionPolicy: {
allowPermanentDelete: true,
days: 300,
enabled: true,
},
isVersioningEnabled: true,
resourceGroupName: "res4410",
});
import pulumi
import pulumi_azure_native as azure_native
blob_service_properties = azure_native.storage.BlobServiceProperties("blobServiceProperties",
account_name="sto8607",
blob_services_name="default",
delete_retention_policy={
"allow_permanent_delete": True,
"days": 300,
"enabled": True,
},
is_versioning_enabled=True,
resource_group_name="res4410")
package main
import (
storage "github.com/pulumi/pulumi-azure-native-sdk/storage/v3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := storage.NewBlobServiceProperties(ctx, "blobServiceProperties", &storage.BlobServicePropertiesArgs{
AccountName: pulumi.String("sto8607"),
BlobServicesName: pulumi.String("default"),
DeleteRetentionPolicy: &storage.DeleteRetentionPolicyArgs{
AllowPermanentDelete: pulumi.Bool(true),
Days: pulumi.Int(300),
Enabled: pulumi.Bool(true),
},
IsVersioningEnabled: pulumi.Bool(true),
ResourceGroupName: pulumi.String("res4410"),
})
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 blobServiceProperties = new AzureNative.Storage.BlobServiceProperties("blobServiceProperties", new()
{
AccountName = "sto8607",
BlobServicesName = "default",
DeleteRetentionPolicy = new AzureNative.Storage.Inputs.DeleteRetentionPolicyArgs
{
AllowPermanentDelete = true,
Days = 300,
Enabled = true,
},
IsVersioningEnabled = true,
ResourceGroupName = "res4410",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.storage.BlobServiceProperties;
import com.pulumi.azurenative.storage.BlobServicePropertiesArgs;
import com.pulumi.azurenative.storage.inputs.DeleteRetentionPolicyArgs;
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 blobServiceProperties = new BlobServiceProperties("blobServiceProperties", BlobServicePropertiesArgs.builder()
.accountName("sto8607")
.blobServicesName("default")
.deleteRetentionPolicy(DeleteRetentionPolicyArgs.builder()
.allowPermanentDelete(true)
.days(300)
.enabled(true)
.build())
.isVersioningEnabled(true)
.resourceGroupName("res4410")
.build());
}
}
resources:
blobServiceProperties:
type: azure-native:storage:BlobServiceProperties
properties:
accountName: sto8607
blobServicesName: default
deleteRetentionPolicy:
allowPermanentDelete: true
days: 300
enabled: true
isVersioningEnabled: true
resourceGroupName: res4410
When deleteRetentionPolicy is enabled, deleted blobs remain recoverable for the specified number of days. Setting allowPermanentDelete to true permits authorized operations to skip the retention period and delete immediately. The isVersioningEnabled property works alongside soft delete to preserve blob history.
Track last access time for lifecycle policies
Storage lifecycle policies can move or delete blobs based on when they were last accessed, reducing costs by tiering cold data automatically.
import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";
const blobServiceProperties = new azure_native.storage.BlobServiceProperties("blobServiceProperties", {
accountName: "sto8607",
blobServicesName: "default",
lastAccessTimeTrackingPolicy: {
blobType: ["blockBlob"],
enable: true,
name: azure_native.storage.Name.AccessTimeTracking,
trackingGranularityInDays: 1,
},
resourceGroupName: "res4410",
});
import pulumi
import pulumi_azure_native as azure_native
blob_service_properties = azure_native.storage.BlobServiceProperties("blobServiceProperties",
account_name="sto8607",
blob_services_name="default",
last_access_time_tracking_policy={
"blob_type": ["blockBlob"],
"enable": True,
"name": azure_native.storage.Name.ACCESS_TIME_TRACKING,
"tracking_granularity_in_days": 1,
},
resource_group_name="res4410")
package main
import (
storage "github.com/pulumi/pulumi-azure-native-sdk/storage/v3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := storage.NewBlobServiceProperties(ctx, "blobServiceProperties", &storage.BlobServicePropertiesArgs{
AccountName: pulumi.String("sto8607"),
BlobServicesName: pulumi.String("default"),
LastAccessTimeTrackingPolicy: &storage.LastAccessTimeTrackingPolicyArgs{
BlobType: pulumi.StringArray{
pulumi.String("blockBlob"),
},
Enable: pulumi.Bool(true),
Name: pulumi.String(storage.NameAccessTimeTracking),
TrackingGranularityInDays: pulumi.Int(1),
},
ResourceGroupName: pulumi.String("res4410"),
})
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 blobServiceProperties = new AzureNative.Storage.BlobServiceProperties("blobServiceProperties", new()
{
AccountName = "sto8607",
BlobServicesName = "default",
LastAccessTimeTrackingPolicy = new AzureNative.Storage.Inputs.LastAccessTimeTrackingPolicyArgs
{
BlobType = new[]
{
"blockBlob",
},
Enable = true,
Name = AzureNative.Storage.Name.AccessTimeTracking,
TrackingGranularityInDays = 1,
},
ResourceGroupName = "res4410",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.storage.BlobServiceProperties;
import com.pulumi.azurenative.storage.BlobServicePropertiesArgs;
import com.pulumi.azurenative.storage.inputs.LastAccessTimeTrackingPolicyArgs;
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 blobServiceProperties = new BlobServiceProperties("blobServiceProperties", BlobServicePropertiesArgs.builder()
.accountName("sto8607")
.blobServicesName("default")
.lastAccessTimeTrackingPolicy(LastAccessTimeTrackingPolicyArgs.builder()
.blobType("blockBlob")
.enable(true)
.name("AccessTimeTracking")
.trackingGranularityInDays(1)
.build())
.resourceGroupName("res4410")
.build());
}
}
resources:
blobServiceProperties:
type: azure-native:storage:BlobServiceProperties
properties:
accountName: sto8607
blobServicesName: default
lastAccessTimeTrackingPolicy:
blobType:
- blockBlob
enable: true
name: AccessTimeTracking
trackingGranularityInDays: 1
resourceGroupName: res4410
The lastAccessTimeTrackingPolicy records read operations at the granularity you specify. Setting trackingGranularityInDays to 1 means access times update daily. The blobType array limits tracking to specific blob types; here, only block blobs are tracked. Lifecycle policies can then reference this access time to tier or delete cold data.
Configure comprehensive blob service settings
Production storage accounts typically combine soft delete, versioning, change feed for auditing, and CORS for browser access.
import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";
const blobServiceProperties = new azure_native.storage.BlobServiceProperties("blobServiceProperties", {
accountName: "sto8607",
blobServicesName: "default",
changeFeed: {
enabled: true,
retentionInDays: 7,
},
cors: {
corsRules: [
{
allowedHeaders: [
"x-ms-meta-abc",
"x-ms-meta-data*",
"x-ms-meta-target*",
],
allowedMethods: [
azure_native.storage.AllowedMethods.GET,
azure_native.storage.AllowedMethods.HEAD,
azure_native.storage.AllowedMethods.POST,
azure_native.storage.AllowedMethods.OPTIONS,
azure_native.storage.AllowedMethods.MERGE,
azure_native.storage.AllowedMethods.PUT,
],
allowedOrigins: [
"http://www.contoso.com",
"http://www.fabrikam.com",
],
exposedHeaders: ["x-ms-meta-*"],
maxAgeInSeconds: 100,
},
{
allowedHeaders: ["*"],
allowedMethods: [azure_native.storage.AllowedMethods.GET],
allowedOrigins: ["*"],
exposedHeaders: ["*"],
maxAgeInSeconds: 2,
},
{
allowedHeaders: ["x-ms-meta-12345675754564*"],
allowedMethods: [
azure_native.storage.AllowedMethods.GET,
azure_native.storage.AllowedMethods.PUT,
],
allowedOrigins: [
"http://www.abc23.com",
"https://www.fabrikam.com/*",
],
exposedHeaders: [
"x-ms-meta-abc",
"x-ms-meta-data*",
"x -ms-meta-target*",
],
maxAgeInSeconds: 2000,
},
],
},
defaultServiceVersion: "2017-07-29",
deleteRetentionPolicy: {
days: 300,
enabled: true,
},
isVersioningEnabled: true,
resourceGroupName: "res4410",
});
import pulumi
import pulumi_azure_native as azure_native
blob_service_properties = azure_native.storage.BlobServiceProperties("blobServiceProperties",
account_name="sto8607",
blob_services_name="default",
change_feed={
"enabled": True,
"retention_in_days": 7,
},
cors={
"cors_rules": [
{
"allowed_headers": [
"x-ms-meta-abc",
"x-ms-meta-data*",
"x-ms-meta-target*",
],
"allowed_methods": [
azure_native.storage.AllowedMethods.GET,
azure_native.storage.AllowedMethods.HEAD,
azure_native.storage.AllowedMethods.POST,
azure_native.storage.AllowedMethods.OPTIONS,
azure_native.storage.AllowedMethods.MERGE,
azure_native.storage.AllowedMethods.PUT,
],
"allowed_origins": [
"http://www.contoso.com",
"http://www.fabrikam.com",
],
"exposed_headers": ["x-ms-meta-*"],
"max_age_in_seconds": 100,
},
{
"allowed_headers": ["*"],
"allowed_methods": [azure_native.storage.AllowedMethods.GET],
"allowed_origins": ["*"],
"exposed_headers": ["*"],
"max_age_in_seconds": 2,
},
{
"allowed_headers": ["x-ms-meta-12345675754564*"],
"allowed_methods": [
azure_native.storage.AllowedMethods.GET,
azure_native.storage.AllowedMethods.PUT,
],
"allowed_origins": [
"http://www.abc23.com",
"https://www.fabrikam.com/*",
],
"exposed_headers": [
"x-ms-meta-abc",
"x-ms-meta-data*",
"x -ms-meta-target*",
],
"max_age_in_seconds": 2000,
},
],
},
default_service_version="2017-07-29",
delete_retention_policy={
"days": 300,
"enabled": True,
},
is_versioning_enabled=True,
resource_group_name="res4410")
package main
import (
storage "github.com/pulumi/pulumi-azure-native-sdk/storage/v3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := storage.NewBlobServiceProperties(ctx, "blobServiceProperties", &storage.BlobServicePropertiesArgs{
AccountName: pulumi.String("sto8607"),
BlobServicesName: pulumi.String("default"),
ChangeFeed: &storage.ChangeFeedArgs{
Enabled: pulumi.Bool(true),
RetentionInDays: pulumi.Int(7),
},
Cors: &storage.CorsRulesArgs{
CorsRules: storage.CorsRuleArray{
&storage.CorsRuleArgs{
AllowedHeaders: pulumi.StringArray{
pulumi.String("x-ms-meta-abc"),
pulumi.String("x-ms-meta-data*"),
pulumi.String("x-ms-meta-target*"),
},
AllowedMethods: pulumi.StringArray{
pulumi.String(storage.AllowedMethodsGET),
pulumi.String(storage.AllowedMethodsHEAD),
pulumi.String(storage.AllowedMethodsPOST),
pulumi.String(storage.AllowedMethodsOPTIONS),
pulumi.String(storage.AllowedMethodsMERGE),
pulumi.String(storage.AllowedMethodsPUT),
},
AllowedOrigins: pulumi.StringArray{
pulumi.String("http://www.contoso.com"),
pulumi.String("http://www.fabrikam.com"),
},
ExposedHeaders: pulumi.StringArray{
pulumi.String("x-ms-meta-*"),
},
MaxAgeInSeconds: pulumi.Int(100),
},
&storage.CorsRuleArgs{
AllowedHeaders: pulumi.StringArray{
pulumi.String("*"),
},
AllowedMethods: pulumi.StringArray{
pulumi.String(storage.AllowedMethodsGET),
},
AllowedOrigins: pulumi.StringArray{
pulumi.String("*"),
},
ExposedHeaders: pulumi.StringArray{
pulumi.String("*"),
},
MaxAgeInSeconds: pulumi.Int(2),
},
&storage.CorsRuleArgs{
AllowedHeaders: pulumi.StringArray{
pulumi.String("x-ms-meta-12345675754564*"),
},
AllowedMethods: pulumi.StringArray{
pulumi.String(storage.AllowedMethodsGET),
pulumi.String(storage.AllowedMethodsPUT),
},
AllowedOrigins: pulumi.StringArray{
pulumi.String("http://www.abc23.com"),
pulumi.String("https://www.fabrikam.com/*"),
},
ExposedHeaders: pulumi.StringArray{
pulumi.String("x-ms-meta-abc"),
pulumi.String("x-ms-meta-data*"),
pulumi.String("x -ms-meta-target*"),
},
MaxAgeInSeconds: pulumi.Int(2000),
},
},
},
DefaultServiceVersion: pulumi.String("2017-07-29"),
DeleteRetentionPolicy: &storage.DeleteRetentionPolicyArgs{
Days: pulumi.Int(300),
Enabled: pulumi.Bool(true),
},
IsVersioningEnabled: pulumi.Bool(true),
ResourceGroupName: pulumi.String("res4410"),
})
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 blobServiceProperties = new AzureNative.Storage.BlobServiceProperties("blobServiceProperties", new()
{
AccountName = "sto8607",
BlobServicesName = "default",
ChangeFeed = new AzureNative.Storage.Inputs.ChangeFeedArgs
{
Enabled = true,
RetentionInDays = 7,
},
Cors = new AzureNative.Storage.Inputs.CorsRulesArgs
{
CorsRules = new[]
{
new AzureNative.Storage.Inputs.CorsRuleArgs
{
AllowedHeaders = new[]
{
"x-ms-meta-abc",
"x-ms-meta-data*",
"x-ms-meta-target*",
},
AllowedMethods = new[]
{
AzureNative.Storage.AllowedMethods.GET,
AzureNative.Storage.AllowedMethods.HEAD,
AzureNative.Storage.AllowedMethods.POST,
AzureNative.Storage.AllowedMethods.OPTIONS,
AzureNative.Storage.AllowedMethods.MERGE,
AzureNative.Storage.AllowedMethods.PUT,
},
AllowedOrigins = new[]
{
"http://www.contoso.com",
"http://www.fabrikam.com",
},
ExposedHeaders = new[]
{
"x-ms-meta-*",
},
MaxAgeInSeconds = 100,
},
new AzureNative.Storage.Inputs.CorsRuleArgs
{
AllowedHeaders = new[]
{
"*",
},
AllowedMethods = new[]
{
AzureNative.Storage.AllowedMethods.GET,
},
AllowedOrigins = new[]
{
"*",
},
ExposedHeaders = new[]
{
"*",
},
MaxAgeInSeconds = 2,
},
new AzureNative.Storage.Inputs.CorsRuleArgs
{
AllowedHeaders = new[]
{
"x-ms-meta-12345675754564*",
},
AllowedMethods = new[]
{
AzureNative.Storage.AllowedMethods.GET,
AzureNative.Storage.AllowedMethods.PUT,
},
AllowedOrigins = new[]
{
"http://www.abc23.com",
"https://www.fabrikam.com/*",
},
ExposedHeaders = new[]
{
"x-ms-meta-abc",
"x-ms-meta-data*",
"x -ms-meta-target*",
},
MaxAgeInSeconds = 2000,
},
},
},
DefaultServiceVersion = "2017-07-29",
DeleteRetentionPolicy = new AzureNative.Storage.Inputs.DeleteRetentionPolicyArgs
{
Days = 300,
Enabled = true,
},
IsVersioningEnabled = true,
ResourceGroupName = "res4410",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.storage.BlobServiceProperties;
import com.pulumi.azurenative.storage.BlobServicePropertiesArgs;
import com.pulumi.azurenative.storage.inputs.ChangeFeedArgs;
import com.pulumi.azurenative.storage.inputs.CorsRulesArgs;
import com.pulumi.azurenative.storage.inputs.DeleteRetentionPolicyArgs;
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 blobServiceProperties = new BlobServiceProperties("blobServiceProperties", BlobServicePropertiesArgs.builder()
.accountName("sto8607")
.blobServicesName("default")
.changeFeed(ChangeFeedArgs.builder()
.enabled(true)
.retentionInDays(7)
.build())
.cors(CorsRulesArgs.builder()
.corsRules(
CorsRuleArgs.builder()
.allowedHeaders(
"x-ms-meta-abc",
"x-ms-meta-data*",
"x-ms-meta-target*")
.allowedMethods(
"GET",
"HEAD",
"POST",
"OPTIONS",
"MERGE",
"PUT")
.allowedOrigins(
"http://www.contoso.com",
"http://www.fabrikam.com")
.exposedHeaders("x-ms-meta-*")
.maxAgeInSeconds(100)
.build(),
CorsRuleArgs.builder()
.allowedHeaders("*")
.allowedMethods("GET")
.allowedOrigins("*")
.exposedHeaders("*")
.maxAgeInSeconds(2)
.build(),
CorsRuleArgs.builder()
.allowedHeaders("x-ms-meta-12345675754564*")
.allowedMethods(
"GET",
"PUT")
.allowedOrigins(
"http://www.abc23.com",
"https://www.fabrikam.com/*")
.exposedHeaders(
"x-ms-meta-abc",
"x-ms-meta-data*",
"x -ms-meta-target*")
.maxAgeInSeconds(2000)
.build())
.build())
.defaultServiceVersion("2017-07-29")
.deleteRetentionPolicy(DeleteRetentionPolicyArgs.builder()
.days(300)
.enabled(true)
.build())
.isVersioningEnabled(true)
.resourceGroupName("res4410")
.build());
}
}
resources:
blobServiceProperties:
type: azure-native:storage:BlobServiceProperties
properties:
accountName: sto8607
blobServicesName: default
changeFeed:
enabled: true
retentionInDays: 7
cors:
corsRules:
- allowedHeaders:
- x-ms-meta-abc
- x-ms-meta-data*
- x-ms-meta-target*
allowedMethods:
- GET
- HEAD
- POST
- OPTIONS
- MERGE
- PUT
allowedOrigins:
- http://www.contoso.com
- http://www.fabrikam.com
exposedHeaders:
- x-ms-meta-*
maxAgeInSeconds: 100
- allowedHeaders:
- '*'
allowedMethods:
- GET
allowedOrigins:
- '*'
exposedHeaders:
- '*'
maxAgeInSeconds: 2
- allowedHeaders:
- x-ms-meta-12345675754564*
allowedMethods:
- GET
- PUT
allowedOrigins:
- http://www.abc23.com
- https://www.fabrikam.com/*
exposedHeaders:
- x-ms-meta-abc
- x-ms-meta-data*
- x -ms-meta-target*
maxAgeInSeconds: 2000
defaultServiceVersion: 2017-07-29
deleteRetentionPolicy:
days: 300
enabled: true
isVersioningEnabled: true
resourceGroupName: res4410
This configuration enables multiple features together. The changeFeed property records all blob operations for auditing, retaining events for the specified days. The cors property defines rules for cross-origin browser requests: which origins, methods, and headers are allowed. The defaultServiceVersion sets the API version for requests that don’t specify one. All these features operate independently but share the same blob service configuration.
Beyond these examples
These snippets focus on specific blob service features: soft delete and versioning, last access time tracking, and change feed and CORS configuration. They’re intentionally minimal rather than full storage solutions.
The examples reference pre-existing infrastructure such as Azure storage accounts and resource groups. They focus on configuring the blob service rather than provisioning the storage account itself.
To keep things focused, common blob service patterns are omitted, including:
- Container-level soft delete (containerDeleteRetentionPolicy)
- Point-in-time restore (restorePolicy)
- Automatic snapshot policies (automaticSnapshotPolicyEnabled)
These omissions are intentional: the goal is to illustrate how each blob service feature is wired, not provide drop-in storage modules. See the BlobServiceProperties resource reference for all available configuration options.
Let's configure Azure Blob Storage Service Properties
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Resource Configuration & Constraints
blobServicesName parameter only accepts the value default and cannot be changed after creation.accountName, blobServicesName, and resourceGroupName. You must recreate the resource to change any of these.Data Protection & Retention
isVersioningEnabled for new configurations. The automaticSnapshotPolicyEnabled property is deprecated.deleteRetentionPolicy with enabled: true and specify the retention period in days (e.g., 300 days).true in deleteRetentionPolicy, it allows permanent deletion of soft-deleted blobs before the retention period expires.changeFeed with enabled: true and optionally specify retentionInDays for how long to keep change feed events.isVersioningEnabled: true to enable versioning for blobs in the storage account.Access & CORS Configuration
CorsRule elements. If no rules are included, all CORS rules will be deleted and CORS will be disabled.lastAccessTimeTrackingPolicy with enable: true, specify blobType (e.g., blockBlob), and set trackingGranularityInDays (e.g., 1 for daily tracking).