Managing Azure DevOps with Scripts: Streamline Your Workflows

Computer screen and 3D abstract of programming code, software development and scripting

In the fast-paced world of software development, efficiency is paramount. Managing projects, pipelines, and deployments in Azure DevOps can become a daunting task for development teams.

Scripting offers a powerful solution to automate, transform, and streamline processes in your Azure DevOps environment, empowering your teams to achieve higher productivity, gain better control, and increase visibility into your projects.

Things to know about Managing Azure DevOps with Scripts:

  1. What is Azure DevOps?
  2. Managing ADO Without Scripting
  3. Writing Scripts for ADO
  4. Examples of Managing Azure DevOps with Scripts

What is Azure DevOps?

Azure DevOps (ADO) is a project management tool from Microsoft that enables collaboration between your entire project team. It allows you to use project management methodologies like Agile and Kanban and facilitates testing and deploying your code.

Given that it has such a broad scope, ADO can be very complex. Often, managing work-items in ADO is a full-time role for a project manager or project coordinator. This work involves repetitive, manual steps and is ripe for automation.

Azure DevOps includes a work-item hierarchy of Initiatives, Epics, Features, Stories, and Tasks, depending on which process you choose. With these many levels of work-items and multiple project teams, keeping the parent and child relationships in the hierarchy updated can become a headache for your project managers.

Development teams are working on Stories, but project stakeholders want to see the current state of the project at the Initiative and Epic level. When the development teams complete all the work-item Stories in a Feature, someone must manually update the parent’s status. There used to be an extension that automated this process, but it is no longer available.

Managing ADO Without Scripting

ADO can be managed without scripts; ADO has a powerful query function. A project manager can build queries to find the out-of-sync parents and children or work-items lingering in prior sprints. Often, a story gets marked as completed, but the tasks are not marked completed. This may just be administrative overhead for your development teams – most likely, the work to deliver the story has been completed and ADO just hasn’t been kept up to date.

But to use these queries, PMs must manually run the queries and review the output. This turns into mind-numbing, repetitive work for your highly-paid PM. They spend time dragging a mouse around rather than putting mental energy into your project. There are more valuable things for your PMs to spend their time on.

Writing Scripts for ADO

Microsoft has several tools for programmatic interactions with Azure DevOps. They publish a REST API, libraries for NodeJS, .Net, and many other languages and the Azure Command-Line Interface with an extension for Azure DevOps. As a bonus tip, all available extensions are listed here.

