How to .pfx sign code using local machine in Azure Devops Pipelines? - azure-devops

We have developed for the client Microsoft Office extension, and they do require to be code signed.
For development I have made a .pfx in VS -> Project properties -> Signing -> Create Test Certificate. Put empty password, dumped .pfx into source control and it was building for the whole company while developing.
For the release of the addon, things are different, we need to sign an extension with the clients .pfx and they want to add a step in the Azure DevOps build a pipeline to do it automatically. The thing is they can't use cloud solutions, so as I understand Azure Key Vault is out of the picture. They do have a local trusted machine that we could put their .pfx for signing.
I couldn't find a way how to do a signing in Azure Pipelines, that would not involve Azure Key Vault, or Azure Secure Files but I would expect there to be a mechanism for this as it seems like a quite common thing to do.
What is the preferred solution to .pfx sign code using local machine in Azure Devops Pipelines?

We ended up with following:
Added .pfx on local build machine.
Added hidden variable to Azure build pipeline pfxPassword
Then added following build steps tp Azure build pipeline:
trigger:
- main
pool:
name: 'XXX Build Pool'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
#change to actual directory where signtool is.
pathToSignTool: "\"C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.19041.0\\x64\\signtool.exe\""
pathToPfx: "C:\\XXX\\Install\\XXX_Applications.pfx"
pathToBuildDirectory: "\\XXXYYY\\bin\\Release\\"
pathToMageTool: "\"C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v10.0A\\bin\\NETFX 4.8 Tools\\mage.exe\""
pathToContinousDeployment: "C:\\ContinousDeployment\\YYY\\"
name: $(MajorVersion).$(MinorVersion).$(date:yy)$(DayOfYear)$(rev:.r)
steps:
- task: Assembly-Info-NetFramework#2
inputs:
Path: '$(Build.SourcesDirectory)'
FileNames: |
**\AssemblyInfo.cs
InsertAttributes: true
FileEncoding: 'auto'
WriteBOM: false
VersionNumber: '$(Build.BuildNumber)'
FileVersionNumber: '$(Build.BuildNumber)'
InformationalVersion: '$(Build.BuildNumber)'
LogLevel: 'verbose'
FailOnWarning: false
DisableTelemetry: false
- task: VSBuild#1
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: VSTest#2
inputs:
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- script:
$(pathToMageTool) -s $(Build.Repository.LocalPath)$(pathToBuildDirectory)XXXYYY.vsto -cf $(pathToPfx) -pwd %MAPPEDPASS%
env:
MAPPEDPASS: $(pfxPassword)
- script:
$(pathToMageTool) -s $(Build.Repository.LocalPath)$(pathToBuildDirectory)XXXYYY.dll.manifest -cf $(pathToPfx) -pwd %MAPPEDPASS%
env:
MAPPEDPASS: $(pfxPassword)
- script:
$(pathToSignTool) sign /f $(pathToPfx) /p %MAPPEDPASS% $(Build.Repository.LocalPath)$(pathToBuildDirectory)XXXYYY.dll
env:
MAPPEDPASS: $(pfxPassword)
- task: CopyFiles#2
inputs:
SourceFolder: '$(Build.Repository.LocalPath)$(pathToBuildDirectory)'
Contents: '**'
TargetFolder: '$(pathToContinousDeployment)'
OverWrite: true

Related

Azure Pipeline - Strong named assemblies in solution

