Public/Users/Invoke-UKGUserBulk.ps1

function Invoke-UKGUserBulk {
    <#
    .SYNOPSIS
        Performs bulk operations on users in the UKG HR Service Delivery system.

    .DESCRIPTION
        Creates, updates, or patches multiple users in a single API call.
        Returns a request ID that can be used to check the status of the operation.

    .PARAMETER Create
        Create new users.

    .PARAMETER Update
        Update existing users (PUT - full replacement).

    .PARAMETER Patch
        Patch existing users (PATCH - partial update).

    .PARAMETER Users
        Array of user objects to process.

    .PARAMETER WaitForCompletion
        Wait for the bulk operation to complete before returning.

    .PARAMETER TimeoutSeconds
        Maximum time to wait for completion (default: 300 seconds).

    .EXAMPLE
        $users = @(
            @{ email = "admin1@company.com"; first_name = "Admin"; last_name = "One"; role_id = "role123" },
            @{ email = "admin2@company.com"; first_name = "Admin"; last_name = "Two"; role_id = "role123" }
        )
        Invoke-UKGUserBulk -Create -Users $users

    .OUTPUTS
        UKG.BulkRequest object with the request ID and status.
    #>

    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Create')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ParameterSetName = 'Create')]
        [switch]$Create,

        [Parameter(Mandatory, ParameterSetName = 'Update')]
        [switch]$Update,

        [Parameter(Mandatory, ParameterSetName = 'Patch')]
        [switch]$Patch,

        [Parameter(Mandatory, ValueFromPipeline)]
        [object[]]$Users,

        [Parameter()]
        [switch]$WaitForCompletion,

        [Parameter()]
        [ValidateRange(30, 3600)]
        [int]$TimeoutSeconds = 300
    )

    begin {
        $allUsers = @()
    }

    process {
        foreach ($user in $Users) {
            if ($user -is [hashtable]) {
                $allUsers += $user
            }
            else {
                $ht = @{}
                foreach ($prop in $user.PSObject.Properties) {
                    if ($prop.Name -notin @('PSTypeName')) {
                        $ht[$prop.Name] = $prop.Value
                    }
                }
                $allUsers += $ht
            }
        }
    }

    end {
        if ($allUsers.Count -eq 0) {
            Write-Warning "No users provided for bulk operation."
            return
        }

        $method = switch ($PSCmdlet.ParameterSetName) {
            'Create' { 'POST' }
            'Update' { 'PUT' }
            'Patch' { 'PATCH' }
        }

        $operationName = switch ($PSCmdlet.ParameterSetName) {
            'Create' { 'Create' }
            'Update' { 'Update (PUT)' }
            'Patch' { 'Patch' }
        }

        if ($PSCmdlet.ShouldProcess("$($allUsers.Count) users", "Bulk $operationName")) {
            $body = @{
                users = $allUsers
            }

            $response = Invoke-UKGRequest -Endpoint '/users/bulk' -Method $method -Body $body

            if ($response) {
                $response.PSObject.TypeNames.Insert(0, 'UKG.BulkRequest')

                if ($WaitForCompletion -and $response.request_id) {
                    $startTime = Get-Date
                    $completed = $false

                    Write-Verbose "Waiting for bulk operation to complete..."

                    while (-not $completed) {
                        Start-Sleep -Seconds 2

                        $status = Get-UKGUserBulkStatus -RequestId $response.request_id

                        if ($status.status -eq 'completed' -or $status.status -eq 'failed') {
                            $completed = $true
                            return $status
                        }

                        $elapsed = ((Get-Date) - $startTime).TotalSeconds
                        if ($elapsed -ge $TimeoutSeconds) {
                            Write-Warning "Timeout waiting for bulk operation to complete. Use Get-UKGUserBulkStatus to check status."
                            return $status
                        }

                        Write-Verbose "Status: $($status.status), Progress: $($status.processed_count)/$($status.total_count)"
                    }
                }
            }

            return $response
        }
    }
}

function Get-UKGUserBulkStatus {
    <#
    .SYNOPSIS
        Gets the status of a bulk user operation.

    .DESCRIPTION
        Retrieves the current status and results of a previously submitted bulk operation.

    .PARAMETER RequestId
        The request ID returned from Invoke-UKGUserBulk.

    .EXAMPLE
        Get-UKGUserBulkStatus -RequestId "req123"

    .OUTPUTS
        UKG.BulkRequest object with status and results.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('request_id')]
        [string]$RequestId
    )

    process {
        $response = Invoke-UKGRequest -Endpoint "/users/bulk/$RequestId" -Method GET

        if ($response) {
            $response.PSObject.TypeNames.Insert(0, 'UKG.BulkRequest')
        }

        return $response
    }
}

function Send-UKGUserInvite {
    <#
    .SYNOPSIS
        Sends an invitation to a user.

    .DESCRIPTION
        Sends an email invitation to a user to activate their account.

    .PARAMETER Id
        The unique identifier of the user.

    .EXAMPLE
        Send-UKGUserInvite -Id "user123"

    .EXAMPLE
        Get-UKGUser -All | Where-Object { -not $_.activated } | Send-UKGUserInvite

    .OUTPUTS
        Response from the API confirming the invitation was sent.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('UserId')]
        [string]$Id
    )

    process {
        if ($PSCmdlet.ShouldProcess($Id, 'Send User Invite')) {
            $response = Invoke-UKGRequest -Endpoint "/users/$Id/invite" -Method POST
            return $response
        }
    }
}