# Using Fabric CLI in Fabric Notebook

First I would like to thank [Edgar Cotte](https://www.linkedin.com/in/edgarcotte/) (Sr PM Fabric CAT) for the inspiration for this blog. Edgar shared this recently in a workshop so I got curious and explored more. Fabric CLI, as the name suggests, allows you to interact with the Fabric environment using command line interface. Whether you are a Fabric admin or a developer, it’s essential for exploration and automation from a convenient interface. But if you work with external clients like I do where there may be restrictions on installing libraries, using Fabric CLI in Fabric notebook may be an easier option especially if you are already familiar with all the commands.

To learn more about Fabric CLI, refer to:

* [Fabric command line interface - Microsoft Fabric REST APIs | Microsoft Learn](https://learn.microsoft.com/en-us/rest/api/fabric/articles/fabric-command-line-interface)
    
* [Microsoft Fabric CLI | fabric-cli](https://microsoft.github.io/fabric-cli/)
    
* [Microsoft Fabric CLI: Turbo-charge Ops with Retro Command-Line Power](https://www.youtube.com/watch?v=y2rWzSFStZ8)
    
* [What's Coming in Fabric Automation and CI/CD | BRK205](https://www.youtube.com/watch?v=-3OjBT6f_Yw)
    

%[https://www.youtube.com/watch?v=y2rWzSFStZ8] 

## Installation:

In Fabric python notebook, first install fabric cli :

```python
%pip install ms-fabric-cli --q
```

## Set up Auth Token:

This will use the identity of the user executing the notebook. You can also use service principal authentication as [shown here](https://github.com/pawarbi/snippets/blob/main/fabcli-sp_token.py).

```python
token = notebookutils.credentials.getToken('pbi')
os.environ['FAB_TOKEN'] = token
os.environ['FAB_TOKEN_ONELAKE'] = token
```

## Commands:

There are two types of commands - command line and interactive. In the notebook, you should be able to run all commands supported in command line mode. The documentation does a great job of describing if a command can be used in command line, interactive or both.

There are two ways you can execute the supported commands - using magic or using a Python wrapper for the commands.

### Using cell magic

Once installed and auth setup, use `!fab` magic to run the shell command. Below I get a list of all the workspaces along with the capacity attached and capacity id.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749152405199/3c57aa85-835e-4857-b580-5cb81ffc7faf.png align="center")

To get a list of all items in a workspace:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749152587886/5a8029bf-e566-4e8c-9430-80a45d9dd3cd.png align="center")

This works even if you have spaces in item/workspace names:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749152761203/d713a13f-3ce2-4f0e-8f3e-e06812da53e3.png align="center")

You can use [JMESPath](https://jmespath.org/) in the query or you can also use shell commands like below to filter the lines that contain word “Trial” and print the first column (i.e. workspace names)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749153667441/70802d78-4270-4b45-b6c9-7c724b2ae0dc.png align="center")

If a command is not available, you can call an API inline as well:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749153926222/e997c6f1-2a02-4086-8603-c6ab87dcc6b8.png align="center")

To download items to an attached default lakehouse:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749154451855/134d485b-4803-48d9-ac00-53c5171974c0.png align="center")

### Multi-line

`!fab` allows only one line command. To create more complex multi line commands, you can use `%%sh` cell magic like below:

```powershell
%%sh
echo "=== Exploring Reid Workspace ==="

# jump to workspace
fab -c "cd Reid.Workspace" || echo "Failed to navigate"

echo "Listing all items:"
fab -c "ls Reid.Workspace -l" || echo "No items or access denied"

echo ""
echo "Notebooks only:"
fab -c "ls Reid.Workspace" | grep "\.Notebook" || echo "No notebooks found"

echo ""
echo "Reports only:"
fab -c "ls Reid.Workspace" | grep "\.Report" || echo "No reports found"

echo "Exploration completed"
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749171066823/c990a193-3ad1-44e9-9aa7-d9e055a951e2.png align="center")

You can also pass Python variables to commands, making it very dynamic:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749171699172/4642f5e8-5d38-4d5d-bc59-4026d6110231.png align="center")

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">It would be great if <code>fabric-cli</code> library is installed in the default Python runtime with the token set up so users can use <code>!fab</code> easily. That would be super handy.</div>
</div>

## Using Python

Above method is great for interactive exploration. For automation, you can create Python functions and use it like any other Python function. For example, I downloaded one notebook but for more complex patterns/automation, I can use `subprocess.run()` to execute the commands.

To download all notebooks from a workspace to a lakehouse location:

```python
def download_item(workspace, item_name, local_path="/lakehouse/default/Files/tmp", force=True):
    """
    Download a Fabric item to local dir
    """
    try:

        os.makedirs(local_path, exist_ok=True)
        cmd = f"export {workspace}.Workspace/{item_name} -o {local_path}"
        if force:
            cmd += " -f"
        result = subprocess.run(["fab", "-c", cmd], capture_output=True, text=True)
        
        if result.returncode == 0:
            print(f"Downloaded {item_name} to {local_path}")
            return True
        else:
            print(f"Failed: {result.stderr}")
            return False
           
    except Exception as e:
        print(f"Error: {e}")
        return False
```

For loop:

```python
def download_all_notebooks(workspace, local_path="/lakehouse/default/Files/tmp"):
    """Download all notebooks from workspace"""
    try:

        result = subprocess.run(["fab", "-c", f"ls {workspace}.Workspace"], capture_output=True, text=True)
        notebooks = [line.strip() for line in result.stdout.split('\n') if '.Notebook' in line]

        success_count = 0
        for notebook in notebooks:
            if download_item(workspace, notebook, local_path):
                success_count += 1
        
        print(f"Downloaded {success_count}/{len(notebooks)} notebooks")
        return success_count
        
    except Exception as e:
        print(f"Error downloading: {e}")
        return 0
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749155367727/adf4e4dd-6666-46de-98f7-7373ce4d12e1.png align="center")

You can generalize this to create re-usable functions, e.g. below lists all workspaces:

```python
import subprocess
import json
from typing import Optional, Dict, Any

def exec_fabcli(command: str, capture_output: bool = False, silently_continue: bool = False) -> Optional[str]:
    """
    Run a Fabric CLI command from within a notebook.
    
    Args:
        command: The fab command to run (without 'fab' prefix)
        capture_output: Whether to capture and return the output
        silently_continue: Whether to suppress exceptions on errors
    
    Returns:
        Command output if capture_output=True, otherwise None
    """
    try:
        result = subprocess.run(["fab", "-c", command], capture_output=True, text=True, timeout=60)
        
        if not silently_continue and result.returncode != 0:
            error_msg = f"Command failed with exit code {result.returncode}\n"
            error_msg += f"STDOUT: {result.stdout}\n"
            error_msg += f"STDERR: {result.stderr}"
            raise Exception(error_msg)
        
        if capture_output:
            return result.stdout.strip()
        else:
            # output
            if result.stdout:
                print(result.stdout)
            if result.stderr:
                print(f"Warning: {result.stderr}")
                
    except subprocess.TimeoutExpired:
        raise Exception("Command timed out after 60 seconds")
    except FileNotFoundError:
        raise Exception("Fabric CLI not found. Make sure 'fab-cli' is installed")

def list_workspaces(detailed: bool = False, show_hidden: bool = False) -> str:
    """List all workspaces"""
    flags = ""
    if detailed:
        flags += " -l"
    if show_hidden:
        flags += " -a"
    return exec_fabcli(f"ls{flags}", capture_output=True)

print(list_workspaces(detailed=True))
```

### Semantic Link/Labs vs Fabric CLI

Semantic Link and Semantic Link Labs also provide similar capabilities but if you are already using CLI, this is a convenient way to reuse what you already know. This will especially be helpful for automating workflows. Different tools, different use cases. Fabric CLI can be usedf or CI/CD with Github and ADO automation. [Jacob Knightley](https://www.linkedin.com/in/jacobknightley) shared this excellent comparison at FabCon :

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749247421596/fad7661e-dc9f-4e28-bbd0-8161373b77cc.png align="center")

Here are additional resource to learn more:

* [RuiRomano/fabric-cli-powerbi-cicd-sample](https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FRuiRomano%2Ffabric-cli-powerbi-cicd-sample&data=05%7C02%7Csandeeppawar%40hitachisolutions.com%7C121210cd6da247bd9cca08dda4d25902%7Ce85feadf11e747bba16043b98dcc96f1%7C0%7C0%7C638847945593115950%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=GcXj%2BJPRH6NcUdw4FYIC3Y7o8EFWtMkgzqogULaHq7s%3D&reserved=0)
    
* Fabric CLI to deploy FUAM : [fabric-toolbox/monitoring/fabric-unified-admin-monitoring/scripts/Deploy\_FUAM.ipynb at main · microsoft/fabric-toolbox](https://github.com/microsoft/fabric-toolbox/blob/main/monitoring/fabric-unified-admin-monitoring/scripts/Deploy_FUAM.ipynb)
    
* Demos by [Aitor Murguzur](https://murggu.com/): [murggu/fab-demos](https://github.com/murggu/fab-demos)
    
* Multi-tenant scenario: [alisonpezzott/pbi-ci-cd-isv-multi-tenant: CI/CD scenario Multi Tenant for Microsoft Power BI PRO projects by utilizing fabric-cli and GitHub Actions](https://github.com/alisonpezzott/pbi-ci-cd-isv-multi-tenant)
    
* [ecotte/Fabric-Monitoring-RTI](https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fecotte%2FFabric-Monitoring-RTI&data=05%7C02%7Csandeeppawar%40hitachisolutions.com%7C121210cd6da247bd9cca08dda4d25902%7Ce85feadf11e747bba16043b98dcc96f1%7C0%7C0%7C638847945593164781%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=9fHIIP9qnTvuOnky5IVZlWj0NX5D5jz8PUwbMpQDp1Q%3D&reserved=0) by Edgar Cotte
    

## Experiment : `fabgpt`

I can’t complete the blog without mentioning AI, can I ? :D I love how Fabric CLI allows you to query, automate so easily. I was wondering what if all of that could be achieved with natural language? In the below example, I used Open AI + notebook cell magic to wrap above Python functions in a cell magic which I am calling `fabgpt` :D It generates above Python code and executes it.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749247691995/3e26df1c-ffcc-4b63-a311-ad4743e56368.png align="center")
