You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用PowerShell命令部署应用至Azure静态网站

本地Azure DevOps Server部署Angular到Azure静态网站的PowerShell方案

Azure静态网站的底层是Azure存储账户$web专用容器托管的静态资源服务,和Azure App Service的底层架构完全不同,Publish-AzWebApp命令无法适配该场景,直接通过Az.Storage模块操作存储Blob即可完成部署,支持直接传入zip格式的编译制品作为输入。

前置准备

  • 提前在目标Azure存储账户中启用静态网站功能,设置默认索引文档为index.html,功能启用后系统会自动创建名为$web的专用Blob容器,所有静态站点内容都存放在这个容器中
  • 部署执行的代理机器需要提前安装Az PowerShell模块(最低兼容版本5.0),可直接在代理机执行Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force完成安装
  • 提前在Azure AD创建服务主体,给该主体分配目标存储账户的存储Blob数据参与者权限,记录服务主体的ClientId、ClientSecret、TenantId信息,在DevOps Server流水线中配置为秘密变量,避免明文泄露
  • Angular项目编译阶段产出的zip制品包,需要确保解压后根目录直接存在index.html、打包生成的js/css/资源文件,不要嵌套多层项目文件夹,否则上传后站点会因为找不到入口文件报404

核心部署PowerShell脚本

该脚本可直接在DevOps Server的原生PowerShell任务中调用,入参直接关联流水线产出的zip制品路径即可:

param(
    [Parameter(Mandatory=$true)]
    [string]$ArtifactZipPath, # Angular编译产出的zip包本地路径
    [Parameter(Mandatory=$true)]
    [string]$StorageAccountName, # 目标静态网站对应的存储账户名称
    [Parameter(Mandatory=$true)]
    [string]$TenantId,
    [Parameter(Mandatory=$true)]
    [string]$ClientId,
    [Parameter(Mandatory=$true)]
    [string]$ClientSecret
)

# 服务主体登录Azure
$secureSecret = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($ClientId, $secureSecret)
Connect-AzAccount -ServicePrincipal -Tenant $TenantId -Credential $cred | Out-Null

# 临时解压制品到本地临时目录,避免污染代理机工作目录
$tempExtractPath = Join-Path $env:TEMP ([System.Guid]::NewGuid().ToString())
New-Item -ItemType Directory -Path $tempExtractPath | Out-Null
Expand-Archive -Path $ArtifactZipPath -DestinationPath $tempExtractPath -Force

# 获取存储账户上下文,静态网站内容固定存放在$web容器
$storageContext = New-AzStorageContext -StorageAccountName $StorageAccountName -UseConnectedAccount

# 清空$web容器内旧版本文件,避免历史残留资源导致版本异常
Get-AzStorageBlob -Container '$web' -Context $storageContext | Remove-AzStorageBlob -Force

# 递归上传所有静态资源,手动映射常用文件的Content-Type,避免默认识别错误导致页面无法正常渲染
Get-ChildItem -Path $tempExtractPath -File -Recurse | ForEach-Object {
    $relativePath = $_.FullName.Substring($tempExtractPath.Length + 1).Replace('\','/')
    $contentType = switch ($_.Extension.ToLower()) {
        '.html' { 'text/html; charset=utf-8' }
        '.js'   { 'application/javascript; charset=utf-8' }
        '.css'  { 'text/css; charset=utf-8' }
        '.json' { 'application/json; charset=utf-8' }
        '.png'  { 'image/png' }
        '.jpg'  { 'image/jpeg' }
        '.jpeg' { 'image/jpeg' }
        '.svg'  { 'image/svg+xml' }
        '.ico'  { 'image/x-icon' }
        '.woff' { 'font/woff' }
        '.woff2'{ 'font/woff2' }
        '.ttf'  { 'font/ttf' }
        default { 'application/octet-stream' }
    }
    # 非入口文件设置1年长缓存,提升访问性能
    $cacheControl = if ($relativePath -eq 'index.html') { 'no-cache' } else { 'public, max-age=31536000' }
    Set-AzStorageBlobContent -File $_.FullName -Container '$web' -Blob $relativePath `
        -Context $storageContext -Properties @{ContentType=$contentType, CacheControl=$cacheControl} -Force | Out-Null
}

# 清理临时解压目录
Remove-Item -Path $tempExtractPath -Recurse -Force

配置注意事项

  • 脚本中的敏感参数(ClientId、ClientSecret、TenantId)禁止硬编码,直接引用DevOps Server中配置的秘密变量传参即可,调用示例:-ClientSecret $(StorageSpnSecret)
  • 如果Angular路由使用PathLocationStrategy(HTML5路由模式),需要在存储账户静态网站配置页将错误文档路径也设置为index.html,否则刷新子路由时会返回404错误
  • 如果存储账户开启了防火墙限制,需要把DevOps Server代理节点的出口IP加到存储账户的允许IP列表中,否则上传时会报403权限错误
  • 如果站点配置了CDN加速,可在上传完成后追加Az.Cdn模块的缓存清除命令,避免用户访问到旧版本资源
  • Angular编译时需要确保base href配置和静态网站的访问路径匹配,否则部署后会出现静态资源加载404的问题

内容的提问来源于stack exchange,提问作者Paul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 05:31:01