Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changes/unreleased/added-20251113-131349.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: added
body: Enhanced `set` command for items to support any settable property path within the item's definition and metadata structure.
time: 2025-11-13T13:13:49.062462673Z
68 changes: 62 additions & 6 deletions docs/commands/fs/set.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# `set` Command

Set a workspace or item property.
Set a resource property.

**Supported Resource Types:**

- Workspace
- Item
- Capacity
- Domain
- Connection
- Gateway
- Spark Pool
- Folder
- Shortcut

!!! info "Setting domain properties requires tenant-level Fabric Administrator privileges"

Expand All @@ -9,18 +21,62 @@ Set a workspace or item property.
**Usage:**

```
fab set <path> -q <jmespath_query> -i <input_to_replace> [-f]
fab set <path> -q <jmespath_query> -i <input_value> [-f]
```

**Parameters:**

- `<path>`: Path to the resource.
- `-q, --query <jmespath_query>`: JMESPath query.
- `-i, --input <input_to_replace>`: Input value to replace.
- `-q, --query <jmespath_query>`: JMESPath query to the property.
- `-i, --input <input_value>`: Input value to set.

!!! tip "Working with JSON input"
For guidance on providing JSON input values across different shells, see [JSON Input Handling](../../essentials/parameters.md#json-input-handling).

- `-f, --force`: Force set without confirmation. Optional.

**Example:**

```bash
fab set ws1.Workspace -q displayName -i "New Name" -f
```
fab set ws1.Workspace/nb1.Notebook -q .property -i value
```

## Limitations

- Only one property path can be specified per `--query` argument
- Paths must map directly to JSON paths **without** filters or wildcards
- If the property path doesn't exist on the item definition, it will be added, provided it's valid according to the item's schema. The `set` command supports creation of 1 level at a time (e.g., to set `a.b.c`, first set `a.b`, then set `a.b.c`)
- Properties that expect a JSON string value are not supported - JSON input is always parsed as an object

## Query Support

The following table shows supported queries per resource type:

| Resource | Supported Queries |
|----------------|-------------------|
| **Item** | `displayName`, `description`, `properties` (`.VariableLibrary` only), `definition`[^1] |
| **Workspace** | `displayName`, `description`, `sparkSettings` |
| **Capacity** | `sku.name` |
| **Domain** | `displayName`, `description`, `contributorsScope` |
| **Connection** | `displayName`, `privacyLevel`, `credentialDetails` |
| **Gateway** | `displayName`, `allowCloudConnectionRefresh`, `allowCustomConnectors`, `capacityId`, `inactivityMinutesBeforeSleep`, `numberOfMemberGateways` |
| **Spark Pool** | `name`, `nodeSize`, `autoScale.enabled`, `autoScale.minNodeCount`, `autoScale.maxNodeCount` |
| **Folder** | `displayName` |
| **Shortcut** | `name`, `target` |

#### Item-Specific Definition Path Aliasing

!!! warning "Note on definition path aliases"

These definition path aliases may be deprecated in a future release. We strongly recommend using explicit JSON paths within the item's [definition structure][^definition-structure] rather than aliases.

These are definition path aliases that map to specific paths in the item's definition structure. When using the `set` command, you can use these aliases directly (as the `-query` / `--q` argument value) as they map to the correct definition paths:

| Item Type | Definition Path Alias | Notes |
|-----------|----------------|-------|
| **Notebook** | `lakehouse`, `environment`, `warehouse` | |
| **Report** | `semanticModelId` | Applies only to [Report definition.pbir version 1](https://learn.microsoft.com/en-us/power-bi/developer/projects/projects-report?tabs=v1%2Cdesktop#definitionpbir). For other versions, check the correct property path in the Report definition documentation |
| **SparkJobDefinition** | `payload` | |

#### Notes
[^1]: For **Items**, you can set any explicit path within the `definition` structure using dot notation for nested properties (e.g., `definition.parts[0].b`). Paths must map directly to JSON paths **without** filters or wildcards. Refer to the [Microsoft Fabric item definitions](https://learn.microsoft.com/en-us/rest/api/fabric/articles/item-management/definitions) for the complete definition structure.
22 changes: 22 additions & 0 deletions docs/essentials/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,25 @@ For a complete list of commands, see the [Commands page](../commands/index.md).
| `--type` | Specify the type for `ln` or `job` |
| `--vorder` | Apply v-order for `table optimize` |
| `--zorder` | Apply Z-order indexing for `table optimize` |


## JSON Input Handling

When providing JSON input in command-line mode, different shells process quotes and escape characters before passing them to the CLI.

**Best Practice:** Use single quotes (`'`) around JSON input. Some shells (like PowerShell) may require escaping inner double quotes with backslashes (`\"`)

=== "PowerShell"
```
fab set item.Resource -q query -i '{\"key\":\"value\"}'
```

=== "Bash"
```bash
fab set item.Resource -q query -i '{"key":"value"}'
```

=== "Zsh"
```
fab set item.Resource -q query -i '{"key":"value"}'
```
42 changes: 29 additions & 13 deletions docs/examples/item_examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ fab ls ws1.Workspace/sem1.SemanticModel

### Update Item

For detailed information on updating item properties, including limitations and property paths, see the [`set` command documentation](../commands/fs/set.md#setting-item-properties).

#### Update Display Name

Change the display name of an item.
Expand Down Expand Up @@ -191,32 +193,46 @@ Delete an item without confirmation prompts.
fab rm ws1.Workspace/lh1.Lakehouse -f
```

### Update Item Properties
### Set Item Properties

**Configurable Properties by Item Type:**
#### Set default lakehouse

- All supported items: `displayName`, `description`
- Notebook: `lakehouse`, `environment`, `warehouse`
- Report: `semanticModelId`
- SparkJobDefinition: `payload`
```
fab set ws1.Workspace/nb1.Notebook -q definition.parts[0].payload.metadata.dependencies.lakehouse -i '{"known_lakehouses": [{"id": "00000000-0000-0000-0000-000000000001"}],"default_lakehouse": "00000000-0000-0000-0000-000000000001", "default_lakehouse_name": "lh1","default_lakehouse_workspace_id": "00000000-0000-0000-0000-000000000000"}'
```

#### Set default lakehouse, environment, or warehouse for a notebook.
#### Set Default Environment for a Notebook

```
# Set default lakehouse
fab set ws1.Workspace/nb1.Notebook -q lakehouse -i '{"known_lakehouses": [{"id": "00000000-0000-0000-0000-000000000001"}],"default_lakehouse": "00000000-0000-0000-0000-000000000001", "default_lakehouse_name": "lh1","default_lakehouse_workspace_id": "00000000-0000-0000-0000-000000000000"}'

# Set default environment
fab set ws1.Workspace/nb1.Notebook -q environment -i '{"environmentId": "00000000-0000-0000-0000-000000000002", "workspaceId": "00000000-0000-0000-0000-000000000000"}'
```

# Set default warehouse
#### Set Default Warehouse for a Notebook

```
fab set ws1.Workspace/nb1.Notebook -q warehouse -i '{"known_warehouses": [{"id": "00000000-0000-0000-0000-000000000003", "type": "Datawarehouse"}], "default_warehouse": "00000000-0000-0000-0000-000000000003"}'
```

#### Rebind Report to Semantic Model

For Report PBIR definition version 1:

```
fab set ws1.Workspace/rep1.Report -q semanticModelId -i "00000000-0000-0000-0000-000000000000"
```

For Report PBIR definition version 2:

```
fab set ws1.Workspace/rep1.Report -q definition.parts[0].payload.datasetReference.byConnection.ConnectionString -i "ConnectionStringPrefix....semanticmodelid=00000000-0000-0000-0000-000000000000"
```

#### Update Notebook Cell Code

Update the default lakehouse in a specific notebook.

```
fab set ws1.Workspace/rep1.Report -q semanticModelId -i "00000000-0000-0000-0000-000000000000
fab set nb1.Notebook -q definition.parts[0].payload.metadata.dependencies.lakehouse.default_lakehouse -i 00000000-0000-0000-0000-000000000001
```


Expand Down
31 changes: 22 additions & 9 deletions src/fabric_cli/client/fab_api_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,11 @@ def delete_item(


def get_item_withdefinition(args: Namespace, item_uri: Optional[bool] = False) -> dict:
"""https://learn.microsoft.com/en-us/rest/api/fabric/core/items/get-item-definition"""
response = get_item(args, item_uri)
item = json.loads(response.text)

args.uri = f"workspaces/{args.ws_id}/items/{args.id}/getDefinition{args.format}"
args.method = "post"
args.wait = True # Wait for the details to be retrieved

try:
def_response = fabric_api.do_request(args)
def_response = get_item_definition(args)
definition = json.loads(def_response.text)
if isinstance(definition, dict):
item.update(definition)
Expand All @@ -77,7 +72,10 @@ def get_item_withdefinition(args: Namespace, item_uri: Optional[bool] = False) -
)
except FabricCLIError as ex:
# Case where user can view the item but not its definitions we will return the item without definitions
if ex.status_code == fab_constant.ERROR_UNAUTHORIZED or ex.status_code == fab_constant.ERROR_FORBIDDEN:
if (
ex.status_code == fab_constant.ERROR_UNAUTHORIZED
or ex.status_code == fab_constant.ERROR_FORBIDDEN
):
return item
else:
raise ex
Expand All @@ -100,9 +98,24 @@ def get_item(
return fabric_api.do_request(args)


def update_item_definition(args: Namespace, payload: str) -> ApiResponse:
def get_item_definition(args: Namespace) -> ApiResponse:
"""https://learn.microsoft.com/en-us/rest/api/fabric/core/items/get-item-definition"""
args.uri = f"workspaces/{args.ws_id}/items/{args.id}/getDefinition{args.format}"
args.method = "post"
args.wait = True

return fabric_api.do_request(args)


def update_item_definition(
args: Namespace, payload: str, item_uri: Optional[bool] = False
) -> ApiResponse:
"""https://learn.microsoft.com/en-us/rest/api/fabric/core/items/update-item-definition"""
args.uri = f"workspaces/{args.ws_id}/items/{args.id}/updateDefinition"
if item_uri:
args.uri = f"workspaces/{args.ws_id}/{args.item_uri}/{args.id}/updateDefinition"
else:
args.uri = f"workspaces/{args.ws_id}/items/{args.id}/updateDefinition"

args.method = "post"

return fabric_api.do_request(args, data=payload)
Expand Down
95 changes: 61 additions & 34 deletions src/fabric_cli/commands/fs/set/fab_fs_set_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
from argparse import Namespace

from fabric_cli.client import fab_api_item as item_api
from fabric_cli.commands.fs.get import fab_fs_get_item as get_item
from fabric_cli.core import fab_constant
from fabric_cli.core.fab_commands import Command
from fabric_cli.core.fab_exceptions import FabricCLIError
from fabric_cli.core.fab_types import definition_format_mapping, format_mapping
from fabric_cli.core.hiearchy.fab_hiearchy import Item
from fabric_cli.errors.common import CommonErrors
from fabric_cli.utils import fab_cmd_set_utils as utils_set
from fabric_cli.utils import fab_mem_store as utils_mem_store
from fabric_cli.utils import fab_ui as utils_ui
Expand All @@ -17,48 +20,72 @@ def exec(item: Item, args: Namespace) -> None:
force = args.force
query = args.query

utils_set.validate_expression(query, item.get_mutable_properties())
query_value = item.extract_friendly_name_path_or_default(query)

# Get item
args.output = None
args.deep_traversal = True
item_def = get_item.exec(item, args, verbose=False, decode=False)
utils_set.validate_item_query(query_value, item)

utils_set.print_set_warning()
if force or utils_ui.prompt_confirm():
args.output = None
args.deep_traversal = True
args.ws_id = item.workspace.id
args.id = item.id
args.item_uri = format_mapping.get(item.item_type, "items")

query_value = item.get_property_value(query)
if query_value.startswith(fab_constant.ITEM_QUERY_DEFINITION):
args.format = definition_format_mapping.get(item.item_type, "")
def_response = item_api.get_item_definition(args)
definition = json.loads(def_response.text)

# Update item
json_payload, updated_def = utils_set.update_fabric_element(
item_def, query_value, args.input, decode_encode=True
)
updated_def = _update_element(
definition, query_value, args.input, decode_encode=True
)

definition_base64_to_update, name_description_properties = (
utils_set.extract_json_schema(updated_def)
)
definition_base64_to_update, _ = utils_set.extract_json_schema(updated_def)
update_item_definition_payload = json.dumps(definition_base64_to_update)

args.ws_id = item.workspace.id
args.id = item.id
update_item_definition_payload = json.dumps(definition_base64_to_update)
update_item_payload = json.dumps(name_description_properties)

utils_ui.print_grey(f"Setting new property for '{item.name}'...")
item_api.update_item(args, update_item_payload)

try:
if query_value.startswith("definition") and item.check_command_support(
Command.FS_EXPORT
):
item_api.update_item_definition(args, update_item_definition_payload)
except Exception:
utils_ui.print_grey(
"Item supports only updating displayName or description, not definition",
utils_ui.print_grey(f"Setting new property for '{item.name}'...")
item_api.update_item_definition(args, update_item_definition_payload)
else:
item_metadata = json.loads(item_api.get_item(args, item_uri=True).text)

updated_metadata = _update_element(
item_metadata, query_value, args.input, decode_encode=False
)

# Update mem_store
new_item_name = name_description_properties["displayName"]
item._name = new_item_name
utils_mem_store.upsert_item_to_cache(item)
update_payload_dict = utils_set.extract_updated_properties(
updated_metadata, query_value
)
item_update_payload = json.dumps(update_payload_dict)

utils_ui.print_grey(f"Setting new property for '{item.name}'...")

item_api.update_item(args, item_update_payload, item_uri=True)

if fab_constant.ITEM_QUERY_DISPLAY_NAME in update_payload_dict:
new_item_name = updated_metadata[fab_constant.ITEM_QUERY_DISPLAY_NAME]
item._name = new_item_name
utils_mem_store.upsert_item_to_cache(item)

utils_ui.print_output_format(args, message="Item updated")


def _update_element(
resource_def: dict,
query_value: str,
input_value: str,
decode_encode: bool,
) -> dict:
try:
_, updated_def = utils_set.update_fabric_element(
resource_def,
query_value,
input_value,
decode_encode=decode_encode,
)
return updated_def
except (ValueError, KeyError, IndexError):
raise FabricCLIError(
CommonErrors.invalid_set_item_query(query_value),
fab_constant.ERROR_INVALID_QUERY,
)
14 changes: 14 additions & 0 deletions src/fabric_cli/core/fab_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
ERROR_INVALID_PROPERTY = "InvalidProperty"
ERROR_INVALID_DETLA_TABLE = "InvalidDeltaTable"
ERROR_INVALID_WORKSPACE_TYPE = "InvalidWorkspaceType"
ERROR_INVALID_QUERY = "InvalidQuery"
ERROR_INTERNAL_SERVER_ERROR = "InternalServerError"
ERROR_UNSUPPORTED_ITEM_TYPE = "UnsupportedItemType"
ERROR_UNSUPPORTED_COMMAND = "UnsupportedCommand"
Expand Down Expand Up @@ -316,3 +317,16 @@
"workspaceId",
"folderId",
}

# Item set constants
ITEM_QUERY_DEFINITION = "definition"
ITEM_QUERY_PROPERTIES = "properties"
ITEM_QUERY_DISPLAY_NAME = "displayName"
ITEM_QUERY_DESCRIPTION = "description"

# Allowed metadata keys for item set operations
ITEM_SET_ALLOWED_METADATA_KEYS = [
ITEM_QUERY_DISPLAY_NAME,
ITEM_QUERY_DESCRIPTION,
ITEM_QUERY_PROPERTIES,
]
Loading