What are Veza Custom Properties?¶
Overview¶
Custom properties allow you to extend OAA templates with additional fields beyond the standard Veza properties. Any field in your source data that doesn’t map to a predefined OAA field can be added as a custom property.
Use the custom_type parameter in field mappings to specify the data type for your custom property:
field_mapping:
department:
destination_field: department
custom_type: STRING # Custom field of type STRING
is_contractor:
destination_field: is_contractor
custom_type: BOOLEAN # Custom field of type BOOLEAN
Available Data Types¶
All custom properties must have one of the following custom_type values:
Type |
Description |
Valid Values |
Example |
|---|---|---|---|
STRING |
Text values |
Any string |
|
BOOLEAN |
True/False values |
|
|
NUMBER |
Numeric values (integer or decimal) |
Any number |
|
STRING_LIST |
Array/list of strings |
JSON array of strings |
|
TIMESTAMP |
Date/time in ISO 8601 format |
ISO 8601 or supported formats (see below) |
|
TIMESTAMP_AD |
Active Directory timestamp (Windows filetime) |
64-bit integer |
|
⚠️ Critical: custom_type is ONLY for Custom Properties¶
custom_type must ONLY be used on fields that are not built-in to the OAA entity type. Applying custom_type to built-in fields causes validation errors.
Built-in LocalUser Fields (do NOT use custom_type)¶
These fields are built-in to LocalUser and should never use custom_type:
Required:
unique_idIdentity:
name,emailStatus:
is_active,user_typeTimestamps:
last_login_at,created_at,deactivated_at,password_last_changed_at
Incorrect:
field_mapping:
email_address:
destination_field: email
custom_type: STRING # ❌ email is built-in; do not add custom_type
username:
destination_field: unique_id
custom_type: STRING # ❌ unique_id is built-in; do not add custom_type
Correct:
field_mapping:
email_address:
destination_field: email # ✓ No custom_type needed (built-in field)
username:
destination_field: unique_id # ✓ No custom_type needed (built-in field)
department_code:
destination_field: department # ✓ Custom field; use custom_type
custom_type: STRING
hire_date:
destination_field: hire_date # ✓ Custom field; use custom_type
custom_type: TIMESTAMP
Refer to the OAA Provider Object reference for a complete list of built-in fields per entity type (LocalGroup, LocalRole, etc.).
Field Type Examples¶
STRING¶
field_mapping:
department:
destination_field: department
custom_type: STRING
office_location:
destination_field: office_location
custom_type: STRING
employee_id:
destination_field: employee_id
custom_type: STRING # Keep as string, not number
BOOLEAN¶
field_mapping:
is_active:
destination_field: is_active
custom_type: BOOLEAN
is_manager:
destination_field: is_manager
custom_type: BOOLEAN
has_mfa:
destination_field: has_mfa
custom_type: BOOLEAN
NUMBER¶
field_mapping:
salary:
destination_field: salary
custom_type: NUMBER
years_of_service:
destination_field: years_of_service
custom_type: NUMBER
performance_rating:
destination_field: performance_rating
custom_type: NUMBER
STRING_LIST¶
field_mapping:
certifications:
destination_field: certifications
custom_type: STRING_LIST
access_groups:
destination_field: access_groups
custom_type: STRING_LIST
assigned_roles:
destination_field: assigned_roles
custom_type: STRING_LIST
TIMESTAMP¶
field_mapping:
hire_date:
destination_field: hire_date
custom_type: TIMESTAMP
last_login:
destination_field: last_login
custom_type: TIMESTAMP
password_last_changed:
destination_field: password_last_changed
custom_type: TIMESTAMP
TIMESTAMP_AD¶
field_mapping:
ad_last_logon:
destination_field: ad_last_logon
custom_type: TIMESTAMP_AD
ad_pwd_last_set:
destination_field: ad_pwd_last_set
custom_type: TIMESTAMP_AD
Timestamp Formats¶
Veza OAA accepts multiple timestamp input formats and converts them to ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ).
Input Formats Accepted¶
Use the date_convert transform to automatically convert various date formats:
field_mapping:
hire_date:
destination_field: hire_date
custom_type: TIMESTAMP
transforms:
- name: date_convert
Supported Timestamp Formats¶
Input Format |
Example Input |
Output (ISO 8601) |
|---|---|---|
MM-DD-YYYY |
|
|
MM/DD/YYYY |
|
|
MM-DD-YY |
|
|
MM/DD/YY |
|
|
YYYY-MM-DD |
|
|
YYYY-MM-DDTHH:MM |
|
|
ISO 8601 |
|
|
Timestamp Examples¶
Converting Multiple Formats¶
sources:
employees:
destination: LocalUser
field_mapping:
user_id:
destination_field: unique_id
# Handles: "01-15-2024", "01/15/2024", etc.
hire_date:
destination_field: hire_date
custom_type: TIMESTAMP
transforms:
- name: date_convert
# Already in ISO format, no conversion needed
last_login:
destination_field: last_login
custom_type: TIMESTAMP
Windows Active Directory Timestamp¶
field_mapping:
ad_last_logon:
destination_field: ad_last_logon
custom_type: TIMESTAMP_AD
# No conversion needed - send as-is (integer value)
# Example: 133456789012345678
Important Considerations¶
Data Type Consistency¶
Ensure your source data values match the custom_type you specify:
# ✅ CORRECT - Value is boolean
field_mapping:
is_active:
destination_field: is_active
custom_type: BOOLEAN
# Data: true, false
# ❌ WRONG - String value for BOOLEAN type
field_mapping:
is_active:
destination_field: is_active
custom_type: BOOLEAN
# Data: "true", "false" (strings, not booleans)
⚠️ Critical: Empty String vs. Null for Timestamp Fields¶
Built-in timestamp fields (last_login_at, created_at, deactivated_at, password_last_changed_at) require valid RFC3339 format or NULL. An empty string is rejected by Veza.
The Problem:
OAA field mapping converts None (NULL) → "" (empty string) for all built-in fields. If your source has NULL values, they become empty strings, which Veza rejects.
Solution: Clean empty strings in PRE_PUSH_OAA hook
from oaa.hooks import hook, OAAHookEvent
@hook(event=OAAHookEvent.PRE_PUSH_OAA)
def clean_empty_timestamps(provider=None, **kwargs):
"""Replace empty strings with None for timestamp fields."""
for user in provider.local_users.values():
for field in ["last_login_at", "created_at",
"deactivated_at", "password_last_changed_at"]:
if getattr(user, field, None) == "":
setattr(user, field, None)
Or skip unmapped fields entirely at the source level:
def fetch(**kwargs):
def clean_record(record):
# Only include last_login_at if it has a valid ISO timestamp
if record.get("last_login") and is_valid_timestamp(record["last_login"]):
record["last_login_at"] = record["last_login"]
else:
record["last_login_at"] = None # Explicitly None, not empty
return record
return lazy_dict_table(
(clean_record(r) for r in users_generator()),
header=["id", "email", "last_login_at", ...]
)
Recommendation: Always validate timestamp fields in PRE_PUSH_OAA before pushing to Veza.
Other NULL/Empty Values¶
Empty strings and null values in custom properties are handled gracefully
Veza will display them appropriately in the UI
Optional custom fields can remain empty
Timestamp Best Practices¶
Always use ISO 8601 format when possible:
YYYY-MM-DDTHH:MM:SSZUse
date_converttransform for non-standard formatsEnsure timezone information is included when available
For AD timestamps, use
TIMESTAMP_ADtype (requires Windows filetime integer format)
Complete Example¶
sources:
users:
destination: LocalUser
field_mapping:
# Standard OAA fields
user_id:
destination_field: unique_id
email:
destination_field: email
name:
destination_field: name
# Custom STRING fields
department:
destination_field: department
custom_type: STRING
office_location:
destination_field: office_location
custom_type: STRING
# Custom BOOLEAN fields
is_contractor:
destination_field: is_contractor
custom_type: BOOLEAN
has_mfa_enabled:
destination_field: has_mfa_enabled
custom_type: BOOLEAN
# Custom NUMBER fields
salary:
destination_field: salary
custom_type: NUMBER
# Custom TIMESTAMP fields (with format conversion)
hire_date:
destination_field: hire_date
custom_type: TIMESTAMP
transforms:
- name: date_convert
# Custom STRING_LIST fields
certifications:
destination_field: certifications
custom_type: STRING_LIST
See Also¶
What is an OAA Config File? - Configuration reference
What is the Modules Folder? - Python module guidance