ImportTechnicalAccountsFromCSVExecutor
Long Running Task (LRT) that bulk-creates or updates Technical Accounts in CzechIdM by reading a semicolon-separated CSV file. Each CSV row represents one technical account. The task maps configurable column names to entity fields, validates the whole row before it writes anything, supports dry-run mode, and never re-points an existing account to a different provisioning system.
Version
| Version | Compatible with product | Notes |
|---|---|---|
| 15.3.0 | 15.15.1 | First implementation of the task. |
How It Works
- The task reads the attached CSV file line by line (first line = header).
- For each data row it looks up an existing Technical Account by the value in the identifier column (matched against
code) scoped to the system named insystemcolumn. The samecodemay legally exist on different systems - the lookup returns only the account linked to the row's system, if any. - Depending on whether the account exists and which behaviour flags are set, the account is created, updated or skipped.
- Before anything is written, the whole row is validated in one gate. Every problem found in the row is reported together and the row is aborted without a partial write.
- Optional columns assign identity and role guarantors, link the account to a provisioning system and associate it with a Technical Asset.
- All item-level results are written to the LRT task log.
CSV Format
| Property | Value |
|---|---|
| Encoding | UTF-8 |
| Column separator | ; (configurable) |
| First row | Header with column names |
| Multi-values | | by default (configurable) |
| Date format | yyyy-MM-dd (ISO 8601) |
The header check runs on the first data row, so a header-only file is accepted with any column configuration, and it validates column-name parameters only -
multivalueseparator and the behaviour flags are not column names.
Task Parameters
File and Format
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
importFile | Attachment | Yes | - | CSV file to import |
separator | String | No | ; | Column separator character |
encoding | String | No | utf-8 | File encoding |
Column Mapping
Each of the column parameters below is the exact name of a column in the CSV header (case-sensitive). multivalueseparator is the exception - it is a value, not a column name.
| Parameter | Description |
|---|---|
identifiercolumn | Required. Unique identifier of the account; stored as code and used as the uid of the AccAccount created for a new account. Used to look up existing accounts. |
namecolumn | Technical account name (maps to externalCode - the Name field in the UI). Written unconditionally when the parameter is configured, so a blank cell clears the existing name on update. Leaving the parameter unconfigured keeps the current value. |
descriptioncolumn | Free-text description. Same blank-cell rule as namecolumn - a blank cell clears the value. |
validfromcolumn | Validity start date (yyyy-MM-dd). A blank cell leaves the field unchanged. |
validtillcolumn | Validity end date (yyyy-MM-dd). A blank cell leaves the field unchanged. |
systemcolumn | Name (codeable identifier) of the provisioning system. Required when creating new accounts; for existing accounts it is a consistency check, see below. |
mappingcolumn | Provisioning mapping name. Must be a TECHNICAL_ACCOUNT PROVISIONING mapping on the system. |
directidentitycolumn | Identity guarantors - list of usernames separated by the multi-value separator. |
guaranteerolecolumn | Role guarantors - list of role codes separated by the multi-value separator. |
technicalassetcolumn | Code of the Technical Asset to associate with the account. |
tiercolumn | Tier written to the AccAccount linked to the account, for both new and existing accounts. A blank cell or an unconfigured column keeps the current value; it never clears it. |
zonecolumn | Zone written to the AccAccount linked to the account, for both new and existing accounts. Same blank-cell rule as tiercolumn. |
disabledcolumn | Disabled status of the account. `1`, `true` and `yes` mean disabled, `0`, `false` and `no` mean enabled; matching is case-insensitive and the cell is trimmed. A blank cell leaves the value unchanged. Any other value fails the whole row. |
multivalueseparator | Character separating values in multi-value columns. Default |. |
Behaviour Flags
| Parameter | Type | Default | Description |
|---|---|---|---|
cancreate | Boolean | false | Allow creating new Technical Accounts. When false, rows whose identifier is not found are logged as skipped. |
canoverwrite | Boolean | false | Allow updating existing accounts. When false, an existing account is skipped entirely - no field, no guarantor and no AccAccount change. When true, every configured column of the row is applied: externalCode, description, validFrom, validTill, disabled, the technical asset (still subject to canchangeta) and the tier and zone of the linked AccAccount; guarantors from the row are added as well. |
canchangeta | Boolean | false | Gates a change of the technical asset on an already existing account. It does not switch the column off - see Technical Asset Assignment. |
Processing Logic per Row
read identifier from identifiercolumn read system name from systemcolumn (if configured) |- find existing TechnicalAccount by code = identifier | (scoped to the system from systemcolumn - see Code and Name Uniqueness Rules) |- NOT FOUND + cancreate = false -> log CSV_IMPORT_ACCOUNT_SKIPPED (EXCEPTION), stop |- FOUND + canoverwrite = false -> log CSV_IMPORT_ACCOUNT_SKIPPED (EXCEPTION), stop validatePreSave - one gate, nothing is written yet |- new account: resolve system and mapping (mandatory), check the code is free on that system |- existing account: check the linked AccAccount against systemcolumn / mappingcolumn |- resolve every identity guarantor and every role guarantor |- resolve the technical asset by code, apply the canchangeta rule |- parse the disabled cell |- run the asset assignment check script for the resulting (asset, account) pair |- any problem -> all problems joined into one CSV_IMPORT_ACCOUNT_ERROR, row aborted write |- existing account: tier / zone written to the linked AccAccount and saved FIRST |- save the technical account (the technical asset is already set on it) |- new account: create the AccAccount (uid = identifier) and the TechnicalAccountAccount link |- add identity and role guarantors
CSV_IMPORT_ACCOUNT_ERROR - the account is neither created nor updated and no guarantor is assigned. All problems found in the row are reported in a single message. The import continues with the following rows.
Multi-Value Columns
directidentitycolumn and guaranteerolecolumn can contain multiple values separated by the multi-value separator (default `|`). Whitespace around each value is trimmed.
Example cell value:
jnovak|pdvorak|mmaly
Each value is resolved independently, but a value that cannot be resolved does not get skipped - it fails the whole row together with every other problem found in it.
System Linking Rules
Every Technical Account is expected to have exactly one AccAccount. The link is maintained via the TechnicalAccountAccount join entity.
Creating a New Account
When cancreate = true and the identifier is not found:
systemcolumnis mandatory. If it is not configured or the system is not found in CzechIdM, the row fails withCSV_IMPORT_ACCOUNT_ERROR.- If
mappingcolumnis configured, the named mapping must exist on the system and must be of type TECHNICAL_ACCOUNT PROVISIONING. Otherwise the row fails. - If
mappingcolumnis not configured, the system must have exactly one TECHNICAL_ACCOUNT PROVISIONING mapping. Zero or more than one also fails the row. - Once system and mapping are validated, a new
AccAccountis created (uid = identifier) and linked viaTechnicalAccountAccount.
| Scenario | Behaviour |
|---|---|
systemcolumn not configured | Error - row fails with CSV_IMPORT_ACCOUNT_ERROR. |
| System name not found in CzechIdM | Error - row fails with CSV_IMPORT_ACCOUNT_ERROR. |
| Mapping specified, not found on system | Error - row fails with CSV_IMPORT_ACCOUNT_ERROR. |
| Mapping specified, found but not TECHNICAL_ACCOUNT type | Error - row fails with CSV_IMPORT_ACCOUNT_ERROR. |
| Mapping not specified, no TECHNICAL_ACCOUNT mapping on system | Error - row fails with CSV_IMPORT_ACCOUNT_ERROR. |
| Mapping not specified, multiple TECHNICAL_ACCOUNT mappings on system | Error - specify the mapping explicitly; row fails with CSV_IMPORT_ACCOUNT_ERROR. |
| System and mapping resolved successfully | New AccAccount (uid = identifier) created; TechnicalAccountAccount link saved. |
Updating an Existing Account
The system, mapping and uid of the linked AccAccount are never changed and no new AccAccount or link is created. The tier and zone of the linked AccAccount are written when the corresponding columns are configured.
The systemcolumn and mappingcolumn values are a hard consistency check: a mismatch fails the row.
| Scenario | Behaviour |
|---|---|
systemcolumn not configured or the cell is blank | System check skipped. |
| System name not found in CzechIdM | The lookup degrades to code-only, so the existing account is still found, and the row then fails on the system mismatch with CSV_IMPORT_ACCOUNT_ERROR. |
| AccAccount is on a different system than specified | Row fails with CSV_IMPORT_ACCOUNT_ERROR; nothing is written. |
mappingcolumn not configured | Mapping check skipped. |
| AccAccount uses a different mapping than specified | Row fails with CSV_IMPORT_ACCOUNT_ERROR; nothing is written. |
| The account has no linked AccAccount (orphan) or the link is dangling | The whole consistency check is skipped, tier and zone are not written and the row succeeds. |
If a Technical Account already exists in CzechIdM, its provisioning links were set up previously, manually or by an earlier import run. Re-pointing the AccAccount to another system or mapping could make CzechIdM's provisioning engine act on a live system. Tier and zone are the deliberate exception - they are attributes of the account, not of its link to a system.
The linked AccAccount (tier, zone) is saved first, the technical account second. The order is deliberate: saving the technical account triggers provisioning, which re-saves the AccAccount in its own event where the skip flag no longer applies, so the account has to carry the target state by then. A failure that the pre-save gate cannot predict - a duplicate
externalCode is the realistic example, because it is validated by the framework at save time - therefore leaves the AccAccount already updated while the technical account is not.
Technical Asset Assignment
Technical assets are looked up by their code (TechnicalAsset.code), not by the Name field. An asset code that cannot be resolved fails the row.
canchangeta does not switch the column off. The column is always read and the asset always resolved; the flag only gates a real change on an already existing account.
| Scenario | Behaviour |
|---|---|
New account, any value of canchangeta | The asset from the CSV is assigned. The flag does not apply - there is no current asset to change. |
| Existing account, cell empty | No-op, the row succeeds and the current asset is kept. |
| Existing account, asset equals the currently assigned one | No-op, the row succeeds. |
Existing account, asset differs, canchangeta = false | Row fails with CSV_IMPORT_ACCOUNT_ERROR; nothing from the row is written. |
Existing account, asset differs, canchangeta = true | The asset is changed. |
| Asset code not found | Row fails with CSV_IMPORT_ACCOUNT_ERROR. |
Assignment Check Script
Whenever a row ends up with a technical asset, the pair (asset, target AccAccount) is validated by a configurable Groovy script through TechnicalAssetService.isAssetAssignable. The script is named by the private configuration property idm.sec.tech.accountAssetAssignment.checkScript.
# Script deciding whether a technical asset may be assigned to an account. # Default: nothing is enforced out of the box. idm.sec.tech.accountAssetAssignment.checkScript=techAccountAssetAssignmentAllowAll
techAccountAssetAssignmentAllowAll- the shipped default, always returnstrue.techAccountAssetAssignmentZoneMatch- requires the asset zone and the AccAccount zone to match; an asset without a zone is always accepted.
The check is evaluated against the effective asset (the one from the CSV, or the one already assigned when the CSV does not change it, so it also runs on rows that do not mention an asset) and the target AccAccount, that is the account as it will look once the row's tier and zone are applied. When the script rejects the pair, the row fails with CSV_IMPORT_ACCOUNT_ERROR before anything is written.
SKIP_ACCOUNT_ASSET_ASSIGNMENT_CHECK). That is why it has to evaluate the script itself up front: the row is written in two steps and the intermediate state between them would otherwise be rejected. The verdict is the same one the processors would give, only it is known before the first write and it holds in dry-run as well.
Code and Name Uniqueness Rules
Code (''identifiercolumn'' -> ''code'')
codeis unique per system. The samecodemay exist on different systems - each combination (code, system) yields its own Technical Account.- The lookup combines
codewith the system named insystemcolumn. An account linked to a different system is invisible to the lookup, so a new account is created for the row's system. - The lookup degrades to code-only in three cases:
systemcolumnis not configured, the cell is blank, or the system named in the cell does not exist in CzechIdM. Uniqueness is then not enforced either - there is no system context to validate against, so "orphan" accounts (those without an AccAccount link) are not constrained oncodeat all and several orphans with the samecodemay coexist. - For a new account the task checks up front that the code is free on the target system and fails the row otherwise. The product-side processors (
TechnicalAccountCodeCheckProcessor,TechnicalAccountAccountCodeCheckProcessor) are the second line of defence; on the import's create path only the latter can still raiseTECHNICAL_ACCOUNT_ALREADY_EXISTS_ON_SYSTEM, when the link is saved.
Name (''namecolumn'' -> ''externalCode'')
externalCode(FE label "Name") must be unique across all Technical Accounts in CzechIdM, regardless of which system the account is linked to.- A blank
namecolumncell (resulting in an emptyexternalCode) skips this validation. - Enforced by the framework in
AbstractReadWriteDtoService.validateEntity. The conflict surfaces asDuplicateExternalCodeExceptionat save time, which the task records on the item asCSV_IMPORT_ACCOUNT_ERROR.
| Scenario | Outcome |
|---|---|
Same code on two different systems in the CSV | OK - two separate Technical Accounts are created, one per system. |
Same code on the same system in the CSV, canoverwrite = false | Second row is skipped - the first row's account is found and not overwritten. |
Same code on the same system in the CSV, canoverwrite = true | Second row updates the first row's account. |
Same name on two rows (any systems) | Second row fails - externalCode is globally unique. |
systemcolumn blank, identifier matches an orphan account | The lookup finds the orphan and proceeds with the update. |
code may appear on several rows targeting different systems and each row creates its own account. Each row must use a globally unique name, or leave
namecolumn blank. Re-running the import with names already present in the database fails those rows as well.
Dry-Run Mode
The task supports CzechIdM's built-in dry-run flag. When dry-run is enabled:
- No Technical Accounts are created or updated.
- No guarantors are assigned.
- No AccAccounts or TechnicalAccountAccount links are created and no tier or zone is written.
- Rows that would be created or updated are logged with
CSV_IMPORT_ACCOUNT_WOULD_BE_CREATEDorCSV_IMPORT_ACCOUNT_WOULD_BE_UPDATEDand stateNOT_EXECUTED. - Skipped and failing rows are logged exactly as in a real run, with state
EXCEPTION- a dry run therefore reports the same failures a real run would hit, including a rejection by the assignment check script. - Would-be operations are written to the application log at INFO level (prefix
[DRY-RUN]). - The task counter still increments, so the row count can be verified.
dryRun = true on the IdmLongRunningTaskDto before calling LongRunningTaskManager.executeSync() in code.
Result Codes
| Code | State | Counted as | Description |
|---|---|---|---|
CSV_IMPORT_ACCOUNT_CREATED | EXECUTED | successItemCount | Technical account was created. |
CSV_IMPORT_ACCOUNT_UPDATED | EXECUTED | successItemCount | Technical account was updated. |
CSV_IMPORT_ACCOUNT_WOULD_BE_CREATED | NOT_EXECUTED | warningItemCount | Dry-run: the account would be created. |
CSV_IMPORT_ACCOUNT_WOULD_BE_UPDATED | NOT_EXECUTED | warningItemCount | Dry-run: the account would be updated. |
CSV_IMPORT_ACCOUNT_SKIPPED | EXCEPTION | failedItemCount | Row skipped (cancreate = false or canoverwrite = false). |
CSV_IMPORT_ACCOUNT_ERROR | EXCEPTION | failedItemCount | Row failed. This is the single code for every row-level failure - unresolved guarantors or asset, an unknown or mismatched system or mapping, a non-boolean disabled value, a would-be asset change with canchangeta = false, a duplicate code, a rejection by the assignment check script, and any exception raised while the row was being written. The original message is carried as the reason. |
EXECUTED and CREATED count as success, EXCEPTION as failed and everything else, including NOT_EXECUTED, as a warning.
Examples
Minimal CSV Example
Only identifier and name columns - no system, guarantors or asset.
identifiercolumn;namecolumn svc-api-gateway;API Gateway svc-db-reader;DB Reader svc-batch-runner;Batch Runner
Task configuration:
| Parameter | Value |
|---|---|
identifiercolumn | identifiercolumn |
namecolumn | namecolumn |
cancreate | true |
canoverwrite | false |
Full CSV Example
identifiercolumn;namecolumn;descriptioncolumn;validfromcolumn;validtillcolumn;systemcolumn;mappingcolumn;directidentitycolumn;guaranteerolecolumn;technicalassetcolumn svc-ldap-reader;LDAP Reader;LDAP read-only service account;2024-01-01;;CMS AD - Users;AD users provisioning mapping tech.;jnovak|pdvorak;role-ldap-admin|role-infra-team;CORE-INFRA svc-db-backup;DB Backup;Database backup service account;2024-01-01;2026-12-31;CMS AD - Users;AD users provisioning mapping tech.;mpolak;role-dba;DATABASES
Task configuration:
| Parameter | Value |
|---|---|
identifiercolumn | identifiercolumn |
namecolumn | namecolumn |
descriptioncolumn | descriptioncolumn |
validfromcolumn | validfromcolumn |
validtillcolumn | validtillcolumn |
systemcolumn | systemcolumn |
mappingcolumn | mappingcolumn |
directidentitycolumn | directidentitycolumn |
guaranteerolecolumn | guaranteerolecolumn |
technicalassetcolumn | technicalassetcolumn |
multivalueseparator | | |
cancreate | true |
canoverwrite | true |
canchangeta | true |
technicalassetcolumn cell holds the asset code, not its name. A blank validtillcolumn cell (like the first row above) leaves the field unchanged - it does not clear an existing validTill date.
See Also
- ImportTechnicalAssetsFromCSVExecutor - the sibling task importing Technical Assets.