Build and Deploy multiple azure function projects using YAML pipeline - azure-devops

I am trying to build and publish multiple azure function projects within a single solution using YAML pipelines but when I publish the packages they are overwriting each other so I only publish the last project built.
I use these steps to build:
- task: DotNetCoreCLI#2
displayName: 'Restore project dependencies'
inputs:
command: 'restore'
feedsToUse: 'config'
nugetConfigPath: Nuget.config
projects: '**/*.csproj'
- task: DotNetCoreCLI#2
displayName: 'Build the project - $(configuration)'
inputs:
command: 'build'
arguments: '--no-restore --configuration $(configuration) -p:DesktopBuildPackageLocation="$(Build.ArtifactStagingDirectory)\Functions" -p:ArtifactStagingDirectory="$(Build.ArtifactStagingDirectory)\Artifacts"'
projects: '**/*.csproj'
- task: DotNetCoreCLI#2
displayName: 'Publish the project - $(configuration)'
inputs:
command: 'publish'
projects: '**/*Functions.csproj'
publishWebProjects: false
arguments: '--no-build --configuration $(configuration) --output $(Build.ArtifactStagingDirectory)\Functions'
zipAfterPublish: true
modifyOutputPath: true
- publish: '$(Build.ArtifactStagingDirectory)\Functions'
displayName: 'Publish drop'
artifact: functions
Is there a way I can publish the functions by project? I tried the VSBuild task to build the solution but when trying to deploy to Azure using the AzureFunctionApp task, it errors out saying msBuild packages are not supported.
Any suggestions welcome!

Is there a way I can publish the functions by project?
Based on your description, you have multiple projects in a single solution file.
As far as I know, in Dotnet Publish task, you could set the publishWebProjects: false. Then the functions will be published by project.
You could refer to my sample:
- task: DotNetCoreCLI#2
displayName: Restore
inputs:
command: restore
projects: '**/*.csproj'
- task: DotNetCoreCLI#2
displayName: Build
inputs:
projects: '**/*.csproj'
arguments: '--no-restore --configuration $(configuration) -p:DesktopBuildPackageLocation="$(Build.ArtifactStagingDirectory)\Functions" -p:ArtifactStagingDirectory="$(Build.ArtifactStagingDirectory)\Artifacts"'
- task: DotNetCoreCLI#2
displayName: Publish
inputs:
command: publish
publishWebProjects: false
projects: '**/*.csproj'
arguments: '--no-build --configuration $(configuration) --output $(Build.ArtifactStagingDirectory)/Functions'
zipAfterPublish: True
continueOnError: true
- task: PublishBuildArtifacts#1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: ' $(build.artifactstagingdirectory)'
condition: succeededOrFailed()
Result:

I figured out a pattern that worked for me. Instead of trying to publish all the projects within my solution with just one task, I split them up and gave each one it's own task. So I went from this:
- task: DotNetCoreCLI#2
displayName: 'Publish Functions'
inputs:
command: publish
projects: '**/*.Functions.csproj'
publishWebProjects: false
arguments: '--no-build --configuration ${{ variables.configuration }} -o $(Build.ArtifactStagingDirectory)\Functions'
modifyOutputPath: true
zipAfterPublish: true
to this:
- task: DotNetCoreCLI#2
displayName: 'Publish Project 1 Functions'
inputs:
command: publish
projects: '**/Project1.Functions.csproj'
publishWebProjects: false
arguments: '--no-build --configuration ${{ variables.configuration }} -o $(Build.ArtifactStagingDirectory)\Functions\Project1'
modifyOutputPath: true
zipAfterPublish: true
- task: DotNetCoreCLI#2
displayName: 'Publish Project2 Functions'
inputs:
command: publish
projects: '**/Project2.Functions.csproj'
publishWebProjects: false
arguments: '--no-build --configuration ${{ variables.configuration }} -o $(Build.ArtifactStagingDirectory)\Functions\Project2'
modifyOutputPath: true
zipAfterPublish: true
I kept the rest of my template the same as in my original post.
Thanks for all the help.

Instead of Publish Pipeline Artifacts task you could use artifact.upload logging command to upload multiple artifacts from a single script step:
Upload: Upload an artifact
##vso[artifact.upload]local file path
Usage
Upload a local file into a file container folder, and optionally publish an artifact as artifactname.
Properties
containerfolder = folder that the file will upload to, folder will be created if needed.
artifactname = artifact name. (Required)
Example
- pwsh: |
Get-ChildItem -LiteralPath '$(Build.ArtifactStagingDirectory)\Functions' -Filter '*.zip' -File |
ForEach-Object {
'##vso[artifact.upload containerfolder={0};artifactname={0}]{1}' -f $_.BaseName, $_.FullName
}
displayName: Publish artifacts

