Imports Neo application roles and group memberships into CF subaccount role collections post-deployment. Resolves XSUAA appIds from live apps, links role-templates, assigns users via BTP CLI, generates checklists.
npx claudepluginhub sap-samples/btp-neo-java-app-migration --plugin sap-btp-neo-migrationThis skill is limited to using the following tools:
Assign Neo application roles and group memberships into CF role collections after app deployment.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Processes PDFs: extracts text/tables/images, merges/splits/rotates pages, adds watermarks, creates/fills forms, encrypts/decrypts, OCRs scans. Activates on PDF mentions or output requests.
Share bugs, ideas, or general feedback.
Assign Neo application roles and group memberships into CF role collections after app deployment.
This skill reads the structured roles export produced by subaccount-roles-export and completes the authorization setup in CF by:
appId for each deployed application (e.g. myapp!t1234)Must run after all applications are deployed. This skill requires live XSUAA apps to exist in CF — the
appId(e.g.myapp!t1234) is only available after the firstcf deploy. Role collections are created byauthentication-xsuaaviaxs-security.json+ deployment; this skill does NOT create role collections.
Role collections are owned by
authentication-xsuaa. Thexs-security.jsongenerated by that skill defines the role collection names and role-template references. This skill only links the deployed role-templates into those collections and assigns users.
The "Everyone" role cannot be directly migrated. In Neo, the Everyone role grants access to all authenticated users implicitly. In CF, you must explicitly assign role collections to all users. This skill flags this as a manual step.
subaccount-roles-export must have run — .migration/neo-roles.json must exist
All applications must be deployed to CF — each app's authentication-xsuaa skill must have run, xs-security.json must be deployed, and cf deploy . -f must have completed successfully for every app
BTP CLI installed and logged in:
btp target
If not logged in, instruct the user to run:
btp login --sso
Target CF subaccount ID — the GUID of the CF subaccount
0a. Read the export file:
cat .migration/neo-roles.json
If the file does not exist, stop and instruct the user to run subaccount-roles-export first.
0b. Check for existing CF config:
if [ -f .migration/cf-migration-config.json ]; then
cat .migration/cf-migration-config.json
fi
Read cfSubaccountId if present.
0b2. Read the trust import report to determine the IdP origin key:
if [ -f .migration/neo-trust-import-report.json ]; then
cat .migration/neo-trust-import-report.json
fi
Extract the originKey from the first successfully imported IdP:
IDP_ORIGIN=$(python3 -c "
import json, sys
with open('.migration/neo-trust-import-report.json') as f:
report = json.load(f)
imported = report.get('imported', [])
already = report.get('alreadyConfigured', [])
all_idps = imported + already
print(all_idps[0]['originKey'] if all_idps else 'sap.default')
" 2>/dev/null || echo "sap.default")
If .migration/neo-trust-import-report.json does not exist or contains no imported IdPs, fall back to sap.default and note it in the report:
"No trust import report found — using
sap.defaultas the IdP origin for user assignments. If users authenticate via a custom IdP, re-run this skill after completingsubaccount-trust-import."
0c. Ask the user for any values still missing:
0d. Save CF config if not already present:
Write { "cfSubaccountId": "<guid>" } to .migration/cf-migration-config.json if it doesn't exist.
1a. Verify BTP CLI session:
btp target
If this fails, stop and tell the user:
"No active BTP CLI session found. Please run
btp login --ssoin your terminal, then re-invoke this skill."
1b. Check for data to process:
If both applications and groups arrays are empty in the export, inform the user:
"The Neo subaccount has no application roles or groups configured. Nothing to import."
Then stop — this is a successful no-op.
Fetch the list of deployed XSUAA applications from the CF subaccount:
XSUAA_APPS=$(btp --format json list security/app --subaccount "${CF_SUBACCOUNT_ID}")
Build a map of appName → appId using jq:
# For each app name from neo-roles.json, find its deployed appId
# XSUAA appIds have the format: appname!tNNNNN
# Match by the prefix before the '!' separator
echo "$XSUAA_APPS" | jq -r '.[] | "\(.name) \(.appId)"'
For each application in neo-roles.json:
APP_NAME="<appName from neo-roles.json>"
APP_ID=$(echo "$XSUAA_APPS" | jq -r --arg name "$APP_NAME" \
'.[] | select(.name == $name or (.appId | startswith($name + "!"))) | .appId' | head -1)
Classification:
| Condition | Action |
|---|---|
APP_ID found | Proceed to Steps 3 and 4 for this app |
APP_ID not found | Add to manual checklist: "App {appName} not found in CF — ensure it has been deployed and authentication-xsuaa has been run for it. Re-run this skill after deployment." Skip role-template assignment for this app; still attempt user assignments if role collections exist. |
For each application with a resolved APP_ID, for each role in application.roles[]:
3a. Determine the role collection name:
First, try to read xs-security.json from the application directory to find the exact collection name defined there:
# If app directories are known (e.g. from cf-migration-config.json or user input):
APP_DIR="<path to app source directory>"
if [ -f "${APP_DIR}/xs-security.json" ]; then
# Find role-collection that references this role-template
COLLECTION_NAME=$(jq -r --arg role "${ROLE_NAME}" \
'.["role-collections"][] | select(.["role-template-references"][] | contains($role)) | .name' \
"${APP_DIR}/xs-security.json" | head -1)
fi
If xs-security.json is not found or the role is not listed there, fall back to the naming convention:
COLLECTION_NAME="${APP_NAME}-${ROLE_NAME}"
3b. Add the role-template to the collection:
btp add security/role \
--role-collection "${COLLECTION_NAME}" \
--role-name "${ROLE_NAME}" \
--app-id "${APP_ID}" \
--role-template "${ROLE_NAME}" \
--subaccount "${CF_SUBACCOUNT_ID}"
Handle responses:
| Outcome | Action |
|---|---|
| Success (exit 0) | Mark as role_assigned |
| "already exists" / conflict | Mark as already_assigned — treat as success |
| Role collection not found | Add to manual checklist: "Role collection {collectionName} not found — ensure authentication-xsuaa was run for {appName} and the app is deployed. Create the collection manually or re-run authentication-xsuaa." |
| Any other error | Mark as failed, capture error, continue |
4a. Assign users from application role assignments:
For each application, for each role, for each user in role.userAssignments[]:
btp assign security/role-collection "${COLLECTION_NAME}" \
--to-user "${USER_ID}" \
--of-idp "${IDP_ORIGIN}" \
--subaccount "${CF_SUBACCOUNT_ID}"
Handle responses:
| Outcome | Action |
|---|---|
| Success (exit 0) | Record as assigned |
| "user not found" error | Add to manual steps: "User {userId} not found in CF — the user must log in at least once to be created. Assign manually in BTP Cockpit > Security > Users." |
| Any other error | Record as failed, capture error, continue |
4b. Create and populate group role collections:
For each group in groups[]:
The group role collection may already exist if subaccount-trust-import created it for assertion-based group rules. Check first:
EXISTING=$(btp --format json list security/role-collection --subaccount "${CF_SUBACCOUNT_ID}" | \
jq -r --arg name "${GROUP_NAME}-Group" '.[] | select(.name == $name) | .name')
If it does not exist, create it:
btp create security/role-collection "${GROUP_NAME}-Group" \
--description "Migrated from Neo group: ${GROUP_NAME}" \
--subaccount "${CF_SUBACCOUNT_ID}" 2>/dev/null || true
Assign users from group.userAssignments[]:
btp assign security/role-collection "${GROUP_NAME}-Group" \
--to-user "${USER_ID}" \
--of-idp "${IDP_ORIGIN}" \
--subaccount "${CF_SUBACCOUNT_ID}"
4c. Document group→role links in the manual checklist:
For each entry in group.roleAssignments[], CF role collections cannot contain other role collections. Each group→role link must be handled by adding the role-template from the referenced app to the group's collection:
btp add security/role \
--role-collection "${GROUP_NAME}-Group" \
--role-name "${ROLE_NAME}" \
--app-id "${APP_ID}" \
--role-template "${ROLE_NAME}" \
--subaccount "${CF_SUBACCOUNT_ID}"
If the referenced app is not deployed yet, add to manual checklist:
"{groupName}-Group: In Neo this group had role {applicationName}/{roleName}. Add the role-template from
{applicationName}(appId:{appId}) to this role collection after the app is deployed."
Collect all items that could not be automated:
| Condition | Manual Step |
|---|---|
| Always | "The implicit 'Everyone' role in Neo has no CF equivalent. Explicitly assign the appropriate role collection(s) to all users who should have basic authenticated access, or configure a default role collection in the trust configuration." |
| Any app not found in CF | "Deploy {appName} to CF (cf deploy . -f from the app directory), then re-run subaccount-roles-import to complete role-template and user assignments for this app." |
| Any role collection not found | "Role collection {collectionName} not found — ensure authentication-xsuaa was run and the app is deployed. Create manually in BTP Cockpit > Security > Role Collections, then add role-template {roleName} from app {appId}." |
| Any user assignment failure | "User {userId} not found — ensure they have logged in at least once. Assign {collectionName} manually in BTP Cockpit > Security > Users." |
| Any group with unresolved role links | "{groupName}-Group: add role-template {roleName} from app {appId} after deployment." |
Save to .migration/neo-roles-import-report.json using the Write tool:
{
"targetSubaccount": "<CF subaccount GUID>",
"importTimestamp": "<ISO 8601 timestamp>",
"sourceFile": ".migration/neo-roles.json",
"idpOrigin": "<origin key used for user assignments, e.g. nss.migrated or sap.default>",
"appsResolved": [
{ "name": "myapp", "appId": "myapp!t1234" }
],
"appsNotFound": ["otherapp"],
"roleTemplateAssignments": {
"succeeded": 3,
"alreadyAssigned": 1,
"failed": 0,
"failedDetails": []
},
"userAssignments": {
"succeeded": 5,
"failed": 0,
"failedDetails": []
},
"groupCollectionsCreated": ["managers-Group"],
"groupCollectionsAlreadyExist": ["viewers-Group"],
"manualSteps": [
"The implicit 'Everyone' role in Neo has no CF equivalent. Assign role collections to all authenticated users explicitly.",
"Deploy otherapp to CF, then re-run subaccount-roles-import."
],
"requiresManualSteps": true
}
Roles Import Complete
=====================
Target subaccount: <ID>
XSUAA Apps Resolved: <count> / <total>
[If any unresolved:] ✗ <appName>: not found in CF — deploy first
Role-Template Assignments:
Succeeded: <count>
Already assigned: <count>
Failed: <count>
User Assignments:
Succeeded: <count>
Failed: <count>
Groups processed: <count>
Collections created: <count>
Collections already exist: <count>
Manual Steps Required: yes
1. <step>
2. <step>
...
Report saved to: .migration/neo-roles-import-report.json
| File | Location | Purpose |
|---|---|---|
neo-roles.json | .migration/ | Input — roles export from subaccount-roles-export |
neo-trust-import-report.json | .migration/ | Input — trust import report; provides the IdP origin key for user assignments |
cf-migration-config.json | .migration/ | CF target subaccount details |
neo-roles-import-report.json | .migration/ | Output — assignment results and manual checklist |
None — uses BTP CLI only.
List role collections and check role-template references:
btp --format json list security/role-collection --subaccount <CF_SUBACCOUNT_ID> | \
jq '.[] | {name, roleRefs: [.roleReferences[].roleTemplateName]}'
Verify a specific collection has users assigned:
btp --format json get security/role-collection "myapp-Admin" --subaccount <CF_SUBACCOUNT_ID> | \
jq '{name, users: [.userReferences[].value]}'
List deployed XSUAA apps:
btp --format json list security/app --subaccount <CF_SUBACCOUNT_ID> | \
jq '.[] | {name, appId}'
Cause: BTP CLI is not installed or not on PATH. Solution: Download from SAP BTP Tools and ensure it is on PATH.
Cause: BTP CLI session has expired.
Solution: Run btp login --sso in your terminal.
btp list security/appCause: The application has not been deployed to CF yet, or authentication-xsuaa was not run for it.
Solution: Run the full app migration (jakarta → sdk-replacement → authentication-xsuaa → mta-descriptor → cf deploy . -f), then re-run this skill.
Cause: authentication-xsuaa was not run, or the app deploy failed and xs-security.json was never applied to CF.
Solution: Verify cf services shows the XSUAA service instance. Check btp list security/role-collection for the expected collection name. If missing, re-run authentication-xsuaa and redeploy.
Cause: The user has never logged into the CF subaccount. Solution: Ask the user to log in via the application URL or BTP cockpit once. Then assign manually in BTP Cockpit > Security > Users.
Cause: The naming convention in xs-security.json differs from {appName}-{roleName}.
Solution: Read the app's xs-security.json to find the exact role collection name, then use btp add security/role --role-collection "<exact-name>" ... manually.
After completing this skill: