Skip to main content

Appendix

Reference material for the education organization sync scripts: the CSV format, the supported organization types, what the import writes, the full .env variable list, and troubleshooting. Every script also documents its parameters in comment-based help (Get-Help ./export-edorgs.ps1 -Full).

The CSV format

export-edorgs.ps1 writes (and import-edorgs.ps1 reads) a UTF-8 CSV with a header row and these columns:

ColumnNotes
educationOrganizationIdThe ODS education organization id. Required, unique
nameOfInstitutionRequired
shortNameOfInstitutionOptional
discriminatorThe organization type, exactly as the ODS stores it (for example edfi.School) — see the supported types below
parentEducationOrganizationIdOptional; the parent organization's id

The file can be reviewed, trimmed, or even hand-authored between the export and the import — remove rows to exclude organizations, or fix a parent id. A parentEducationOrganizationId that matches no other row imports as a root (the import notes this), unless the parent already exists in the Admin App under the same tenant/ODS, in which case the child is linked to it. The import validates hand-edited files before touching the database: duplicate ids, a row listing itself as its parent, and parent cycles are all rejected with the offending ids named.

Supported organization types

The discriminator value must be one of the types the Admin App models; rows with any other type (for example edfi.CommunityOrganization, edfi.CommunityProvider) are skipped with a warning:

DiscriminatorShown in the Admin App as
edfi.StateEducationAgencySEA
edfi.EducationServiceCenterESC
edfi.LocalEducationAgencyLEA
edfi.SchoolSchool
edfi.EducationOrganizationNetworkNetwork
edfi.PostSecondaryInstitutionUni
edfi.OrganizationDepartmentOrg-Dept
edfi.OtherOther

The export derives each row's parent from the ODS subtype tables:

TypeParent
SchoolIts local education agency
Local education agencyIts parent agency, else its education service center, else its state education agency
Education service centerIts state education agency
Organization departmentIts parent education organization
All other typesNone (root)

What the import writes

The import mimics what the Admin App's own synchronization produces, so the imported rows are indistinguishable from natively synced ones:

  • One edorg row per CSV row — id, name, short name, and type — stamped with the tenant, environment, and ODS registration it was attached to
  • The parentId link for each row whose parent exists in the same scope (only rows without a parent are ever updated, so links written by the Admin App itself are never overwritten)
  • The edorg_closure ancestor/self pairs that back the Admin App's tree queries, computed for the whole scope — which also heals a partially synced scope rather than corrupting it

Rows that already exist under the tenant/ODS (same educationOrganizationId) keep their row and their parentId link, but their name, short name, and type are corrected to the CSV values when they differ — the same three columns the Admin App's own synchronization maintains. This is what fixes the Institution #<id> / edfi.Other placeholder rows the Admin App writes when an ODS is registered with allowed education organization ids. Re-runs are no-ops, and the whole load is a single transaction.

The import also records the ids it actually inserted (never the pre-existing ones) in an imported-ids.csv manifest next to the CSV, keyed by tenant/ODS scope and merged across runs. cleanup-edorgs.ps1 deletes only ids recorded there, and consumes the scope's entries on success — keep the manifest for as long as you may want to undo the import.

Environment variable reference

Passwords (ODS_DB_PASSWORD, ODS_POSTGRES_PASSWORD, ADMIN_APP_DB_PASSWORD, POSTGRES_APP_PASSWORD) may be left empty: run.ps1 and cleanup-edorgs.ps1 prompt for the ones they need, with the input masked. Set them in the file only for unattended runs.

Source ODS (read-only):

