Working with Dataverse Column Data Types using Web API

Working with Dataverse Column Data Types using Web API

This guide covers how to create and update common Dataverse column data types using the Dataverse Web API with PowerShell. While standard types are relatively simple to handle, complex types like Lookup require specific formatting and payload structure to be processed correctly.

The Basics

I already covered the basic CRUD operations using the Web API so I strongly recommend reviewing this post first. Basically, you’ll either be using the methods post for create and patch for update. Then, we will pass the payload containing all the columns we want to create or update. We also need to ensure that the body is converted to json format. When working in PowerShell, we’ll create a hashtable and then convert it using the ConvertTo-Json cmdlet. You’ll also want to ensure you are using the logical name for the columns not the Display Name. There are some exceptions to this such as for the Lookup Field using the schema name. More on this later.

The Transaction Table Example

I have the following Dataverse table called “Transaction” (Logical Table Name: crbed_transaction) with the following columns and their data types:

Display NameLogical NameData Type
Itemcrbed_itemSingle line of Text
Transaction Datecrbed_transactiondateDate Only
Transaction Date and Timecrbed_transactiondateandtimeDate and Time
Amountcrbed_amountCurrency
Categorycrbed_categoryChoice
Vendorcrbed_vendorLookup
Paid Forcrbed_paidforBoolean
Notescrbed_notesText Area

I also have a csv called TransactionItems.csv. I want to populate it’s row into the table and it’s columns correspond to the columns in my Dataverse table.

In the next section, I’ll breakdown each of the column types before showcasing the example PowerShell script to import this data.

Column Types

Text

Text (and by extension text area and multiple lines of text) are pretty straight-forward. Just enclose the string in double-quotes.

$Body = @{
    "crbed_item" = "Target Shopping"
    "crbed_notes" = "For the upcoming wedding"
} | ConvertTo-Json

Be mindful of your character limits on your text fields. By default, the single line of text and text area is 100 characters while multi-line is 150. It can be increased on the column’s settings. If you exceed that column’s limit, the web request will error indicating the limitation. You can read more about the limits here.

Number

Depending on the type (whole, decimal, or float), you’ll need to declare the type in your script for precision. For example, using the currency type, we will need to convert to decimal, but for whole numbers, use integer

$Body = @{
    "crbed_amount" = 40.34 -as [decimal],
    "crbed_quantity" = 2 -as [int]
} | ConvertTo-Json

Choice

The choice value, not the actual choice label, is expected. From the local choices in my Transactions table, here are the choices:

$Body = @{
    "crbed_category" = 624120000
} | ConvertTo-Json

Lookup

Lookup is the tricky one. We need to use the schema name for the lookup column as part of the body and also reference end point logical name for the associated table. For example, I have a vendors table which is used as a lookup for the transaction record. We need to get the guid of the vendor we want to associate.

Copy both the schema and set names under the tools menu in the table details.
Vendor is the Unique Identifier for this table
$Body = @{
    "[email protected]" = "/crbed_vendors(711f8360-6d1f-ee11-9cbd-000d3a34dc1d)"
} | ConvertTo-Json

In my example script at the end, I’ll showcase making the web request to get all the vendors, storing them as a pscustomobject, and filtering on that object to get the desired record.

Boolean

Even though in Dataverse the Yes/No column values are represented as 1 or 0 when reviewing the column details, we will need ensure it evaluates to “TRUE” or “FALSE” as a string. The boolean $true and $false also works from my testing.

$Body = @{
"crbed_paidfor" = "TRUE"
} | ConvertTo-Json

Date and Time

For columns where date and time used, ensure that the string format follows the yyyy-MM-dd format. If your column includes time use the format shown below.

When you set Date Time columns to user local, the datetime submitted is assumed to be UTC time. Unless you are in UTC time, you likely need to add a timezoneoffset that matches your computer’s timezone settings. For example, if you are in PST time, currently as of April 2025, it’s 7 hours behind UTC, so I’ll need to add 7 hours to my date time. The example below, I am using my local system’s timezone info.

$offsetHours = [Math]::Abs([System.TimeZoneInfo]::Local.GetUtcOffset([datetime]::Now).TotalHours)
$CurrentDate = get-date
$PSTTimeConvert = ($CurrentDate  -as [datetime]).AddHours($offsetHours)
$TransactionDate = ($CurrentDate  -as [datetime]).ToString("yyyy-MM-dd")
$TransactionDateTime = ($PSTTimeConvert).ToString("yyyy-MM-dd hh:mm:ss tt")

$Body = @{
"crbed_transactiondate" = $TransactionDate
"crbed_transactiondateandtime" = $TransactionDateTime
} | ConvertTo-Json

Putting it all Together