I have an Azure Pipeline that builds a Winform solution with multiple projects that are Strong signed with a PFX.
I found the following link:
https://dhvik.blogspot.com/2020/01/building-strong-named-assemblies-in.html
This is my YAML file:
# .NET Desktop
# Build and run tests for .NET Desktop or Windows classic desktop solutions.
# Add steps that publish symbols, save build artifacts, and more:
# https://learn.microsoft.com/azure/devops/pipelines/apps/windows/dot-net
trigger:
- master
pool:
vmImage: 'windows-latest'
variables:
- group: 'CertPass'
steps:
- task: DownloadSecureFile#1
displayName: Download Pfx
name: myCertificatePfx
inputs:
secureFile: ventasmlcert.pfx
- task: DownloadSecureFile#1
displayName: Download sni
name: snInstallPfx
inputs:
secureFile: SnInstallPfx.exe
- task: PowerShell#2
env:
SN_INSTALL_PFX: $(snInstallPfx.secureFilePath)
MYCERTIFICATE_PFX: $(myCertificatePfx.secureFilePath)
MYCERTIFICATE_PFX_PASSWORD: $(certpass)
inputs:
targetType: 'inline'
script: '&"$($ENV:SN_INSTALL_PFX)" "$($ENV:MYCERTIFICATE_PFX)" "$($ENV:MYCERTIFICATE_PFX_PASSWORD)"'
- task: NuGetToolInstaller#1
- task: NuGetCommand#2
inputs:
restoreSolution: '$(solution)'
- task: VSBuild#1
displayName: 'Build .csproj file'
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: VSTest#2
inputs:
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
Everything works fine in the pipeline for the signing of my PFX:
But when building the solution I still get the error, but was looking that the Container where the signing was added is different from the other Containers of each project that needs to be signed.
This is the error:
Error MSB3325: Cannot import the following key file: ***cert.pfx. The key file may be password protected. To correct this, try to import the certificate again or manually install the certificate to the Strong Name CSP with the following key container name: VS_KEY_71452E506F1E61FB
Any clue?

Azure DevOps: Populating secure file references with job matrix variables

