PowerShell 技能连载 - Azure 存储服务管理

适用于 PowerShell 7.0 及以上版本

Azure 存储服务是云基础设施中不可或缺的一环。无论是应用程序数据持久化、静态网站托管、大数据分析流水线,还是灾备归档,都离不开它。一个中型企业往往拥有数十个存储账户、数百个容器和海量的 Blob 对象,靠手工在 Azure 门户中逐一配置既容易出错,也难以保证一致性。

PowerShell 通过 Az.Storage 模块提供了对 Azure 存储的完整管理能力。从存储账户的创建与访问层选择,到 Blob 容器的生命周期策略,再到细粒度的访问控制和成本监控,都可以编写脚本实现自动化。对于需要跨区域部署或遵循合规要求的场景,脚本能确保每次操作都遵循相同的规范。

本文将通过三个实战场景,展示如何使用 PowerShell 高效管理 Azure 存储:首先是存储账户与 Blob 容器的批量管理,其次是文件共享与生命周期策略配置,最后是存储分析与成本监控。

存储账户与 Blob 容器管理

在 Azure 中,存储账户是所有存储服务的根容器。创建时需要选择合适的复制策略和访问层,以平衡性能和成本。下面的脚本展示了如何批量创建存储账户、配置访问层,以及管理 Blob 容器的完整流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 连接 Azure 账户(如已登录可跳过)
Connect-AzAccount

# 定义存储账户配置
$resourceGroup = "blog-storage-rg"
$location = "eastasia"
$accounts = @(
@{ Name = "stbloghotprod"; AccessTier = "Hot"; Sku = "Standard_LRS" }
@{ Name = "stblogcoolarch"; AccessTier = "Cool"; Sku = "Standard_GRS" }
@{ Name = "stblogpremv2"; AccessTier = "Hot"; Sku = "Premium_LRS" }
)

# 确保资源组存在
if (-not (Get-AzResourceGroup -Name $resourceGroup -ErrorAction SilentlyContinue)) {
New-AzResourceGroup -Name $resourceGroup -Location $location
Write-Host "已创建资源组: $resourceGroup"
}

# 批量创建存储账户
foreach ($acct in $accounts) {
$existing = Get-AzStorageAccount -ResourceGroupName $resourceGroup `
-Name $acct.Name -ErrorAction SilentlyContinue

if ($existing) {
Write-Host "存储账户 $($acct.Name) 已存在,跳过创建"
} else {
New-AzStorageAccount -ResourceGroupName $resourceGroup `
-Name $acct.Name `
-Location $location `
-SkuName $acct.Sku `
-Kind StorageV2 `
-AccessTier $acct.AccessTier `
-EnableHierarchicalNamespace $false
Write-Host "已创建存储账户: $($acct.Name) ($($acct.AccessTier) / $($acct.Sku))"
}
}

# 获取第一个存储账户的上下文并创建容器
$storageAccount = Get-AzStorageAccount -ResourceGroupName $resourceGroup `
-Name $accounts[0].Name
$ctx = $storageAccount.Context

# 定义容器列表及公开访问级别
$containers = @(
@{ Name = "images"; Access = "Blob" }
@{ Name = "documents"; Access = "Off" }
@{ Name = "logs"; Access = "Off" }
@{ Name = "backups"; Access = "Off" }
)

foreach ($c in $containers) {
$existing = Get-AzStorageContainer -Name $c.Name -Context $ctx `
-ErrorAction SilentlyContinue

if ($existing) {
Write-Host "容器 $($c.Name) 已存在"
} else {
New-AzStorageContainer -Name $c.Name -Context $ctx `
-Permission $c.Access
Write-Host "已创建容器: $($c.Name) (公开访问: $($c.Access))"
}
}

