functions/Client.ps1

New-Variable -Name MedlemsserviceUrl -Value "https://medlemsservice.spejdernet.dk" -Scope Global -Force
New-Variable -Name MedlemsserviceCsrfToken -Value $Null -Scope Global -Force
New-Variable -Name MedlemsserviceSession -Value $Null -Scope Global -Force
New-Variable -Name MedlemsserviceContext -Value $Null -Scope Global -Force
New-Variable -Name MedlemsserviceContextGroup -Value $Null -Scope Global -Force
New-Variable -Name MedlemsserviceRequestCount -Value 0 -Scope Global -Force
New-Variable -Name ClientDefaultProperties -Scope Global -Force -Value @{
    Proxy       = $Null
    ContentType = "application/json"
    Headers     = @{
        "X-Requested-With" = "XMLHttpRequest"
        "User-Agent" = "MedlemsservicePowershellModule"
    }
    Verbose = $False
    SkipCertificateCheck = $False
}

function Set-MedlemsserviceUrl {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification='No side effects')]
    [CmdletBinding()]
    param($ServerUrl)

    Set-Variable -Scope Global -Name MedlemsserviceUrl -Value $ServerUrl | Out-Null
}

function Get-MedlemsserviceUrl {
    return (Get-Variable -Scope Global -Name MedlemsserviceUrl).Variable
}

function Set-MedlemsserviceContextGroup {
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", '', Justification = 'Just context')]
    [CmdletBinding()]
    param([Parameter(Mandatory=$True)][int]$Id)

    Set-Variable -Scope Global -Name MedlemsserviceContextGroup -Value $Id | Out-Null
}

function Get-MedlemsserviceContextGroup {
    (Get-Variable -Scope Global -Name MedlemsserviceContextGroup).Value
}

function Set-MedlemsserviceProxy {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification='No side effects')]
    param(
        [Parameter(Mandatory=$True, Position=0)]
        $Uri
    )

    $cfg = $(Get-Variable -Scope Global -Name ClientDefaultProperties).Value
    $cfg.Proxy = $Uri
    Set-Variable -Scope Global -Name ClientDefaultProperties -Value $cfg | Out-Null
}

function Invoke-MedlemsserviceLogin {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", '')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', 'Password', Justification = 'Obsolete')]
    [CmdletBinding()]
    param(
        $Username,
        $Password
    )

    If("$Username" -eq "" -or "$Password" -eq "") {
        throw "Username and password must be specified"
    }

    $MedlemsserviceSession = $Null
    Invoke-WebRequest -SessionVariable "MedlemsserviceSession" -uri "${MedlemsserviceUrl}"  -Method GET -UseBasicParsing -Proxy $ClientDefaultProperties.Proxy -SkipCertificateCheck:$ClientDefaultProperties.SkipCertificateCheck | Out-Null
    Set-Variable -Scope Global -Name MedlemsserviceSession -Value $MedlemsserviceSession | Out-Null

    $csrfToken = Get-MedlemsserviceCsrfToken -UrlPath "/web/login"

    $formData = @{
        login = $Username
        password = $Password
        csrf_token = $csrfToken
        redirect = ""
    }
    $loginResult = Invoke-WebRequest -WebSession $MedlemsserviceSession -uri "${MedlemsserviceUrl}/web/login" -Method POST -ContentType "application/x-www-form-urlencoded" -Body $formData -Proxy $ClientDefaultProperties.Proxy -SkipCertificateCheck:$ClientDefaultProperties.SkipCertificateCheck -SkipHeaderValidation
    if($loginResult.StatusCode -ne 200) {
        throw ("Unexpected login response status code {0}: {1}" -f $loginResult.StatusCode, $loginResult.Content)
    }

    $isExpectedMatch = $loginResult.Content -match "odoo\.__session_info__"
    if(-not ($isExpectedMatch)) {
        Write-Warning "Unexpected response from login POST call"
        $loginResult.Content | Write-Warning
        throw "Could not login"
    }

    Set-Variable -Scope Global -Name MedlemsserviceSession -Value $MedlemsserviceSession | Out-Null
    Set-Variable -Scope Global -Name MedlemsserviceCsrfToken -Value $csrfToken | Out-Null

    $ctx = Get-MedlemsserviceSessionContext
    Set-MedlemsserviceContextGroup -Id $ctx.user_companies.current_company
}

