.NET Obfuscation in Azure DevOps Pipelines
If ArmDot is already integrated into your project via MSBuild and NuGet, obfuscation in an Azure DevOps pipeline requires no additional tooling. The pipeline runs dotnet publish or invokes VSBuild, and the MSBuild target handles obfuscation automatically. The only platform-specific setup is passing the license key securely - and Azure DevOps offers two mechanisms for that, depending on your organization's secrets infrastructure.
If you have not yet added the NuGet packages and MSBuild target to your project, do that first: Integrating .NET Obfuscation with MSBuild and NuGet →
A basic pipeline
A standard .NET pipeline in Azure DevOps already produces obfuscated output if the MSBuild target is in the .csproj:
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
buildConfiguration: 'Release'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '8.0.x'
- task: DotNetCoreCLI@2
displayName: 'Restore'
inputs:
command: 'restore'
- task: DotNetCoreCLI@2
displayName: 'Publish'
inputs:
command: 'publish'
arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
- task: PublishBuildArtifacts@1
displayName: 'Upload artifact'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'protected-build'The DotNetCoreCLI publish task triggers the MSBuild target, which runs ArmDot on the compiled assembly. The published artifact is already obfuscated. In demo mode (no license key), protected assemblies stop working after two weeks.
License key via secure files
Azure DevOps secure files provide a straightforward way to store the license key outside the repository. Upload the license file once, then download it during the pipeline run.
In Azure DevOps, navigate to Pipelines > Library > Secure files and upload the file containing your license key. Then add the download task and pass the file path to the build:
steps:
- task: DownloadSecureFile@1
name: armDotLicenseKey
displayName: 'Download ArmDot license key'
inputs:
secureFile: 'armdot-license-key.txt'
- task: DotNetCoreCLI@2
displayName: 'Publish'
inputs:
command: 'publish'
arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
env:
ARMDOT_LICENSE_FILE_PATH: $(armDotLicenseKey.secureFilePath)The DownloadSecureFile task downloads the file to a temporary location on the agent. The secureFilePath output variable provides the path, which is passed to the MSBuild task via ARMDOT_LICENSE_FILE_PATH.
The first time the pipeline runs, Azure DevOps requires explicit permission to access the secure file. A prompt appears in the pipeline run view - click Permit to authorize access for subsequent runs.
License key via Azure Key Vault
Organizations that already use Azure Key Vault for secrets management can store the license key there and reference it through a variable group. This approach keeps the license key alongside other organizational credentials and uses the same access control policies.
In Azure DevOps, create a variable group under Pipelines > Library, link it to your Key Vault, and select the secret containing the license key. Then reference the variable group in the pipeline:
variables:
- group: 'ArmDot-Credentials'
- name: buildConfiguration
value: 'Release'
steps:
- script: |
echo "$(ArmDotLicenseKey)" > $(Agent.TempDirectory)/ArmDotLicenseKey
displayName: 'Write license to file'
- task: DotNetCoreCLI@2
displayName: 'Publish'
inputs:
command: 'publish'
arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
env:
ARMDOT_LICENSE_FILE_PATH: $(Agent.TempDirectory)/ArmDotLicenseKeyThe Key Vault approach adds a step - writing the secret value to a temporary file - because the MSBuild task reads a file path rather than a secret value directly. The secret is not logged in build output; Azure DevOps masks Key Vault-backed variable values automatically.
For teams that do not use Key Vault, the secure files approach above is simpler and sufficient.
Multi-stage pipeline with approval gates
Enterprise release pipelines often separate the build from deployment, with a manual approval gate before production artifacts are released. Obfuscation fits naturally into this model because it runs during the build stage, not the deployment stage:
stages:
- stage: Build
jobs:
- job: BuildAndObfuscate
pool:
vmImage: 'ubuntu-latest'
steps:
- task: DownloadSecureFile@1
name: armDotLicenseKey
inputs:
secureFile: 'armdot-license-key.txt'
- task: DotNetCoreCLI@2
displayName: 'Publish'
inputs:
command: 'publish'
arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)'
env:
ARMDOT_LICENSE_FILE_PATH: $(armDotLicenseKey.secureFilePath)
- task: PublishBuildArtifacts@1
inputs:
ArtifactName: 'protected-build'
- stage: Deploy
dependsOn: Build
condition: succeeded()
jobs:
- deployment: Production
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- script: echo "Deploy obfuscated artifact"The environment: 'production' reference triggers any approval checks or gates configured on that environment in Azure DevOps. The obfuscated artifact is produced once in the Build stage and consumed by all downstream stages without re-obfuscation.
Debug builds vs release builds
To keep local debug builds unobfuscated while Release builds in the pipeline are protected, add a condition to the MSBuild target in the .csproj:
<Target Name="Protect" AfterTargets="AfterCompile" BeforeTargets="BeforePublish"
Condition="'$(Configuration)' == 'Release'">This is the same condition used for GitHub Actions and applies identically in Azure DevOps pipelines.
Agent pool considerations
ArmDot runs on Microsoft-hosted agents (Ubuntu, Windows, macOS) with no platform-specific installation. The NuGet packages are restored during the normal restore step. If your organization uses self-hosted agents, verify that the .NET SDK version matches the project's target framework and that NuGet package restore has network access to nuget.org.
If you use VSBuild@1 instead of DotNetCoreCLI@2, the MSBuild target works identically - the obfuscation task is invoked by MSBuild regardless of which tool triggers the build. Pass the license path via the env block on the VSBuild task the same way.
Classic (GUI) pipelines
Azure DevOps also supports the older classic pipeline editor. The integration works the same way: the MSBuild target fires during the build task regardless of whether the pipeline was defined in YAML or in the classic editor. Add the DownloadSecureFile task before the build task, and set ARMDOT_LICENSE_FILE_PATH as an environment variable on the build task. No YAML required.
Verifying the artifact
Check the build log for [ArmDot] output lines confirming obfuscation ran. A successful run shows Names obfuscation started, Conversion started for method, and Writing protected assembly.
To verify the artifact itself, download it from the build's artifact drop and open the main assembly in ILSpy. Non-public members should show obfuscated names. Virtualized methods should show the VM interpreter loop. String literals should be absent from the decompiled output.
For a complete step-by-step walkthrough with screenshots of the Azure DevOps UI: Obfuscation .NET applications with Azure DevOps Pipelines →
Back to: .NET Obfuscation in CI/CD →
ArmDot in Azure DevOps
ArmDot runs on Microsoft-hosted and self-hosted Azure DevOps agents with no platform-specific installation. NuGet packages restore alongside all other project dependencies. Full documentation →
