Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 0
🚀 [Feature]: Manage Atlassian Confluence Cloud from PowerShell#17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
f86f5279be6481fa9a9fa04ae285fdedf803f82493fbfdad2cb0065859542da71423fb56b279a4e867fc4fcca569f23e484cd4886e2824578b717c26712085a8dc0d74a3ebf4225a70b85b625f1526135879bf3317c45bd705fc27c8600744f24d633cb3ebff9542c22ea7ccb7729327fb8d9acd288e616aee945d26bc21f22d2687e225b1648ab3c65f1ffb22129eaFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,51 @@ | ||
| # Confluence | ||
| Confluence is intended to be a PowerShell module for interacting with Atlassian Confluence. | ||
| Confluence is a PowerShell module for interacting with the Atlassian Confluence Cloud REST API, both interactively and in automation. | ||
| ## Status | ||
| It exposes pages, blog posts, folders, spaces, comments, labels, content properties, attachments, restrictions and more as PowerShell commands. | ||
| Every command is a thin wrapper over a single generic REST entry point (`Invoke-ConfluenceRestMethod`). Credentials and module configuration | ||
| are stored with the [Context](https://github.com/PSModule/Context) module, so you connect once and reuse the profile across sessions. | ||
| This repository is currently a placeholder. The module source still contains scaffold code and examples, so there are no supported Confluence-specific commands or usage examples to document yet. | ||
| ## Installation | ||
| Install the module from the PowerShell Gallery: | ||
| ```powershell | ||
| Install-PSResource -Name Confluence | ||
| Import-Module -Name Confluence | ||
| ``` | ||
| ## Usage | ||
| Connect with a scoped Atlassian API token, then call the resource commands. The token is kept as a `SecureString` in the Context vault. | ||
| ```powershell | ||
| # Connect and store a reusable, named credential profile | ||
| $token = Read-Host -AsSecureString # a scoped Atlassian API token | ||
| Connect-Confluence -Site 'yoursite' -Username 'you@example.com' -Token $token -SpaceKey 'DOCS' | ||
| # Work with content | ||
| $space = Get-ConfluenceSpace -Key 'DOCS' | ||
| $page = New-ConfluencePage -SpaceId $space.id -Title 'Release notes' -Body '<p>Hello</p>' | ||
| Update-ConfluencePage -PageId $page.id -Body '<p>Updated</p>' | ||
| Get-ConfluencePageChild -PageId $page.id | ||
| ``` | ||
| See the [examples](examples) folder for more, including managing contexts and pages. | ||
| The service-account token must be granted the module's required Confluence scopes — see [SCOPES.md](SCOPES.md). | ||
| ## Documentation | ||
| When this module is implemented, command details should live in PowerShell help and generated documentation rather than being duplicated in this README. | ||
| Documentation is published at [psmodule.io/Confluence](https://psmodule.io/Confluence/). | ||
| Use PowerShell help and command discovery for module details: | ||
| ```powershell | ||
| Get-Command -Module Confluence | ||
| Get-Help New-ConfluencePage -Examples | ||
| ``` | ||
| ## Contributing | ||
| Issues and pull requests are welcome when implementation work begins. | ||
| Issues and pull requests are welcome. Please use the repository issue tracker to report bugs, request features, or discuss improvements. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # Required Atlassian scopes | ||
| `Confluence` authenticates with a **scoped** Atlassian API token (the `ATSTT…` | ||
| prefix) used as HTTP Basic authentication (`<service-account-email>:<token>`) | ||
| against the API-gateway host `https://api.atlassian.com/ex/confluence/<cloudId>`. | ||
| When you create the token for the service account, select **granular** Confluence | ||
| scopes (not the classic `*-confluence-*` scopes). Use this document when | ||
| generating or rotating a token. | ||
| > Note: cmdlet help lists scopes in a shortened form (for example `read:page`), | ||
| > omitting the `:confluence` suffix for brevity. The canonical, fully-qualified | ||
| > scope strings you grant on the token are the ones in this document (for example | ||
| > `read:page:confluence`). | ||
| ## Configured scopes | ||
| The token is currently configured with **48** granular Confluence scopes | ||
| (**25 read**, **14 write**, **9 delete**). This is broader than the set the | ||
| module's commands use today (see the tables below), leaving headroom for new | ||
| commands. | ||
| ### Read (25) | ||
| ```text | ||
| read:analytics.content:confluence | ||
| read:attachment:confluence | ||
| read:comment:confluence | ||
| read:configuration:confluence | ||
| read:content:confluence | ||
| read:content-details:confluence | ||
| read:content.metadata:confluence | ||
| read:content.permission:confluence | ||
| read:content.property:confluence | ||
| read:content.restriction:confluence | ||
| read:custom-content:confluence | ||
| read:database:confluence | ||
| read:email-address:confluence | ||
| read:folder:confluence | ||
| read:group:confluence | ||
| read:hierarchical-content:confluence | ||
| read:label:confluence | ||
| read:page:confluence | ||
| read:permission:confluence | ||
| read:space:confluence | ||
| read:space-details:confluence | ||
| read:space.permission:confluence | ||
| read:space.property:confluence | ||
| read:space.setting:confluence | ||
| read:user:confluence | ||
| ``` | ||
| ### Write (14) | ||
| ```text | ||
| write:attachment:confluence | ||
| write:audit-log:confluence | ||
| write:comment:confluence | ||
| write:configuration:confluence | ||
| write:content:confluence | ||
| write:content.property:confluence | ||
| write:content.restriction:confluence | ||
| write:custom-content:confluence | ||
| write:database:confluence | ||
| write:embed:confluence | ||
| write:folder:confluence | ||
| write:group:confluence | ||
| write:label:confluence | ||
| write:page:confluence | ||
| ``` | ||
| ### Delete (9) | ||
| ```text | ||
| delete:attachment:confluence | ||
| delete:comment:confluence | ||
| delete:content:confluence | ||
| delete:custom-content:confluence | ||
| delete:database:confluence | ||
| delete:embed:confluence | ||
| delete:folder:confluence | ||
| delete:page:confluence | ||
| delete:whiteboard:confluence | ||
| ``` | ||
| ## Scopes used by the current commands | ||
| | Scope | Used by | | ||
| | --- | --- | | ||
| | `read:space:confluence` | `Get-ConfluenceSpace`, `Get-ConfluenceSiteInfo` | | ||
| | `read:space.permission:confluence` | `Get-ConfluenceSpacePermission` | | ||
| | `read:space.property:confluence` | `Get-ConfluenceSpaceProperty` | | ||
| | `read:page:confluence` | `Get-ConfluencePage`, `Get-ConfluencePageChild`, `Get-ConfluencePageVersion`, `Update-ConfluencePage` (reads before updating) | | ||
| | `read:folder:confluence` | `Get-ConfluenceFolder` | | ||
| | `read:attachment:confluence` | `Get-ConfluenceAttachment` | | ||
| | `read:comment:confluence` | `Get-ConfluenceComment` | | ||
| | `read:label:confluence` | `Get-ConfluenceLabel` | | ||
| | `read:content.property:confluence` | `Get-ConfluenceContentProperty` | | ||
| | `read:hierarchical-content:confluence` | `Get-ConfluenceDescendant` | | ||
| | `read:content-details:confluence` | `Get-ConfluenceRestriction`, `Get-ConfluenceCurrentUser`, `Add-ConfluenceAttachment` (upload) | | ||
| | `write:page:confluence` | `New-ConfluencePage`, `Update-ConfluencePage` | | ||
| | `write:folder:confluence` | `New-ConfluenceFolder` | | ||
| | `write:attachment:confluence` | `Add-ConfluenceAttachment` | | ||
| | `write:comment:confluence` | `Add-ConfluenceComment` | | ||
| | `write:label:confluence` | `Add-ConfluenceLabel`, `Remove-ConfluenceLabel` | | ||
| | `write:content.property:confluence` | `Set-ConfluenceContentProperty`, `Remove-ConfluenceContentProperty` | | ||
| | `delete:page:confluence` | `Remove-ConfluencePage` | | ||
| | `delete:folder:confluence` | `Remove-ConfluenceFolder` | | ||
| | `delete:attachment:confluence` | `Remove-ConfluenceAttachment` | | ||
| | `delete:comment:confluence` | `Remove-ConfluenceComment` | | ||
| > Important: `Get-ConfluenceBlogPost` needs `read:blogpost:confluence`, which is | ||
| > **not** in the configured set. Add that scope (or retire the command) — the | ||
| > `/blogposts` endpoints will otherwise return 403. There is also no | ||
| > `write:blogpost` / `delete:blogpost`, so blog posts cannot be created or removed. | ||
| ## Additional scopes available for new commands | ||
| These scopes are configured but not yet backed by a command. Each row lists the | ||
| candidate cmdlets it would enable (verbs in `{}` share one scope family): | ||
| | Scope(s) | Candidate commands | | ||
| | --- | --- | | ||
| | `{read,write,delete}:custom-content:confluence` | Full custom-content CRUD: `Get-`, `New-`, `Set-`, `Remove-ConfluenceCustomContent` | | ||
| | `{read,write,delete}:database:confluence` | Database CRUD: `Get-`, `New-`, `Set-`, `Remove-ConfluenceDatabase` | | ||
| | `read:group:confluence` + `write:group:confluence` | `Get-ConfluenceGroup`, `Get-ConfluenceGroupMember`, `New-ConfluenceGroup`, `Add-`/`Remove-ConfluenceGroupMember` | | ||
| | `read:user:confluence` + `read:email-address:confluence` | `Get-ConfluenceUser` (by accountId, incl. email); simplifies `Get-ConfluenceCurrentUser` | | ||
| | `{read,write}:content.restriction:confluence` | `Add-`, `Set-`, `Remove-ConfluenceRestriction` (complements the existing `Get-ConfluenceRestriction`) | | ||
| | `read:analytics.content:confluence` | `Get-ConfluenceContentAnalytics` (page/blog view + viewer counts) | | ||
| | `write:comment:confluence` | `Set-ConfluenceComment` (update an existing comment) | | ||
| | `read:permission:confluence` + `read:content.permission:confluence` | `Test-ConfluenceContentPermission` / `Get-ConfluenceOperationCheck` | | ||
| | `{read,write}:configuration:confluence` | `Get-`/`Set-ConfluenceConfiguration` (look-and-feel / system settings; needs admin) | | ||
| | `read:space.setting:confluence` + `read:space-details:confluence` | `Get-ConfluenceSpaceSetting`; richer `Get-ConfluenceSpace` details | | ||
| | `{read,write,delete}:content:confluence`, `read:content.metadata:confluence` | Generic/classic content search & operations, e.g. `Find-ConfluenceContent` (CQL) | | ||
| | `{write,delete}:embed:confluence` | `New-`/`Remove-ConfluenceEmbed` (Smart Links) — no `read:embed`, so read-back is unavailable | | ||
| | `delete:whiteboard:confluence` | `Remove-ConfluenceWhiteboard` only — no `read:`/`write:whiteboard`, so create/read is unavailable | | ||
| | `write:audit-log:confluence` | Audit-log export/write (admin) — no `read:audit-log`, so reading records is unavailable | | ||
| ## Notes | ||
| - Scopes are only an **API ceiling**. Effective access is still bounded by the | ||
| service account's Confluence **permissions** (global and per-space), so grant it | ||
| the matching permissions on the spaces you target. Admin APIs (`configuration`, | ||
| `audit-log`, `group`) additionally need org/site admin rights. | ||
| - **Asymmetric sets** worth noting: `whiteboard` is delete-only; `embed` has | ||
| write/delete but no read; `audit-log` is write-only. Commands for those can only | ||
| cover the granted verbs. | ||
| - The module provides space-administration commands (`New-ConfluenceSpace`, | ||
| `Update-ConfluenceSpace`, `Set-ConfluenceSpace`, `Remove-ConfluenceSpace`), but | ||
| this token set has **no | ||
| space-administration write** scope (`write:space` / `delete:space`): the token | ||
| can read space settings, permissions and properties but cannot create, update | ||
| or delete spaces until `write:space:confluence` and `delete:space:confluence` | ||
| are added (space delete also needs `read:content.metadata:confluence`). Space | ||
| create/update/delete are v1-only operations, and delete is asynchronous (the | ||
| API returns HTTP 202 with a long-running task). | ||
| - A few commands reach v1 endpoints that Atlassian maps to these granular scopes: | ||
| label add/remove (`write:label:confluence`), attachment upload | ||
| (`write:attachment:confluence` plus `read:content-details:confluence`), page | ||
| restriction reads and the current user (`read:content-details:confluence`). | ||
| - Atlassian applies a soft cap of roughly **50 scopes** per token; at **48** this | ||
| set is near the cap, so adding the missing read scopes (blogpost, whiteboard, | ||
| embed) would leave little room and may require dropping unused ones. | ||
| - Reference: [Confluence OAuth 2.0 scopes](https://developer.atlassian.com/cloud/confluence/scopes-for-oauth-2-3LO-and-forge-apps/). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| <# | ||
| .SYNOPSIS | ||
| Connect to Confluence and manage credential profiles (contexts). | ||
| .DESCRIPTION | ||
| Confluence stores each connection as a named context in the Context vault. | ||
| The token is kept as a SecureString. One context is the default; any command | ||
| can target a specific context with -Context. | ||
| #> | ||
| # Import the module | ||
| Import-Module -Name 'Confluence' | ||
| ### | ||
| ### CONNECTING | ||
| ### | ||
| # Connect with a scoped Atlassian API token and store a reusable default profile. | ||
| # -Site takes a subdomain, host, or any URL on the site and resolves the cloud ID for you. | ||
| $token = Read-Host -AsSecureString # a scoped Atlassian API token | ||
| Connect-Confluence -Site 'yoursite' -Username 'you@example.com' -Token $token -SpaceKey 'DOCS' | ||
| # Store several sites/accounts under explicit names and select per call. | ||
| # If you already know the cloud ID, pass it directly with -CloudId to skip the lookup. | ||
| Connect-Confluence -CloudId '<cloudId>' -Username 'you@example.com' -Token $token -Name 'sandbox' | ||
| Get-ConfluenceSpace -Key 'DOCS' -Context 'sandbox' | ||
| ### | ||
| ### CONTEXTS / PROFILES | ||
| ### | ||
| # The default context | ||
| Get-ConfluenceContext | ||
| # All stored contexts | ||
| Get-ConfluenceContext -ListAvailable | ||
| ### | ||
| ### MODULE CONFIGURATION | ||
| ### | ||
| # Read and set module configuration (persisted in the same vault). | ||
| Get-ConfluenceConfig | ||
| Set-ConfluenceConfig -Name 'PerPage' -Value 50 | ||
| ### | ||
| ### DISCONNECTING | ||
| ### | ||
| Disconnect-Confluence -Name 'sandbox' |
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <# | ||
| .SYNOPSIS | ||
| Create, read, update and remove Confluence pages. | ||
| .DESCRIPTION | ||
| Assumes a default context is already connected (see Connecting.ps1). | ||
| #> | ||
| # Import the module | ||
| Import-Module -Name 'Confluence' | ||
| # Resolve the target space | ||
| $space = Get-ConfluenceSpace -Key 'DOCS' | ||
| # Create a page | ||
| $page = New-ConfluencePage -SpaceId $space.id -Title 'Release notes' -Body '<p>First draft</p>' | ||
| # Read it back (storage format by default) | ||
| Get-ConfluencePage -PageId $page.id | ||
| # Update the body (the version number is incremented automatically) | ||
| Update-ConfluencePage -PageId $page.id -Body '<p>Updated content</p>' | ||
| # Add a child page, then list children and descendants | ||
| New-ConfluencePage -SpaceId $space.id -Title 'Details' -ParentId $page.id -Body '<p>Nested</p>' | ||
| Get-ConfluencePageChild -PageId $page.id | ||
| Get-ConfluenceDescendant -PageId $page.id | ||
| # Labels and comments | ||
| Add-ConfluenceLabel -PageId $page.id -Label 'release', 'published' | ||
| Add-ConfluenceComment -PageId $page.id -Body '<p>Looks good.</p>' | ||
| # Remove the whole subtree (moves the pages to trash) | ||
| Remove-ConfluencePage -PageId $page.id -Recurse |
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| function Resolve-ConfluenceContext { | ||
| <# | ||
| .SYNOPSIS | ||
| Resolve a context argument into a usable credential-context object. | ||
| .DESCRIPTION | ||
| Accepts a context object/hashtable (returned as-is), a context name | ||
| (looked up in the vault), or nothing (falls back to the default context | ||
| recorded in the module configuration). | ||
| #> | ||
| [CmdletBinding()] | ||
| param( | ||
| # A context object, a context name, or $null for the default context. | ||
| [object]$Context | ||
| ) | ||
| if ($null -ne $Context -and $Context -isnot [string]) { | ||
| return $Context | ||
| } | ||
| Initialize-ConfluenceConfig | ||
| $id = if ([string]::IsNullOrEmpty([string]$Context)) { $script:Confluence.Config['DefaultContext'] } else { [string]$Context } | ||
| if ([string]::IsNullOrEmpty($id)) { | ||
| throw 'No Confluence context was specified and no default context is set. Run Connect-Confluence first.' | ||
| } | ||
| $resolved = $null | ||
| try { | ||
| $resolved = Get-Context -ID $id -Vault $script:Confluence.ContextVault -ErrorAction Stop | ||
| } catch { | ||
| $resolved = $null | ||
| } | ||
| if (-not $resolved) { | ||
| throw "Confluence context '$id' was not found in vault '$($script:Confluence.ContextVault)'." | ||
| } | ||
| return $resolved | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.