Related

Publish a website via WebDeploy from Azure DevOps

I'm trying to deploy a website from Azure DevOps. So, I created a pipeline with the following YAML file
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: UseDotNet#2
displayName: 'Use dotnet 6'
inputs:
version: '6.0.x'
- task: DotNetCoreCLI#2
displayName: Restore packages
inputs:
command: 'restore'
feedsToUse: 'select'
vstsFeed: 'c800d0d7-e2af-4567-997f-de7cf7888e6c'
- task: DotNetCoreCLI#2
displayName: Build project
inputs:
command: 'build'
projects: '**/SurveyUI.Client.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI#2
displayName: Prepare the package
inputs:
command: 'pack'
packagesToPack: '**/SurveyUI.Client.csproj'
versionEnvVar: 'PackageVersion'
arguments: '-t:pack'
- task: DotNetCoreCLI#2
inputs:
command: 'publish'
publishWebProjects: false
projects: '**/SurveyUI.Client.csproj'
arguments: '--output $(Build.BinariesDirectory)/publish_output'
zipAfterPublish: false
modifyOutputPath: false
- task: DotNetCoreCLI#2
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--output $(Build.BinariesDirectory)/publish_output'
zipAfterPublish: false
modifyOutputPath: false
- task: ArchiveFiles#2
inputs:
rootFolderOrFile: '$(Build.BinariesDirectory)/publish_output'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
replaceExistingArchive: true
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'Container'
With this YAML, Azure DevOps builds the projects and for a specific one, it creates a zip with all the necessary files. After this, I created a Release pipeline with the following YAML (the password is omitted):
steps:
- task: rschiefer.MSDeployAllTheThings.msdeploy-package-sync.MSDeployPackageSync#0
displayName: 'MSDeploy Package Sync: '
inputs:
Package: '$(System.DefaultWorkingDirectory)/_SurveyGenerator (1)/drop/*.zip'
DestinationComputer: 'https://mywebsite.com:8172/msdeploy.axd?site=survey.puresourcecode.com'
AuthType: basic
Username: forumpur
Password: ''
With these settings from Visual Studio, I can deploy to the website. When the Release pipeline starts, there is an error
System.Management.Automation.RemoteException: Error Code: ERROR_USER_NOT_AUTHORIZED_FOR_CONTENTPATH
System.Management.Automation.RemoteException: More Information: Could not complete an operation with the specified provider ("contentPath") when connecting using the Web Management Service. This can occur if the server administrator has not authorized the user for this operation. contentPath http://go.microsoft.com/fwlink/?LinkId=178034
System.Management.Automation.RemoteException: Learn more at: http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_USER_NOT_AUTHORIZED_FOR_CONTENTPATH. Learn more at: https://go.microsoft.com/fwlink/?LinkId=221672#ERROR_USER_NOT_AUTHORIZED_FOR_CONTENTPATH.
System.Management.Automation.RemoteException: Error count: 1.
System.Management.Automation.RuntimeException: MSDeploy command failed. See logs for details. ---> System.Management.Automation.RuntimeException: MSDeploy command failed. See logs for details.
LegacyVSTSPowerShellHost.exe completed with return code: -1.
Is this the correct way to use Web Deploy on Azure DevOps? How can I fix the issue?

Azure Pipeline: File Transform task fails

