Azure DevOps部署Web应用遇权限错误及目录结构问题求助
你遇到的「权限不足」和「Swagger资源找不到」问题,核心原因是Azure DevOps流水线部署后的目录结构和Visual Studio手动发布不一致——手动发布时文件直接部署到Web根目录,而流水线部署时,发布输出被嵌套在了额外的子文件夹里,导致IIS无法定位到正确的入口文件(比如index.html、web.config)。
问题根源分析
从你的azure-pipelines.yaml配置来看,DotNetCoreCLI的publish任务把输出放到了$(Build.ArtifactStagingDirectory)/$(buildConfiguration),而默认情况下dotnet publish会为每个项目生成单独的子文件夹(比如Release/Kronova.MovingPortal.Web.Host/...)。当Azure Web Apps部署这个artifact时,它会把整个Estimated_Pad_Api artifact作为根目录,而实际的Web应用文件却在子文件夹里,自然找不到资源。
具体解决方案
我们需要调整流水线配置,确保发布输出直接放在$(Build.ArtifactStagingDirectory)根目录,同时合并前端Angular的构建结果到Web项目的发布目录中:
1. 修正.NET项目的Publish任务
指定具体的Web项目(避免多项目发布产生子文件夹),并调整输出路径到$(Build.ArtifactStagingDirectory)根目录:
- task: DotNetCoreCLI@2 displayName: 'Publish the project - $(buildConfiguration)' inputs: command: publish projects: 'src/Kronova.MovingPortal.Web.Host/Kronova.MovingPortal.Web.Host.csproj' # 明确指定Web项目路径 publishWebProjects: false arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory) --self-contained false' zipAfterPublish: false
2. 合并前端Angular构建输出到发布目录
添加CopyFiles任务,把Angular构建好的wwwroot内容复制到.NET项目的发布目录中,确保前端资源和后端API文件在同一根目录:
- task: CopyFiles@2 displayName: 'Copy Angular build output to Web publish directory' inputs: SourceFolder: 'src/Kronova.MovingPortal.Web.Host/wwwroot' TargetFolder: '$(Build.ArtifactStagingDirectory)/wwwroot' OverWrite: true # 覆盖现有文件,确保最新构建结果
这个任务要放在DotNetCoreCLI的publish任务之后,PublishBuildArtifacts任务之前。
3. 保留或调整Artifact发布任务
你的PublishBuildArtifacts任务可以保持不变,但此时$(Build.ArtifactStagingDirectory)下已经直接包含web.config、wwwroot等Web应用核心文件,没有额外的子文件夹:
- task: PublishBuildArtifacts@1 displayName: 'Publish Artifact: Estimated_Pad_Api' condition: succeeded() inputs: PathtoPublish: '$(Build.ArtifactStagingDirectory)' ArtifactName: 'Estimated_Pad_Api' publishLocation: 'Container'
4. 可选:确认Azure Web Apps的默认文档配置
登录Azure Portal,进入你的Web应用 → 「配置」→ 「默认文档」,确保列表中包含index.html和swagger/index.html(如果需要直接访问Swagger)。如果没有,可以手动添加,或者在项目的web.config中添加如下配置:
<system.webServer> <defaultDocument> <files> <add value="index.html" /> <add value="swagger/index.html" /> </files> </defaultDocument> </system.webServer>
修改后的完整流水线配置
trigger: - master pool: name: Hosted Windows 2019 with VS2019 demands: npm variables: buildConfiguration: 'Release' steps: - task: Npm@1 displayName: 'angular-cli' inputs: workingDir: src/Kronova.MovingPortal.Web.Host verbose: false - task: Npm@1 displayName: 'ng build' inputs: command: custom workingDir: src/Kronova.MovingPortal.Web.Host verbose: false customCommand: 'run publish-full' - task: UseDotNet@2 displayName: 'Use .NET SDK 3.1' inputs: packageType: 'sdk' version: '3.1.x' - task: DotNetCoreCLI@2 displayName: 'Restore project dependencies' inputs: command: 'restore' projects: '**/*.csproj' - task: DotNetCoreCLI@2 displayName: 'Build the project - $(buildConfiguration)' inputs: command: 'build' arguments: '--no-restore --configuration $(buildConfiguration)' projects: | **/*.csproj !**/*Mobile*.csproj !**/*Client*.csproj !**Kronova.MovingPortal.Application.Client/** - task: DotNetCoreCLI@2 displayName: 'Publish the project - $(buildConfiguration)' inputs: command: publish projects: 'src/Kronova.MovingPortal.Web.Host/Kronova.MovingPortal.Web.Host.csproj' publishWebProjects: false arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory) --self-contained false' zipAfterPublish: false - task: CopyFiles@2 displayName: 'Copy Angular build output to Web publish directory' inputs: SourceFolder: 'src/Kronova.MovingPortal.Web.Host/wwwroot' TargetFolder: '$(Build.ArtifactStagingDirectory)/wwwroot' OverWrite: true - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact: Estimated_Pad_Api' condition: succeeded() inputs: PathtoPublish: '$(Build.ArtifactStagingDirectory)' ArtifactName: 'Estimated_Pad_Api' publishLocation: 'Container'
这样调整后,流水线部署的目录结构就和VS手动发布一致了,IIS能正确找到入口文件,权限和Swagger的问题也就解决了。
内容的提问来源于stack exchange,提问作者goofy5752

