Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terraform REST API calls with cURL

Tags:

terraform

What is the best way to make REST API calls from Terraform? I'm currently using a null_resource with the local-exec provisioner to make a cURL call:

resource "null_resource" "cloudability-setup" {
  provisioner "local-exec" {
      command = <<EOT
        curl -s -X POST https://api.cloudability.com/v3/vendors/aws/accounts \
             -H 'Content-Type: application/json' \
             -u "$${CldAbltyAPIToken:?Missing Cloudability API Token Env Variable}:" \
             -d '{"vendorAccountId": "${data.aws_caller_identity.current.account_id}", "type": "aws_role" }'
EOT
  }

However, the cURL return code is successful for HTTP 200 and HTTP 400 responses. I'd like the resource to be marked as failed if the new account cannot be registered.

I've tried returning just the HTTP Response Code:

resource "null_resource" "cloudability-setup" {
  provisioner "local-exec" {
      command = <<EOT
        curl -s -o /dev/null -w "%{http_code}" \
             -X POST https://api.cloudability.com/v3/vendors/aws/accounts \
             -H 'Content-Type: application/json' \
             -u "$${CldAbltyAPIToken:?Missing Cloudability API Token Env Variable}:" \
             -d '{"vendorAccountId": "${data.aws_caller_identity.current.account_id}", "type": "aws_role" }'
EOT
  }

But then I lose the API response body, which contains valuable information. There are also times when a HTTP 400 code indicates the account already exists, which I consider a success from the overall setup standpoint.

like image 671
skohrs Avatar asked Jul 05 '18 18:07

skohrs


2 Answers

I haven't used it myself, but this may be of use to you: https://github.com/Mastercard/terraform-provider-restapi

like image 81
moebius Avatar answered Nov 10 '22 02:11

moebius


This question has been viewed over 10,000 times and I realized I never posted my solution to the problem. I ended up writing a Python script to handle the various API responses and controlling the return codes to Terraform.

Terraform resource:

resource "null_resource" "cloudability-setup" {
  provisioner "local-exec" {
      command = "${path.module}/cloudability_setup.py -a ${data.aws_caller_identity.current.account_id} -t aws_role"
  }

  depends_on = ["aws_iam_role.cloudability-role"]
}

Python script:

import getopt
import json
import os
import requests
import sys

def print_help():
    print '''
Usage: cloudability_setup.py [options]

cloudability_setup -- Register new account with Cloudability

Options:
  -h, --help            Show this help message and exit
  -a <acct #>, --acctnum=<acct #>
                        Required argument: IaaS Account Number
  -t <type>, --type=<type>
                        Required argument: IaaS Account Type
'''

def register_acct(acctnum, type):

    url = 'https://api.cloudability.com/v3/vendors/aws/accounts'
    token = os.environ['CldAbltyAPIToken']
    headers = {'Content-Type': 'application/json'}
    data = '{"vendorAccountId": "' + acctnum + '", "type": "'+ type + '" }'

    response = requests.post(url, auth=(token,''), headers=headers, data=data)

    # If new account was registered successfully, update externalID:
    if response.status_code == requests.codes.created:
      update_acct(acctnum, type)

    # If account already exists, update externalID:
    elif str(response.status_code) == '409':
      update_acct(acctnum, type)

    else:
      print "Bad response from Cloudability API while registering new account."
      print "HTTP: " + str(response.status_code)
      sys.exit(3)


def update_acct(acctnum, type):

    url = 'https://api.cloudability.com/v3/vendors/aws/accounts/' + acctnum
    token = os.environ['CldAbltyAPIToken']
    headers = {'Content-Type': 'application/json'}
    data = '{"type": "' + type + '", "externalId": "XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX" }'

    response = requests.put(url, auth=(token,''), headers=headers, data=data)

    if response.status_code == requests.codes.ok:
      sys.exit()

    else:
      print "Bad response from Cloudability API while updating account."
      print "HTTP: " + str(response.status_code)
      sys.exit(3)


def main(argv=None):
    '''
    Main function: work with command line options and send an HTTPS request to the Cloudability API.
    '''

    try:
        opts, args = getopt.getopt(sys.argv[1:], 'ha:t:',
                                   ['help', 'acctnum=', 'type='])
    except getopt.GetoptError, err:
        # Print help information and exit:
        print str(err)
        print_help()
        sys.exit(2)

    # Initialize parameters
    acctnum = None
    type = None

    # Parse command line options
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            print_help()
            sys.exit()
        elif opt in ('-a', '--acctnum'):
            acctnum = arg
        elif opt in ('-t', '--type'):
            type = arg

    # Enforce required arguments
    if not acctnum or not type:
      print_help()
      sys.exit(4)

    register_acct(acctnum, type)


if __name__ == '__main__':
    sys.exit(main())
like image 33
skohrs Avatar answered Nov 10 '22 02:11

skohrs