I added a file transform task to the build pipeline. Here is a part of the pipeline:
- task: FileTransform#1
inputs:
folderPath: '$(System.DefaultWorkingDirectory)/**/*.zip'
fileType: 'json'
targetFiles: 'local.settings.json'
- task: DotNetCoreCLI#2
displayName: 'DotNet Build Projects'
inputs:
command: 'build'
projects: '**/*.csproj'
arguments: --configuration $(buildConfiguration)
- task: DotNetCoreCLI#2
displayName: 'Run Unit Tests'
inputs:
command: 'test'
projects: '**/LATICRETE.IdentityFunctions.Tests/*.csproj'
arguments: '--configuration $(buildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(Build.SourcesDirectory)/TestResults/Coverage/'
publishTestResults: true
- script: |
dotnet tool install -g dotnet-reportgenerator-globaltool
reportgenerator -reports:$(Build.SourcesDirectory)/TestResults/Coverage/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/CodeCoverage -reporttypes:HtmlInline_AzurePipelines;Cobertura
displayName: Create Code Coverage Report
- task: PublishCodeCoverageResults#1
displayName: 'Publish Code Coverage'
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(Build.SourcesDirectory)/**/coverage.cobertura.xml'
reportDirectory: '$(Build.SourcesDirectory)/TestResults/Coverage/'
- task: DotNetCoreCLI#2
displayName: Publish Function App
inputs:
command: 'publish'
publishWebProjects: false
projects: '**/LATICRETE.IdentityFunctions/*.csproj'
arguments: '--configuration $(buildConfiguration) --output $(build.artifactstagingdirectory)'
zipAfterPublish: True
- task: PublishBuildArtifacts#1
displayName: 'Publish Function Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: 'function'
The build in Azure fails with the following error:
##[warning]Can't find loc string for key: Nopackagefoundwithspecifiedpattern
##[error]Error: Nopackagefoundwithspecifiedpattern D:\a\1\s***.zip
How can this be fixed?
You have wrong order here since you try to change package
- task: FileTransform#1
inputs:
folderPath: '$(System.DefaultWorkingDirectory)/**/*.zip'
fileType: 'json'
targetFiles: 'local.settings.json'
Please change your folderPath to directory where local.settings.json is or move FileTransform after PublishFunctionApp step, but make sure that you provide correct path because you publish package to $(build.artifactstagingdirectory).

Convert steps to publish function app to artifact in Azure Pipeline to GitHub actions

I have the following three steps to publish a function app to artifact in Azure Pipeline:
- task: DotNetCoreCLI#2
displayName: 'dotnet publish function app'
inputs:
command: publish
arguments: '--configuration Release --output updater_publish_output'
projects: 'Service/XYZ/Hosts.FA/*.csproj'
publishWebProjects: false
modifyOutputPath: false
zipAfterPublish: false
- task: ArchiveFiles#2
displayName: 'archive function app files'
inputs:
rootFolderOrFile: "$(System.DefaultWorkingDirectory)/updater_publish_output"
includeRootFolder: false
archiveFile: "$(System.DefaultWorkingDirectory)/Hosts.FA.zip"
- task: PublishBuildArtifacts#1
displayName: 'publish function app files'
inputs:
PathtoPublish: '$(System.DefaultWorkingDirectory)/Hosts.FA.zip'
ArtifactName: '$(Build.BuildNumber)'
Here is the project structure:
I have updated the first step to:
- name: dotnet publish function app
run: dotnet publish Service/XYZ/Hosts.FA/Hosts.FA.csproj --configuration Release --output updater_publish_output
How do I convert the tasks ArchiveFiles#2 & PublishBuildArtifacts#1 to GitHub Actions?
Use the Upload-Artifact task from here: https://github.com/actions/upload-artifact. It will replace both ArchiveFiles#2 (zipping) and PublishBuildArtifacts#1 (uploading).
- uses: actions/upload-artifact#v2
with:
name: ${{github.run_number}}
path: |
updater_publish_output
As per https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context github.run_number is
A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run.
You could also use github.run_id:
A unique number for each run within a repository. This number does not change if you re-run the workflow run.

Azure DevOps Multi Stage Pipeline Error: No package found with specified pattern: /home/vsts/work/1/s/**/*.zip - How do I fix?

