🚀✨ I am in your pipeline, decorating it with compliance 🛠️🔒

Back in 2022 I wrote a blog post I am in your pipeline reading all your secrets! about how secrets can be leaked in Azure Pipelines. I think it’s time to offset that blog post and have a look at how compliance of all pipelines in a single Azure DevOps Project can be achieved. This is done by using a feature called Pipeline Decorators .

What are pipeline decorators?

In most organizations there are certain required compliance and security policies. For example; to be compliant with corporate policies a static code analysis tool must to be executed on all pipelines before executing the actual pipeline tasks. This is where pipeline decorators come in, pipeline authors don’t need to remember to add that step. We as Azure DevOps Organization owners create a decorator that automatically injects the step into all pipelines during their runtime. Ensuring on an Azure DevOps Organization level that all pipelines are compliant with our organization’s policies.

If Pipeline Decorators are so awesome why isn’t everyone using them and why isn’t this feature widespread in the community?

Be honest,…

But don’t worry, we got this! Trough this blog post we will learn in a step by step manner how to create and publish our own pipeline decorator using the Microsoft Security DevOps Task . This free to use task is a collection of static analysis tools aimed at enhancing the code quality, security and compliance of the development lifecycle. So to double down on the earlier given example we are going to implement it as part of our Pipeline Decorator.

We got this!

Pipeline Decorators types

There are two types of pipeline decorators:

  • Build Decorators
  • Release Decorators

The Build Decorators apply to Classic Build Pipelines and to the newer YAML Multistage Pipelines. The Release Decorators, instead, apply ONLY to the Classic Release Pipelines. Since the Classic Release Pipelines are being deprecated, we will focus on the Build Decorators and consider the Release decorators out of scope.

Decomposing a pipeline

To make sure everyone is on the same page let’s quickly recap the YAML Multistage Pipelines composition. As show in the following image, a YAML Multistage Pipeline is composed of one or more stages, each stage is composed of one or more jobs (jobs are executed on an agent), a job contains steps and each step is composed of tasks.

Pipeline Decomposition

Injection options

Pipeline decorators inject steps to the beginning, the end of every pipeline job and before and after a certain task. The injection point is defined by the target property in the decorator configuration.

Take a closer look!

The following targets are supported for YAML Multistage Pipeline:

Decorator Injection options
Target Description Run before other tasks in a classic build or YAML pipeline. Run after the last checkout task in a classic build or YAML pipeline. Run after other tasks in a classic build or YAML pipeline. Run before specified task in a classic build or YAML pipeline. Run after specified task in a classic build or YAML pipeline.


Lets decorate your pipelines

First lets make sure we are all set by checking the following prerequisites.

  • Get our VS Code or any other code editor ready and create a new folder. Preferable one that is source controlled!
  • Make sure the identity we are using is a member of the Project Collection Administrators in our Azure DevOps Organization.
  • Make sure we have the Microsoft Security DevOps extension installed in our Azure DevOps Organization.
  • Make sure we have Node.js installed on our machines.
  • Install the packaging tool (TFX) by running this command: npm install -g tfx-cli

Create the decorator extension

  1. In the folder we created as part of the prerequisites initialize a new npm package manifest by running hte following command. A package.json file has been created in your directory, it describes the libraries required by the extension.

  2. Install the Microsoft VSS Web Extension SDK package and save it to our npm package manifest by running hte following command. The vss-web-extension-sdk package is now installed inside the node_modules subdirectory. This SDK includes a JavaScript library that provides APIs required for communicating with the page our extension is embedded in.

  3. Inside the folder we created as part of the prerequisites we create a file named vss-extension.json. Next we copy the following content into this file.

  4. Now we replace the placeholders in the vss-extension.json file with your own values, we can use the example values provided in the table for our lab. Full details on all available attributes can be found at the Extension manifest reference .

    Placeholder Value
    UNIQUE_ID_OF_YOUR_EXTENSION This is the unique id of your extension, typically a string consisting of lowercase letters, numbers, and hyphens. For example: microsoft-security-devops-decorator
    OUR_PUBLISHER_ID Our publisher ID of the Visual Studio Marketplace Publishing Portal. Leave it empty for now as we will create it later on
    NAME_OF_OUR_EXTENSION The name of our extension. For example: Microsoft Security DevOps Decorator
    DESCRIPTION_OF_OUR_EXTENSION A brief description of what the extension does.
    UNIQUE_ID_OF_OUR_CONTRIBUTION Contribution id, typically a string composed of lowercase letters, numbers, and hyphens. It must be unique within the scope of the extension.For example: microsoft-security-devops-decorator-task.
    OUR_YAML_TEMPLATE_FILE_NAME.yml The name of the YAML pipeline file that contains the decorator itself. For example: run-microsoft-security-devops.yml.
    The attribute public must be set for false for extensions containing decorators. Otherwise the extension wont function.
  5. Lets create a new file in the same folder named run-microsoft-security-devops.yml. We need to make sure the name aligns with the OUR_YAML_TEMPLATE_FILE_NAME.yml placeholder you replaced in the vss-extension.json file. Copy the following content into this file. This is the actual decorator that will be injected into all pipelines. We will cover it in more detail in the following chapters.