Though each method varies, you can run a simple command and get information back in a machine-readable JSON format. A command like az boards iteration project list will list your project’s iterations and yield a response like:

  "attributes": { 
    "finishDate": "2023-10-09T00:00:00Z", 
    "startDate": "2023-09-25T00:00:00Z" 
  "children": null, 
  "hasChildren": false, 
  "id": 255, 
  "identifier": "OBFUSCATED", 
  "name": "Sprint 2 : 25-September to 9-October", 
  "path": "\\My Project\\Iteration\\Sprint 2 : 25- September to 9-October", 
  "structureType": "iteration", 
  "url": "OBFUSCATED" 

Combining shell scripts and the Azure Command-Line interface gives you the ability to chain together many of the functions to automate what would be otherwise cumbersome activities. If you have more advanced programming skills, the client libraries can be used with your favorite programming language to write robust programs to help you manage Azure DevOps. 

Examples of Managing Azure DevOps with Scripts

Measuring Backlog Health

As a Scrum Master, it’s in my best interests that my projects have a healthy backlog. What defines a healthy backlog is the subject for a different article, but to measure backlog health, you must know how many work-items are in your backlog and in what state or with what attributes. The az boards query command will let you execute an ADO query from the command line, combined with jq to parse the JSON and bc to do some math, you can get some statistics about your backlog. 

The first three commands will run ADO queries and count the work-items returned. The queries are all variations of a theme: 

  1. work-items from your product backlog 
  1. those work-items with a state that the work-item is workable 
  1. workable work-items but the acceptance criteria field is blank 
PRODUCT_BACKLOG=$(az boards query --organization --id product-backlog-query-guid | jq 'length') 
BACKLOG_STATE=$(az boards query --organization --id product-backlog-in-backlog-state-query-guid | jq 'length') 
NO_AC=$(az boards query --organization --id backlog-items-with-blank-acceptance-query-guid | jq 'length') 
WORKABLE_BACKLOG=$( echo "scale=2; ($BACKLOG_STATE - $NO_AC) / $BACKLOG_STATE * 100"  | bc -l ) 

The final three commands do some math and figure out what percentage of the backlog is ready to be worked by their state, and which of those might need to be reviewed for completeness. 

sqlite3 ~/projects/sprint-tracker/tracker.db "insert into backlog_health(project_id,backlog_count,unhealthy_count, health_type) values (10, $BACKLOG_STATE, $NO_AC , 'workable backlog');" 
sqlite3 ~/projects/sprint-tracker/tracker.db "insert into backlog_health(project_id,backlog_count,unhealthy_count, health_type) values (10, $PRODUCT_BACKLOG, $NOT_IN_BACKLOG , 'backlog health');" 

This data can then be saved in a database so that you can collect the history of your project’s backlog; having empirical data about your project is core to the Three Pillars of Scrum (Transparency, Inspection, and Adaptation).

Add Next Iteration

In Azure DevOps, iterations are created for a project and then added to each team in the project separately. In a project with many teams, this can turn into a mindless, point-and-click exercise for your PM every iteration. This can be scripted to the point where a command is executed and 30 seconds later, the next iteration has been assigned to each team. The steps to add iterations to a team and a NodeJS script, using the azure-devops-node-api module are outlined below.

  1. Read the project information from a configuration file
  2. Get the iterations for the project
  3. Use the dates to identify the next two iterations by their ID
  4. Get all the teams for the project
  5. Loop through all the teams and assign the iterations to the teams
const azdev = require('azure-devops-node-api') 
const {readConfig} = require('@beauraines/node-helpers').config 
const dayjs = require('dayjs') 

const main = async () => { 

    const configFile = 'adoUtilities.json' 
    const config = await readConfig(configFile) 

    const orgUrl =; 
    const token= config.token 
    const teamContext = { project: config.project, team:}; 

    const authHandler = azdev.getPersonalAccessTokenHandler(token);  
    const connection = new azdev.WebApi(orgUrl, authHandler);     
    const coreAPI = await connection.getCoreApi() 
    const workAPI = await connection.getWorkApi(); 
    const workItemTrackingAPI = await connection.getWorkItemTrackingApi() 
    const currentIteration = (await workAPI.getTeamIterations(teamContext,'Current'))[0] 
    console.log('Current Iteration: ', 

    const currentIterationEnd = dayjs(currentIteration.attributes.finishDate) 
    const nextIterationStart = currentIterationEnd.add(1,'day') 

    const classificationNodes = await workItemTrackingAPI.getClassificationNodes(config.project,[],2) 
    const iterations = classificationNodes.filter(x=> x.path == `\\${config.project}\\Iteration`)[0].children      

    const nextIteration = iterations.filter(x => dayjs(x.attributes.startDate).isSame(nextIterationStart))[0] 

    const nextIterationEnd = dayjs(nextIteration.attributes.finishDate) 
    const nextNextIterationStart = nextIterationEnd.add(1,'day') 
    const nextNextIteration = iterations.filter(x => dayjs(x.attributes.startDate).isSame(nextNextIterationStart))[0] 

    console.log('Next iteration: ', 
    console.log('And the one after than: ', 

    const teams = (await coreAPI.getAllTeams()).filter(t => t.projectName == config.project) 

    for (const team of teams) { 
        const teamContext = { project: config.project, team:}; 
        // eslint-disable-next-line no-unused-vars 
        const nextResponse = await workAPI.postTeamIteration({id: nextIteration.identifier}, teamContext) 
        // eslint-disable-next-line no-unused-vars 
        const nextNextResponse = await workAPI.postTeamIteration({id: nextNextIteration.identifier}, teamContext) 
        // console.log(response) 
        console.log(`Added iterations ${} and ${} to ${}`) 


AIM – More Than Just Project Management

Effectively managing Azure DevOps with the above scripts can dramatically enhance your development workflow. By automating repetitive tasks, you can ensure consistency, drive efficiency, and empower your teams to focus on creating high-quality software rather than mundane management activities.

When you look for a strategic partner to transform your software development processes, help you enable change and embrace best practices, and cause your productivity to soar, turn to AIM Consulting.

AIM’s Delivery Leadership practice is more than just TPMs and PMs. We’re technical experts who can jump into any technology. Not only can we help your project teams be successful, but we can also revolutionize the way they work and help you achieve more with less.

Ready to Streamline Your Workflows?

AIM Consulting can help you automate processes and realize the full value of your teams. Let’s partner to eliminate cumbersome, manual tasks, allow your teams to shift their time to more valuable work, and ultimately deliver better software, faster.