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:
<BAMBOO_URL>– The base URL of your Bamboo instance<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.<USERNAME>– The username of an account with access to the project<PASSWORD>– The password or Personal Access Token (PAT) for the user with project access<OUTPUT_DIR>– The absolute path to the directory where you want the exported specs to be saved<FORMAT>– The desired export format; accepted values arejavaoryaml
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.Was this helpful?