Package and publish the decorator extension

Since the only way to deploy a Pipeline Decorator is by packaging it as an extension and publishing it to the Visual Studio Marketplace a publisher account is required. This is part of our next steps.

  1. We start with creating a publisher by signing in with a Microsoft identity to Visual Studio Marketplace Publishing Portal At the very minimum we need to provide a Name and an ID, which is sufficient for a lab set-up. When we are creating this for production scenario’s populating all the fields including a logo is strongly recommended.
    The ID must be unique across the whole Marketplace. Note the ID down as it’s going to be needed next. Have a look at the following image for an example.

    Create your publisher example
  2. Open the vss-extension.json and replace the OUR_PUBLISHER_ID placeholder with the ID from the previous step.

  3. Package the extension by running the following command from the folder you created as part of the prerequisites. If no errors are shown you should see the included output. A new file should have been created in the folder with the extension .vsix. This is the file that needs to be uploaded to the Visual Studio Marketplace.

  4. Navigate to the management page of the Visual Studio Marketplace Publishing Portal

  5. On the top horizontal bar, select New extension > Azure DevOps. The following pop-up will appear.

    Upload the extension
  6. Drag and drop your file or select it to find your .vsix file, which you created in the previous packaging step, and then choose Upload. A validation process will start, if everything is correct you will see a green checkmark and the option to save the extension.
    At this point, your extension isn’t visible to any accounts and can’t be installed until you share it.

    Extension ready for sharing
    Microsoft runs a virus scan on each new and updated extension package published. Until the scan is all clear, we cant publish the extension in the Marketplace for public usage. This way Microsoft also avoids surfacing inappropriate or offensive content on the Marketplace pages.
  7. Now lets share our extension with our organization. Select the extension we just uploaded uploaded. Click on the … located just behind the name of the extension and select Share/Unshare.

    Share extension

  8. Click on the + Organization and fill in our Azure DevOps Organization name. As show in the following image.

    Share the extension with our organization

    The extension is shared with our organization and our organization only. Meaning in can only be installed into our root organization and not in any other organization. It’s possible ot share it with additional organizations by repeating this step.
  9. Next lets click again on the … located just behind the name of the extension but this time select the View Extension option.

    View the extension

  10. Click on the Get it free button to install the extension in our Azure DevOps Organization.

    Install the extension

  11. Lets verify our results by navigating to the Organization Settings > Extensions and verify that the extension is installed.


Now that everything is in place, we can test drive the decorator by creating a new pipeline and see the decorator in action. However to fully understand it’s inner workings we need dissect the run-microsoft-security-devops.yml file.

Dissecting the run-microsoft-security-devops.yml

The run-microsoft-security-devops.yml file is a YAML file that contains the decorator itself. Since a decorator injects a task this is exactly what we see defined in the file. In our case we inject the execution of the Microsoft Security DevOps task we go from the Visual Studio Marketplace. However any type of task can be injected, including a Bash or a PowerShell script task. And injecting a combination of tasks is also possible.

Perhaps the most interesting thing to note is the conditional statement on line 2. This statement checks if the executing pipeline instance already has a Microsoft Security DevOps task declared. Only when it is missing it is injected into the pipeline. This is a great way to ensure that the decorator is only injected once into a pipeline.

To implement such a condition the ID of the task is required. These can be found in the sources of the task in question, inside the task.json file. The sources of the Microsoft Azure DevOps build-in tasks are located here . The Microsoft Security DevOps task sources are located here


Test drive the decorator

To test drive the decorator, we simply create a Hello World pipeline and run it. See following image as an example, it contains a PowerShell task that echos $PSVersionTable. When this pipeline is run we can see the decorator in the task list on the left hand side ‘Injected - Run Microsoft Security DevOps’. Note that the task fails this is means that the decorator is doing it’s job, the repo where this pipeline is part of contains hardcoded secrets.

There are more examples of Pipeline Decorators available at the simply-scripted repo part of the The Cloud Explorers GitHub organization.

Hello World pipeline


Use cases everywhere

There is a wide variety of the applications for pipeline decorators. They can be used to enforce compliance, security, or quality standards across all pipelines in an Azure DevOps Organization. This is a powerful feature that can save a lot of time and effort for the organization’s administrators and developers. There are however three major downsides to using this feature which attribute to the lack of adoption by the community.

  • The lack of visibility of the decorators in the Azure DevOps UI (not counting the Extensions view).
  • The cumbersome way of creating and managing decorators.
  • Decorators are publicly viewable on the Visual Studio Marketplace which could be a security concern even though without permissions they are not installable.

Wrapping up

And that’s all folks! I hope you found this post educational. If you are interested in the reference material used to make this post, please visit the following links.

As always, a big thanks for reading this post. If you liked it, don’t be shy and have a look at my other posts .