Deploy the AAP self-service portal (Red Hat Developer Hub with the AAP plugin) via Helm chart into an RHDP environment. Runs playbooks/portal.yml, which creates the OAuth application, deploys the chart, and syncs the org list. TRIGGER when: the user wants to deploy the self-service portal, asks about RHDH or Developer Hub, or wants non-admin users to launch templates from a browser. SKIP: if the portal is already deployed and the user wants to use it, or if the user wants to install OpenShift...
Pro shows the line behind each finding and how to fix it
Scanned 10/6/2026
npx -y skills add ericcames/sales.demos --skill sales-demos-portal --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Sales Demos Portal?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/ericcames-sales-demos-portal)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
name: sales-demos-portal
description: "Deploy the AAP self-service portal (Red Hat Developer Hub with the AAP plugin) via Helm chart into an RHDP environment. Runs playbooks/portal.yml, which creates the OAuth application, deploys the chart, and syncs the org list. TRIGGER when: the user wants to deploy the self-service portal, asks about RHDH or Developer Hub, or wants non-admin users to launch templates from a browser. SKIP: if the portal is already deployed and the user wants to use it, or if the user wants to install OpenShift Virtualization — that is sales-demos-setup."
---
# sales-demos-portal
Deploy the AAP self-service portal into an RHDP environment. Takes about 11
minutes end to end.
This skill contains **no logic**. All the work is in
[`playbooks/portal.yml`](../../../playbooks/portal.yml). See `CLAUDE.md` →
*Skills and playbooks*.
## What it does
1. Creates a gateway OAuth application (delete + recreate — never PATCH
`client_secret`, the gateway hashes differently and gives `invalid_client`).
2. Creates the `aap-portal` namespace, a durable service token, and three
Secrets the chart expects.
3. Deploys `redhat-rhaap-portal` 2.1.0 via Helm.
4. Updates the OAuth redirect URI to the actual portal Route.
5. Syncs the org list from AAP so all organizations are visible.
6. Patches the portal ConfigMap: 1-minute sync interval, permissions disabled
so non-admin users can see and launch templates.
## Preflight Check
```bash
./utilities/preflight.sh "${ENV:-sandbox}" --k8s
# helm binary is available
command -v helm >/dev/null \
&& echo "✅ helm $(helm version --short 2>/dev/null)" \
|| echo "❌ helm not found — https://helm.sh/docs/intro/install/"
# Kubeconfig for Helm module
ENV=${ENV:-sandbox}
test -f ".kube/${ENV}.kubeconfig" \
&& echo "✅ kubeconfig exists for $ENV" \
|| bash utilities/make-kubeconfig.sh "$ENV"
```
If any check fails, stop and tell the user exactly which one and the fix shown
beside it. Do not attempt the run with a failing prerequisite.
```bash
python3 - <<'PY'
import os, ssl, json, urllib.request
ctx = ssl.create_default_context(); ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE
req = urllib.request.Request(os.environ["OCP_URL"].rstrip("/") + "/apis",
headers={"Authorization": "Bearer " + os.environ["OCP_TOKEN"]})
groups = json.load(urllib.request.urlopen(req, context=ctx, timeout=20))["groups"]
print("✅ cluster reachable — %d API groups" % len(groups))
PY
```
## Collect inputs
Only one input, and it has a default. Ask the user only if it is ambiguous:
| Variable | Default | Meaning |
|---|---|---|
| `ENV` (inventory limit) | `sandbox` | Which environment to target — `sandbox` or `demo` |
## Run
```bash
mkdir -p ~/ansible-logs
export ANSIBLE_LOG_PATH=~/ansible-logs/portal-sandbox-$(date +%F-%H%M).log
./utilities/run-ansible.sh playbooks/portal.yml -i inventory --limit sandbox -e target_env=sandbox \
--vault-id sales.demos@~/secrets/.vault_pass_sales_demos
```
**Always set `ANSIBLE_LOG_PATH`** — this run takes about 11 minutes and the log
is the only evidence left if it fails. Logs live outside the repo, in
`~/ansible-logs/`. Tell the user the path so they can find it later.
**Never pipe the run through `tee`.** In a pipeline the exit status comes from
`tee`, not `ansible-playbook`, so a failed run reports success.
Tell the user this takes about 11 minutes and stream the output.
## Verify it in the EE before merging a change
See `/sales-demos-verify-ee` for why and how. The one command:
```bash
utilities/run-in-ee.sh playbooks/portal.yml \
-i inventory --limit sandbox -e target_env=sandbox \
--vault-id sales.demos@~/secrets/.vault_pass_sales_demos
```
## Verify on the cluster
**A green playbook run is not proof.** Ask the cluster using the
`openshift-sandbox` (or `openshift-demo`) MCP tools:
1. Check namespace exists: `pods_list_in_namespace` for `aap-portal`
2. Check deployment is ready: `resources_get` for Deployment
`rhaap-portal-redhat-developer-hub` in `aap-portal`
3. Check Route exists: `resources_list` for Routes in `aap-portal`
Then open the portal URL in a browser. It should show the Red Hat Developer Hub
login page. Log in with the AAP credentials — templates from the configured
organizations should be visible.
4. Confirm the two self-service entry points are in the catalog:
`Self-Service - Request Linux Server` and
`Self-Service - Request Windows Server`. Each should render its survey as a
request form (hypervisor, VM size tier, workload role, how many VMs). Allow
one sync interval — the catalog refreshes every minute.
## The portal cannot surface workflows, and that is why those two exist
The catalog syncs JOB templates only. There is no setting to change it: the
plugin has no workflow provider at all.
**Measured 2026-09-08, not inferred from the docs** (which are silent on the
question). `playbooks/portal.yml` writes
`catalog.providers.rhaap.production.sync.jobTemplates` because that is the only
key available, and grepping the deployed plugin bundle inside the running
`rhaap-portal` pod (chart 2.1.0) returns 200 occurrences of `jobTemplates` and
**zero** of `workflowJobTemplates`, `WorkflowJobTemplate` or
`AAPWorkflowJobTemplateProvider`. dc1.azure reached the same conclusion the same
way and records it in its own `playbooks/launch_workflow.yml`.
So the demo's headline entry points — `Linux Day 1 - 0 Workflow`,
`Windows Day 1 - 0 Workflow`, `Cluster Day 0`, `Windows Day 2 - 0 Break Fix` —
are invisible to the portal on their own. #242 closed that for the two
provisioning paths with thin launcher job templates that fire the workflow
(`playbooks/launch_workflow.yml`). If a future workflow needs to be portal-
reachable, it needs a launcher too; do not go looking for a config key.
## When it finishes
Report the playbook summary **and** the verification result above, then tell the
user the portal is live and accessible at the URL shown.
## If it fails
| Symptom | Cause | Fix |
|---|---|---|
| `401` / `Unauthorized` on the first task | RHDP bearer token expired | Refresh `openshift_api_token` in the vault, re-run `make-kubeconfig.sh` |
| `invalid_client` at `/o/token/` | OAuth client_secret was PATCHed instead of recreated | The playbook deletes and recreates; if this still happens, manually delete the `aap-selfservice-portal` application in the gateway |
| Helm deploy hangs or times out | Chart repo unreachable or cluster resources exhausted | Check `helm repo add openshift-charts https://charts.openshift.io/ && helm search repo redhat-rhaap-portal` |
| `rhaap-portal-app-config not found` | Helm deployment failed silently | Check the Helm release: `helm list -n aap-portal --kubeconfig .kube/<env>.kubeconfig` |
| Portal shows `UNRECOGNIZED` or no templates | Org sync not applied or still syncing | Wait 1 minute for the sync interval, or re-run the playbook |
| A workflow is missing from the catalog | Expected — the plugin syncs job templates only, and has no workflow provider | Not a fault to fix. Give the workflow a launcher job template, as #242 did for the two provisioning workflows |
| `Self-Service - Request …` is missing | `config.yml` has not run since #242, or it ran against the other environment | Re-run `config.yml` with the right `--limit`, then wait one sync interval |
| `Attempting to decrypt but no vault secrets found` | `--vault-id` missing from the command | Add `--vault-id sales.demos@~/secrets/.vault_pass_sales_demos` |
Never paste a live cluster hostname or token into a commit message, issue, or
PR. This repo is public — see `CLAUDE.md`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!