function Get-MedlemsserviceCsrfToken {
    param(
        $UrlPath
    )

    $MedlemsserviceSession = (Get-Variable -Scope Global -Name MedlemsserviceSession).Value
    $response = Invoke-WebRequest  -WebSession $MedlemsserviceSession -uri "${MedlemsserviceUrl}${UrlPath}" -Method GET -UseBasicParsing -Proxy $ClientDefaultProperties.Proxy -SkipCertificateCheck:$ClientDefaultProperties.SkipCertificateCheck
    Set-Variable -Scope Global -Name MedlemsserviceSession -Value $MedlemsserviceSession | Out-Null

    $isMatch = $response.Content -match "csrf_token: `"([^`"]+)`""
    $csrfToken = $Matches[1]
    if(-not $isMatch -or "" -eq "${csrfToken}") {
        throw "Could not get CSRF Token"
    }

    $csrfToken
}

function Get-MedlemsserviceSessionContext {
    $MedlemsserviceContext = Invoke-MedlemsserviceCallRequest -Path "/web/session/get_session_info" -SkipContext | Where-Object { $_.GetType().IsPublic }
    Set-Variable -Scope Global -Name MedlemsserviceContext -Value $MedlemsserviceContext | Out-Null
    $MedlemsserviceContext
}

function TryGetMember {
    param(
        [Parameter(Mandatory=$True, Position=0)]
        $InputObject,
        [Parameter(Mandatory=$True, Position=1)]
        $PropertyName
    )

    try {
        $InputObject.$propertyName
    } catch {
        $Null
    }
}

function Invoke-MedlemsserviceCallRequest {
    [CmdletBinding(SupportsShouldProcess=$false)]
    param(
        $Path,
        $Params = @{},
        $ContextParameterName = $Null,
        [Switch]$SkipContext
    )

    $MedlemsserviceRequestCount = (Get-Variable -Scope Global -Name MedlemsserviceRequestCount).Value
    $MedlemsserviceRequestCount += 1 
    Set-Variable -Scope Global -Name MedlemsserviceRequestCount -Value $MedlemsserviceRequestCount | Out-Null

    $req = @{
        jsonrpc = "2.0"
        id = $MedlemsserviceRequestCount
        method  = "call"
        params  = $Params
    }

    $MedlemsserviceContext = (Get-Variable -Scope Global -Name MedlemsserviceContext).Value
    $MedlemsserviceContextGroup = (Get-Variable -Scope Global -Name MedlemsserviceContextGroup).Value
    $MedlemsserviceSession = (Get-Variable -Scope Global -Name MedlemsserviceSession).Value
    $ClientDefaultProperties = (Get-Variable -Scope Global -Name ClientDefaultProperties).Value

    If (-not $SkipContext -and $Null -ne $MedlemsserviceContext) {
        $value = $req.params
        if ($Null -ne $ContextParameterName) { $value = $value.$ContextParameterName }
        If ($Null -eq ( TryGetMember -InputObject $value -PropertyName "context")) { $value.context = @{} }
        $value = $value.context
        $value.tz = $MedlemsserviceContext.user_context.tz
        $value.lang = $MedlemsserviceContext.user_context.lang
        $value.uid = $MedlemsserviceContext.user_context.uid
        $value.allowed_company_ids = @($MedlemsserviceContextGroup)
        if($Null -ne $MedlemsserviceContextGroup) {
            $value.active_id = $MedlemsserviceContextGroup
            $value.active_ids = @(,$MedlemsserviceContextGroup)
            $value.search_default_organization_id = $MedlemsserviceContextGroup
            $value.search_default_state = "active"
        }
    }

    $body = $req | ConvertTo-Json -Depth 10
    #$result = Invoke-RestMethod -WebSession $MedlemsserviceSession `
    $response = Invoke-WebRequest -WebSession $MedlemsserviceSession -UseBasicParsing `
        -Uri "${MedlemsserviceUrl}${Path}" `
        -Method POST -Body $body `
        @ClientDefaultProperties

    #Write-Host ("Response headers from POST request to {0} with result {1} are:" -f $Path, $response.StatusCode)
    #$response.Headers
    #Write-Host "Content:"
    #Write-Host $response.Content

    $result = $response.Content | ConvertFrom-Json

    if ($result.PSObject.Properties.Name -contains "error") {
        Write-Error $result.error.data.message
        throw $result.error
    }
    else {
        return $result.result
    }
}

function Invoke-MedlemsserviceFormDataRequest {
    param(
        [Parameter(Mandatory=$true)]
        $DataObject,
        $UrlPath = "/web/export/csv"
    )

    $csrfToken = Get-MedlemsserviceCsrfToken -UrlPath "/web"


    $MedlemsserviceSession = (Get-Variable -Scope Global -Name MedlemsserviceSession).Value
    $encodedData = ($DataObject | ConvertTo-Json -Depth 100 -Compress -EnumsAsStrings)
    $LF = "`r`n"
    $boundary = "----WebKitBoundaryPs{0}" -f [System.Guid]::NewGuid().ToString("N")

    $bodyLines = ( 
        "--$boundary",
        "Content-Disposition: form-data; name=`"data`"",
        "",
        $encodedData,
        "--$boundary" ,
        "Content-Disposition: form-data; name=`"csrf_token`"",
        "",
        $csrfToken,
        "--$boundary--"
    ) -join $LF

    $response = Invoke-WebRequest -WebSession $MedlemsserviceSession -uri "${MedlemsserviceUrl}${UrlPath}" -Method POST -ContentType "multipart/form-data; boundary=$boundary" -Body $bodyLines -Proxy $ClientDefaultProperties.Proxy -SkipCertificateCheck:$ClientDefaultProperties.SkipCertificateCheck -Headers @{ "Origin"=$MedlemsserviceUrl; "Referer" = "${MedlemsserviceUrl}/web"; "X-Requested-With"= "Powershell"; "Accept"="*/*" }
    $response.Content
}