functions/New/New-PullRequest.ps1

function New-PullRequest {
    Param(
        [parameter(Mandatory = $true)]
        [string]$Owner,
        [parameter(Mandatory = $true)]
        [string]$Repo,
        [parameter(Mandatory = $true, ParameterSetName='AuthToken')]
        [string]$AuthToken,
        [parameter(Mandatory = $true, ParameterSetName='PAT')]
        [string]$Username,
        [parameter(Mandatory = $true, ParameterSetName='PAT')]
        [string]$PAT,
        [parameter(Mandatory = $true)]
        [string]$Title,
        [parameter(Mandatory = $true)]
        [string]$Head,
        [parameter(Mandatory = $true)]
        [string]$Base,        
        [parameter(Mandatory = $false)]
        [string]$Body,
        [parameter(Mandatory = $false)]
        [bool]$MaintainerCanModify = $true,
        [parameter(Mandatory = $false)]
        [bool]$Draft = $false
    )

    switch ($PsCmdlet.ParameterSetName)
    {
        'PAT'{
            $bytes = [System.Text.Encoding]::UTF8.GetBytes("$($Username):$($PAT)")
            $AuthToken = [System.Convert]::ToBase64String($bytes)
        }
    }

    try {
        $headers = @{
            Authorization = "Basic $AuthToken";
        }
        $headers.Add('Content-Type', 'application/json')
        $headers.Add('User-Agent', 'PoShPipeline')
        $headers.Add('Accept', 'application/vnd.github+json')

        $objBody = 
        @{
            title = $Title
            head = $Head
            base = $Base
            body = $Body
            maintainer_can_modify = $MaintainerCanModify
            draft = $Draft
        };
        $jsonBody = ConvertTo-Json -InputObject $objBody
        $result = Invoke-WebRequest -Uri "https://api.github.com/repos/$Owner/$Repo/pulls" -Headers $headers -Method POST -Body $jsonBody | ConvertFrom-Json

        return $result.id
    }
    catch {
        $ErrorMessage = $_.Exception.Message
        # GitHub explains a failure (bad credentials, missing permission, SAML enforcement...) in the response body.
        # PowerShell 7 exposes it as ErrorDetails.Message; the exception message alone is just the status code.
        if ($_.ErrorDetails -and $_.ErrorDetails.Message) {
            $ErrorMessage += " | GitHub response: $($_.ErrorDetails.Message)"
        }
        $response = $_.Exception.Response
        if ($response -and $response.Headers) {
            $missing = @('X-Accepted-GitHub-Permissions', 'X-GitHub-SSO') | ForEach-Object {
                $values = $null
                if ($response.Headers.TryGetValues($_, [ref]$values)) { "$_ = $($values -join ', ')" }
            }
            if ($missing) { $ErrorMessage += " | $($missing -join '; ')" }
        }
        Write-Error "Failed to create pull request: $ErrorMessage"
        Write-Output "##[error]Failed to create pull request: $ErrorMessage"

        throw
    }
}