Skip to main content

Appendix

Environment Variable Reference

The .env file in the repository's quick-start/ folder configures all the scripts. Copy .env.example to .env and edit as needed before running run.ps1. The defaults match the local Docker stack.

Passwords (KEYCLOAK_ADMIN_PASSWORD, APP_DB_PASSWORD, POSTGRES_APP_PASSWORD, SECURITY_DB_PASSWORD, POSTGRES_SECURITY_PASSWORD) may be left empty: run.ps1 and cleanup.ps1 prompt for the ones they need, with the input masked. Set them in the file only for unattended runs.

Identity provider

VariableDefaultDescription
PROVIDERkeycloakIdentity provider for the machine client: keycloak
KEYCLOAK_BASE_URLhttp://localhost:8080Keycloak admin API base URL (bootstrap; Keycloak only)
KEYCLOAK_ADMIN_USERadminKeycloak master admin user
KEYCLOAK_ADMIN_PASSWORD(empty)Keycloak master admin password — used when PROVIDER=keycloak; prompted when empty
KEYCLOAK_REALMedfiRealm holding the machine client

Machine (service-account) client

VariableDefaultDescription
MACHINE_CLIENT_IDedfiadminapp-machineClient id of the service account
MACHINE_CLIENT_SECRETedfi-machine-secret-456Client secret used to request tokens
MACHINE_AUDIENCEedfiadminapp-apiMust equal the Admin App's AUTH0_CONFIG_SECRET.MACHINE_AUDIENCE
MACHINE_USERNAMEquick-start-machineUsername of the machine user seeded in the Admin App database

Admin App database

Used by bootstrap.ps1 to seed the machine user and by cleanup.ps1.

VariableDefaultDescription
DB_ENGINEmssqlDatabase engine: mssql or pgsql
DATABASE_NAMEsbaaAdmin App application database name
APP_DB_USERNAMEedfiadminappSQL Server login for the dedicated least-privilege app user created by the Windows installer (install-all.ps1 -AppDbPassword)
APP_DB_PASSWORD(empty)Password for APP_DB_USERNAME — used when DB_ENGINE=mssql; prompted when empty. The scripts deliberately never connect as sa
POSTGRES_APP_PASSWORD(empty)PostgreSQL app-user password — used when DB_ENGINE=pgsql; prompted when empty
POSTGRES_HOSTlocalhostPostgreSQL host
POSTGRES_PORT5432PostgreSQL port
POSTGRES_APP_USERedfiadminappPostgreSQL user
USE_POSTGRES_DOCKERfalsetrue: run psql inside the edfiadminapp-postgres Docker container

Quick start (Admin App API)

VariableDefaultDescription
API_BASE_URLhttps://localhost/adminapp-api/apiAdmin App API base URL (through the reverse proxy)
TOKEN_URLhttps://localhost/auth/realms/edfi/protocol/openid-connect/tokenYour issuer's token endpoint (see Finding your token endpoint)
OAUTH_SCOPElogin:appOAuth scope requested for the machine token (Keycloak: login:app)
TEAM_NAMEQuick StartTeam to create
ENVIRONMENT_NAMEEd-Fi ODS/API v7.3Environment to create
ENVIRONMENT_LABELQuickStartEnvironment label
ADMIN_API_URLhttps://localhost/AdminApiODS Admin API URL
ODS_API_DISCOVERY_URLhttps://localhost/WebApiODS/API discovery URL
TENANT_NAMEdefaultEd-Fi tenant to create under the environment
ADMIN_USERNAME(empty)Username of the human bootstrap admin. When set, quick-start.ps1 also adds that user to the team (Tenant admin role) so the Applications and Profiles pages work for them; leave empty to skip
ODSS_JSON(two sample instances)JSON array of ODS instances to attach; ids must match EdFi_Admin.dbo.OdsInstances on the target ODS/API
SKIP_CERTIFICATE_CHECKtruetrue: skip TLS validation (local self-signed certificates)

Claim set copies (EdFi_Security)

Used by copy-claimsets.ps1 to copy the built-in claim sets in the ODS/API's EdFi_Security database. EdFi_Security is a different database from the Admin App's — it can even run on a different engine (SECURITY_DB_ENGINE, defaulting to DB_ENGINE). SQL Server uses its own SECURITY_DB_USERNAME / SECURITY_DB_PASSWORD login (the APP_DB_* login has no rights there), or Windows integrated authentication with SECURITY_USE_INTEGRATED_SECURITY=true; PostgreSQL uses the POSTGRES_SECURITY_* values, each falling back to the app-side POSTGRES_* value above when empty.