For context, I am trying to use an Azure build pipeline to build multiple flavors of an Android app. Each flavor has its own separate signing keystore, and all of those keystores are stored in my 'secure files' in the library.
However, when I try to dereference the $(Keystore) variable during the 'android signing' task, it doesn't seem to recognize that that is a variable that exists, and tries instead to locate a file called '$(Keystore)'
Am I doing something wrong here? This seems like it should work.
A sanitized example looks like this:
# Android
# Build your Android project with Gradle.
# Add steps that test, sign, and distribute the APK, save build artifacts, and more:
# https://learn.microsoft.com/azure/devops/pipelines/languages/android
trigger:
- feat/ci-setup
pool:
vmImage: 'macos-latest'
variables:
${{ if startsWith(variables['build.sourceBranch'], 'refs/heads/feat/') }}:
Branch_Type: 'feature'
${{ if startsWith(variables['build.sourceBranch'], 'refs/heads/hotfix/') }}:
Branch_Type: 'hotfix'
${{ if startsWith(variables['build.sourceBranch'], 'refs/heads/release/') }}:
Branch_Type: 'release'
${{ if eq(variables['Branch_Type'], 'release') }}:
Configuration: 'release'
ConfigurationCC: 'Release'
${{ if ne(variables['Branch_Type'], 'release') }}:
Configuration: 'debug'
ConfigurationCC: 'Debug'
jobs:
- job: Build
variables:
- group: android_keystores
strategy:
maxParallel: 2
matrix:
Flavor_1:
AppFlavor: '1'
AppFlavorCC: '1'
Keystore: 'flavor1.keystore'
KeyAlias: 'flavor1'
KeystorePass: '$(flavor1_storepass)'
KeyPass: '$(flavor1_keypass)'
Flavor_2:
AppFlavor: '2'
AppFlavorCC: '2'
Keystore: 'flavor2.keystore'
KeyAlias: 'flavor2'
KeystorePass: '$(flavor2_storepass)'
KeyPass: '$(flavor2_keypass)'
steps:
- task: Gradle#2
inputs:
workingDirectory: ''
gradleWrapperFile: 'gradlew'
gradleOptions: '-Xmx3072m'
publishJUnitResults: false
tasks: 'assemble$(AppFlavorCC)$(ConfigurationCC)'
- task: AndroidSigning#3
displayName: Signing .apk
inputs:
apkFiles: 'app/build/outputs/apk/$(AppFlavor)/$(Configuration)/*.apk'
apksign: true
apksignerKeystoreFile: '$(Keystore)'
apksignerKeystorePassword: '$(KeystorePass)'
apksignerKeystoreAlias: '$(KeyAlias)'
apksignerKeyPassword: '$(KeyPass)'
zipalign: true
- task: Bash#3
displayName: Move APK to Artifact Folder
continueOnError: true
inputs:
targetType: 'inline'
script: |
mv \
app/build/outputs/apk/$(AppFlavor)/$(Configuration)/*.apk \
$(Build.ArtifactStagingDirectory)/$(ArtifactName)/
- task: PublishBuildArtifacts#1
displayName: Publish Build Artifacts
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'Blueprint-Build'
publishLocation: 'Container'
But when the pipeline runs I am told this:
There was a resource authorization issue: "The pipeline is not valid. Job Build: Step AndroidSigning input keystoreFile references secure file $(Keystore) which could not be found. The secure file does not exist or has not been authorized for use. For authorization details, refer to https://aka.ms/yamlauthz."
Azure DevOps: Populating secure file references with job matrix variables
This is a limitation from the task itself.
When we test it with Classic mode, we could find out that the value of the option Keystore file could not be entered manually, we could only select a certain file through the drop-down menu:
That the reason why it doesn't seem to recognize that that is a variable that exists, and tries instead to locate a file called '$(Keystore)'.
To resolve this issue, you could change the task version from 3 to 1, which supports manual input:
And as another solution, you could also use the command line to sign the *.apk:
Android apk signing: sign an unsigned apk using command line
You're missing the step to download the Secure File. Unlike variable groups, you need to explicitly download them to have access via the secure file name.
You'll want to add something similar to the example task below to your steps to pull the secure file. Then, you'll access your secure file via NAME_PARAMETER.secureFilePath:
- task: DownloadSecureFile#1
displayName: "Download Keyfile 1"
name: "YOUR_SECUREFILE_NAME"
inputs:
secureFile: keyfile1
- task: AndroidSigning#3
displayName: Signing .apk
inputs:
apkFiles: 'app/build/outputs/apk/$(AppFlavor)/$(Configuration)/*.apk'
apksign: true
apksignerKeystoreFile: '$(YOUR_SECUREFILE_NAME.secureFilePath)'
apksignerKeystorePassword: '$(KeystorePass)'
apksignerKeystoreAlias: '$(KeyAlias)'
apksignerKeyPassword: '$(KeyPass)'
zipalign: true

How to generate EF Core migrations script when ConnectionString is only known after ARM template deployment?

I want to release an app to Azure and deploy migrations to a database before deploying the Web App. That sounds relatively simple, you can create a migrations.sql script with dotnet-ef in your Build pipeline and apply this script in your Release pipeline.
However, I cannot create a a migrations.sql script in the Build pipeline as I am using four different databases for a DTAP environment. Thus, I would need to generate a migrations.sql script per environment and perform these separately against each of the databases. (as I understand it)
In my Release pipeline I use an incremental ARM template to deploy resources and set the ConnectionString (which comes from an Azure Key Vault) in the Azure Web App application settings configuration.
How/where do I generate the migrations.sql script? Do I do this in a Release pipeline? Am I making a major mistake in my reasoning?
EDIT:
Thanks for Madej's answer that shows the environment doesn't matter. I tried implementing creating the migrations.sql script in my pipelines.
# ASP.NET Core (.NET Framework)
# Build and test ASP.NET Core projects targeting the full .NET Framework.
# Add steps that publish symbols, save build artifacts, and more:
# https://learn.microsoft.com/azure/devops/pipelines/languages/dotnet-core
trigger:
- master
pool:
vmImage: 'windows-latest'
variables:
projects: '**/*.csproj'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: DotNetCoreCLI#2
displayName: "Install dotnet-ef"
inputs:
command: 'custom'
custom: 'tool'
arguments: 'install --global dotnet-ef'
- task: DotNetCoreCLI#2
displayName: "Restore tools"
inputs:
command: 'custom'
custom: 'tool'
arguments: 'restore'
- task: DotNetCoreCLI#2
displayName: "Restore"
inputs:
command: 'restore'
projects: '$(projects)'
feedsToUse: 'select'
- task: DotNetCoreCLI#2
displayName: "Build"
inputs:
command: 'build'
projects: '$(projects)'
arguments: '--configuration $(BuildConfiguration)'
- task: DotNetCoreCLI#2
displayName: "Create migrations.sql"
inputs:
command: 'custom'
custom: 'ef'
arguments: 'migrations script --configuration $(BuildConfiguration) --no-build --idempotent --output $(Build.ArtifactStagingDirectory)\migrations.sql'
workingDirectory: 'WebApi.api'
- task: DotNetCoreCLI#2
displayName: "Publish"
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: false
- task: PublishBuildArtifacts#1
displayName: "Publish to Azure Pipelines"
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'Container'
My pipeline doesn't work, in the task "Create migrations.sql" I run into the following error:
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: DefaultAzureCredential failed to retrieve a token from the included credentials.
- EnvironmentCredential authentication unavailable. Environment variables are not fully configured.
- ManagedIdentityCredential authentication unavailable. No Managed Identity endpoint found.
- Visual Studio Token provider can't be accessed at C:\Users\VssAdministrator\AppData\Local\.IdentityService\AzureServiceAuth\tokenprovider.json
- Stored credentials not found. Need to authenticate user in VSCode Azure Account.
- Please run 'az login' to set up account
This is because in my Program.cs I add a keyvault and authenticate with the Azure.Identity DefaultAzureCredential as follows:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
{
var settings = config.Build();
var credentials = new DefaultAzureCredential(
new DefaultAzureCredentialOptions() {
ExcludeSharedTokenCacheCredential = true,
VisualStudioTenantId = settings["VisualStudioTenantId"],
}
);
config.AddAzureKeyVault(new Uri(settings["KeyVault:Endpoint"]), credentials).Build();
})
.UseStartup<Startup>();
});
The Azure Pipelines cannot get a token from DefaultAzureCredential. How do I authenticate the Azure Pipelines?
I have figured out the solution to the problem in my edit. The primary way that the DefaultAzureCredential class gets credentials is via environment variables.
Thus, I had to define the environment variables somewhere. I didn't want to do this in the pipeline variables to avoid having to manage them as they should be available from the project in the form of a service connection to Azure.
I did the following:
In my pipelines added an AzureCLI task to read out the service principal id, key and tenant id and set them to job variables as follows:
- task: AzureCLI#2
inputs:
azureSubscription: '<subscription>'
scriptType: 'ps'
scriptLocation: 'inlineScript'
inlineScript: |
Write-Host '##vso[task.setvariable variable=AZURE_CLIENT_ID]'$env:servicePrincipalId
Write-Host '##vso[task.setvariable variable=AZURE_CLIENT_SECRET]'$env:servicePrincipalKey
Write-Host '##vso[task.setvariable variable=AZURE_TENANT_ID]'$env:tenantId
addSpnToEnvironment: true
In my "Create migrations.sql" task pass these variables as environment variables as follows:
- task: DotNetCoreCLI#2
displayName: "Create migrations.sql"
inputs:
command: 'custom'
custom: 'ef'
arguments: 'migrations script --configuration $(BuildConfiguration) --no-build --idempotent --output $(Build.ArtifactStagingDirectory)\migrations.sql'
workingDirectory: 'WebApi.api'
env:
AZURE_CLIENT_ID: $(AZURE_CLIENT_ID)
AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)
AZURE_TENANT_ID: $(AZURE_TENANT_ID)
Added the service principal to the Azure Key Vault RBAC as a Key Vault Secrets User. I could only do this with az:
az role assignment create --role 'Key Vault Secrets User (preview)' --scope '/subscriptions/<subscription ID>/resourcegroups/<resource group name>/providers/Microsoft.KeyVault/vaults/<vault name>' --assignee '<service principal object id>'
This absolutely solved my problems without having to manage any more secrets/variables as they are all contained in the pipeline itself and don't pose any security threats.
You can do this in a build pipeline because migration.sql script makes some checks if specific migration was already applied or not.
To create migration script when you use Azure Key Vault in you confiugration the easiest way is to run command from Azure Clit task:
- task: AzureCLI#2
inputs:
azureSubscription: 'rg-tcm-si'
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: 'dotnet ef migrations script --configuration $(BuildConfiguration) --no-build --idempotent --output $(Build.ArtifactStagingDirectory)\migrations.sql'
workingDirectory: 'Itan.Database'
Before that you need to add get and list permissions to your serivde principal which is behind your connection service:
And then even if you need to deploy the same script to different environments/databases it is all fine until they haven't been drifted. So if you do all changes through ef core you are good to go with migration.sql done once and applied many times.
In database you should have:
which contains already applied migrations. ANd then in script you will find:
IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20200101111512_InitialCreate')
BEGIN
CREATE TABLE [SomeTable] (
[Id] uniqueidentifier NOT NULL,
[StorageDate] datetime2 NOT NULL,
.....
);
END;
GO
Thus you are safe to run it against multiple databases.
And then to deploy you can use
steps:
- task: SqlAzureDacpacDeployment#1
displayName: 'Azure SQL SqlTask'
inputs:
azureSubscription: 'YourSubscription'
ServerName: 'YourServerName'
DatabaseName: 'YourDatabaseName'
SqlUsername: UserName
SqlPassword: '$(SqlServerPassword)'
deployType: SqlTask
SqlFile: '$(System.DefaultWorkingDirectory)/staging/drop/migrations.sql'

