-
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
Draft
Marius Storhaug (MariusStorhaug)
wants to merge
22
commits into
main
Choose a base branch
from
feat/decompose-module
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
f86f527
🚀 [Feature]: Decompose the Confluence client into the PSModule source…
MariusStorhaug 9be6481
🪲 [Fix]: Resolve PSScriptAnalyzer findings in examples and tests
MariusStorhaug fa9a9fa
Remove header.ps1 and move the module requirements to the REST entry …
MariusStorhaug 04ae285
Remove the source manifest; tags come from repository topics
MariusStorhaug fdedf80
Match the GitHub module comment-based help style and link each comman…
MariusStorhaug 3f82493
Merge origin/main into feat/decompose-module
MariusStorhaug fbfdad2
Green the source-code and docs pipelines
MariusStorhaug cb00658
Document the required Atlassian scopes for the service-account token
MariusStorhaug 59542da
⚙️ [Maintenance]: Declare test secrets and variables via Process-PSMo…
MariusStorhaug 71423fb
📖 [Docs]: Record configured token scopes and candidate commands in SC…
MariusStorhaug 56b279a
🚀 [Feature]: Add ConvertTo-ConfluenceCloudId to resolve a site to its…
MariusStorhaug 4e867fc
🚀 [Feature]: Connect by site name; add accessible-resources and v1/v2…
MariusStorhaug 4fcca56
♻️ [Maintenance]: Rename ConvertTo-ConfluenceCloudId to Get-Confluenc…
MariusStorhaug 9f23e48
♻️ [Maintenance]: Pass a single TestData secret to Process-PSModule (…
MariusStorhaug 4cd4886
📖 [Docs]: Fix Connect-Confluence examples to use -Site/-CloudId and a…
MariusStorhaug e282457
🩹 [Patch]: Harden site URI parsing, surface site-info probe errors, a…
MariusStorhaug 8b717c2
🩹 [Patch]: Preserve page status on update; trash-then-purge for Remov…
MariusStorhaug 6712085
🩹 [Patch]: Skip integration tests unless all Confluence credentials a…
MariusStorhaug a8dc0d7
🩹 [Patch]: Allow compatible Context 8.x; document shortened scope for…
MariusStorhaug 4a3ebf4
🩹 [Patch]: Use a concrete Context MaximumVersion so the module manife…
MariusStorhaug 225a70b
🧪 [Test]: Exercise every command with live CRUD and context tests
MariusStorhaug 85b625f
🩹 [Patch]: Retry attachment delete on transient 409 in tests
MariusStorhaug File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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>' | ||
| Set-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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| # 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`, `Set-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`, `Set-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. | ||
|
MariusStorhaug marked this conversation as resolved.
|
||
|
|
||
| ## 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. | ||
| - This 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. | ||
| - 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/). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) | ||
| Set-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.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.