Here’s the completed script. We make requests to gather both the choices values for Categories and Vendors so we can reference these values dynamically. As we loop through the csv file, we use where-object to filter on the name where it match the corresponding name in the csv file.

#Loop through Transaction CSV File and Populate Dataverse Custom Table

$TransactionFilePath = "D:\My Downloads\BudgetItems.csv" # Replace with your file path
$TransactionFileImport = Import-Csv $TransactionFilePath

# Dot Source Access Token

. .\GetDataverseAccessToken.ps1

$Params =@{
    ClientId =  "CLIENT ID HERE" 
    TenantId =  "TENANT ID HERE" 
    ClientSecret = "CLIENT SECRET HERE"
    EnvironmentURL =  "https://ORGNAMEHERE.crm.dynamics.com/"
}

$offsetHours = [Math]::Abs([System.TimeZoneInfo]::Local.GetUtcOffset([datetime]::Now).TotalHours)
$authResponseObject = Get-DataverseAccessToken @Params

$HeaderInfo = @{
    "Authorization" = "$($authResponseObject.token_type) $($authResponseObject.access_token)"
    "Accept" = "application/json"
    "OData-MaxVersion" = "4.0"
    "OData-Version" = "4.0"
    "Content-Type" = "application/json"
    "Prefer" = "return=representation"
}

$BaseURL = "$($Params.EnvironmentURL)api/data/v9.2"

# Define tables as customobject to reference

$CategoryTable = @{
    entityLogicalName = "crbed_transaction"
    attributeLogicalName = "crbed_category"
    selectQuery = "`$select=LogicalName"
    expandQuery = "`$expand=OptionSet(`$select=Options)"
}

$TransactionTable = @{
    entityLogicalName = "crbed_transaction"
    entitySetName = "crbed_transactions"
    entitySchemaName = "crbed_Transaction"
}

$VendorTable = @{
    entityLogicalName = "crbed_vendor"
    entitySetName = "crbed_vendors"
    entitySchemaName = "crbed_Vendor"
}

$VendorURI =  $BaseURL + "/" + $($VendorTable.entitySetName)
$TransactionURI =  $BaseURL + "/" + $($TransactionTable.entitySetName)
$CategoryOptionsURI = "$BaseURL/EntityDefinitions(LogicalName='$($CategoryTable.entityLogicalName)')/Attributes(LogicalName='$($CategoryTable.attributeLogicalName)')/Microsoft.Dynamics.CRM.PicklistAttributeMetadata?$($CategoryTable.selectQuery)&$($CategoryTable.expandQuery)"

# Get Transaction Table's Local Category Choices for filtering on later

$LocalCategoryChoices = (Invoke-RestMethod -Uri $CategoryOptionsURI -Headers $HeaderInfo -Method Get).OptionSet.Options `
| Select-Object Value, @{l='Name';e={$_.Label.LocalizedLabels.Label}}

# Get Vendor LookUp Data for filtering on later

$VendorData = (Invoke-RestMethod -Uri $VendorURI -Headers $HeaderInfo -Method Get).value | Select-Object crbed_vendorid,crbed_vendorname

Foreach ($transaction in $TransactionFileImport){

    # Map columns

    $Name= $transaction.Name

    $TimeConvert = ($transaction.TransactionDateTime -as [datetime]).AddHours($offsetHours)

    $TransactionDate = ($transaction.TransactionDate -as [datetime]).ToString("yyyy-MM-dd")
    $TransactionDateTime = ($TimeConvert).ToString("yyyy-MM-dd hh:mm:ss tt")

    $VendorLookUpID = ($VendorData | Where-Object {$_.crbed_vendorname -eq $transaction.Vendor}).crbed_vendorid
    $PaidFor = $transaction.PaidFor -as [string]
    $CategoryValue = ($LocalCategoryChoices | Where-Object {$_.Name -eq $transaction.Category}).Value -as [int]
    $Notes = $transaction.Notes
    $Amount = $transaction.Amount -as [decimal]

    $Body = @{
        "crbed_item" = $Name
        "crbed_transactiondate" = $TransactionDate
        "crbed_transactiondateandtime" = $TransactionDateTime
        "crbed_amount" = $Amount
        "crbed_notes" = $Notes
        "crbed_category" = $CategoryValue
        "crbed_paidfor" = $PaidFor
        "[email protected]" = "/crbed_vendors($VendorLookUpID)"
    } | ConvertTo-Json

    $PostRequest = [ordered]@{
        URI = $TransactionURI
        Headers = $HeaderInfo
        Method = 'Post'
        Body = $Body
    }

    Write-Host "Creating Transaction: $($Name)"

    # Create the transaction record

    Invoke-RestMethod @PostRequest -ErrorAction Stop | Out-Null

}
The end results!

Leave a Reply