VariableDefaultDescription
COPY_CLAIMSETStruefalse: skip the claim set copy step entirely
CLAIMSET_NAMES(empty = every built-in claim set)Claim sets to copy, semicolon-separated; blank copies all built-ins except internal-use ones (ForApplicationUseOnly = 1, e.g. Bootstrap Descriptors and EdOrgs)
CLAIMSET_PREFIX"AA "Prefix for the copies; quote it to keep the trailing space
SECURITY_DB_ENGINE(empty = same as DB_ENGINE)Engine hosting EdFi_Security: mssql or pgsql. Set it when the ODS/API side runs a different engine than the Admin App database
SECURITY_DATABASE_NAMEEdFi_SecuritySecurity database name
SECURITY_SQL_SERVERtcp:localhost,1433SQL Server hosting EdFi_Security
SECURITY_DB_USERNAME(empty)SQL Server login with rights on EdFi_Security; required unless SECURITY_USE_INTEGRATED_SECURITY=true
SECURITY_DB_PASSWORD(empty)Password for SECURITY_DB_USERNAME; prompted when empty
SECURITY_USE_INTEGRATED_SECURITYfalsetrue: Windows integrated authentication (SECURITY_DB_* not needed)
POSTGRES_SECURITY_PASSWORD(empty = POSTGRES_APP_PASSWORD)PostgreSQL password for EdFi_Security; prompted when both are empty
POSTGRES_SECURITY_HOST(empty = POSTGRES_HOST)PostgreSQL host for EdFi_Security
POSTGRES_SECURITY_PORT(empty = POSTGRES_PORT)PostgreSQL port for EdFi_Security
POSTGRES_SECURITY_USER(empty = POSTGRES_APP_USER)PostgreSQL user for EdFi_Security
SECURITY_USE_POSTGRES_DOCKER(empty = USE_POSTGRES_DOCKER)true: run psql inside the SECURITY_POSTGRES_CONTAINER container
SECURITY_POSTGRES_CONTAINERed-fi-db-adminThe ODS stack's admin/security db container (PostgreSQL in Docker)

Finding your token endpoint

The token endpoint is not universal. The Docker stack proxies Keycloak at https://localhost/auth/realms/edfi/...; other deployments (e.g. IIS-hosted, or newer standalone Keycloak that drops the /auth prefix) differ. Resolve it from the issuer the Admin App is configured to trust (AUTH0_CONFIG_SECRET.ISSUER) — the token must be minted by that same issuer, because the Admin App fetches its signing keys from there:

$issuer = 'PASTE_AUTH0_CONFIG_SECRET.ISSUER' # e.g. https://localhost:8443/realms/edfi
$disco = Invoke-RestMethod -Uri "$issuer/.well-known/openid-configuration" -SkipCertificateCheck
$disco.token_endpoint # use this as TOKEN_URL

Script Options

run.ps1

OptionPurpose
-EnvFile <path>Use a different env file (default: ./.env)
-SkipBootstrapSkip bootstrap.ps1 (IdP client + machine-user seed)
-SkipClaimsetsSkip copy-claimsets.ps1 (built-in claim set copies in EdFi_Security)

cleanup.ps1

Reads the same .env for the database connection and the names to delete; any parameter passed explicitly overrides the .env value.

OptionPurpose
-ForceSkip the confirmation prompt (automation)
-EnvFile <path>Use a different env file (default: ./.env)
-SkipClaimsetsLeave the claim set copies in EdFi_Security (e.g. an application still uses one)
-EnvironmentName / -TeamName / -MachineUsernameOverride the names to delete
-DbEngine, -DatabaseName, -AppDbUsername, -AppDbPassword, -PostgresAppPasswordOverride the database connection (mssql always uses the app login, never sa)
-SecuritySqlServerOverride the SQL Server hosting EdFi_Security (everything else comes from the SECURITY_* / POSTGRES_SECURITY_* .env settings, with the engine from SECURITY_DB_ENGINE defaulting to DB_ENGINE)

Running the scripts directly

bootstrap.ps1, quick-start.ps1, and copy-claimsets.ps1 are plain parameter-driven scripts — run.ps1 only maps .env values onto their parameters. To run one directly (for example, to provision a second environment with different values, or to copy additional claim sets), see its comment-based help:

Get-Help ./bootstrap.ps1 -Full
Get-Help ./quick-start.ps1 -Full
Get-Help ./copy-claimsets.ps1 -Full