# 批量上传文件到 images 容器
$uploadPath = "/data/blog-assets"
if (Test-Path $uploadPath) {
$files = Get-ChildItem -Path $uploadPath -File
foreach ($file in $files) {
Set-AzStorageBlobContent -File $file.FullName `
-Container "images" `
-Blob $file.Name `
-Context $ctx `
-StandardBlobTier Hot `
-Force
Write-Host "已上传: $($file.Name) ($('{0:N2}' -f ($file.Length / 1KB)) KB)"
}
}

执行结果示例:

1
2
3
4
5
6
7
8
9
10
11
已创建资源组: blog-storage-rg
已创建存储账户: stbloghotprod (Hot / Standard_LRS)
已创建存储账户: stblogcoolarch (Cool / Standard_GRS)
已创建存储账户: stblogpremv2 (Hot / Premium_LRS)
已创建容器: images (公开访问: Blob)
已创建容器: documents (公开访问: Off)
已创建容器: logs (公开访问: Off)
已创建容器: backups (公开访问: Off)
已上传: hero-banner.png (245.67 KB)
已上传: logo-dark.svg (3.21 KB)
已上传: favicon.ico (4.12 KB)

文件共享与生命周期管理

当存储规模增长后,手动管理数据的分层和过期变得不现实。Azure 存储的生命周期管理(Lifecycle Management)可以根据规则自动将数据从热层迁移到冷层或归档层,甚至自动删除过期数据。结合 SAS 令牌,还可以为第三方应用授予临时访问权限,而无需暴露存储账户密钥。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# 获取存储账户上下文
$storageAccount = Get-AzStorageAccount -ResourceGroupName "blog-storage-rg" `
-Name "stbloghotprod"
$ctx = $storageAccount.Context

# 创建文件共享(Azure Files)
$shareName = "blog-shared-assets"
$share = Get-AzStorageShare -Name $shareName -Context $ctx `
-ErrorAction SilentlyContinue

if (-not $share) {
New-AzStorageShare -Name $shareName -Context $ctx
Write-Host "已创建文件共享: $shareName"
}

# 上传目录到文件共享
$localDir = "/data/shared-docs"
if (Test-Path $localDir) {
Get-ChildItem -Path $localDir -File | ForEach-Object {
Set-AzStorageFileContent -ShareName $shareName `
-Source $_.FullName `
-Path $_.Name `
-Context $ctx `
-Force
Write-Host "已上传到文件共享: $($_.Name)"
}
}

# 生成 SAS 令牌(只读,7 天有效)
$sasToken = New-AzStorageAccountSASToken `
-Service Blob,File `
-ResourceType Service,Container,Object `
-Permission "rl" `
-ExpiryTime (Get-Date).AddDays(7) `
-Context $ctx

Write-Host "SAS 令牌已生成(7 天只读):"
Write-Host $sasToken.Substring(0, 30) "..."

# 配置生命周期管理策略
$lifecycleRules = @(
@{
Name = "move-logs-to-cool"
Enabled = $true
Definition = @{
Filters = @{
PrefixMatch = @("logs/"]
BlobTypes = @("blockBlob")
}
Actions = @{
BaseBlob = @{
TierToCool = @{ DaysAfterModificationGreaterThan = 30 }
TierToArchive = @{ DaysAfterModificationGreaterThan = 90 }
Delete = @{ DaysAfterModificationGreaterThan = 365 }
}
}
}
}
@{
Name = "archive-old-backups"
Enabled = $true
Definition = @{
Filters = @{
PrefixMatch = @("backups/"]
BlobTypes = @("blockBlob")
}
Actions = @{
BaseBlob = @{
TierToArchive = @{ DaysAfterModificationGreaterThan = 60 }
Delete = @{ DaysAfterModificationGreaterThan = 180 }
}
}
}
}
)

# 应用生命周期策略
$policy = Set-AzStorageAccountManagementPolicy `
-ResourceGroupName "blog-storage-rg" `
-StorageAccountName "stbloghotprod" `
-Policy (@{ Rules = $lifecycleRules } | ConvertTo-Json -Depth 10 | ConvertFrom-Json)

Write-Host "已配置 $($lifecycleRules.Count) 条生命周期管理规则"

# 查看各容器中的 Blob 层级分布
$containers = @("images", "documents", "logs", "backups")
foreach ($containerName in $containers) {
$blobs = Get-AzStorageBlob -Container $containerName -Context $ctx `
-ErrorAction SilentlyContinue
$total = ($blobs | Measure-Object).Count
$totalSize = ($blobs | ForEach-Object { $_.Length } | Measure-Object -Sum).Sum
$sizeMB = if ($totalSize) { '{0:N2}' -f ($totalSize / 1MB) } else { '0.00' }
Write-Host "容器 $containerName : $total 个 Blob, 总计 $sizeMB MB"
}

执行结果示例:

1
2
3
4
5
6
7
8
9
10
11
已创建文件共享: blog-shared-assets
已上传到文件共享: architecture-diagram.pdf
已上传到文件共享: deployment-guide.docx
已上传到文件共享: runbook.xlsx
SAS 令牌已生成(7 天只读):
?sv=2024-08-03&ss=bf&srt=sco&sp=r ...
已配置 2 条生命周期管理规则
容器 images : 3 个 Blob, 总计 0.25 MB
容器 documents : 12 个 Blob, 总计 8.45 MB
容器 logs : 156 个 Blob, 总计 234.67 MB
容器 backups : 28 个 Blob, 总计 1024.33 MB

存储分析与监控

持续监控存储使用情况和访问模式是控制云成本的关键。通过 Az.StorageAz.Monitor 模块,可以自动化查询存储指标、分析访问日志,并生成成本报告。下面的脚本演示了如何收集这些信息并输出结构化的分析结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 获取存储账户信息
$resourceGroup = "blog-storage-rg"
$storageAccountName = "stbloghotprod"
$storageAccount = Get-AzStorageAccount -ResourceGroupName $resourceGroup `
-Name $storageAccountName
$ctx = $storageAccount.Context

# 查询存储账户的容量和使用情况
$usage = Get-AzStorageUsage -Location $storageAccount.Location
Write-Host "`n=== 存储账户配额使用情况 ==="
foreach ($u in $usage) {
$pct = if ($u.Limit -gt 0) { '{0:P1}' -f ($u.CurrentValue / $u.Limit) } else { 'N/A' }
Write-Host ("{0,-40} {1,6}/{2,6} ({3})" -f $u.Name.Value, `
$u.CurrentValue, $u.Limit, $pct)
}

# 启用 Blob 服务的访问日志(如尚未启用)
$logging = $ctx.Logging
if (-not $logging.LoggingOperations -contains "Read") {
Set-AzStorageServiceLoggingProperty -ServiceType Blob `
-LoggingOperations Read,Write,Delete `
-RetentionDays 30 `
-Context $ctx
Write-Host "`n已启用 Blob 访问日志(保留 30 天)"
}

# 查询最近的存储指标
$endTime = Get-Date
$startTime = $endTime.AddDays(-7)
$metricNames = @("UsedCapacity", "TransactionCount", "Ingress", "Egress")

Write-Host "`n=== 最近 7 天存储指标 ==="
foreach ($metric in $metricNames) {
$metrics = Get-AzMetric -ResourceId $storageAccount.Id `
-MetricName $metric `
-StartTime $startTime `
-EndTime $endTime `
-AggregationType Total `
-ErrorAction SilentlyContinue

if ($metrics) {
$data = $metrics.Data | Where-Object { $_.Total -gt 0 } | Select-Object -Last 1
if ($data) {
$value = switch ($metric) {
"UsedCapacity" { '{0:N2} GB' -f ($data.Total / 1GB) }
"TransactionCount" { '{0:N0}' -f $data.Total }
"Ingress" { '{0:N2} MB' -f ($data.Total / 1MB) }
"Egress" { '{0:N2} MB' -f ($data.Total / 1MB) }
}
Write-Host ("{0,-25} {1}" -f $metric, $value)
}
}
}

# 分析各容器的 Blob 层级和大小分布
Write-Host "`n=== Blob 层级分布 ==="
$containers = Get-AzStorageContainer -Context $ctx
$summary = foreach ($container in $containers) {
$blobs = Get-AzStorageBlob -Container $container.Name -Context $ctx `
-ErrorAction SilentlyContinue
if (-not $blobs) { continue }

$blobs | Group-Object AccessTier | ForEach-Object {
$sizeBytes = ($_.Group | ForEach-Object { $_.Length } | Measure-Object -Sum).Sum
[PSCustomObject]@{
Container = $container.Name
Tier = $_.Name
Count = $_.Count
SizeMB = [math]::Round($sizeBytes / 1MB, 2)
}
}
}

$summary | Format-Table -AutoSize

# 生成成本估算摘要
Write-Host "`n=== 月度成本估算 ==="
$hotBlobs = $summary | Where-Object { $_.Tier -eq "Hot" }
$coolBlobs = $summary | Where-Object { $_.Tier -eq "Cool" }
$archiveBlobs = $summary | Where-Object { $_.Tier -eq "Archive" }

$hotCost = ($hotBlobs | Measure-Object SizeMB -Sum).Sum * 0.018 / 1000
$coolCost = ($coolBlobs | Measure-Object SizeMB -Sum).Sum * 0.01 / 1000
$archiveCost = ($archiveBlobs | Measure-Object SizeMB -Sum).Sum * 0.0004 / 1000

Write-Host ("Hot 层存储: {0,10:N4} USD/月" -f $hotCost)
Write-Host ("Cool 层存储: {0,10:N4} USD/月" -f $coolCost)
Write-Host ("Archive 层存储:{0,10:N4} USD/月" -f $archiveCost)
Write-Host ("--------------------------")
Write-Host ("合计估算: {0,10:N4} USD/月" -f ($hotCost + $coolCost + $archiveCost))

执行结果示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
=== 存储账户配额使用情况 ===
StorageAccounts 3/ 250 (1.2%)
已启用 Blob 访问日志(保留 30 天)

=== 最近 7 天存储指标 ===
UsedCapacity 1.24 GB
TransactionCount 12,456
Ingress 456.78 MB
Egress 2345.67 MB

=== Blob 层级分布 ===
Container Tier Count SizeMB
--------- ---- ----- ------
backups Hot 28 1024.33
documents Hot 12 8.45
images Hot 3 0.25
logs Hot 156 234.67

=== 月度成本估算 ===
Hot 层存储: 0.0229 USD/月
Cool 层存储: 0.0000 USD/月
Archive 层存储: 0.0000 USD/月
--------------------------
合计估算: 0.0229 USD/月

注意事项

  1. SkuName 选择Standard_LRS 适合开发测试,生产环境建议使用 Standard_ZRSStandard_GRS 以获得更高的数据持久性。Premium_LRS 仅支持页 Blob 和文件共享,不支持块 Blob 的访问层分层。

  2. 生命周期策略延迟:生命周期管理规则的应用并非实时生效,Azure 通常每天执行一次策略评估。规则变更后可能需要 24-48 小时才能看到效果,不要误以为配置未生效。

  3. SAS 令牌安全:生成的 SAS 令牌等同于访问凭证,务必通过安全渠道分发。建议优先使用存储在 Azure Key Vault 中的 SAS 定义,而非在脚本中硬编码令牌。设置最短的有效期以满足业务需求即可。

  4. 容器公开访问级别Blob 级别允许匿名读取单个 Blob,Container 级别则允许列出容器内容。默认值 Off 最安全,仅在需要公开访问的场景(如静态网站资源)下才开启。

  5. 上传大文件分块Set-AzStorageBlobContent 对大于 256 MB 的文件会自动分块上传,但网络不稳定时建议手动设置 -ConcurrentTaskCount 参数来控制并发数,避免上传超时。

  6. 成本监控盲区Get-AzMetric 返回的指标有数分钟到数小时的延迟,不适合做实时成本告警。如需精确的实时成本控制,应配置 Azure Cost Management 的预算告警规则,并通过 Az.Billing 模块查询。

PowerShell 技能连载 - Azure 存储管理

适用于 PowerShell 5.1 及以上版本

Azure 存储服务概述

在现代云原生架构中,对象存储已经成为应用数据持久化的首选方案。Azure Blob Storage 作为微软 Azure 平台的核心存储服务,支持海量非结构化数据的存放,包括文档、镜像、日志文件、备份归档等。无论是 DevOps 流水线中的制品管理,还是数据分析管道中的原始数据暂存,Blob Storage 都扮演着不可替代的角色。

对于运维工程师和自动化开发者来说,通过 PowerShell 管理 Azure 存储资源可以带来显著的效率提升。Azure 提供了两个主要的 PowerShell 模块:Az.Storage(基于 Az 资源管理器)和 Azure.Storage(经典管理模式)。本文将围绕推荐的 Az.Storage 模块,介绍存储账户创建、Blob 容器操作、文件上传下载以及存储访问策略配置等常见操作。

掌握这些操作后,你可以将存储管理无缝集成到现有的自动化脚本中,实现从基础设施配置到数据流转的全链路 PowerShell 自动化。

连接 Azure 并创建存储账户

在操作 Blob Storage 之前,需要先安装 Az 模块并通过 Connect-AzAccount 完成身份认证。认证通过后,即可创建资源组和存储账户。存储账户是所有 Blob、文件、队列和表服务的顶级命名空间,其名称在 Azure 全局范围内必须唯一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 安装 Az 模块(仅首次需要)
Install-Module -Name Az -Repository PSGallery -Force -Scope CurrentUser

# 登录 Azure(会弹出浏览器窗口进行认证)
Connect-AzAccount

# 选择目标订阅
$subscriptionId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Set-AzContext -SubscriptionId $subscriptionId

# 创建资源组
$resourceGroupName = "rg-storage-demo"
$location = "eastasia"
$null = New-AzResourceGroup -Name $resourceGroupName -Location $location -Force
Write-Host "资源组 [$resourceGroupName] 已创建于 [$location]"

# 创建存储账户(Standard LRS 冗余级别,适合开发测试)
$storageAccountName = "st$(Get-Random -Minimum 100000 -Maximum 999999)"
$storageSku = "Standard_LRS"
$storageKind = "StorageV2"

$storageAccount = New-AzStorageAccount `
-ResourceGroupName $resourceGroupName `
-Name $storageAccountName `
-SkuName $storageSku `
-Kind $storageKind `
-Location $location `
-EnableHttpsTrafficOnly $true `
-MinimumTlsVersion "TLS1_2"

Write-Host "存储账户 [$storageAccountName] 已创建"
Write-Host " SKU: $($storageAccount.Sku.Name)"
Write-Host " 类型: $($storageAccount.Kind)"
Write-Host " 位置: $($storageAccount.Location)"
Write-Host " 访问端点: $($storageAccount.PrimaryEndpoints.Blob)"
1
2
3
4
5
6
资源组 [rg-storage-demo] 已创建于 [eastasia]
存储账户 [st729384] 已创建
SKU: Standard_LRS
类型: StorageV2
位置: eastasia
访问端点: https://st729384.blob.core.windows.net/

Blob 容器操作与文件上传

存储账户创建完成后,下一步是创建 Blob 容器(Container),它类似于文件夹的概念,用于组织和隔离不同业务场景的 Blob 对象。Azure 提供三种访问级别:Private(默认,需认证)、Blob(允许匿名读取 Blob)和 Container(允许匿名列出和读取)。对于生产环境,建议始终保持 Private,通过 SAS 令牌或 Azure AD 认证控制访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 获取存储账户上下文(后续操作都需要)
$ctx = $storageAccount.Context

# 创建 Blob 容器
$containerName = "app-logs"
$container = New-AzStorageContainer -Name $containerName -Context $ctx -Permission Off
Write-Host "容器 [$containerName] 已创建,访问级别: $($container.PublicAccess)"

# 准备测试文件
$tempDir = Join-Path -Path $env:TEMP -ChildPath "storage-demo"
$null = New-Item -Path $tempDir -ItemType Directory -Force

$logFiles = @(
@{ Name = "app-2025-10-01.log"; Content = "2025-10-01 INFO Application started" }
@{ Name = "app-2025-10-02.log"; Content = "2025-10-02 INFO Database connected" }
@{ Name = "app-2025-10-03.log"; Content = "2025-10-03 WARN Memory usage high" }
)

foreach ($log in $logFiles) {
$filePath = Join-Path -Path $tempDir -ChildPath $log.Name
Set-Content -Path $filePath -Value $log.Content -Encoding UTF8
Write-Host "已生成本地文件: $($log.Name)"
}

# 批量上传文件到 Blob 容器
Write-Host "`n开始上传文件到 Blob 容器..."
$localFiles = Get-ChildItem -Path $tempDir -Filter "*.log"
foreach ($file in $localFiles) {
$blob = Set-AzStorageBlobContent `
-File $file.FullName `
-Container $containerName `
-Blob $file.Name `
-Context $ctx `
-StandardBlobTier Hot

Write-Host " 已上传: $($blob.Name) | 大小: $($blob.Length) 字节 | 层级: $($blob.StandardBlobTier)"
}

# 列出容器中的所有 Blob
Write-Host "`n容器 [$containerName] 中的文件列表:"
$blobs = Get-AzStorageBlob -Container $containerName -Context $ctx
foreach ($blob in $blobs) {
Write-Host " - $($blob.Name) ($($blob.Length) 字节) 最后修改: $($blob.LastModified.ToString('yyyy-MM-dd HH:mm:ss'))"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
容器 [app-logs] 已创建,访问级别: Off
已生成本地文件: app-2025-10-01.log
已生成本地文件: app-2025-10-02.log
已生成本地文件: app-2025-10-03.log

开始上传文件到 Blob 容器...
已上传: app-2025-10-01.log | 大小: 38 字节 | 层级: Hot
已上传: app-2025-10-02.log | 大小: 40 字节 | 层级: Hot
已上传: app-2025-10-03.log | 大小: 38 字节 | 层级: Hot

容器 [app-logs] 中的文件列表:
- app-2025-10-01.log (38 字节) 最后修改: 2025-10-07 10:30:15
- app-2025-10-02.log (40 字节) 最后修改: 2025-10-07 10:30:16
- app-2025-10-03.log (38 字节) 最后修改: 2025-10-07 10:30:17

生成 SAS 令牌与访问策略管理

在实际应用中,经常需要将 Blob 的访问权限临时授予外部系统或客户端应用,而不希望暴露存储账户的密钥。共享访问签名(Shared Access Signature,SAS)正是为此设计的。SAS 令牌可以精确控制访问权限(读、写、删除)、有效时间范围和允许的 IP 地址,是 Azure 存储安全模型的核心组成部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 为整个容器生成 SAS 令牌(只读,有效期 1 小时)
$sasToken = New-AzStorageContainerSASToken `
-Name $containerName `
-Context $ctx `
-Permission "r" `
-StartTime (Get-Date) `
-ExpiryTime (Get-Date).AddHours(1)

Write-Host "容器 SAS 令牌(只读,1小时有效):"
Write-Host $sasToken

# 为单个 Blob 生成 SAS 令牌(读写,有效期 30 分钟)
$targetBlob = "app-2025-10-01.log"
$blobSasToken = New-AzStorageBlobSASToken `
-Container $containerName `
-Blob $targetBlob `
-Context $ctx `
-Permission "rw" `
-StartTime (Get-Date) `
-ExpiryTime (Get-Date).AddMinutes(30)

Write-Host "`nBlob [$targetBlob] SAS 令牌(读写,30分钟有效):"
Write-Host $blobSasToken

# 使用 SAS 令牌通过 HTTP 下载 Blob
$fullUrl = "$($ctx.BlobEndPoint)$containerName/$targetBlob$blobSasToken"
$downloadPath = Join-Path -Path $tempDir -ChildPath "downloaded-$targetBlob"
Invoke-WebRequest -Uri $fullUrl -OutFile $downloadPath
Write-Host "`n通过 SAS URL 下载成功: $downloadPath"
Write-Host "文件内容: $((Get-Content -Path $downloadPath -Raw).Trim())"

# 配置存储访问策略(用于长期管理的最佳实践)
$accessPolicy = @{
StartTime = Get-Date
ExpiryTime = (Get-Date).AddDays(30)
Permission = "rwl"
}

$policyName = "app-log-write-policy"
$null = Set-AzStorageContainerAcl `
-Name $containerName `
-Context $ctx `
-Policy $accessPolicy `
-PolicyName $policyName

Write-Host "`n已创建存储访问策略: [$policyName]"
Write-Host " 权限: $($accessPolicy.Permission)(读写+列表)"
Write-Host " 有效期: $($accessPolicy.StartTime.ToString('yyyy-MM-dd')) - $($accessPolicy.ExpiryTime.ToString('yyyy-MM-dd'))"

# 查看容器上的所有访问策略
$acl = Get-AzStorageContainerAcl -Name $containerName -Context $ctx
foreach ($policy in $acl) {
Write-Host "`n策略: $($policy.Id)"
Write-Host " 权限: $($policy.AccessPolicy.Permission)"
Write-Host " 起始: $($policy.AccessPolicy.StartTime.ToString('yyyy-MM-dd HH:mm:ss'))"
Write-Host " 过期: $($policy.AccessPolicy.ExpiryTime.ToString('yyyy-MM-dd HH:mm:ss'))"
}

# 清理临时文件
Remove-Item -Path $tempDir -Recurse -Force
Write-Host "`n临时文件已清理"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
容器 SAS 令牌(只读,1小时有效):
?sv=2023-01-03&ss=b&srt=co&sp=r&se=2025-10-07T11:30:00Z&st=2025-10-07T10:30:00Z&spr=https&sig=abc123XYZ...

Blob [app-2025-10-01.log] SAS 令牌(读写,30分钟有效):
?sv=2023-01-03&ss=b&srt=sco&sp=rw&se=2025-10-07T11:00:00Z&st=2025-10-07T10:30:00Z&spr=https&sig=def456UVW...

通过 SAS URL 下载成功: /tmp/storage-demo/downloaded-app-2025-10-01.log
文件内容: 2025-10-01 INFO Application started

已创建存储访问策略: [app-log-write-policy]
权限: rwl(读写+列表)
有效期: 2025-10-07 - 2025-11-06

策略: app-log-write-policy
权限: rwl
起始: 2025-10-07 10:30:00
过期: 2025-11-06 10:30:00

临时文件已清理

注意事项

  1. 存储账户命名规则:存储账户名称只能包含小写字母和数字,长度为 3-24 个字符,且在 Azure 全局范围内必须唯一。建议在脚本中使用 Get-Random 或业务前缀加哈希的方式生成名称,避免因名称冲突导致创建失败。

  2. SAS 令牌安全保管:SAS 令牌本质上是一个携带权限信息的 URL 参数,任何获得该令牌的人都可以在有效期内访问对应资源。在日志记录、API 响应和脚本输出中应避免打印完整的 SAS URL。建议使用存储访问策略(Stored Access Policy)来管理 SAS,这样可以在令牌泄露时通过撤销策略立即失效。

  3. Blob 层级选择:Azure Blob Storage 提供热(Hot)、冷(Cool)和归档(Archive)三种访问层级。热层级访问性能最优但存储成本最高,归档层级存储成本最低但读取需要数小时的解冻时间。在脚本中可以通过 StandardBlobTier 参数在上传时指定层级,也可以对已有 Blob 调用 Set-AzStorageBlobTier 进行层级转换。

  4. Az 模块版本兼容性Az.Storage 模块更新频繁,不同版本之间的 cmdlet 参数可能有差异。建议在脚本开头通过 #Requires -Modules @{ ModuleName="Az.Storage"; ModuleVersion="5.0.0" } 声明最低版本要求,确保脚本在目标环境中能正常运行。

  5. 大文件上传策略:对于超过 256 MB 的文件,应使用分块上传(Block Blob)方式,将文件拆分为多个 Block 分别上传后提交组合。Set-AzStorageBlobContent 会自动处理分块逻辑,但在网络不稳定的环境下,建议手动控制分块大小并实现重试机制,避免因单次网络中断导致整个上传任务失败。

  6. 资源清理与成本控制:测试和演示完毕后,务必通过 Remove-AzStorageAccountRemove-AzResourceGroup 清理不再使用的资源。存储账户即使不活跃也会产生最低存储费用和计费周期费用。建议在非生产环境中为资源组添加自动过期标签,并通过 Azure Policy 或定时脚本定期清理过期资源。