Export all plan configurations from a Bamboo Build project

Platform Notice: Data Center Only - This article only applies to Atlassian apps on the Data Center platform.

Note that this KB was created for the Data Center version of the product. Data Center KBs for non-Data-Center-specific features may also work for Server versions of the product, however they have not been tested. Support for Server* products ended on February 15th 2024. If you are running a Server product, you can visit the Atlassian Server end of support announcement to review your migration options.

*Except Fisheye and Crucible

Summary

This article explains how to export all plan configurations from a specific Bamboo Build project using the Bamboo REST API. While Bamboo does not natively support exporting all plans in a project at once, you can use the provided scripts (shell or Python) to automate exporting each plan’s configuration individually. Please note that only plan configurations are exported; project-level settings such as repositories, variables, shared credentials, and permissions are not included.

Environment

The solution has been validated in Bamboo 10.2.7 but may be applicable to other versions.

Limitations

  • The scripts export only plan configurations, not project-level settings (such as repositories, variables, shared credentials, permissions, etc.).

  • There is no native Bamboo feature to export all plans in a project at once. This solution uses the REST API to automate individual plan exports.

  • Ensure the user account used has sufficient permissions to access all plans within the project.

Solution

This task can be accomplished using Bamboo APIs along with scripting (Python or shell).

Before running the script, replace the following placeholders with your actual values:

  1. <BAMBOO_URL> – The base URL of your Bamboo instance

  2. <PROJECT_KEY> – The key of the build project from which you want to export plan configurations. You can find this by navigating to Build Project → Project Settings → Project Details, where the Project Key will be displayed.

  3. <USERNAME> – The username of an account with access to the project

  4. <PASSWORD> – The password or Personal Access Token (PAT) for the user with project access

  5. <OUTPUT_DIR> – The absolute path to the directory where you want the exported specs to be saved

  6. <FORMAT> – The desired export format; accepted values are java or yaml

Shell Script

Please note: The script below requires the jq package.

#!/bin/bash BAMBOO_URL="<BAMBOO_URL>" PROJECT_KEY="<PROJECT_KEY>" USERNAME="<USERNAME>" PASSWORD="<PASSWORD>" OUTPUT_DIR="<OUTPUT_DIR>" FORMAT="<FORMAT>" mkdir -p "$OUTPUT_DIR" # Get all plans in the project (JSON), remove control chars PLANS_JSON=$(curl -s -u "$USERNAME:$PASSWORD" \ -H "Accept: application/json" \ "$BAMBOO_URL/rest/api/latest/project/$PROJECT_KEY?expand=plans.plan" | \ tr -d '\000-\011\013\014\016-\037') if ! echo "$PLANS_JSON" | jq . >/dev/null 2>&1; then echo "Error: Response is not valid JSON. Here is the response:" echo "$PLANS_JSON" exit 1 fi PLAN_KEYS=$(echo "$PLANS_JSON" | jq -r '.plans.plan[].key') PLAN_NAMES=$(echo "$PLANS_JSON" | jq -r '.plans.plan[].name') INDEX=0 for PLAN_KEY in $PLAN_KEYS; do PLAN_NAME=$(echo "$PLAN_NAMES" | sed -n "$((INDEX+1))p" | tr ' ' '_') echo "Exporting Specs for plan: $PLAN_KEY ($PLAN_NAME)" SPECS=$(curl -s -u "$USERNAME:$PASSWORD" \ -H "Accept: application/json" \ "$BAMBOO_URL/rest/api/latest/plan/$PLAN_KEY/specs?format=$FORMAT" | \ tr -d '\000-\011\013\014\016-\037' | \ jq -r '.spec.code') if [[ "$SPECS" == "---"* || "$SPECS" == "package"* ]]; then echo "$SPECS" > "$OUTPUT_DIR/${PLAN_KEY}_${PLAN_NAME}.$FORMAT" echo "Saved: $OUTPUT_DIR/${PLAN_KEY}_${PLAN_NAME}.$FORMAT" else echo "Failed to export Specs for $PLAN_KEY" echo "Response: $RESPONSE" fi INDEX=$((INDEX+1)) done echo "Done."