All the scripts are idempotent: re-runs update, reuse, or skip existing resources instead of duplicating them.

Troubleshooting

  • Create environment failed: ... 400 (Bad Request). The API could not reach the ODS/API or Admin API while creating the environment — for example Connection to Ed-Fi API Discovery URL timed out. Please ensure the URL is correct and the server is reachable. Confirm the ODS/API and Admin API are up and reachable at the ODS_API_DISCOVERY_URL / ADMIN_API_URL values (open them in a browser or curl them), then run the script again.
  • Create environment failed: … 403 (Forbidden) (or, for Admin API v2 environments, environment creation reports a failed sync with no clear error). The Admin API rejected the Admin App's credential registration at /connect/register because Authentication:AllowRegistration is false — the Admin App registers its own client credentials there when creating the environment. Set the flag to true in the Admin API's appsettings.json, restart the Admin API, and re-run ./run.ps1 -SkipBootstrap (the flag can be turned off again afterwards).
  • Create environment failed: with "adminApiUrl": … "Internal server error (500) - service may be down". The Admin API's root URL responded, but its POST /connect/register endpoint threw a server-side error — usually a database problem: the Admin API's own tables (the adminapi schema in EdFi_Admin) were never installed, or its ConnectionStrings are wrong. Reproduce it by manually registering a client (see the tip on the prerequisites page), then check the Admin API's log file for the underlying exception and confirm the adminapi schema exists in EdFi_Admin.
  • 403 Forbidden on Applications / Profiles ({"message":"Forbidden resource"...} at …/edfi-tenants/<id>/admin-api/v2/profiles/). This is the Admin App's own authorization, not the live Admin API. Two common causes:
    • The signed-in user has no team membership — the Global admin role alone lacks the team-scoped profile privilege. Set ADMIN_USERNAME to the human bootstrap admin's username and re-run: quick-start.ps1 adds them to the team with the Tenant admin role.
    • The membership uses role 2 (Global admin) instead of 6 (Tenant admin). Re-run the script (it upgrades the membership automatically) or PUT the membership's roleId to 6.
  • Empty ODS instances list. The ids in ODSS_JSON must exist in EdFi_Admin.dbo.OdsInstances on the target ODS/API; otherwise the sync finds nothing to attach. Query the table and use its real OdsInstanceId values — see Set Up the ODS Instances.
  • ODS instance "<name>" does not exist in Admin API when creating an application. The Admin App matches the environment's ODS against the Admin API's GET /v2/odsInstances list by name, and that list comes from EdFi_Admin.dbo.OdsInstances. The named instance is missing from that table, or its Name differs from the name in ODSS_JSON. Check with SELECT OdsInstanceId, Name FROM dbo.OdsInstances against EdFi_Admin; if the row is missing, create it (see Set Up the ODS Instances), restart the ODS/API, and make sure ODSS_JSON uses the exact same name.
  • /auth/me returns 401 with "Token claim validation failed". The token's iss does not equal the Admin App's configured AUTH0_CONFIG_SECRET.ISSUER, or its aud does not include MACHINE_AUDIENCE. Mint the token from the exact issuer the Admin App trusts, and confirm the audience mapper emits edfiadminapp-api. Decode the token (its middle segment) to confirm the exact values.
  • /auth/me returns 401 with "Authentication failed". The token verified, but the machine-user step failed. Two common causes:
    • No matching machine user — the bootstrap.ps1 script seeds one (clientId = edfiadminapp-machine, userType = machine, active, Global admin role). Confirm that row exists in the Admin App database.
    • The aud claim is an array (e.g. ["edfiadminapp-api","account"]). The Admin App compares the whole aud claim by strict equality against MACHINE_AUDIENCE, so it must be the single value edfiadminapp-api. The extra account audience is injected by Keycloak's Audience Resolve mapper in the default roles client scope — on the machine client, set that roles scope from Default to Optional so aud is just edfiadminapp-api. Decode the token (its middle segment) to confirm.
  • Token request fails with 404 Not Found (often an IIS/web-server error page). TOKEN_URL is not your Keycloak — the /auth/... path is the Docker stack convention and your host serves something else there. Resolve the real endpoint from your issuer's discovery document (see Finding your token endpoint).
  • Token request fails with 401 invalid_client / 400 unauthorized_client. The service-account client does not exist, the secret is wrong, or it is missing the client_credentials grant. On Keycloak confirm the client exists with Service accounts enabled and the audience / login:app / client_id mappers in place (see Setup Keycloak machine account).