Publishing VS Code extension via Azure DevOps

After reading VSCode Publish Extension docs, I've succeeded to publish a VSCode extension manually with vsce.
I'm wondering if there is a way to publish extensions automatically via Azure DevOps pipelines (build or release) instead of doing it manually.
I've tried to use vsce there but I'm getting an authentication error
Resource not available for anonymous access. Client authentication required.
Using vsce publish -p <access_token> is not possible because the pipeline is public and everyone can see the access token...
So, is there a way to publish a Visual Studio Code extension automatically via Azure DevOps Pipeline or even Travis CI?
You can add the Personal Access Token as a secret variable, then nobody can couldn't see it.
Go to Azure DevOps to your pipeline and click on "Edit", not in the top left click on "Variables":
Now click on the + icon and add the variable, mark the checkbox "Keep this value secret":
Now you can use it in this way: $(PAT), for example:
vsce publish -p $(PAT)
The variable value will not appear in the YAML :)
Is there a way to publish a Visual Studio Code extension automatically
via Azure DevOps Pipeline?
Of course yes!
To have a good experience for CI/CD in Azure Devops, I recommend you store the source code in Azure Devops or Github.
Build \ CI
In build, most of work is update the version which in manifest of VSIX, build\create package. For the version increased, here I use the counter expression feature which supported in VSTS to achieve that:
counter('name', seed)
Use this expression in variable declaration bloc. For detailed and completed build process, refer to my sample YAML code:
trigger:
- '*'
pool:
vmImage: 'windows-2019'
variables:
VersionPatch: $[counter('versioncount', 24)]
solution: '**/*.sln'
BuildPlatform: 'Any CPU'
BuildConfiguration: 'Release'
name: 2.0.$(VersionPatch)
steps:
- task: UseDotNet#2
inputs:
packageType: 'sdk'
version: '3.0.100'
includePreviewVersions: true
- task: NuGetToolInstaller#1
inputs:
versionSpec: 5.1.0
- task: PowerShell#2
displayName: Update version
inputs:
filePath: 'Build\VersionUpdate.ps1'
arguments: '$(Build.BuildNumber)'
pwsh: true
- task: NuGetCommand#2
inputs:
command: 'restore'
- task: DotNetCoreCLI#2
displayName:
inputs:
command: 'restore'
projects: 'tests/**/*.csproj'
vstsFeed: '{My feed ID}'
includeNuGetOrg: false
- task: VSBuild#1
inputs:
solution: '**\*.sln'
maximumCpuCount: true
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- task: VSTest#2
inputs:
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- task: CopyFiles#2
inputs:
SourceFolder: '$(Build.SourcesDirectory)'
Contents: |
Build/**
**/*.vsix
**/*.nupkg
README.md
TargetFolder: '$(Build.ArtifactStagingDirectory)'
- task: PublishPipelineArtifact#0
inputs:
artifactName: 'ExtensionDrop'
targetPath: '$(Build.ArtifactStagingDirectory)'
In UpdateVersion.ps1 file:
$VerbosePreference="Continue"
$version = $args[0]
if (!$version) {
$version = "0.0.0"
}
Write-Host "This Version is: $version"
$FullPath = Resolve-Path $PSScriptRoot\..\src\Merlin.Compiler.Vsix\source.vsixmanifest
Write-Host $FullPath
[xml]$content = Get-Content $FullPath
$content.PackageManifest.Metadata.Identity.Version = $version
$content.Save($FullPath)
Release\ CD
After build succeed, set the release pipeline for this repos. In release, use powershell script and VsixPublisher.exe to publish the vsix file.
$PAToken = $args[0]
$VsixPath = "$PSScriptRoot\..\src\Merlin.Compiler.Vsix\bin\Release\Merlin.Compiler.Vsix"
$ManifestPath = "$PSScriptRoot\ExtensionManifest.json"
$Installation = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -format json | ConvertFrom-Json
$Path = $Installation.installationPath
$VsixPublisher = Join-Path -Path $Path -ChildPath "VSSDK\VisualStudioIntegration\Tools\Bin\VsixPublisher.exe" -Resolve
& $VsixPublisher publish -payload $VsixPath -publishManifest $ManifestPath -personalAccessToken $PAToken -ignoreWarnings "VSIXValidatorWarning01,VSIXValidatorWarning02,VSIXValidatorWarning08"
In CD, use VsixPublisher.exe which exist in VS to publish the vsix file.
You can set the PAToken in Variable tab, then set it as secret. Thus it would not be public for others. Here PAT token is a necessary one which could not be replaced by others. And also, when generate the token, need choose All accessible organizations. Or it will cause the permission error.
Further #Shayki's answer there are some more steps because you can't just run vsce publish -p $(PAT).
The vsce should be installed (can be in devDependencies)
Add a "deploy" (or name it as you like) script to the package.json scripts.
"deploy": "vsce publish -p"
Add a "publish" step in the azure-pipeline.yml file. the condition is for running the publish script only on master so Pull Requests will not publish. Also run it only in Linux's build, in case you configured multiple platforms. If you configured only one (for example, windows) replace Linux with that platform
- bash: |
echo ">>> Publish"
yarn deploy $(token)
displayName: Publish
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['Agent.OS'], 'Linux'))
Example azure-pipeline.yml

Azure Devops : Build a winforms project and copy release files to Azure blob storage

I want to set up CI CD for a winforms project Dot Net Framework 4.5.2 to build the project and then copy the release files to an Azure blob.
When I create a new build pipeline and select my Azure Repo the following YAML is created
# .NET Desktop
# Build and run tests for .NET Desktop or Windows classic desktop solutions.
# Add steps that publish symbols, save build artifacts, and more:
# https://learn.microsoft.com/azure/devops/pipelines/apps/windows/dot-net
trigger:
- master
pool:
vmImage: 'VS2017-Win2016'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: NuGetToolInstaller#0
- task: NuGetCommand#2
displayName: 'NuGet restore'
inputs:
restoreSolution: '**\*.sln'
feedsToUse: config
nugetConfigPath: 'NuGet.config'
- task: VSBuild#1
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
The pipeline builds successfully
However I am having trouble setting up the Release pipeline to copy the release files to Azure blob storage.
I created a new release pipeline with an Empty Job.
Then I added an Azure File Copy Task
What do I put as the source ?
When I click the elipse I see I can select a myapp (Build) folder from within a Linked artifacts folder.
I was able to set up the storage and container names , but left the Blob Prefix blank.
When I run the Agent job I get an error on AzureBlob File Copy
(edited)
##[section]Starting: AzureBlob File Copy
==============================================================================
Task : Azure File Copy
Description : Copy files to Azure blob or VM(s)
Version : 2.1.3
Author : Microsoft Corporation
Help : [More Information](https://aka.ms/azurefilecopyreadme)
==============================================================================
##[command]Import-Module -Name C:\Program Files\WindowsPowerShell\Modules\AzureRM\2.1.0\AzureRM.psd1 -Global
##[warning]The names of some imported commands from the module 'AzureRM.Websites' include unapproved verbs that might make them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with the Verbose parameter. For a list of approved verbs, type Get-Verb.
##[command]Import-Module -Name C:\Program Files\WindowsPowerShell\Modules\AzureRM.Profile\2.1.0\AzureRM.Profile.psm1 -Global
##[command]Add-AzureRMAccount -ServicePrincipal -Tenant *** -Credential System.Management.Automation.PSCredential -EnvironmentName AzureCloud #processScope
##[command] Select-AzureRMSubscription -SubscriptionId blahblah -TenantId ***
Uploading files from source path: 'd:\a\r1\a\_Viv2' to storage account: 'viv' in container: 'viv2' with blob prefix: ''
##[command] & "AzCopy\AzCopy.exe" /Source:"d:\a\r1\a\_Viv2" /Dest:"https://vivapps.blob.core.windows.net/viv2" /#:"d:\a\_temp\n40zblahblah" /XO /Y /SetContentType /Z:"AzCopy" /V:"AzCopy\AzCopyVerbose_20blahblah.log" /S
[2019/02/13 01:26:46][ERROR] Error parsing source location "d:\a\r1\a\_Viv2": Failed to enumerate directory d:\a\r1\a\_Viv2\ with file pattern *. The system cannot find the path specified. (Exception from HRESULT: 0x80070003) For more details, please type "AzCopy /?:Source" or use verbose option /V.
##[error]Upload to container: 'vivj2' in storage account: 'vivapps' with blob prefix: '' failed with error: 'AzCopy.exe exited with non-zero exit code while uploading files to blob storage.' For more info please refer to https://aka.ms/azurefilecopyreadme
##[section]Finishing: AzureBlob File Copy
[Update]
I think the issue must be to do with the Source
looking at the build logs I see path names like "D:\a\1\s\blahblah"
I also see
creating bin\Release
but how do I figure out what I should be putting in the Source property ?
Trying
$(System.DefaultWorkingDirectory)/_Viv2/bin/Release
No joy.
Exception from HRESULT: 0x80070003 means the system cannot find the file specified.
[Update]
The default YAML created does not included a task to publish the build "Artifacts" (Not to be confused with the Project Artifacts )
I tried adding one
- task: PublishBuildArtifacts#1
displayName: 'Publish Artifact: drop'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
artifactName: drop
but the task log says
##[warning]Directory 'D:\a\1\a' is empty. Nothing will be added to build artifact 'drop'
Please have a try to append the copy file and publish Build Artifacts task after VS build task in the build pipeline.
In the build pipeline:
...
- task: VSBuild#1
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: CopyFiles#2
displayName: 'Copy Files'
inputs:
SourceFolder: '$(build.sourcesdirectory)'
TargetFolder: '$(build.artifactstagingdirectory)'
- task: PublishBuildArtifacts#1
displayName: 'Publish Artifact: drop'
In the release pipeline:
Azure copy file source should be
$(System.DefaultWorkingDirectory)/{Source alias}/drop/xx/xxx/bin/Release
We could get source alias from this screenshot
After we build successfully then we could choose the source path.
It works correctly on my side, I check it from the release log and azure storage container