How-to: apply
crm apply -f spec.yaml stands up a whole custom table — publisher, solution,
entity, columns, option sets, relationships, and views — from a single
declarative spec. It orchestrates the existing metadata commands in dependency
order (publisher → solution → entities → option sets → attributes →
relationships → views), creating each with if_exists=skip and running
PublishAllXml once at the end. Re-applying an unchanged spec is a no-op.
See the CLI reference for the flags.
Spec schema
The spec is YAML (JSON is also accepted — it is a YAML subset). Every section is optional; provide only what you want to create.
publisher:
unique_name: contosopub # required
friendly_name: Contoso Publisher
prefix: contoso # required — 2-8 alphanumerics, customizationprefix
option_value_prefix: 10000 # required — 10000-99999
solution:
unique_name: ContosoCore # required; created components land here
friendly_name: Contoso Core
version: 1.0.0.0
optionsets: # global (org-level) option sets
- name: contoso_priority # required, must include the publisher prefix
display_name: Priority # required
options:
- {value: 100000000, label: Low}
- {value: 100000001, label: High}
entities:
- schema_name: contoso_Project # required, PascalCase with prefix
display_name: Project # required
display_collection_name: Projects
ownership: UserOwned
primary_attr: {schema_name: contoso_Name, label: Name}
attributes:
- {kind: string, schema_name: contoso_Code, display_name: Code, max_length: 100}
- {kind: picklist, schema_name: contoso_Priority, display_name: Priority, optionset_name: contoso_priority}
- {kind: lookup, schema_name: contoso_Owner, display_name: Owner, target_entity: systemuser}
relationships:
- schema_name: contoso_project_task
referenced_entity: contoso_project # the "1" side
referencing_entity: contoso_task # the "many" side
lookup_schema: contoso_ProjectId
lookup_display: Project
views:
- {name: Active Projects, columns: [contoso_name, contoso_code]}
attributes[].kind is any kind metadata add-attribute accepts (string,
memo, integer, bigint, decimal, double, money, boolean,
datetime, picklist, multiselect, image, file, lookup). A picklist
needs optionset_name (a global set, usually declared under optionsets) or
inline options; a lookup needs target_entity. View columns are entity
logical names; use name:width (or {name, width}) to set a column width
(default 100). Malformed input is rejected up front, before any HTTP call.
Stand up a table in one shot
crm --json apply -f project.yaml
Output is {ok, data:{applied, skipped, planned, failed}, meta:{staged}}. Each
entry is {kind, name}; a failed entry also carries error. A second run
reports everything under skipped.
Preview a greenfield spec
crm --dry-run --json apply -f project.yaml
Dry-run reports everything that would be created under planned and makes no
changes. Dependents of a not-yet-created resource (a column on a new table, a
picklist on a new option set, a solution on a new publisher) are reported
planned too, instead of erroring.
A brand-new table's
ObjectTypeCodeis often not readable until the apply's final publish, so its views land asplanned. Runapplya second time after the first publish to create them.
Stage without publishing
crm --stage-only --json apply -f project.yaml
--stage-only creates every component but skips PublishAllXml; meta.staged
is true. Publish later with crm solution publish.
Partial failure
Metadata POSTs are not transactional. If a step fails, apply aborts the
remaining steps, reports the failure under failed (with the error), exits
non-zero, and does not publish. Whatever was already created is left
staged-but-unpublished (meta.staged is true) — fix the spec and re-apply;
the already-created resources are skipped.