VariableDefaultPurpose
ODS_DB_ENGINEmssqlmssql or pgsql
ODS_DATABASE_NAMEThe ODS to export from. Required
ODS_SQL_SERVERtcp:localhost,1433SQL Server hosting the ODS
ODS_DB_USERNAME / ODS_DB_PASSWORDsa / —SQL Server login; the password is prompted when empty
ODS_USE_INTEGRATED_SECURITYfalsetrue = Windows authentication (no password needed)
ODS_POSTGRES_HOST / ODS_POSTGRES_PORTlocalhost / 5432PostgreSQL host/port
ODS_POSTGRES_USER / ODS_POSTGRES_PASSWORDpostgres / —PostgreSQL login; the password is prompted when empty
ODS_USE_POSTGRES_DOCKERfalsetrue = run psql inside the ODS stack's database container
ODS_POSTGRES_CONTAINERed-fi-db-odsThat container's name

Target Admin App database:

VariableDefaultPurpose
DB_ENGINEmssqlmssql or pgsql
DATABASE_NAMEsbaaThe Admin App application database
SQL_SERVERtcp:localhost,1433SQL Server hosting it
ADMIN_APP_DB_USER / ADMIN_APP_DB_PASSWORDedfi_adminapp / —SQL Server login; the password is prompted when empty
USE_INTEGRATED_SECURITYfalsetrue = Windows authentication
POSTGRES_HOST / POSTGRES_PORTlocalhost / 5432PostgreSQL host/port
POSTGRES_APP_USER / POSTGRES_APP_PASSWORDedfiadminapp / —PostgreSQL login; the password is prompted when empty
USE_POSTGRES_DOCKERfalsetrue = run psql inside the Admin App stack's database container
POSTGRES_CONTAINERedfiadminapp-postgresThat container's name

Scope and files:

VariableDefaultPurpose
TENANT_NAMEdefaultThe Admin App tenant to import into
ENVIRONMENT_NAMEOnly needed when the same tenant name exists in more than one environment
ODS_DB_NAMEODS_DATABASE_NAMEWhich registered ODS (the Admin App registration's database name) to attach to; only needed when the registration's name differs or the tenant has several
CSV_PATH./edorgs.csvWhere the export writes and the import reads. The import records the ids it inserts in imported-ids.csv next to this file, and the cleanup deletes from that manifest

Troubleshooting

SymptomCause / fix
No registered ODS found for tenant ...The Admin App database has no tenant or no registered ODS to attach to. Register them first through the Admin App UI, then re-run. If the registration's database name differs from the real ODS name, set ODS_DB_NAME to the registered name
Tenant ... matches more than one scopeThe tenant has several registered ODS databases (or exists in several environments). Set ODS_DB_NAME (and ENVIRONMENT_NAME if needed) to pick one
educationOrganizationId value(s) exceed the SQL Server ... int rangeThe Admin App's SQL Server schema stores 32-bit ids. Remove the offending rows from the CSV, or use a PostgreSQL Admin App database
skipping N row(s) of type ...Those ODS organization types are not modeled by the Admin App and cannot be imported
Row ... lists itself as its parent / Parent cycle in the CSV ...The (hand-edited) CSV contains a self-parent or a parent loop; fix the parentEducationOrganizationId values named in the message. Nothing was written
An organization shows as Institution #<id> with type OtherThat is the placeholder the Admin App writes when an ODS is registered with allowed organization ids. Running the import corrects its name, short name, and type
Imported organizations missing from a team's dropdownGlobal admins see everything; other teams need ownership of the tenant, environment, ODS, or the individual organizations
Cleanup says Nothing to do: no import manifest at ...The import inserted nothing (or the scope was already cleaned up), so there is nothing recorded to delete. To delete ids from an arbitrary CSV instead, pass -CsvPath explicitly — that mode deletes every listed id, including rows the Admin App created itself
Cleanup warns team access was removed for N grant(s)A team had been granted ownership of an imported organization; the grant must be removed for the delete to succeed, and the cleanup does so in the same transaction, naming each team/organization pair. Grant the access again after re-importing
Organizations imported before the ODS registration was fixedRun cleanup-edorgs.ps1 (it deletes what the import recorded in imported-ids.csv), correct .env, and re-import