Output will look similar to the following:

Exporting Specs for plan: TP-TP (Test_Project_-_Test_Plan) Saved: /Users/vpentela/specs-export/output/TP-TP_Test_Project_-_Test_Plan.java Exporting Specs for plan: TP-TP2 (Test_Project_-_Test_Plan_2) Saved: /Users/vpentela/specs-export/output/TP-TP2_Test_Project_-_Test_Plan_2.java Exporting Specs for plan: TP-TP3 (Test_Project_-_Test_Plan_3) Saved: /Users/vpentela/specs-export/output/TP-TP3_Test_Project_-_Test_Plan_3.java Exporting Specs for plan: TP-TP4 (Test_Project_-_Test_Plan_4) Saved: /Users/vpentela/specs-export/output/TP-TP4_Test_Project_-_Test_Plan_4.java Done.

Python script

Please note: The script below requires the requests package.

import os import requests import json import re BAMBOO_URL = "<BAMBOO_URL>" PROJECT_KEY = "<PROJECT_KEY>" USERNAME = "<USERNAME>" PASSWORD = "<PASSWORD>" OUTPUT_DIR = "<OUTPUT_DIR>" FORMAT = "<FORMAT>" os.makedirs(OUTPUT_DIR, exist_ok=True) def clean_control_chars(s): # Remove all control characters except newline (\n) and tab (\t) return re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '', s) def get_json(url, auth): resp = requests.get(url, auth=auth, headers={"Accept": "application/json"}) text = clean_control_chars(resp.text) try: return json.loads(text) except Exception as e: print("Error: Response is not valid JSON. Here is the response:") print(text) raise def main(): auth = (USERNAME, PASSWORD) project_url = f"{BAMBOO_URL}/rest/api/latest/project/{PROJECT_KEY}?expand=plans.plan" data = get_json(project_url, auth) plans = data.get("plans", {}).get("plan", []) if not plans: print("No plans found.") return for plan in plans: plan_key = plan.get("key") plan_name = plan.get("name", "").replace(" ", "_") print(f"Exporting Specs for plan: {plan_key} ({plan_name})") specs_url = f"{BAMBOO_URL}/rest/api/latest/plan/{plan_key}/specs?format={FORMAT}" resp = requests.get(specs_url, auth=auth, headers={"Accept": "application/json"}) resp_text = clean_control_chars(resp.text) try: specs_json = json.loads(resp_text) specs_code = specs_json.get("spec", {}).get("code", "") if specs_code.startswith("---") or specs_code.startswith("package"): filename = f"{plan_key}_{plan_name}.{FORMAT}" filepath = os.path.join(OUTPUT_DIR, filename) with open(filepath, "w") as f: f.write(specs_code) print(f"Saved: {filepath}") else: print(f"Failed to export Specs for {plan_key}") print(f"Response: {resp_text}") except Exception as e: print(f"Failed to parse specs response for {plan_key}") print(f"Response: {resp_text}") print("Done.") if __name__ == "__main__": main()

Output will look similar to the following:

Exporting Specs for plan: TP-TP (Test_Project_-_Test_Plan) Saved: /Users/vpentela/specs-export/output/TP-TP_Test_Project_-_Test_Plan.yaml Exporting Specs for plan: TP-TP2 (Test_Project_-_Test_Plan_2) Saved: /Users/vpentela/specs-export/output/TP-TP2_Test_Project_-_Test_Plan_2.yaml Exporting Specs for plan: TP-TP3 (Test_Project_-_Test_Plan_3) Saved: /Users/vpentela/specs-export/output/TP-TP3_Test_Project_-_Test_Plan_3.yaml Exporting Specs for plan: TP-TP4 (Test_Project_-_Test_Plan_4) Saved: /Users/vpentela/specs-export/output/TP-TP4_Test_Project_-_Test_Plan_4.yaml Done.

Updated on November 18, 2025

Still need help?

The Atlassian Community is here for you.