Blog

Accessing Oracle NetSuite REST API using PowerShell

September 13, 2023

As part of a recent project I needed to lookup and export data from Oracle NetSuite using PowerShell.

It took me several hours of spinning my wheels on this problem, but I was finally able to connect. I’m writing this post in hopes that I can save someone else a little time and hair pulling.

Credentials Setup

The first thing you’ll need to do is access NetSuite using an account that has the administrator role or the Integration Application permission assigned.

From the NetSuite application Dashboard you should see a Setup item in the top/main menu:

Oracle NetSuite Setup Main Menu

Click the main Setup link in the menu to view the Setup Manager:

Oracle NetSuite Setup Manager

Click on Company » Enable Features » SuiteCloud

Toward the bottom under SuiteTalk (Web Services) check the REST WEB SERVICES box (SOAP can also be enabled here):

SuiteTalk (Web Services) Enabling Features

Click Save.

While still in Setup Manager click Integration » Manage Integrations:

Integrations

Click New, enter an application NAME, make sure the STATE is enabled.

You can uncheck all of the Token-based Authentication options

Within the OAuth 2.0 section, check CLIENT CREDENTIALS (MACHINE TO MACHINE) GRANT and REST WEB SERVICES under SCOPE

New Integration

Click Save.

After saving you’ll be presented with a CONSUMER KEY / CLIENT ID AND CONSUMER SECRET / CLIENT SECRET:

Client ID and Secret

This information is only presented one time so you’ll need to write it down.

Now we need to create a certificate that will be used to sign our REST requests.

For this I used OpenSSL, but any method for generating a valid x509 certificate should work.

Here is the CMD to generate a local certificate:

openssl req -x509 -newkey rsa:3072 -keyout "{Output Path}\private.pem" -out "{Output Path}\public.pem" -days 365 -nodes

You’ll be prompted to enter the certificate information; you can accept the default values by simply hitting ENTER.

While still in Setup Manager click Integrations » OAuth 2.0 Client Credentials Setup:

OAuth 2.0 Client Credentials Setup

Click Create New.

New Client Credentials Mapping

Select an appropriate user account for the ENTITY. The roles and permissions assigned to this user will then populate the ROLE list.

Select an appropriate ROLE assigned to the user. This will affect what data and operations are available via the REST Web Services.

Select the APPLICATION that was previously setup in Manage Integrations

Choose the public key file (public.pem) that was previously created for the CERTIFICATE

Click Save.

Once the credentials have been saved there will be a CERTIFICATE ID available on the main setup screen. This will be used later in our REST requests.

At this point everything should be setup within NetSuite.

PowerShell  (PowerShell 6 (Core) or better required)

In order to use the REST Web Services you must first acquire an access_token

This is done via a POST request to the API’s token endpoint:

https://<accountID>.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token
Here are the official documents on how to do this from Oracle.

To accomplish this we’ll need to work with JSON Web Tokens (JWTs).

There are some good PowerShell libraries for working with JWT such as Posh-Jwt, however I wanted a deeper understanding of the technology.

In order to create a valid JWT we need to encode some of the configuration information and then appropriately sign the request.

First create and encode the header:

# typ is always JWT
# alg is the Algorithm use to sign the request, others are supported, but RS256 is all that I could get to work
# kid is the certificate ID provided on the OAuth 2.0 Client Credentials Setup screen, we use the private key to sign later
[hashtable]$header = @{
     "typ" = "JWT";
     "alg" = "RS256";
     "kid" = "[Certificate ID from OAuth 2.0 Client Credentials Setup]";
}

# Serialize to JSON, Convert to Base64 string, make Base64 string URL safe
[string]$encodedHeader = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($(ConvertTo-Json $header))) -replace '\+','-' -replace '/','_' -replace '='

Next we need to create and encode the JWT payload:

# iss is the Client ID provided during the Integration Setup, the information is only provided once immediately after setup
# scope is the comma delimited list of possible services:  restlets, rest_webservices, suite_analytics
# aud is always the token endpoint
# exp is the date the JWT expires (60 minutes is the max) in Epoch/Unix numeric time, note this is NOT the expiration of the access_token
# iat is the date the JWT was issued (current date/time) in Epoch/Unix numeric time
[hashtable]$payload = @{
     "iss" = "[Client ID provided during Integration Setup]";
     "scope" = "rest_webservices";
     "aud" = "https://<accountID>.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token";
     "exp" = ([System.DateTimeOffset]$((Get-Date).AddSeconds(3600))).ToUnixTimeSeconds();
     "iat" = ([System.DateTimeOffset]$(Get-Date)).ToUnixTimeSeconds()
}

# Serialize to JSON, Convert to Base64 string, make Base64 string URL safe
[string]$encodedPayload = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($(ConvertTo-Json $payload))) -replace '\+','-' -replace '/','_' -replace '='

Now we need to sign the JWT:

# Combine header and payload
[string]$baseSignature = "$encodedHeader.$encodedPayload"
[byte[]]$byteSignature = [System.Text.Encoding]::UTF8.GetBytes($baseSignature)

# Load certificate
[System.Security.Cryptography.X509Certificates.X509Certificate2]$signingCertificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::CreateFromPemFile("<File path to public.pem>","<File path to private.pem>")

# Sign using private key
[byte[]]$byteSignedSignature = $signingCertificate.PrivateKey.SignData($byteSignature,[System.Security.Cryptography.HashAlgorithmName]::SHA256,[System.Security.Cryptography.RSASignaturePadding]::Pkcs1)

# Convert to Base64 string and make Base64 string URL safe
[string]$signedSignature = [Convert]::ToBase64String($byteSignedSignature) -replace '\+','-' -replace '/','_' -replace '='

With the JWT token properly configured and signed we’re ready to request an access_token from the API’s token endpoint and then use it to access the API’s customer endpoint.

# grant_type is always client_credentials
[string]$grant_type = "client_credentials"

# client_assertion_type is always urn:ietf:params:oauth:client-assertion-type:jwt-bearer, needs to be URL encoded
[string]$client_assertion_type = [System.Web.HttpUtility]::UrlEncode("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")

# client_assertion is a combination of the $baseSignature and $signedSignature
[string]$client_assertion = "$baseSignature.$signedSignature"

# send access_token request
$response = Invoke-WebRequest `
                 -Uri "https://<accountID>.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token" `
                 -Method "POST" `
                 -Body "grant_type=$grant_type&client_assertion_type=$client_assertion_type&client_assertion=$client_assertion" `
                 -Headers @{"Content-Type"="application/x-www-form-urlencoded";} `
                 -UseBasicParsing

# should get a JSON response body
if ($null -ne $response `
    -and (Test-Json $response.Content)) {
     [hashtable]$token = ConvertFrom-Json $response.Content -AsHashtable
     if ($token.ContainsKey("access_token") `
         -and $token["access_token"].Length -gt 0) {

         # now we can use the access_token in subsequent requests via the Authorization header
         $response = Invoke-WebRequest `
                         -Uri "https://<accountID>.suitetalk.api.netsuite.com/services/rest/record/v1/customer/<id>" `
                         -Method "GET" `
                         -Headers @{"Authorization"="Bearer $($token["access_token"])";} `
                         -UseBasicParsing

         # Convert Byte[] to JSON string, then convert JSON to object
         Write-Output (ConvertFrom-Json ([System.Text.Encoding]::UTF8.GetString($response.Content)))
     }
}

Click here for full script.

Official REST API Developer Guide:  https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/book_1559132836.html#SuiteTalk-REST-Web-Services-API-Guide

0 Comments

Leave Your Comment

Your email address will not be published. Required fields are marked *


about me

An information technology professional with twenty five years experience in systems administration, computer programming, requirements gathering, customer service, and technical support.