Skip to Content
New release 12 available 🎉

Azure DevOps

Run Babel Obfuscator on Azure Pipelines by referencing the Babel Obfuscator NuGet package from a private Azure Artifacts feed, with the license supplied as a secret variable.

Azure Pipelines builds the project with the .NET SDK, and the Babel.Obfuscator package inserts the Babel task into that build. Nothing on the agent needs Babel installed: the package carries the build tools, and the pipeline only has to authenticate to the feed that hosts it and hand Babel a license.

The sample used on this page is a small console application:

git clone https://github.com/babelfornet/devops-integration.git

Host the Package on an Azure Artifacts Feed

The Babel.Obfuscator package is not published on nuget.org — you receive it with the Ultimate edition or with a Babel Licensing site edition. Push it to a private feed that only your organisation can read.

Never push the Babel Obfuscator package to a public feed. The package contains the Babel build tools, and republishing it breaks the terms of your license.

In Azure DevOps open Artifacts, create a feed named Babel, then push the package to it:

dotnet nuget push Babel.Obfuscator.12.0.0.nupkg \ --source "https://pkgs.dev.azure.com/ORGANISATION/_packaging/Babel/nuget/v3/index.json" \ --api-key az

Add a NuGet.config next to the solution so both your machine and the build agent resolve the package from that feed:

NuGet.config
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <clear /> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> <add key="babel" value="https://pkgs.dev.azure.com/ORGANISATION/_packaging/Babel/nuget/v3/index.json" /> </packageSources> </configuration>

Credentials are deliberately absent from this file. On a developer machine the Azure Artifacts Credential Provider  prompts for them once; in the pipeline the NuGetAuthenticate task supplies the build identity’s token. Neither writes a secret into the repository.

Reference the Package

Add the package reference to every project whose assembly you want obfuscated:

DevOpsIntegration.csproj
<ItemGroup> <PackageReference Include="Babel.Obfuscator" Version="12.0.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup>

PrivateAssets keeps the reference out of the packages your project produces: Babel is a build-time tool, not a dependency of the shipped assembly.

Supply the License as a Secret

Babel needs a license to produce a permanently working assembly; without one it runs in evaluation mode and the output stops working after a short period.

Do not commit babel.licenses to the repository. A license file in a repository is readable by everyone who can read the repository, and by everyone who forks it — including after you delete the file, because the blob stays reachable in the fork and in the original repository’s history.

Store the license key as a secret variable instead. In the pipeline, under Variables, add BabelLicense and mark it secret. Then map it to an environment variable in the build step and read it from the project:

DevOpsIntegration.csproj
<PropertyGroup Condition="'$(BABEL_LICENSE)' != ''"> <BabelLicense>$(BABEL_LICENSE)</BabelLicense> </PropertyGroup>

The BabelLicense property accepts a license file path, a license key, or a floating license user key written as floating:<user key>. On a build agent the last two are the ones to use, because neither needs a file on disk. Locally, the property is left unset and Babel picks up the babel.licenses file from the project folder or a parent folder, so developers keep working without any pipeline configuration. See Package Setup.

The Pipeline

azure-pipelines.yml
trigger: - main pool: vmImage: ubuntu-latest variables: buildConfiguration: Release steps: - task: UseDotNet@2 displayName: Install the .NET SDK inputs: version: 10.0.x - task: NuGetAuthenticate@1 displayName: Authenticate to the Babel feed - script: dotnet restore DevOpsIntegration.sln displayName: Restore - script: dotnet build DevOpsIntegration.sln --configuration $(buildConfiguration) --no-restore displayName: Build and obfuscate env: BABEL_LICENSE: $(BabelLicense) - task: PublishPipelineArtifact@1 displayName: Publish the obfuscated output inputs: targetPath: DevOpsIntegration/bin/$(buildConfiguration)/net10.0 artifact: DevOpsIntegration

Two details are worth pointing out.

NuGetAuthenticate@1 is what makes the private feed reachable. It configures the credential provider with the pipeline’s own identity, so the NuGet.config above needs no packageSourceCredentials section and no token of yours.

Secret variables are not passed to scripts automatically — that is the point of marking them secret. The env: block on the build step is what maps BabelLicense into the process, and it is the only place the key appears.

Obfuscation runs as part of dotnet build; there is no separate Babel step. The Babel log appears in the build output, and a licensing or configuration error fails the step.

The agent image only has to run the .NET SDK. ubuntu-latest is the cheapest choice and works for any target runtime, because Babel operates on IL: a Linux agent can obfuscate assemblies destined for Windows. Use windows-latest when the build itself needs Windows — .NET Framework targets, WPF, or Windows Forms.

Configure the Obfuscation

Babel is configured with MSBuild properties in the project file. A common arrangement is to leave Debug builds untouched, so local debugging behaves normally, and protect Release builds:

DevOpsIntegration.csproj
<PropertyGroup Condition="'$(Configuration)' == 'Debug'"> <BabelEnabled>false</BabelEnabled> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)' == 'Release'"> <StringEncryption>stream</StringEncryption> <ControlFlowObfuscation>if=on;goto=on;switch=on;case=on;call=on</ControlFlowObfuscation> <ControlFlowIterations>3</ControlFlowIterations> <ResourceEncryption>true</ResourceEncryption> </PropertyGroup>

Every property is listed in Package Reference, and Configuring Obfuscation explains how they map to Babel features.

For anything finer than a whole-assembly switch, add a rules file. The rule below keeps control flow obfuscation off short methods, where flattening costs more than it hides:

babelRules.xml
<?xml version="1.0" encoding="utf-8" ?> <Rules> <Rule name="reduce control flow" feature="control flow" exclude="false" applyToMembers="true"> <Target>Classes,Structures</Target> <Pattern>*</Pattern> <Properties> <MaxSwitchTargets>5</MaxSwitchTargets> <MinInstructionCount>18</MinInstructionCount> <UseValueEncryption>false</UseValueEncryption> </Properties> <Description>Do not scramble methods with few instructions.</Description> </Rule> </Rules>

Point the build at it with the BabelRules property:

<PropertyGroup> <BabelRules>$(MSBuildThisFileDirectory)babelRules.xml</BabelRules> </PropertyGroup>

Verifying the Result

Download the pipeline artifact and check that the assembly really is obfuscated, rather than assuming it. The Detecting Babel Obfuscation example shows how to test this automatically, which is worth adding to the pipeline as a gate: a misconfigured license or a skipped build step otherwise ships a plain assembly without failing anything.

Keep the map file produced by the build if you intend to decode stack traces coming back from production. See Decoding Stack Traces.

Last updated on