Using the AWS Organization API with Postman

John Wheeler
5 min readMar 13, 2021

I’ve been implementing AWS Control Tower and as I began migrating accounts from our legacy organization to a new one, I wanted to capture meta data with each account. As I was capturing information in Confluence like account alias, account number, and root email I wanted to see what information I could extract from the organizations api. I’ve had an opportunity to interact with other AWS api using postman for IAM, EC2, and route53.

Service URL

On of the things that was challenge for me initially was figuring out the service URL. This page defines Service URL’s and names.

Regional endpoints

Most Amazon Web Services offer a Regional endpoint that you can use to make your requests. The general syntax of a Regional endpoint is as follows.

protocol://service-code.region-code.amazonaws.com

For example, https://dynamodb.us-west-2.amazonaws.com is the endpoint for the Amazon DynamoDB service in the US West (Oregon) Region.

The definition above is important. The service-code is what’s used in Authorization section. The field Service Name will be populated with this value. For the organizations api, it’s a global resource and the endpoint is defined in the us-east-1 region.

Endpoint to call When using the CLI or the AWS API

For the current release of Organizations, you must specify the us-east-1 region for all AWS API and CLI calls. You can do this in the CLI by using these parameters and commands:

Use the following parameter with each command to specify both the endpoint and its region:

--endpoint-url https://organizations.us-east-1.amazonaws.com

Use the default endpoint, but configure your default region with this command:

aws configure set default.region us-east-1

Use the following parameter with each command to specify the endpoint:

--region us-east-1

Authentication

Use the value organizations for Service Name. I’ve set an environment with the appropriate values for AccessKey and SecretAccessKey and tested on IAM to confirm that my credentials worked.

Postman Authorization AWS Signature section

ListAccounts in Postman

I was a little anxious and just tried a similar GET request structure to what I use for IAM to ListAccounts in my organization. Interactions with other AWS api’s like IAM led me to believe that this would be the right format. I added two request parameters

  • Action=ListAccounts
  • Version=2016–11–28

This didn’t work and I received a fairly nebulous message.

The action or operation requested is invalid. Verify that the action is typed correctly.

I decided I need to dive into the documentation to look for an example. I found the following in the example section for ListAccounts

POST / HTTP/1.1 
X-Amz-Target: AWSOrganizationsV20161128.ListAccounts
{}

It looked like this endpoint requires a POST. Interestingly, the welcome section of the documentation seems to indicate that both GET and POST work for all actions.

Organizations supports GET and POST requests for all actions. That is, the API does not require you to use GET for some actions and POST for others. However, GET requests are subject to the limitation size of a URL. Therefore, for operations that require larger sizes, use a POST request.

I followed the example and change my request to a POST. Based on the example, it also looks like it’s using a header to indicate the action. This is the first api I’ve used that has the X-Amz-Target header. It looks like this encodes both the action as well as the api version.

Updating my postman request to resemble this example.

Request body with empty JSON
Updated the headers to include the X-Amz-Target header
Failed request

Again I received the ominous

The action or operation requested is invalid. Verify that the action is typed correctly.

This last failure was a bit frustrating. I spent a lot of time changing all sorts of things. POST to GET, trying to enable different headers. I spent some time searching, but I never ran across an answer that solved it.

ListAccounts with aws cli

After my searches came up empty I thought I’d try to see if I could get the cli to produce any results.

% aws --profile <MYPROFILE> organizations list-accounts

This produced results as expected. I wondered if I could get more information on the request if I enabled debug.

% aws --profile <MYPROFILE> organizations list-accounts --debug

The output was extremely helpful!

......
2021-03-13 13:56:48,948 - MainThread - botocore.endpoint - DEBUG - Making request for OperationModel(name=ListAccounts) with params: {'url_path': '/', 'query_string': '', 'method': 'POST', 'headers': {'X-Amz-Target': 'AWSOrganizationsV20161128.ListAccounts', 'Content-Type': 'application/x-amz-json-1.1', 'User-Agent': 'aws-cli/2.1.10 Python/3.9.1 Darwin/20.3.0 source/x86_64 prompt/off command/organizations.list-accounts'}, 'body': b'{}', 'url': 'https://organizations.us-east-1.amazonaws.com/', 'context': {'client_region': 'aws-global', 'client_config': <botocore.config.Config object at 0x1105ba4f0>, 'has_streaming_input': False, 'auth_type': None}}
.........

of particular interest was this header

'Content-Type': 'application/x-amz-json-1.1'

I had read a few other posts about others having to set this header or minor variations of this header.

ListAccounts in Postman — Success!

Adding the Content-Type and setting it to application/x-amz-json-1.1 was the final solution. None of the documentation mentions this header which seems a bit strange.

AWS Organizations ListAccounts

I was finally able to replicate the output from the cli and retrieve the records with Postman.

--

--

John Wheeler

Security professional, Mac enthusiast, writing code when I have to.