src/Solutions/Customization/AppModule/Add-XrmAppComponents.ps1

<#
    .SYNOPSIS
    Add multiple components to a model-driven app in a single SDK call.

    .DESCRIPTION
    Add a batch of components (tables, forms, views, dashboards, BPF, sitemap, etc.) to an existing model-driven app using a single AddAppComponents request.
    More efficient than calling Add-XrmAppComponent in a loop because all references are sent in one request.

    .PARAMETER XrmClient
    Xrm connector initialized to target instance. Use latest one by default. (Dataverse ServiceClient)

    .PARAMETER AppModuleId
    Guid of the appmodule to add components to.

    .PARAMETER Components
    Collection of components to add. Supported input shapes:
    - [Microsoft.Xrm.Sdk.EntityReference]
    - @{ ComponentId = <Guid>; ComponentEntityLogicalName = <string> }
    - PSCustomObject with ComponentId / ComponentEntityLogicalName (or Id / LogicalName) properties

    .OUTPUTS
    Microsoft.Xrm.Sdk.OrganizationResponse. The AddAppComponents response.

    .EXAMPLE
    $refs = @(
        (New-XrmEntityReference -LogicalName "savedquery" -Id $viewId),
        (New-XrmEntityReference -LogicalName "systemform" -Id $formId)
    );
    Add-XrmAppComponents -AppModuleId $appId -Components $refs;

    .EXAMPLE
    $components = @(
        [pscustomobject]@{ ComponentId = $viewId; ComponentEntityLogicalName = "savedquery" },
        [pscustomobject]@{ ComponentId = $formId; ComponentEntityLogicalName = "systemform" }
    );
    Add-XrmAppComponents -AppModuleId $appId -Components $components;

    .LINK
    https://learn.microsoft.com/en-us/power-apps/developer/data-platform/webapi/reference/addappcomponents
#>

function Add-XrmAppComponents {
    [CmdletBinding()]
    [OutputType([Microsoft.Xrm.Sdk.OrganizationResponse])]
    param
    (
        [Parameter(Mandatory = $false, ValueFromPipeline)]
        [Microsoft.PowerPlatform.Dataverse.Client.ServiceClient]
        $XrmClient = $Global:XrmClient,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Guid]
        $AppModuleId,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Object[]]
        $Components
    )
    begin {
        $StopWatch = [System.Diagnostics.Stopwatch]::StartNew();
        Trace-XrmFunction -Name $MyInvocation.MyCommand.Name -Stage Start -Parameters ($MyInvocation.MyCommand.Parameters);
    }
    process {
        $references = @();
        foreach ($component in $Components) {
            if ($component -is [Microsoft.Xrm.Sdk.EntityReference]) {
                $references += $component;
                continue;
            }

            $componentId = $null;
            $logicalName = $null;

            if ($component.PSObject.Properties.Match("ComponentId").Count -gt 0) {
                $componentId = $component.ComponentId;
            }
            elseif ($component.PSObject.Properties.Match("Id").Count -gt 0) {
                $componentId = $component.Id;
            }

            if ($component.PSObject.Properties.Match("ComponentEntityLogicalName").Count -gt 0) {
                $logicalName = $component.ComponentEntityLogicalName;
            }
            elseif ($component.PSObject.Properties.Match("LogicalName").Count -gt 0) {
                $logicalName = $component.LogicalName;
            }

            if (-not $componentId -or -not $logicalName) {
                throw "Invalid component descriptor: each component must provide ComponentId/Id and ComponentEntityLogicalName/LogicalName, or be an EntityReference.";
            }

            $references += (New-XrmEntityReference -LogicalName $logicalName -Id ([Guid]::Parse($componentId.ToString())));
        }

        if ($references.Count -eq 0) {
            return;
        }

        $collection = New-XrmEntityReferenceCollection -EntityReferences $references;

        $request = New-XrmRequest -Name "AddAppComponents";
        $request | Add-XrmRequestParameter -Name "AppId" -Value $AppModuleId | Out-Null;
        $request | Add-XrmRequestParameter -Name "Components" -Value $collection | Out-Null;

        $response = $XrmClient | Invoke-XrmRequest -Request $request;
        $response;
    }
    end {
        $StopWatch.Stop();
        Trace-XrmFunction -Name $MyInvocation.MyCommand.Name -Stage Stop -StopWatch $StopWatch;
    }
}

Export-ModuleMember -Function Add-XrmAppComponents -Alias *;