Public/Deploy-SemanticModelProject.ps1

Function Deploy-SemanticModelProject {
    <#
    .SYNOPSIS
        Imports items using the Power BI Project format (PBIP) into a Fabric workspace from a specified file system source.
 
    .DESCRIPTION
        Deploy-SemanticModelProject function uploads a semantic model project from a local file system location to a specified Fabric workspace.
        It supports overwriting existing items and customizing item properties during deployment.
 
    .PARAMETER path
        The file system path where the semantic model project is located.
 
    .PARAMETER workspaceId
        The ID of the Fabric workspace where the semantic model should be deployed.
 
    .PARAMETER itemProperties
        Optional. A hashtable that lets you override item properties like type and displayName.
        Example: @{"type" = "SemanticModel"; "displayName"="My Semantic Model"}
 
    .PARAMETER overWrite
        Optional. A switch parameter that, when specified, allows overwriting existing items with the same name in the workspace.
 
    .EXAMPLE
        Deploy-SemanticModelProject -path "C:\Projects\MySemanticModel" -workspaceId "12345678-abcd-1234-abcd-1234567890ab"
 
    .EXAMPLE
        Deploy-SemanticModelProject -path "C:\Projects\MySemanticModel" -workspaceId "12345678-abcd-1234-abcd-1234567890ab" -itemProperties @{"displayName"="Sales Analysis Model"} -overWrite
 
    .NOTES
        Requires appropriate permissions to the target workspace.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [string]$path
        ,
        [Parameter(Mandatory)]
        [string]$workspaceId
        ,
        [hashtable]$itemProperties
        ,
        [switch]$overWrite
    )

    # Search for folders with .pbir and .pbism in it
    $itemsInFolder = Get-ChildItem -LiteralPath $path -Depth 1 | Where-Object { @(".pbism") -contains $_.Extension }

    if ($itemsInFolder.Count -eq 0) {
        Write-Log "Cannot find valid item definitions ( *.pbism) in the '$path'"
        return
    }    

    if ($itemsInFolder | ? { $_.Extension -ieq ".pbism" }) {
        $itemType = "SemanticModel"
    }
    else {
        throw "Cannot determine the itemType for the path '$path'. Ensure the folder contains a .pbism file."
    }
    
    # Get existing semantic models of the workspace
    $items = Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/semanticModels" -Method Get

    $files = Get-ChildItem -LiteralPath $path -Recurse -Attributes !Directory

    # Filter out files not required for the API: item.*.json; cache.abf; .pbi folder
    $files = $files | ? { $_.Name -notlike "item.*.json" -and $_.Name -notlike "*.abf" -and $_.Directory.Name -notlike ".pbi" }        

    # Prioritizes reading the displayName and type from itemProperties parameter
    $displayName = $null
    if ($itemProperties -ne $null) {            
        $displayName = $itemProperties.displayName         
    }

    # Try to read the item properties from the .platform file if not found in itemProperties

    if ((!$itemType -or !$displayName) -and (Test-Path -LiteralPath "$path\.platform")) {          
        $itemMetadataStr = Get-Content -LiteralPath "$path\.platform"
        try {
            $itemMetadata = $itemMetadataStr | ConvertFrom-Json
        }
        catch {
            throw "Failed to parse JSON from .platform file: $_"
        }
        $itemType = $itemMetadata.metadata.type
        $displayName = $itemMetadata.metadata.displayName
    }

    if (!$itemType -or !$displayName) {
        throw "Cannot import item if any of the following properties is missing: itemType, displayName"
    }
    $itemPathAbs = Resolve-Path -LiteralPath $path

    $parts = $files |% {

        $filePath = $_.FullName
        
        $fileContent = Get-Content -LiteralPath $filePath -AsByteStream -Raw
        
        
        $partPath = if ([System.Environment]::OSVersion.Platform -eq 'Unix') {
            $filePath.Replace($itemPathAbs, "").TrimStart("/").Replace("\", "/")
        } else {
            $filePath.Replace($itemPathAbs, "").TrimStart("\").Replace("\", "/")
        }

        $fileEncodedContent = ($fileContent) ? [Convert]::ToBase64String($fileContent) : ""
        
        Write-Output @{
            Path        = $partPath
            Payload     = $fileEncodedContent
            PayloadType = "InlineBase64"
        }
    } 

    $itemId = $null

    # Check if there is already an item with same displayName and type
    
    $foundItem = $items | ? { $_.type -ieq $itemType -and $_.displayName -ieq $displayName }
    
    # If there is more than one item with the same displayName and type, throw an error
    if ($foundItem) {
        if ($foundItem.Count -gt 1) {
            throw "Found more than one item for displayName '$displayName'"
        }
        $itemId = $foundItem.id
    }
    # Prepare the request
    $itemRequest = @{ 
            
        displayName = $displayName 
        definition  = @{
            Parts = $parts
        }
    } | ConvertTo-Json -Depth 3        

    # Create the item
    if ($itemId -eq $null) {
        Write-Log "Creating a new item"
                   
        $createItemResult = Invoke-FabricAPIRequest -uri "workspaces/$workspaceId/semanticModels"  -method Post -body $itemRequest

        $itemId = $createItemResult.id

        Write-Log "Created a new item with ID '$itemId'" -ForegroundColor Green

        Write-Output @{
            "id"          = $itemId
            "displayName" = $displayName
            "type"        = $itemType 
        }
    }else  # Update the item definition
    {
        if (!$overWrite) {
            Write-Log "Item '$displayName' of type '$itemType' already exists. Skipping." -ForegroundColor Yellow
        }
        else {
            Write-Log "Updating item definition"
  
            Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/semanticModels/$itemId/updateDefinition" -Method Post -Body $itemRequest

            Write-Log "Updated item with ID '$itemId'" -ForegroundColor Green
        }

        Write-Output @{
            "id"          = $itemId
            "displayName" = $displayName
            "type"        = $itemType
        }
    }
}