I have an Azure DevOps Build (yaml) and Release Pipeline (Classic) successfully deploying to Azure.
I am trying to convert these 2 separate steps in a Multi Stage Yaml Pipeline.
On the Azure App Service Deploy task (AzureRmWebAppDeployment#4), I am getting the following error:
No package found with specified pattern: /home/vsts/work/1/a/*.zip
Below is my Multi Stage Yaml Pipeline
stages:
- stage: Build
jobs:
- job: 'Build'
pool:
vmImage: 'windows-latest'
variables:
buildConfiguration: 'Release'
steps:
- task: DotNetCoreCLI#2
displayName: Restore
inputs:
command: restore
projects: '**/*.csproj'
vstsFeed: 'dd55642d-8943-411f-8856-9714dd0da8af'
- task: DotNetCoreCLI#2
displayName: Build
inputs:
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI#2
displayName: Test
inputs:
command: test
projects: '**/*[Tt]ests/*.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI#2
displayName: Publish
inputs:
command: publish
publishWebProjects: false
projects: '**/Tools.Client.Blazor.ServerApp.csproj'
arguments: '--configuration $(buildConfiguration) --output $(build.artifactstagingdirectory)'
- task: PublishSymbols#2
displayName: 'Publish symbols path'
inputs:
SearchPattern: '**\bin\**\*.pdb'
PublishSymbols: false
continueOnError: true
- task: CopyFiles#2
displayName: 'Copy Files to: $(build.artifactstagingdirectory)\AzureDeploy'
inputs:
SourceFolder: AzureDeploy
TargetFolder: '$(build.artifactstagingdirectory)\AzureDeploy'
- task: PublishBuildArtifacts#1
displayName: 'Publish Artifact: drop'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
condition: succeededOrFailed()
- stage: Systest
jobs:
- job: 'Systest'
variables:
resourceGroupName: '$(appName)-rg-$(environment)'
location: 'East US'
appServiceName: '$(appName)-svc-$(environment)'
appInsightsName: '$(appName)-ins-$(environment)'
appServicePlanName: '$(appName)-asp-$(environment)'
appName: 'tools'
owner: 'Pod'
environment: 'systest'
steps:
- task: AzureResourceManagerTemplateDeployment#3
displayName: 'ARM Template deployment: Resource Group scope'
inputs:
azureResourceManagerConnection: 'Dev/Test Connection'
subscriptionId: ''
resourceGroupName: '$(resourceGroupName)'
location: '$(location)'
csmFile: '$(System.DefaultWorkingDirectory)/AzureDeploy/Tools.azureDeploy.json'
csmParametersFile: '$(System.DefaultWorkingDirectory)/AzureDeploy/Tools.azureDeploy.parameter.json'
overrideParameters: '-appServiceName "$(appServiceName)" -appInsightsName "$(appInsightsName)" -appServicePlanName "$(appServicePlanName)" -owner "$(owner)" -environment "$(environment)" -location "$(location)"'
- task: AzureRmWebAppDeployment#4
displayName: 'Azure App Service Deploy: $(appServiceName)'
inputs:
ConnectionType: 'AzureRM'
azureSubscription: ''
appType: 'webApp'
WebAppName: '$(appServiceName)'
packageForLinux: '$(Build.ArtifactStagingDirectory)/*.zip'
Any help / suggestions would be appreciated.
Because it's 2 stages the second stage doesn't have the file you published in the first stage, you need to download it.
You can use Pipeline artifacts instead of build artifacts.
Pipeline artifacts provide a way to share files between stages in a
pipeline or between different pipelines. They are typically the output
of a build process that needs to be consumed by another job or be
deployed. Artifacts are associated with the run they were produced in
and remain available after the run has completed.
To publish (upload) an artifact for the current run:
steps:
- publish: $(build.artifactstagingdirectory)
artifact: drop
And in the second stage, you download the artifact:
steps:
- download: current
artifact: drop
You can also achieve it with build artifacts and download with DownloadBuildArtifacts#0 task.
During Publish it will not work like this. Instead of using path "/home/vsts/work/1/a/.zip", this path can be used "$(System.DefaultWorkingDirectory)/_Releasepipelinename/drop/.zip"

Azure YAML Pipelines - Jobs/Tasks package issues

So I've been doing some learning into Azure DevOps Yaml Pipelines and I've come across an issue I can't seem to figure out what would be the cause.
I was building my first pipeline for a small class library solution, the idea being to restore, build, test, pack & publish it when changes are committed into master.
I split the different parts of the deployment into stages/jobs (which may not be the correct way to use these), but when I do so, the "Nuget pack" step can never find any of the built files.
This YAML does not work, and errors out on the "NuGet (Pack)" Step because it cannot find the "projects.assets.json" file, which I have confirmed that the build step does produce.
trigger:
- master
pool:
vmImage: 'windows-latest'
name: 'Set dynamically'
variables:
buildConfiguration: 'Release'
version.Major: 1
version.Minor: $[counter(variables['version.Major'], 0)]
version.Patch: 0
version.Revision: $[counter(variables['version.Minor'], 0)]
version.Number: '$(version.Major).$(version.Minor).$(version.Patch).$(version.Revision)'
stages:
- stage: Prepare
jobs:
- job: Prepare_Sources
steps:
- checkout: self
clean: true
- job: Prepare_BuildAndVersionNumbers
steps:
- task: PowerShell#2
displayName: Set the name of the build
inputs:
targetType: 'inline'
script: |
[string] $dateTime = (Get-Date -Format 'yyyyMMdd')
[string] $buildName = "$(Build.DefinitionName)_$(Build.SourceBranchName)_$($dateTime)_$(version.Number)"
Write-Host "Setting the name of the build to '$buildName'."
Write-Host "##vso[build.updatebuildnumber]$buildName"
- stage: Build
jobs:
- job: BuildRestore
steps:
- task: NuGetCommand#2
displayName: 'Restore (NuGet)'
inputs:
command: restore
restoreSolution: '**\*.sln'fs
feedsToUse: select
includeNuGetOrg: true
vstsFeed: 'internalfeed1'
arguments: '--configuration $(buildConfiguration) /p:Version=$(version.Number)'
- task: DotNetCoreCLI#2
displayName: 'Restore (.NET Core)'
inputs:
command: restore
includeNuGetOrg: true
nobuild: true
vstsFeed: 'internalfeed1'
nuGetFeedType: internal
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration) /p:Version=$(version.Number)'
- task: DotNetCoreCLI#2
displayName: 'Build all projects in solution'
inputs:
command: build
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration) /p:Version=$(version.Number)'
- stage: Test
jobs:
- job: Test_UnitTests
steps:
- task: DotNetCoreCLI#2
displayName: 'Run & Analyse UnitTests'
inputs:
command: test
projects: '**/*Tests/*UnitTests.csproj'
arguments: '--configuration $(buildConfiguration) --collect "Code coverage"'
- stage: Package
jobs:
- job: Package_Nuget
steps:
- task: NuGetAuthenticate#0
displayName: "Nuget (Authenticate)"
- task: DotNetCoreCLI#2
displayName: 'NuGet (Package)'
inputs:
nobuild: true
command: pack
packagesToPack: '**/*.csproj'
versioningScheme: byBuildNumber
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI#2
displayName: 'NuGet (Publish)'
inputs:
command: push
searchPatternPush: '$(Build.ArtifactStagingDirectory)/*.nupkg;'
feedPublish: 'internalfeed1'
If I simplify it all down into one single job without the stages/jobs the deployment all works ok (as below)
trigger:
- master
pool:
vmImage: 'windows-latest'
name: 'Set dynamically'
variables:
buildConfiguration: 'Release'
version.Major: 1
version.Minor: $[counter(variables['version.Major'], 0)]
version.Patch: 0
version.Revision: $[counter(variables['version.Minor'], 0)]
version.Number: '$(version.Major).$(version.Minor).$(version.Patch).$(version.Revision)'
steps:
- checkout: self
clean: true
- task: PowerShell#2
displayName: Set the name of the build
inputs:
targetType: 'inline'
script: |
[string] $dateTime = (Get-Date -Format 'yyyyMMdd')
[string] $buildName = "$(Build.DefinitionName)_$(Build.SourceBranchName)_$($dateTime)_$(version.Number)"
Write-Host "Setting the name of the build to '$buildName'."
Write-Host "##vso[build.updatebuildnumber]$buildName"
- task: NuGetCommand#2
displayName: 'Restore (NuGet)'
inputs:
command: restore
restoreSolution: '**\*.sln'fs
feedsToUse: select
includeNuGetOrg: true
vstsFeed: 'internalfeed1'
arguments: '--configuration $(buildConfiguration) /p:Version=$(version.Number)'
- task: DotNetCoreCLI#2
displayName: 'Restore (.NET Core)'
inputs:
command: restore
includeNuGetOrg: true
nobuild: true
vstsFeed: 'internalfeed1'
nuGetFeedType: internal
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration) /p:Version=$(version.Number)'
- task: DotNetCoreCLI#2
displayName: 'Build all projects in solution'
inputs:
command: build
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration) /p:Version=$(version.Number)'
- task: DotNetCoreCLI#2
displayName: 'Run & Analyse UnitTests'
inputs:
command: test
projects: '**/*Tests/*UnitTests.csproj'
arguments: '--configuration $(buildConfiguration) --collect "Code coverage"'
- task: DotNetCoreCLI#2
displayName: 'NuGet (Package)'
inputs:
nobuild: true
command: pack
packagesToPack: '**/*.csproj'
versioningScheme: byBuildNumber
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI#2
displayName: 'NuGet (Publish)'
inputs:
command: push
searchPatternPush: '$(Build.ArtifactStagingDirectory)/*.nupkg;'
feedPublish: 'internalfeed1'
Can't find an answer on the documentation for these pieces of the puzzle that would explain why it wouldn't work when split up into stages/jobs, does anyone know what the reasoning is? Are Stages/Jobs not supposed to interact with each other in this way?
Thanks
This is becuase each job runs on different agent
A stage contains one or more jobs. Each job runs on an agent. A job represents an execution boundary of a set of steps. All of the steps run together on the same agent. For example, you might build two configurations - x86 and x64. In this case, you have one build stage and two jobs.
And this since jon is a boundary for set of steps source code is not shared amongs them. SO if you need keep this as seprate jobs and stages you should repeat in each job checkout step
- checkout: self
clean: true
Please read this basics about pipeline, they will give you high level picture how it works.
And if you want to share some artifact between jobs please take a look here.
And if you need share some variables between stages I wrote an article about this.