| name | subaccount-roles-import |
| description | Assign Neo application roles and group memberships into a CF subaccount after apps are deployed. Reads .migration/neo-roles.json (produced by subaccount-roles-export), resolves live XSUAA appIds from deployed CF apps, adds role-templates from those apps to the role collections created by authentication-xsuaa, and assigns users via BTP CLI. Generates a manual checklist for unresolved apps, group role links, and the Everyone role. MUST run after all applications are deployed to CF. |
| disable-model-invocation | false |
| allowed-tools | Read, Write, Glob, Bash(curl *), Bash(python3 *), Bash(btp *), Bash(cf *), Bash(echo *), Bash(cat *), Bash(ls *), Bash(mkdir *) |
Subaccount Roles Import
Assign Neo application roles and group memberships into CF role collections after app deployment.
Purpose
This skill reads the structured roles export produced by subaccount-roles-export and completes the authorization setup in CF by:
- Resolving the live XSUAA
appId for each deployed application (e.g. myapp!t1234)
- Adding the corresponding role-templates from each deployed XSUAA app to its role collections
- Assigning users to role collections via the BTP CLI
- Assigning group members to group role collections
- Generating a manual checklist for unresolvable apps, group→role links, and the Everyone role
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 first cf deploy. Role collections are created by authentication-xsuaa via xs-security.json + deployment; this skill does NOT create role collections.
Role collections are owned by authentication-xsuaa. The xs-security.json generated 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.
Prerequisites
-
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
Input Resolution
Step 0: Resolve Required Parameters
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.default as the IdP origin for user assignments. If users authenticate via a custom IdP, re-run this skill after completing subaccount-trust-import."
0c. Ask the user for any values still missing:
- "What is the GUID of the target CF subaccount?"
0d. Save CF config if not already present:
Write { "cfSubaccountId": "<guid>" } to .migration/cf-migration-config.json if it doesn't exist.
Step 1: Validate Session and Input
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 --sso in 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.
Step 2: Resolve Deployed XSUAA Apps
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:
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. |
Step 3: Add Role-Templates to Role Collections
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:
APP_DIR="<path to app source directory>"
if [ -f "${APP_DIR}/xs-security.json" ]; then
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 |
Step 4: Assign Users to Role Collections
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."
Step 5: Build Manual Checklist
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." |
Step 6: Save Import Report
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
}
Step 7: Display Summary
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
Configuration Files
| 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 |
CF Services
None — uses BTP CLI only.
Verification
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}'
Common Issues
Issue: "btp: command not found"
Cause: BTP CLI is not installed or not on PATH.
Solution: Download from SAP BTP Tools and ensure it is on PATH.
Issue: "No active session / not logged in"
Cause: BTP CLI session has expired.
Solution: Run btp login --sso in your terminal.
Issue: App not found in btp list security/app
Cause: 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.
Issue: "Role collection not found" when adding role-template
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.
Issue: "User not found" on assignment
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.
Issue: Role collections have wrong names
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.
Next Steps
After completing this skill:
- Complete manual steps — handle the Everyone role, deploy missing apps and re-run, fix failed user assignments
- Test access — log in via the approuter URL in a fresh browser session and verify that role-based access works as expected
- Post-migration checklist — see CLAUDE.md for remaining manual BTP Cockpit steps (set default IdP, re-enter destination secrets, assign entitlements)