jira-confluence-full-instance-backup 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. jira_confluence_full_instance_backup-0.1.0/LICENSE +21 -0
  2. jira_confluence_full_instance_backup-0.1.0/PKG-INFO +343 -0
  3. jira_confluence_full_instance_backup-0.1.0/README.md +304 -0
  4. jira_confluence_full_instance_backup-0.1.0/backup/__init__.py +8 -0
  5. jira_confluence_full_instance_backup-0.1.0/backup/__main__.py +7 -0
  6. jira_confluence_full_instance_backup-0.1.0/backup/archive.py +112 -0
  7. jira_confluence_full_instance_backup-0.1.0/backup/cli.py +414 -0
  8. jira_confluence_full_instance_backup-0.1.0/backup/config.py +136 -0
  9. jira_confluence_full_instance_backup-0.1.0/backup/confluence.py +230 -0
  10. jira_confluence_full_instance_backup-0.1.0/backup/jira.py +314 -0
  11. jira_confluence_full_instance_backup-0.1.0/backup/manifest.py +130 -0
  12. jira_confluence_full_instance_backup-0.1.0/backup/naming.py +63 -0
  13. jira_confluence_full_instance_backup-0.1.0/backup/notify.py +232 -0
  14. jira_confluence_full_instance_backup-0.1.0/backup/ui.py +140 -0
  15. jira_confluence_full_instance_backup-0.1.0/backup/upload.py +167 -0
  16. jira_confluence_full_instance_backup-0.1.0/jira_confluence_full_instance_backup.egg-info/PKG-INFO +343 -0
  17. jira_confluence_full_instance_backup-0.1.0/jira_confluence_full_instance_backup.egg-info/SOURCES.txt +21 -0
  18. jira_confluence_full_instance_backup-0.1.0/jira_confluence_full_instance_backup.egg-info/dependency_links.txt +1 -0
  19. jira_confluence_full_instance_backup-0.1.0/jira_confluence_full_instance_backup.egg-info/entry_points.txt +2 -0
  20. jira_confluence_full_instance_backup-0.1.0/jira_confluence_full_instance_backup.egg-info/requires.txt +19 -0
  21. jira_confluence_full_instance_backup-0.1.0/jira_confluence_full_instance_backup.egg-info/top_level.txt +1 -0
  22. jira_confluence_full_instance_backup-0.1.0/pyproject.toml +58 -0
  23. jira_confluence_full_instance_backup-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 David Malko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,343 @@
1
+ Metadata-Version: 2.4
2
+ Name: jira-confluence-full-instance-backup
3
+ Version: 0.1.0
4
+ Summary: Full-instance backup of Jira & Confluence Cloud (Standard plan) to pluggable cloud storage
5
+ Author-email: David Malko <davidmalko87@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/davidmalko87/jira-confluence-full-instance-backup
8
+ Project-URL: Repository, https://github.com/davidmalko87/jira-confluence-full-instance-backup
9
+ Project-URL: Changelog, https://github.com/davidmalko87/jira-confluence-full-instance-backup/blob/master/CHANGELOG.md
10
+ Project-URL: Bug Tracker, https://github.com/davidmalko87/jira-confluence-full-instance-backup/issues
11
+ Keywords: jira,confluence,backup,atlassian,atlassian-cloud,jenkins,gcs,s3,azure
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: System :: Archiving :: Backup
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: requests>=2.31
25
+ Provides-Extra: gcs
26
+ Requires-Dist: google-cloud-storage>=2.14; extra == "gcs"
27
+ Provides-Extra: s3
28
+ Requires-Dist: boto3>=1.34; extra == "s3"
29
+ Provides-Extra: azure
30
+ Requires-Dist: azure-storage-blob>=12.19; extra == "azure"
31
+ Provides-Extra: ui
32
+ Requires-Dist: rich>=13.0; extra == "ui"
33
+ Provides-Extra: all
34
+ Requires-Dist: google-cloud-storage>=2.14; extra == "all"
35
+ Requires-Dist: boto3>=1.34; extra == "all"
36
+ Requires-Dist: azure-storage-blob>=12.19; extra == "all"
37
+ Requires-Dist: rich>=13.0; extra == "all"
38
+ Dynamic: license-file
39
+
40
+ # Jira & Confluence Full-Instance Backup
41
+
42
+ [![CI](https://github.com/davidmalko87/jira-confluence-full-instance-backup/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/davidmalko87/jira-confluence-full-instance-backup/actions/workflows/ci.yml)
43
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
44
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg?logo=python&logoColor=white)](https://www.python.org/)
45
+ [![Jira Cloud](https://img.shields.io/badge/Jira-Cloud-0052CC.svg?logo=jira&logoColor=white)](https://www.atlassian.com/software/jira)
46
+ [![Confluence Cloud](https://img.shields.io/badge/Confluence-Cloud-172B4D.svg?logo=confluence&logoColor=white)](https://www.atlassian.com/software/confluence)
47
+ [![Storage](https://img.shields.io/badge/storage-GCS%20%7C%20S3%20%7C%20Azure%20%7C%20local-success.svg)](#storage-backends)
48
+ [![Notify](https://img.shields.io/badge/notify-Chat%20%7C%20Slack%20%7C%20Teams%20%7C%20email%20%7C%20webhook-success.svg)](#notification-channels)
49
+ [![Last commit](https://img.shields.io/github/last-commit/davidmalko87/jira-confluence-full-instance-backup.svg)](https://github.com/davidmalko87/jira-confluence-full-instance-backup/commits/master)
50
+
51
+ Automated **full-instance backup** of **Jira Cloud** and **Confluence Cloud** for
52
+ the Atlassian **Standard plan**. Run it by hand from an interactive menu, or
53
+ unattended from Jenkins/cron. Backups are encrypted and shipped to **the cloud
54
+ of your choice** — Google Cloud Storage, AWS S3 (and S3-compatible stores),
55
+ Azure Blob, or a local/mounted directory — with notifications to **any channel**
56
+ you use: Slack, Microsoft Teams, Discord, Google Chat, email, or a generic
57
+ webhook.
58
+
59
+ ---
60
+
61
+ ## Why?
62
+
63
+ On **March 30, 2026**, Atlassian [deprecated the Backup Manager API](https://community.atlassian.com/forums/Jira-questions/Backup-Manager-API-deprecation-is-there-going-to-be-a/qaq-p/3120079) for Jira Cloud. Direct API-token calls to the backup endpoint now return:
64
+
65
+ ```
66
+ HTTP 403
67
+ {"error":"This feature is only accessible from the UI."}
68
+ ```
69
+
70
+ The replacement [v2 Backup & Restore API](https://developer.atlassian.com/cloud/admin/backup/) is Premium/Enterprise only, leaving Standard-plan customers with no automation path for full-instance backup — only the manual UI button. This tool restores that automation by **replaying the browser UI session** for Jira, while Confluence uses the OBM REST API (which still accepts API tokens). Both flow into one pipeline that archives, encrypts, uploads, and notifies.
71
+
72
+ For per-project Jira backup/restore, see the sibling project [`jira-project-backup-restore`](https://github.com/davidmalko87/jira-project-backup-restore).
73
+
74
+ ---
75
+
76
+ ## Features
77
+
78
+ | Feature | Description |
79
+ |---|---|
80
+ | **Full-instance backup** | Jira (all projects, attachments, avatars, logos) + Confluence (all spaces, attachments) in one run |
81
+ | **Run anywhere** | Interactive **menu** for VMs/manual use **and** **CLI flags** for Jenkins/cron — same codebase |
82
+ | **Pluggable storage** | **GCS · AWS S3 / S3-compatible (R2, B2, MinIO, Spaces) · Azure Blob · local** — pick one flag; only that SDK is needed |
83
+ | **Pluggable notifications** | **Slack · Teams · Discord · Google Chat · email (SMTP) · generic webhook** — any combination, no extra deps |
84
+ | **Optional encryption** | 7-Zip AES-256 with encrypted headers — on by default, switch off with `--no-encrypt` |
85
+ | **Configurable compression** | `0` (store) … `9` (ultra) |
86
+ | **Custom filenames** | Name templates: `{product} {site} {date} {time} {datetime} {timestamp}` |
87
+ | **Integrity & housekeeping** | `manifest.json` with sha256 + `--validate`, `--cleanup`, `--skip-existing`, `--dry-run` |
88
+ | **Cooldown-aware** | Atlassian's 48h throttle (HTTP 412) is detected and skipped cleanly — no false failures |
89
+ | **Connection test** | Validates Jira cookies + Confluence token, warns when the Jira session is near expiry |
90
+
91
+ ---
92
+
93
+ ## Install
94
+
95
+ ```bash
96
+ pip install jira-confluence-full-instance-backup # core (requests)
97
+ # add the storage backend you use:
98
+ pip install "jira-confluence-full-instance-backup[s3]" # or [gcs] / [azure]
99
+ # optional nicer interactive output (progress bars, color):
100
+ pip install "jira-confluence-full-instance-backup[ui]"
101
+ # everything:
102
+ pip install "jira-confluence-full-instance-backup[all]"
103
+ ```
104
+
105
+ Or from source:
106
+
107
+ ```bash
108
+ git clone https://github.com/davidmalko87/jira-confluence-full-instance-backup.git
109
+ cd jira-confluence-full-instance-backup
110
+ pip install -r requirements.txt # + requirements-<provider>.txt as needed
111
+ ```
112
+
113
+ Requirements: Python 3.10+, and `7z` on PATH (`apt install p7zip-full`, or set `SEVEN_ZIP_PATH`).
114
+
115
+ ---
116
+
117
+ ## Quick Start
118
+
119
+ ### 1. Configure
120
+
121
+ ```bash
122
+ cp .env.example .env # fill in real values — .env is gitignored
123
+ ```
124
+
125
+ Or use the guided menu (writes `.env` for you): `jira-confluence-backup` → **Configure credentials**.
126
+
127
+ ### 2. Run — interactive menu
128
+
129
+ ```bash
130
+ jira-confluence-backup # or: python main.py / python -m backup
131
+ ```
132
+
133
+ ```
134
+ === Atlassian Full-Instance Backup ===
135
+ Jira https://<your-site>.atlassian.net
136
+ Storage s3:my-backups
137
+ Notify slack
138
+
139
+ 1) Backup Jira 7) Validate backup
140
+ 2) Backup Confluence 8) Cleanup backups
141
+ 3) Backup both 9) Test connections
142
+ 4) Full run 10) Configure credentials
143
+ 5) Archive ./out 11) Show configuration
144
+ 6) Upload ./archive 12) List local backups
145
+ 0) Exit
146
+ ```
147
+
148
+ ### 3. Run — CLI (automation)
149
+
150
+ ```bash
151
+ jira-confluence-backup --all # backup both -> archive -> upload -> notify
152
+ jira-confluence-backup --all --dry-run # preview only, no API calls / no cooldown burn
153
+ jira-confluence-backup --backup jira,confluence --archive --upload --notify
154
+ jira-confluence-backup --validate # check the archive against its manifest
155
+ jira-confluence-backup --cleanup --keep-days 28 # prune incomplete + old local backups
156
+ jira-confluence-backup --test-connection # exit 0 if both auth paths are OK
157
+ ```
158
+
159
+ > Output is plain ASCII (`[INFO]/[OK]`) by default — safe on any console, including legacy Windows. Install the `ui` extra for colored output and progress bars.
160
+
161
+ ---
162
+
163
+ ## How It Works
164
+
165
+ ```
166
+ Setup -> Jira -> Confluence -> Archive -> Upload -> Notify
167
+ (venv) (cookies) (API token) (7z, opt. (<provider>:// (your
168
+ AES-256) <dest>/Y/M/D/) channels)
169
+ ```
170
+
171
+ Stages are independent: a Jira cookie expiry does not stop the Confluence stage.
172
+
173
+ ### Auth model
174
+
175
+ | Product | Endpoint | Auth | Why |
176
+ |---|---|---|---|
177
+ | Jira | `/rest/backup/1/export/runbackup` | **Session cookies + UI headers** | Atlassian gates this endpoint to UI sessions only — API tokens return 403 |
178
+ | Confluence | `/wiki/rest/obm/1.0/runbackup` | **Basic** (email + API token) | OBM never received the UI-only lockdown |
179
+
180
+ > **Do not replace the Jira side with an API token** — it is gated to browser
181
+ > sessions and returns `403 "This feature is only accessible from the UI."`.
182
+ > One dedicated Atlassian **admin account** supplies both: its API token (for
183
+ > Confluence) and its browser session cookies (for Jira).
184
+
185
+ ---
186
+
187
+ ## Storage backends
188
+
189
+ Set `STORAGE_PROVIDER` + `STORAGE_DEST` (or `--provider` / `--dest`). Only the chosen SDK is imported; a missing one prints a `pip install` hint.
190
+
191
+ | Provider | `STORAGE_DEST` | Optional SDK | Credentials |
192
+ |---|---|---|---|
193
+ | `gcs` | bucket | `requirements-gcs.txt` | `GOOGLE_APPLICATION_CREDENTIALS` (SA JSON) |
194
+ | `s3` | bucket | `requirements-s3.txt` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION` |
195
+ | `azure` | container | `requirements-azure.txt` | `AZURE_STORAGE_CONNECTION_STRING` |
196
+ | `local` | directory | *(none)* | *(none)* |
197
+
198
+ **S3-compatible stores** (Cloudflare R2, Backblaze B2, MinIO, DigitalOcean Spaces): use `s3` plus `S3_ENDPOINT_URL`. Objects are written to `<dest>/YYYY/MM/DD/`. Retention is the bucket's job (set a lifecycle rule); use a **write-only** identity where possible.
199
+
200
+ ---
201
+
202
+ ## Notification channels
203
+
204
+ `NOTIFY_CHANNELS` is a comma list — pick any. One report is built and rendered per channel; a failed channel is logged but never blocks the rest.
205
+
206
+ | Channel | Needs | Notes |
207
+ |---|---|---|
208
+ | `slack` / `discord` / `teams` / `google-chat` | `NOTIFY_WEBHOOK_URL` | platform-specific incoming webhook |
209
+ | `email` | `SMTP_*` | stdlib SMTP; port 465 → SSL, else STARTTLS |
210
+ | `webhook` | `NOTIFY_WEBHOOK_URL` | raw JSON POST (PagerDuty / Opsgenie / your API) |
211
+
212
+ ---
213
+
214
+ ## Archiving: encryption, compression, names
215
+
216
+ - **Encryption** (default on): 7-Zip AES-256 with encrypted headers, password from `ARCHIVE_PASSWORD`. Turn it off with `--no-encrypt` or by leaving the password blank.
217
+ - **Compression**: `ARCHIVE_COMPRESSION` / `--compression` `0`–`9`.
218
+ - **Names**: `PRODUCT_NAME_TEMPLATE` (the per-product `.zip`) and `ARCHIVE_NAME_TEMPLATE` (the `.7z`). Tokens: `{product} {site} {date} {time} {datetime} {timestamp} {year} {month} {day}`. Defaults: `{product}-{date}` and `atlassian-backup-{date}`.
219
+
220
+ ## Integrity & housekeeping
221
+
222
+ Each successful run writes a `manifest.json` (timestamp, products, per-file + archive **sha256**, `complete: true`, `encrypted`) next to the archive and uploads it too.
223
+
224
+ - `--validate` — re-checksum the local archive against the manifest.
225
+ - `--cleanup [--keep-days N]` — remove incomplete backups (no manifest) and, optionally, ones older than N days.
226
+ - `--skip-existing` — skip a product already backed up today.
227
+ - `--dry-run` — preview any flow without API calls, archiving, or uploads.
228
+
229
+ ---
230
+
231
+ ## Jenkins
232
+
233
+ The `Jenkinsfile` runs a weekly job (cron) and is driven by environment variables:
234
+
235
+ ```groovy
236
+ environment {
237
+ SITE_JIRA = 'https://<YOUR_SITE>.atlassian.net'
238
+ SITE_CONFLUENCE = 'https://<YOUR_SITE>.atlassian.net/wiki'
239
+ STORAGE_PROVIDER = 'gcs' // gcs | s3 | azure | local
240
+ STORAGE_DEST = '<YOUR_BUCKET>'
241
+ NOTIFY_CHANNELS = 'slack' // any comma list
242
+ }
243
+ ```
244
+
245
+ It installs the matching provider SDK and binds **only** the credentials your config needs from the Jenkins Credentials store (`jira-cookies`, `atlassian-email`/`atlassian-api-token`, `archive-password`, the storage credential, and `notify-webhook-url` / `smtp-*`). No secrets live in the repo.
246
+
247
+ ---
248
+
249
+ ## Cookie Refresh Procedure
250
+
251
+ The Jira `tenant.session.token` JWT expires roughly every 30 days. When it does, the Jira stage exits with code 2 (`Cookie auth rejected — cookies likely expired`). Refresh takes ~60 seconds:
252
+
253
+ 1. Log into Atlassian as the backup admin account.
254
+ 2. Open `https://<your-site>.atlassian.net/secure/admin/CloudExport.jspa`.
255
+ 3. **F12 → Application → Cookies** and copy these five: `tenant.session.token`, `atlassian.xsrf.token`, `JSESSIONID`, `AWSALB`, `AWSALBCORS`.
256
+ 4. Assemble as one semicolon-separated string and update it where it's stored (`JIRA_COOKIES` in `.env`, or the `jira-cookies` Jenkins credential).
257
+
258
+ `Test connections` warns you in advance when the token is within a few days of expiry.
259
+
260
+ ---
261
+
262
+ ## Response Code Semantics
263
+
264
+ | Code | Meaning | Behavior |
265
+ |---|---|---|
266
+ | 200 | Backup queued / status returned | Continue polling |
267
+ | 403 | Auth rejected (UI-only gate) | Exit 2 — refresh Jira cookies |
268
+ | 412 | 48-hour cooldown active | Exit 0 + marker — stays green |
269
+ | 400 | Body schema rejected | Investigate body (Atlassian schema change) |
270
+ | 406 | Confluence cosmetic error | Ignore — backup actually started |
271
+
272
+ ---
273
+
274
+ ## Configuration Reference
275
+
276
+ All values come from environment variables, optionally loaded from `.env` (see `.env.example`). In Jenkins they are bound from the Credentials store at runtime — never from a file in the repo.
277
+
278
+ | Env var | Purpose |
279
+ |---|---|
280
+ | `SITE_JIRA` / `SITE_CONFLUENCE` | Atlassian base URLs |
281
+ | `JIRA_COOKIES` | Browser session cookie blob for Jira |
282
+ | `ATL_EMAIL` / `ATL_TOKEN` | Confluence Basic auth |
283
+ | `ARCHIVE_PASSWORD` | 7-Zip AES-256 passphrase (blank = unencrypted) |
284
+ | `ARCHIVE_COMPRESSION` | 0–9 |
285
+ | `PRODUCT_NAME_TEMPLATE` / `ARCHIVE_NAME_TEMPLATE` | Filename templates |
286
+ | `STORAGE_PROVIDER` / `STORAGE_DEST` | Backend + bucket/container/dir |
287
+ | `S3_ENDPOINT_URL` | S3-compatible endpoint (s3 only) |
288
+ | `GOOGLE_APPLICATION_CREDENTIALS` / `AWS_*` / `AZURE_STORAGE_CONNECTION_STRING` | Provider credentials |
289
+ | `NOTIFY_CHANNELS` | Comma list of channels |
290
+ | `NOTIFY_WEBHOOK_URL` / `SMTP_*` | Notification delivery |
291
+
292
+ ---
293
+
294
+ ## Project Structure
295
+
296
+ ```
297
+ jira-confluence-full-instance-backup/
298
+ ├── main.py # Convenience shim (python main.py)
299
+ ├── Jenkinsfile # Declarative pipeline (provider/channel driven)
300
+ ├── pyproject.toml # Packaging + console script + ruff config
301
+ ├── .env.example # Local-testing template (real .env is gitignored)
302
+ ├── requirements.txt # Core (requests)
303
+ ├── requirements-{gcs,s3,azure,ui}.txt # Optional extras
304
+ └── backup/
305
+ ├── cli.py # Dual-mode entrypoint (menu + CLI)
306
+ ├── jira.py # Cookie-authenticated Jira backup
307
+ ├── confluence.py # OBM Basic-auth Confluence backup
308
+ ├── archive.py # 7-Zip (optional AES-256, configurable level)
309
+ ├── upload.py # Multi-provider upload (gcs/s3/azure/local)
310
+ ├── notify.py # Multi-channel notifier
311
+ ├── manifest.py # manifest.json: completeness + sha256 integrity
312
+ ├── config.py # Env/.env config + Configure-menu persistence
313
+ ├── naming.py # Filename templating
314
+ └── ui.py # Console UI (rich-optional, ASCII-safe)
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Known Limitations
320
+
321
+ These are Atlassian platform constraints, not tool limitations:
322
+
323
+ - **48h Jira cooldown** between full-instance backups (weekly cadence is fine).
324
+ - **Cookie lifetime** ~30 days; monthly manual refresh required.
325
+ - **Confluence Filestore retention** ~14 days (the tool downloads immediately, so this affects only the source file).
326
+ - **No restore automation** — restoring a full-instance backup is manual via Atlassian's UI; for per-project restore use [`jira-project-backup-restore`](https://github.com/davidmalko87/jira-project-backup-restore).
327
+
328
+ ---
329
+
330
+ ## Contributing
331
+
332
+ See [CONTRIBUTING.md](CONTRIBUTING.md). In short: no secrets in the repo, keep
333
+ cloud SDKs optional, ASCII-only console output, and test auth/backup changes
334
+ against a non-prod Atlassian instance. **Do not** switch Jira to API-token auth
335
+ (see [Auth model](#auth-model)).
336
+
337
+ ## License
338
+
339
+ MIT — see [LICENSE](LICENSE).
340
+
341
+ ## Related
342
+
343
+ * [`jira-project-backup-restore`](https://github.com/davidmalko87/jira-project-backup-restore) — per-project Jira Cloud backup/restore via REST API.
@@ -0,0 +1,304 @@
1
+ # Jira & Confluence Full-Instance Backup
2
+
3
+ [![CI](https://github.com/davidmalko87/jira-confluence-full-instance-backup/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/davidmalko87/jira-confluence-full-instance-backup/actions/workflows/ci.yml)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
5
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg?logo=python&logoColor=white)](https://www.python.org/)
6
+ [![Jira Cloud](https://img.shields.io/badge/Jira-Cloud-0052CC.svg?logo=jira&logoColor=white)](https://www.atlassian.com/software/jira)
7
+ [![Confluence Cloud](https://img.shields.io/badge/Confluence-Cloud-172B4D.svg?logo=confluence&logoColor=white)](https://www.atlassian.com/software/confluence)
8
+ [![Storage](https://img.shields.io/badge/storage-GCS%20%7C%20S3%20%7C%20Azure%20%7C%20local-success.svg)](#storage-backends)
9
+ [![Notify](https://img.shields.io/badge/notify-Chat%20%7C%20Slack%20%7C%20Teams%20%7C%20email%20%7C%20webhook-success.svg)](#notification-channels)
10
+ [![Last commit](https://img.shields.io/github/last-commit/davidmalko87/jira-confluence-full-instance-backup.svg)](https://github.com/davidmalko87/jira-confluence-full-instance-backup/commits/master)
11
+
12
+ Automated **full-instance backup** of **Jira Cloud** and **Confluence Cloud** for
13
+ the Atlassian **Standard plan**. Run it by hand from an interactive menu, or
14
+ unattended from Jenkins/cron. Backups are encrypted and shipped to **the cloud
15
+ of your choice** — Google Cloud Storage, AWS S3 (and S3-compatible stores),
16
+ Azure Blob, or a local/mounted directory — with notifications to **any channel**
17
+ you use: Slack, Microsoft Teams, Discord, Google Chat, email, or a generic
18
+ webhook.
19
+
20
+ ---
21
+
22
+ ## Why?
23
+
24
+ On **March 30, 2026**, Atlassian [deprecated the Backup Manager API](https://community.atlassian.com/forums/Jira-questions/Backup-Manager-API-deprecation-is-there-going-to-be-a/qaq-p/3120079) for Jira Cloud. Direct API-token calls to the backup endpoint now return:
25
+
26
+ ```
27
+ HTTP 403
28
+ {"error":"This feature is only accessible from the UI."}
29
+ ```
30
+
31
+ The replacement [v2 Backup & Restore API](https://developer.atlassian.com/cloud/admin/backup/) is Premium/Enterprise only, leaving Standard-plan customers with no automation path for full-instance backup — only the manual UI button. This tool restores that automation by **replaying the browser UI session** for Jira, while Confluence uses the OBM REST API (which still accepts API tokens). Both flow into one pipeline that archives, encrypts, uploads, and notifies.
32
+
33
+ For per-project Jira backup/restore, see the sibling project [`jira-project-backup-restore`](https://github.com/davidmalko87/jira-project-backup-restore).
34
+
35
+ ---
36
+
37
+ ## Features
38
+
39
+ | Feature | Description |
40
+ |---|---|
41
+ | **Full-instance backup** | Jira (all projects, attachments, avatars, logos) + Confluence (all spaces, attachments) in one run |
42
+ | **Run anywhere** | Interactive **menu** for VMs/manual use **and** **CLI flags** for Jenkins/cron — same codebase |
43
+ | **Pluggable storage** | **GCS · AWS S3 / S3-compatible (R2, B2, MinIO, Spaces) · Azure Blob · local** — pick one flag; only that SDK is needed |
44
+ | **Pluggable notifications** | **Slack · Teams · Discord · Google Chat · email (SMTP) · generic webhook** — any combination, no extra deps |
45
+ | **Optional encryption** | 7-Zip AES-256 with encrypted headers — on by default, switch off with `--no-encrypt` |
46
+ | **Configurable compression** | `0` (store) … `9` (ultra) |
47
+ | **Custom filenames** | Name templates: `{product} {site} {date} {time} {datetime} {timestamp}` |
48
+ | **Integrity & housekeeping** | `manifest.json` with sha256 + `--validate`, `--cleanup`, `--skip-existing`, `--dry-run` |
49
+ | **Cooldown-aware** | Atlassian's 48h throttle (HTTP 412) is detected and skipped cleanly — no false failures |
50
+ | **Connection test** | Validates Jira cookies + Confluence token, warns when the Jira session is near expiry |
51
+
52
+ ---
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pip install jira-confluence-full-instance-backup # core (requests)
58
+ # add the storage backend you use:
59
+ pip install "jira-confluence-full-instance-backup[s3]" # or [gcs] / [azure]
60
+ # optional nicer interactive output (progress bars, color):
61
+ pip install "jira-confluence-full-instance-backup[ui]"
62
+ # everything:
63
+ pip install "jira-confluence-full-instance-backup[all]"
64
+ ```
65
+
66
+ Or from source:
67
+
68
+ ```bash
69
+ git clone https://github.com/davidmalko87/jira-confluence-full-instance-backup.git
70
+ cd jira-confluence-full-instance-backup
71
+ pip install -r requirements.txt # + requirements-<provider>.txt as needed
72
+ ```
73
+
74
+ Requirements: Python 3.10+, and `7z` on PATH (`apt install p7zip-full`, or set `SEVEN_ZIP_PATH`).
75
+
76
+ ---
77
+
78
+ ## Quick Start
79
+
80
+ ### 1. Configure
81
+
82
+ ```bash
83
+ cp .env.example .env # fill in real values — .env is gitignored
84
+ ```
85
+
86
+ Or use the guided menu (writes `.env` for you): `jira-confluence-backup` → **Configure credentials**.
87
+
88
+ ### 2. Run — interactive menu
89
+
90
+ ```bash
91
+ jira-confluence-backup # or: python main.py / python -m backup
92
+ ```
93
+
94
+ ```
95
+ === Atlassian Full-Instance Backup ===
96
+ Jira https://<your-site>.atlassian.net
97
+ Storage s3:my-backups
98
+ Notify slack
99
+
100
+ 1) Backup Jira 7) Validate backup
101
+ 2) Backup Confluence 8) Cleanup backups
102
+ 3) Backup both 9) Test connections
103
+ 4) Full run 10) Configure credentials
104
+ 5) Archive ./out 11) Show configuration
105
+ 6) Upload ./archive 12) List local backups
106
+ 0) Exit
107
+ ```
108
+
109
+ ### 3. Run — CLI (automation)
110
+
111
+ ```bash
112
+ jira-confluence-backup --all # backup both -> archive -> upload -> notify
113
+ jira-confluence-backup --all --dry-run # preview only, no API calls / no cooldown burn
114
+ jira-confluence-backup --backup jira,confluence --archive --upload --notify
115
+ jira-confluence-backup --validate # check the archive against its manifest
116
+ jira-confluence-backup --cleanup --keep-days 28 # prune incomplete + old local backups
117
+ jira-confluence-backup --test-connection # exit 0 if both auth paths are OK
118
+ ```
119
+
120
+ > Output is plain ASCII (`[INFO]/[OK]`) by default — safe on any console, including legacy Windows. Install the `ui` extra for colored output and progress bars.
121
+
122
+ ---
123
+
124
+ ## How It Works
125
+
126
+ ```
127
+ Setup -> Jira -> Confluence -> Archive -> Upload -> Notify
128
+ (venv) (cookies) (API token) (7z, opt. (<provider>:// (your
129
+ AES-256) <dest>/Y/M/D/) channels)
130
+ ```
131
+
132
+ Stages are independent: a Jira cookie expiry does not stop the Confluence stage.
133
+
134
+ ### Auth model
135
+
136
+ | Product | Endpoint | Auth | Why |
137
+ |---|---|---|---|
138
+ | Jira | `/rest/backup/1/export/runbackup` | **Session cookies + UI headers** | Atlassian gates this endpoint to UI sessions only — API tokens return 403 |
139
+ | Confluence | `/wiki/rest/obm/1.0/runbackup` | **Basic** (email + API token) | OBM never received the UI-only lockdown |
140
+
141
+ > **Do not replace the Jira side with an API token** — it is gated to browser
142
+ > sessions and returns `403 "This feature is only accessible from the UI."`.
143
+ > One dedicated Atlassian **admin account** supplies both: its API token (for
144
+ > Confluence) and its browser session cookies (for Jira).
145
+
146
+ ---
147
+
148
+ ## Storage backends
149
+
150
+ Set `STORAGE_PROVIDER` + `STORAGE_DEST` (or `--provider` / `--dest`). Only the chosen SDK is imported; a missing one prints a `pip install` hint.
151
+
152
+ | Provider | `STORAGE_DEST` | Optional SDK | Credentials |
153
+ |---|---|---|---|
154
+ | `gcs` | bucket | `requirements-gcs.txt` | `GOOGLE_APPLICATION_CREDENTIALS` (SA JSON) |
155
+ | `s3` | bucket | `requirements-s3.txt` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION` |
156
+ | `azure` | container | `requirements-azure.txt` | `AZURE_STORAGE_CONNECTION_STRING` |
157
+ | `local` | directory | *(none)* | *(none)* |
158
+
159
+ **S3-compatible stores** (Cloudflare R2, Backblaze B2, MinIO, DigitalOcean Spaces): use `s3` plus `S3_ENDPOINT_URL`. Objects are written to `<dest>/YYYY/MM/DD/`. Retention is the bucket's job (set a lifecycle rule); use a **write-only** identity where possible.
160
+
161
+ ---
162
+
163
+ ## Notification channels
164
+
165
+ `NOTIFY_CHANNELS` is a comma list — pick any. One report is built and rendered per channel; a failed channel is logged but never blocks the rest.
166
+
167
+ | Channel | Needs | Notes |
168
+ |---|---|---|
169
+ | `slack` / `discord` / `teams` / `google-chat` | `NOTIFY_WEBHOOK_URL` | platform-specific incoming webhook |
170
+ | `email` | `SMTP_*` | stdlib SMTP; port 465 → SSL, else STARTTLS |
171
+ | `webhook` | `NOTIFY_WEBHOOK_URL` | raw JSON POST (PagerDuty / Opsgenie / your API) |
172
+
173
+ ---
174
+
175
+ ## Archiving: encryption, compression, names
176
+
177
+ - **Encryption** (default on): 7-Zip AES-256 with encrypted headers, password from `ARCHIVE_PASSWORD`. Turn it off with `--no-encrypt` or by leaving the password blank.
178
+ - **Compression**: `ARCHIVE_COMPRESSION` / `--compression` `0`–`9`.
179
+ - **Names**: `PRODUCT_NAME_TEMPLATE` (the per-product `.zip`) and `ARCHIVE_NAME_TEMPLATE` (the `.7z`). Tokens: `{product} {site} {date} {time} {datetime} {timestamp} {year} {month} {day}`. Defaults: `{product}-{date}` and `atlassian-backup-{date}`.
180
+
181
+ ## Integrity & housekeeping
182
+
183
+ Each successful run writes a `manifest.json` (timestamp, products, per-file + archive **sha256**, `complete: true`, `encrypted`) next to the archive and uploads it too.
184
+
185
+ - `--validate` — re-checksum the local archive against the manifest.
186
+ - `--cleanup [--keep-days N]` — remove incomplete backups (no manifest) and, optionally, ones older than N days.
187
+ - `--skip-existing` — skip a product already backed up today.
188
+ - `--dry-run` — preview any flow without API calls, archiving, or uploads.
189
+
190
+ ---
191
+
192
+ ## Jenkins
193
+
194
+ The `Jenkinsfile` runs a weekly job (cron) and is driven by environment variables:
195
+
196
+ ```groovy
197
+ environment {
198
+ SITE_JIRA = 'https://<YOUR_SITE>.atlassian.net'
199
+ SITE_CONFLUENCE = 'https://<YOUR_SITE>.atlassian.net/wiki'
200
+ STORAGE_PROVIDER = 'gcs' // gcs | s3 | azure | local
201
+ STORAGE_DEST = '<YOUR_BUCKET>'
202
+ NOTIFY_CHANNELS = 'slack' // any comma list
203
+ }
204
+ ```
205
+
206
+ It installs the matching provider SDK and binds **only** the credentials your config needs from the Jenkins Credentials store (`jira-cookies`, `atlassian-email`/`atlassian-api-token`, `archive-password`, the storage credential, and `notify-webhook-url` / `smtp-*`). No secrets live in the repo.
207
+
208
+ ---
209
+
210
+ ## Cookie Refresh Procedure
211
+
212
+ The Jira `tenant.session.token` JWT expires roughly every 30 days. When it does, the Jira stage exits with code 2 (`Cookie auth rejected — cookies likely expired`). Refresh takes ~60 seconds:
213
+
214
+ 1. Log into Atlassian as the backup admin account.
215
+ 2. Open `https://<your-site>.atlassian.net/secure/admin/CloudExport.jspa`.
216
+ 3. **F12 → Application → Cookies** and copy these five: `tenant.session.token`, `atlassian.xsrf.token`, `JSESSIONID`, `AWSALB`, `AWSALBCORS`.
217
+ 4. Assemble as one semicolon-separated string and update it where it's stored (`JIRA_COOKIES` in `.env`, or the `jira-cookies` Jenkins credential).
218
+
219
+ `Test connections` warns you in advance when the token is within a few days of expiry.
220
+
221
+ ---
222
+
223
+ ## Response Code Semantics
224
+
225
+ | Code | Meaning | Behavior |
226
+ |---|---|---|
227
+ | 200 | Backup queued / status returned | Continue polling |
228
+ | 403 | Auth rejected (UI-only gate) | Exit 2 — refresh Jira cookies |
229
+ | 412 | 48-hour cooldown active | Exit 0 + marker — stays green |
230
+ | 400 | Body schema rejected | Investigate body (Atlassian schema change) |
231
+ | 406 | Confluence cosmetic error | Ignore — backup actually started |
232
+
233
+ ---
234
+
235
+ ## Configuration Reference
236
+
237
+ All values come from environment variables, optionally loaded from `.env` (see `.env.example`). In Jenkins they are bound from the Credentials store at runtime — never from a file in the repo.
238
+
239
+ | Env var | Purpose |
240
+ |---|---|
241
+ | `SITE_JIRA` / `SITE_CONFLUENCE` | Atlassian base URLs |
242
+ | `JIRA_COOKIES` | Browser session cookie blob for Jira |
243
+ | `ATL_EMAIL` / `ATL_TOKEN` | Confluence Basic auth |
244
+ | `ARCHIVE_PASSWORD` | 7-Zip AES-256 passphrase (blank = unencrypted) |
245
+ | `ARCHIVE_COMPRESSION` | 0–9 |
246
+ | `PRODUCT_NAME_TEMPLATE` / `ARCHIVE_NAME_TEMPLATE` | Filename templates |
247
+ | `STORAGE_PROVIDER` / `STORAGE_DEST` | Backend + bucket/container/dir |
248
+ | `S3_ENDPOINT_URL` | S3-compatible endpoint (s3 only) |
249
+ | `GOOGLE_APPLICATION_CREDENTIALS` / `AWS_*` / `AZURE_STORAGE_CONNECTION_STRING` | Provider credentials |
250
+ | `NOTIFY_CHANNELS` | Comma list of channels |
251
+ | `NOTIFY_WEBHOOK_URL` / `SMTP_*` | Notification delivery |
252
+
253
+ ---
254
+
255
+ ## Project Structure
256
+
257
+ ```
258
+ jira-confluence-full-instance-backup/
259
+ ├── main.py # Convenience shim (python main.py)
260
+ ├── Jenkinsfile # Declarative pipeline (provider/channel driven)
261
+ ├── pyproject.toml # Packaging + console script + ruff config
262
+ ├── .env.example # Local-testing template (real .env is gitignored)
263
+ ├── requirements.txt # Core (requests)
264
+ ├── requirements-{gcs,s3,azure,ui}.txt # Optional extras
265
+ └── backup/
266
+ ├── cli.py # Dual-mode entrypoint (menu + CLI)
267
+ ├── jira.py # Cookie-authenticated Jira backup
268
+ ├── confluence.py # OBM Basic-auth Confluence backup
269
+ ├── archive.py # 7-Zip (optional AES-256, configurable level)
270
+ ├── upload.py # Multi-provider upload (gcs/s3/azure/local)
271
+ ├── notify.py # Multi-channel notifier
272
+ ├── manifest.py # manifest.json: completeness + sha256 integrity
273
+ ├── config.py # Env/.env config + Configure-menu persistence
274
+ ├── naming.py # Filename templating
275
+ └── ui.py # Console UI (rich-optional, ASCII-safe)
276
+ ```
277
+
278
+ ---
279
+
280
+ ## Known Limitations
281
+
282
+ These are Atlassian platform constraints, not tool limitations:
283
+
284
+ - **48h Jira cooldown** between full-instance backups (weekly cadence is fine).
285
+ - **Cookie lifetime** ~30 days; monthly manual refresh required.
286
+ - **Confluence Filestore retention** ~14 days (the tool downloads immediately, so this affects only the source file).
287
+ - **No restore automation** — restoring a full-instance backup is manual via Atlassian's UI; for per-project restore use [`jira-project-backup-restore`](https://github.com/davidmalko87/jira-project-backup-restore).
288
+
289
+ ---
290
+
291
+ ## Contributing
292
+
293
+ See [CONTRIBUTING.md](CONTRIBUTING.md). In short: no secrets in the repo, keep
294
+ cloud SDKs optional, ASCII-only console output, and test auth/backup changes
295
+ against a non-prod Atlassian instance. **Do not** switch Jira to API-token auth
296
+ (see [Auth model](#auth-model)).
297
+
298
+ ## License
299
+
300
+ MIT — see [LICENSE](LICENSE).
301
+
302
+ ## Related
303
+
304
+ * [`jira-project-backup-restore`](https://github.com/davidmalko87/jira-project-backup-restore) — per-project Jira Cloud backup/restore via REST API.
@@ -0,0 +1,8 @@
1
+ """Full-instance backup of Atlassian Cloud (Jira + Confluence).
2
+
3
+ Backs up to pluggable cloud storage (GCS / S3 / Azure / local). Modules run as
4
+ ``python -m backup.<name>`` from the Jenkins pipeline (jira, confluence,
5
+ archive, upload, notify); the dual-mode CLI/menu is ``backup.cli`` (also the
6
+ ``jira-confluence-backup`` console script and ``python -m backup``).
7
+ """
8
+ __version__ = "0.1.0"