Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions codebundles/atlassian-org-license-optimization/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
atlassian_user_inventory.json
atlassian_directory_users.json
atlassian_inactive_billable_*
atlassian_product_overlap_*
atlassian_pending_invite_*
atlassian_reclamation_issues.json
atlassian_license_reclamation_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: runwhen.com/v1
kind: GenerationRules
spec:
platform: atlassian
generationRules:
- resourceTypes:
- atlassian_organization
matchRules:
- type: pattern
pattern: ".+"
properties: ["name"]
mode: substring
slxs:
- baseName: atlassian-org-license-optimization
qualifiers: ["organization"]
baseTemplateName: atlassian-org-license-optimization
levelOfDetail: basic
outputItems:
- type: slx
- type: sli
- type: runbook
templateName: atlassian-org-license-optimization-taskset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
apiVersion: runwhen.com/v1
kind: ServiceLevelIndicator
metadata:
name: {{slx_name}}
labels:
{% include "common-labels.yaml" %}
annotations:
{% include "common-annotations.yaml" %}
spec:
displayUnitsLong: Health Score
displayUnitsShort: score
locations:
- {{default_location}}
description: Measures Atlassian organization license reclamation health for {{ match_resource.name }}.
codeBundle:
{% if repo_url %}
repoUrl: {{repo_url}}
{% else %}
repoUrl: https://github.com/runwhen-contrib/rw-cli-codecollection.git
{% endif %}
{% if ref %}
ref: {{ref}}
{% else %}
ref: main
{% endif %}
pathToRobot: codebundles/atlassian-org-license-optimization/sli.robot
intervalStrategy: intermezzo
intervalSeconds: 300
configProvided:
- name: ATLASSIAN_ORG_ID
value: "{{ match_resource.id }}"
- name: ATLASSIAN_ORG_NAME
value: "{{ match_resource.name }}"
- name: ATLASSIAN_DIRECTORY_ID
value: "{{ custom.atlassian_directory_id | default('') }}"
- name: INACTIVE_DAYS_THRESHOLD
value: "{{ custom.inactive_days_threshold | default('90') }}"
- name: PENDING_INVITE_DAYS_THRESHOLD
value: "{{ custom.pending_invite_days_threshold | default('30') }}"
- name: MIN_OVERLAP_PRODUCTS
value: "{{ custom.min_overlap_products | default('2') }}"
- name: RECLAMATION_MIN_SEATS
value: "{{ custom.reclamation_min_seats | default('5') }}"
- name: SLI_MAX_PAGES
value: "{{ custom.sli_max_pages | default('2') }}"
secretsProvided:
{% if wb_version %}
{% include "atlassian-auth.yaml" ignore missing %}
{% else %}
- name: atlassian_org_api_key
workspaceKey: AUTH DETAILS NOT FOUND
{% endif %}
alertConfig:
tasks:
persona: eager-edgar
sessionTTL: 10m
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: runwhen.com/v1
kind: ServiceLevelX
metadata:
name: {{ slx_name }}
labels:
{% include "common-labels.yaml" %}
annotations:
{% include "common-annotations.yaml" %}
spec:
imageURL: https://storage.googleapis.com/runwhen-nonprod-shared-images/icons/atlassian/atlassian.svg
alias: {{ match_resource.name }} Atlassian License Optimization
asMeasuredBy: License reclamation health score based on inactive users, product overlap, and stale invites.
configProvided:
- name: SLX_PLACEHOLDER
value: SLX_PLACEHOLDER
owners:
- {{ workspace.owner_email }}
statement: Atlassian organization {{ match_resource.name }} should minimize reclaimable license waste before renewal.
additionalContext:
{% include "atlassian-hierarchy.yaml" ignore missing %}
qualified_name: "{{ match_resource.qualified_name }}"
tags:
{% include "atlassian-tags.yaml" ignore missing %}
- name: cloud
value: atlassian
- name: service
value: organization-admin
- name: scope
value: organization
- name: access
value: read-only
- name: category
value: license-optimization
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
apiVersion: runwhen.com/v1
kind: Runbook
metadata:
name: {{slx_name}}
labels:
{% include "common-labels.yaml" %}
annotations:
{% include "common-annotations.yaml" %}
spec:
location: {{default_location}}
description: Analyze Atlassian organization {{ match_resource.name }} for license waste and reclamation opportunities.
codeBundle:
{% if repo_url %}
repoUrl: {{repo_url}}
{% else %}
repoUrl: https://github.com/runwhen-contrib/rw-cli-codecollection.git
{% endif %}
{% if ref %}
ref: {{ref}}
{% else %}
ref: main
{% endif %}
pathToRobot: codebundles/atlassian-org-license-optimization/runbook.robot
configProvided:
- name: ATLASSIAN_ORG_ID
value: "{{ match_resource.id }}"
- name: ATLASSIAN_ORG_NAME
value: "{{ match_resource.name }}"
- name: ATLASSIAN_DIRECTORY_ID
value: "{{ custom.atlassian_directory_id | default('') }}"
- name: INACTIVE_DAYS_THRESHOLD
value: "{{ custom.inactive_days_threshold | default('90') }}"
- name: PENDING_INVITE_DAYS_THRESHOLD
value: "{{ custom.pending_invite_days_threshold | default('30') }}"
- name: MIN_OVERLAP_PRODUCTS
value: "{{ custom.min_overlap_products | default('2') }}"
- name: PRODUCTS
value: "{{ custom.products | default('All') }}"
- name: RECLAMATION_MIN_SEATS
value: "{{ custom.reclamation_min_seats | default('5') }}"
- name: TIMEOUT_SECONDS
value: "{{ custom.timeout_seconds | default('900') }}"
secretsProvided:
{% if wb_version %}
{% include "atlassian-auth.yaml" ignore missing %}
{% else %}
- name: atlassian_org_api_key
workspaceKey: AUTH DETAILS NOT FOUND
{% endif %}
29 changes: 29 additions & 0 deletions codebundles/atlassian-org-license-optimization/.test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Test Infrastructure

This bundle uses mock JSON fixtures instead of live Atlassian API calls for CI-friendly validation.

## Scenarios

| Scenario | Description | Expected issues |
|----------|-------------|-----------------|
| `no_reclamation_candidates` | All billable users active; no stale invites | 0 |
| `inactive_jira_users` | 5+ inactive Jira billable users | ≥1 |
| `overlap_and_invites` | Product overlap + 8 stale pending invites | ≥2 |

## Run Tests

```bash
cd .test
task
```

Or run individual scripts:

```bash
./validate-bundle-structure.sh
./validate-all-tests.sh
```

## Terraform

Live Atlassian organizations cannot be provisioned via Terraform. Mock fixtures in `fixtures/` provide deterministic test data aligned with the design spec scenarios.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: "3"

tasks:
default:
desc: "Run complete test suite"
cmds:
- task: validate-structure
- task: test-all-scenarios

validate-structure:
desc: "Static structure checks"
cmds:
- ./validate-bundle-structure.sh

test-all-scenarios:
desc: "Run mock-based scenario tests"
cmds:
- ./validate-all-tests.sh

clean:
desc: "Remove local test artifacts"
cmds:
- rm -f ../atlassian_*.json ../atlassian_*.md ../atlassian_user_inventory.json ../atlassian_directory_users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"org_id": "test-org-002",
"org_name": "Beta Org",
"fetched_at": "2026-06-10T00:00:00Z",
"partial": false,
"users": [
{
"account_id": "inactive-1",
"name": "Inactive Jira User 1",
"email": "inactive1@beta.example",
"access_billable": true,
"department": "Sales",
"product_access": [
{"key": "jira-software", "name": "Jira", "last_active": "2025-01-01T00:00:00.000Z"}
]
},
{
"account_id": "inactive-2",
"name": "Inactive Jira User 2",
"email": "inactive2@beta.example",
"access_billable": true,
"department": "Sales",
"product_access": [
{"key": "jira-software", "name": "Jira", "last_active": "2025-02-01T00:00:00.000Z"}
]
},
{
"account_id": "inactive-3",
"name": "Inactive Jira User 3",
"email": "inactive3@beta.example",
"access_billable": true,
"department": "Marketing",
"product_access": [
{"key": "jira-software", "name": "Jira", "last_active": "2025-03-01T00:00:00.000Z"}
]
},
{
"account_id": "inactive-4",
"name": "Inactive Jira User 4",
"email": "inactive4@beta.example",
"access_billable": true,
"department": "Marketing",
"product_access": [
{"key": "jira-software", "name": "Jira", "last_active": "2025-04-01T00:00:00.000Z"}
]
},
{
"account_id": "inactive-5",
"name": "Inactive Jira User 5",
"email": "inactive5@beta.example",
"access_billable": true,
"department": "Support",
"product_access": [
{"key": "jira-software", "name": "Jira", "last_active": "2025-05-01T00:00:00.000Z"}
]
},
{
"account_id": "active-1",
"name": "Active User",
"email": "active@beta.example",
"access_billable": true,
"department": "Engineering",
"product_access": [
{"key": "jira-software", "name": "Jira", "last_active": "2026-06-09T00:00:00.000Z"}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"org_id": "test-org-001",
"directory_id": "dir-001",
"fetched_at": "2026-06-10T00:00:00Z",
"partial": false,
"users": [
{
"accountId": "user-active-1",
"email": "active@acme.example",
"name": "Active User",
"membershipStatus": "active",
"accountStatus": "active",
"addedToOrg": "2025-01-01T00:00:00.000Z"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"org_id": "test-org-001",
"org_name": "Acme Corp",
"fetched_at": "2026-06-10T00:00:00Z",
"partial": false,
"users": [
{
"account_id": "user-active-1",
"name": "Active User",
"email": "active@acme.example",
"access_billable": true,
"department": "Engineering",
"product_access": [
{"key": "jira-software", "name": "Jira", "last_active": "2026-06-09T10:00:00.000Z"},
{"key": "confluence", "name": "Confluence", "last_active": "2026-06-08T10:00:00.000Z"}
]
}
]
}
Loading
Loading