Forum Discussion

elik1001's avatar
elik1001
Copper Contributor
Jan 03, 2025

Azure DevOps - How to Publish Artifacts Using REST API in Python?

I'm struggling to implement this functionality and could use some help. Here's my setup:

  • I'm using YAML pipelines in Azure DevOps.
  • Part of the pipeline includes a Python task with a long and complex processing script that generates output reports.
  • For better control, I want to add the Publish Artifacts functionality (based on some logic) directly within the Python script.

So far, I have tried the following REST API calls without success.

Part 1 - Works

url = f"https://dev.azure.com/{organization}/{project}/_apis/build/builds/{build_id}/artifacts?artifactName={artifact_name}&api-version=7.1-preview.5"
    headers = {
        "Authorization": f"Basic {ACCESS_TOKEN}",
        "Accept": "application/json",
        "Content-Type": "application/json",
    }
    payload = {
        "name": artifact_name,
        "resource": {
            "type": "Container",
            "data": artifact_name,
            "properties": {
                "RootId": artifact_name
            }
        },
    }
 
    logger.info("Creating artifact metadata...")
    response = requests.post(url, headers=headers, json=payload)
 
    if response.status_code == 200:
        logger.info("Artifact metadata created successfully.")
        response_json = response.json()
        logger.info(f"Create Pre-Payload Response: {response_json}")

Part 2 - FAILS

headers = {
        "Authorization": f"Basic {ACCESS_TOKEN}",
        "Content-Type": "application/octet-stream",
    }
 
    for artifact_file in artifact_files:
        if not os.path.exists(artifact_file):
            logger.warning(f"File {artifact_file} does not exist. Skipping.")
            continue
 
        # Construct the full upload URL
        item_path = f"{artifact_name}/{os.path.basename(artifact_file)}"       
        upload_url = f"{container_url}?itemPath={item_path}"
        logger.info(f"Uploading: {artifact_file} to {upload_url}")
 
        with open(artifact_file, "rb") as f:
            response = requests.put(upload_url, headers=headers, data=f)
            if response.status_code == 201:
                logger.info(f"File {artifact_file} uploaded successfully.")
            else:
                logger.error(f"Failed to upload {artifact_file}: {response.status_code}, {response.text}")

Part 2 returns a 404 - like the one below..

INFO:__main__:Uploading: reports/test.json to https://dev.azure.com/OrgDevOps/_apis/resources/Containers/c82916f3-4665-43bf-8927-e05a3b6492a9?itemPath=drop_v3/test.json
ERROR:__main__:Failed to upload reports/test.json: 404, <!DOCTYPE html >
<html>
  <head>
    <title>The controller for path '/_apis/resources/Containers/c82916f3-4665-43bf-8927-e05a3b6492a9' was not found or does not implement IController.</title>
    <style type="text/css">html {
    height: 100%;
}
…

Any guidance or working examples would be greatly appreciated.
Thanks in advance!

2 Replies

  • elik1001's avatar
    elik1001
    Copper Contributor

    Hi,

    Thanks for your response.
    Unfortunately I get the same results. the second part comes back with a 404 page.

    INFO:__main__:Creating artifact metadata...
    INFO:__main__:Artifact metadata created successfully.
    INFO:__main__:Create Pre-Payload Response: {'id': 47056, 'name': 'drop_v4', 'source': None, 'resource': {'type': 'Container', 'data': 'drop_v4', 'properties': {'RootId': 'drop_v4'}, 'url': 'https://dev.azure.com/my_org/c82916f3-4665-43bf-8927-e05a3b6492a9/_apis/build/builds/72710/artifacts?artifactName=drop_v4&api-version=7.1-preview.5', 'downloadUrl': 'https://dev.azure.com/my_org/c82916f3-4665-43bf-8927-e05a3b6492a9/_apis/build/builds/72710/artifacts?artifactName=drop_v4&api-version=7.1-preview.5&%24format=zip'}}
    INFO:__main__:Uploading: reports/test-1.json to https://dev.azure.com/my_org/my_project/_apis/resources/Containers/c82916f3-4665-43bf-8927-e05a3b6492a9?itemPath=drop_v4/test-1.json
    ERROR:__main__:Failed to upload reports/test-1.json: 404, <!DOCTYPE html >
    ...
              </div>
              <div class="error-info">
                <div class="title">404 - Page not found</div>
                <div class="details">Looks like this page doesn't exist or can't be found. Make sure the URL is correct.</div>
                <div class="reference-data">
                  <div>1/6/2025 1:31:51 PM (UTC)</div>
                  <div>adcbe8d1-8e32-4c9b-92c0-2586dc535880</div>
                </div>
                <div class="action-container">
                  <a class="action" href="https://dev.azure.com/my_org/">
                    <span>Go back home</span>
                  </a>
                </div>
              </div>
            </div>
          </div>
        </div>
      </body>
    </html>


    Thanks in advance.

  • Try below with logging which help to diagnose the issue:

     

    import os
    import requests
    import logging
    
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)
    
    organization = 'your_organization'
    project = 'your_project'
    build_id = 'your_build_id'
    artifact_name = 'your_artifact_name'
    artifact_files = ['path/to/your/artifact1', 'path/to/your/artifact2']
    container_url = f"https://dev.azure.com/{organization}/{project}/_apis/resources/Containers/c82916f3-4665-43bf-8927-e05a3b6492a9"
    
    # Part 1 - Create artifact metadata
    url = f"https://dev.azure.com/{organization}/{project}/_apis/build/builds/{build_id}/artifacts?artifactName={artifact_name}&api-version=7.1-preview.5"
    headers = {
        "Authorization": f"Basic {ACCESS_TOKEN}",
        "Accept": "application/json",
        "Content-Type": "application/json",
    }
    payload = {
        "name": artifact_name,
        "resource": {
            "type": "Container",
            "data": artifact_name,
            "properties": {
                "RootId": artifact_name
            }
        },
    }
    logger.info("Creating artifact metadata...")
    response = requests.post(url, headers=headers, json=payload)
    if response.status_code == 200:
        logger.info("Artifact metadata created successfully.")
        response_json = response.json()
        logger.info(f"Create Pre-Payload Response: {response_json}")
    else:
        logger.error(f"Failed to create artifact metadata: {response.status_code}, {response.text}")
    
    # Part 2 - Upload artifacts
    headers = {
        "Authorization": f"Basic {ACCESS_TOKEN}",
        "Content-Type": "application/octet-stream",
    }
    for artifact_file in artifact_files:
        if not os.path.exists(artifact_file):
            logger.warning(f"File {artifact_file} does not exist. Skipping.")
            continue
        item_path = f"{artifact_name}/{os.path.basename(artifact_file)}"
        upload_url = f"{container_url}?itemPath={item_path}"
        logger.info(f"Uploading: {artifact_file} to {upload_url}")
        with open(artifact_file, "rb") as f:
            response = requests.put(upload_url, headers=headers, data=f)
            if response.status_code == 201:
                logger.info(f"File {artifact_file} uploaded successfully.")
            else:
                logger.error(f"Failed to upload {artifact_file}: {response.status_code}, {response.text}")
    

     

Resources