Thanks to @rgmatthes1 - I wouldn't have persisted without your hints.
Updated for REST API (still in beta, so YMMV).
Wall-of-code:
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
#--------REST v1.1 - Sharepoint Access, REST_Access_No2FA
$oauth_consumer_key = "Integration Consumer Key here".ToUpper()
$oauth_consumer_secret = "Integration Consumer Secret here".ToLower()
$oauth_token = "Access Token ID here".ToUpper()
$oauth_token_secret = "Access Token Secret here".ToLower()
$oauth_signature_method = "HMAC-SHA256"
$oauth_version = "1.0"
$realm = "Your Realm" #This is *different* from the URL e.g. 1234567-sb1 becomes 1234567_SB1
$HTTP_method = "GET"
$url = "https://$($realm.ToLower().Replace("_","-")).suitetalk.api.netsuite.com"
#$query = "/rest/platform/v1/metadata-catalog/record?select=customer"
$query = "/rest/platform/v1/metadata-catalog/record/customer"
if($query -match "\?"){
$parameters = $query.Split("?")[1]
$query = $query.Split("?")[0]
}
else{$parameters = ""}
$oauth_nonce = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes([System.DateTime]::Now.Ticks.ToString()))
$oauth_timestamp = [int64](([datetime]::UtcNow)-(Get-Date "1970-01-01")).TotalSeconds
$oAuthParamsForSigning = @{}
$oAuthParamsForSigning.Add("oauth_consumer_key",$oauth_consumer_key)
$oAuthParamsForSigning.Add("oauth_token",$oauth_token)
$oAuthParamsForSigning.Add("oauth_signature_method",$oauth_signature_method)
$oAuthParamsForSigning.Add("oauth_version",$oauth_version)
$oAuthParamsForSigning.Add("oauth_nonce",$oauth_nonce)
$oAuthParamsForSigning.Add("oauth_timestamp",$oauth_timestamp)
$parameters.Split("&") | % {
if(![string]::IsNullOrWhiteSpace($_.Split("=")[0])){
$oAuthParamsForSigning.Add($_.Split("=")[0],$_.Split("=")[1])
}
}
$oAauthParamsString = ($oAuthParamsForSigning.Keys | Sort-Object | % {
"$_=$($oAuthParamsForSigning[$_])"
}) -join "&"
$encodedOAuthParamsString = [uri]::EscapeDataString($oAauthParamsString)
$encodedUrl = [uri]::EscapeDataString($url+$query)
$base_string = $HTTP_method + "&" + $encodedUrl + "&" + $encodedOAuthParamsString
$key = $oauth_consumer_secret + "&" + $oauth_token_secret
$hmacsha265 = new-object System.Security.Cryptography.HMACSHA256
$hmacsha265.Key = [System.Text.Encoding]::ASCII.GetBytes($key)
$oauth_signature = [System.Convert]::ToBase64String($hmacsha265.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($base_string)))
#$oauth_signature - can be compared with PostMan and http://lti.tools/oauth/
$authHeaderString = ($oAuthParamsForSigning.Keys | Sort-Object | % {
"$_=`"$([uri]::EscapeDataString($oAuthParamsForSigning[$_]))`""
}) -join ","
$authHeaderString += ",realm=`"$([uri]::EscapeDataString($realm))`""
$authHeaderString += ",oauth_signature=`"$([uri]::EscapeDataString($oauth_signature))`""
$response = Invoke-RestMethod -Uri $([uri]::EscapeUriString($url+$query)) -Headers @{"Authorization"="OAuth $authHeaderString";"Cache-Control"="no-cache";"Accept"="application/swagger+json";"Accept-Encoding"="gzip, deflate"} -Method $HTTP_method -Verbose -ContentType "application/swagger+json"
Broken into functions in a module:
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
function get-netsuiteAuthHeaders(){
[cmdletbinding()]
Param (
[parameter(Mandatory = $true)]
[ValidateSet("GET","POST")]
[string]$requestType
,[parameter(Mandatory = $true)]
[ValidatePattern("http")]
[string]$url
,[parameter(Mandatory=$true)]
[hashtable]$oauthParameters
,[parameter(Mandatory=$true)]
[string]$oauth_consumer_secret
,[parameter(Mandatory=$false)]
[string]$oauth_token_secret
,[parameter(Mandatory=$true)]
[string]$realm
)
$oauth_signature = get-oauthSignature -requestType $requestType -url $url -oauthParameters $oauthParameters -oauth_consumer_secret $oauth_consumer_secret -oauth_token_secret $oauth_token_secret
$authHeaderString = ($oauthParameters.Keys | Sort-Object | % {
"$_=`"$([uri]::EscapeDataString($oauthParameters[$_]))`""
}) -join ","
$authHeaderString += ",realm=`"$([uri]::EscapeDataString($realm))`""
$authHeaderString += ",oauth_signature=`"$([uri]::EscapeDataString($oauth_signature))`""
@{"Authorization"="OAuth $authHeaderString"
;"Cache-Control"="no-cache"
;"Accept"="application/swagger+json"
;"Accept-Encoding"="gzip, deflate"
}
}
function get-netsuiteParameters(){
[cmdletbinding()]
Param()
#Don't really store your keys and secrets in plaintext like this - it's just proof-of-concept
@{oauth_consumer_key = "Integration Consumer Key here".ToUpper()
;oauth_consumer_secret = "Integration Consumer Secret here".ToLower()
;oauth_token = "Access Token ID here".ToUpper()
;oauth_token_secret = "Access Token Secret here".ToLower()
;oauth_signature_method = "HMAC-SHA256"
;oauth_version = "1.0"
;realm = "Your Realm"
}
}
function get-oauthSignature(){
[cmdletbinding()]
Param (
[parameter(Mandatory = $true)]
[ValidateSet("GET","POST")]
[string]$requestType
,[parameter(Mandatory = $true)]
[ValidatePattern("http")]
[string]$url
,[parameter(Mandatory=$true)]
[hashtable]$oauthParameters
,[parameter(Mandatory=$true)]
[string]$oauth_consumer_secret
,[parameter(Mandatory=$false)]
[string]$oauth_token_secret
)
$requestType = $requestType.ToUpper()
$encodedUrl = [uri]::EscapeDataString($url.ToLower())
$oAauthParamsString = (
$oauthParameters.Keys | Sort-Object | % {
if(@("realm","oauth_signature") -notcontains $_){
"$_=$($oauthParameters[$_])"
}
}
) -join "&"
$encodedOAuthParamsString = [uri]::EscapeDataString($oAauthParamsString)
$base_string = $requestType + "&" + $encodedUrl + "&" + $encodedOAuthParamsString
$key = $oauth_consumer_secret + "&" + $oauth_token_secret
Switch($oauthParameters["oauth_signature_method"]){
"HMAC-SHA1" {
$cryptoFunction = new-object System.Security.Cryptography.HMACSHA1
}
"HMAC-SHA256" {
$cryptoFunction = new-object System.Security.Cryptography.HMACSHA256
}
"HMAC-SHA384" {
$cryptoFunction = new-object System.Security.Cryptography.HMACSHA384
}
"HMAC-SHA512" {
$cryptoFunction = new-object System.Security.Cryptography.HMACSHA512
}
default {
Write-Error "Unsupported oauth_signature_method [$_]"
break
}
}
$cryptoFunction.Key = [System.Text.Encoding]::ASCII.GetBytes($key)
$oauth_signature = [System.Convert]::ToBase64String($cryptoFunction.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($base_string)))
$oauth_signature
}
function invoke-netsuiteRestMethod(){
[cmdletbinding()]
Param(
[parameter(Mandatory = $true)]
[ValidateSet("GET","POST")]
[string]$requestType
,[parameter(Mandatory = $true)]
[ValidatePattern("http")]
[string]$url
,[parameter(Mandatory=$false)]
[hashtable]$netsuiteParameters
)
if(!$netsuiteParameters){$netsuiteParameters = get-netsuiteParameters}
if($url -match "\?"){
$parameters = $url.Split("?")[1]
$url = $url.Split("?")[0]
}
else{$parameters = ""}
$oauth_nonce = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes([System.DateTime]::Now.Ticks.ToString()))
$oauth_timestamp = [int64](([datetime]::UtcNow)-(Get-Date "1970-01-01")).TotalSeconds
$oAuthParamsForSigning = @{}
#Add standard oAuth 1.0 parameters
$oAuthParamsForSigning.Add("oauth_nonce",$oauth_nonce)
$oAuthParamsForSigning.Add("oauth_timestamp",$oauth_timestamp)
$oAuthParamsForSigning.Add("oauth_consumer_key",$netsuiteParameters.oauth_consumer_key)
$oAuthParamsForSigning.Add("oauth_token",$netsuiteParameters.oauth_token)
$oAuthParamsForSigning.Add("oauth_signature_method",$netsuiteParameters.oauth_signature_method)
$oAuthParamsForSigning.Add("oauth_version",$netsuiteParameters.oauth_version)
#Add parameters from url
$parameters.Split("&") | % {
if(![string]::IsNullOrWhiteSpace($_.Split("=")[0])){
$oAuthParamsForSigning.Add($_.Split("=")[0],$_.Split("=")[1])
}
}
$netsuiteRestHeaders = get-netsuiteAuthHeaders -requestType $requestType -url $url -oauthParameters $oAuthParamsForSigning -oauth_consumer_secret $netsuiteParameters["oauth_consumer_secret"] -oauth_token_secret $netsuiteParameters["oauth_token_secret"] -realm $netsuiteParameters["realm"]
$response = Invoke-RestMethod -Uri $([uri]::EscapeUriString($url)) -Headers $netsuiteRestHeaders -Method $requestType -Verbose -ContentType "application/swagger+json"
$response
}
Usage:
invoke-netsuiteRestMethod -requestType GET -url "https://YourInstance.suitetalk.api.netsuite.com/rest/platform/v1/metadata-catalog/record/customer"