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.
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:
Click the main Setup
link in the menu to view the 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):
Click Save
.
While still in Setup Manager
click Integration
» Manage 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
Click Save
.
After saving you’ll be presented with a CONSUMER KEY / CLIENT ID
AND CONSUMER SECRET / CLIENT 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
:
Click Create New
.
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.
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
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
An information technology professional with twenty five years experience in systems administration, computer programming, requirements gathering, customer service, and technical support.
0 Comments