4
votes

I am unable to load parameters into the python boto3 Cloudformation client.

Below is my parameter file:

[
  {"ParameterKey": "pVpcId",   "ParameterValue": "vpc-XXXXXX"},
  {"ParameterKey": "pContact", "ParameterValue": "XDXDXX"},
  {"ParameterKey": "pCC",      "ParameterValue": "XXXXX" },
  {"ParameterKey": "pFormat",  "ParameterValue": "True"}
]

I'm loading it into the program in the following way:

    with open(parameter_file, 'r') as infile:
        parameters=ast.literal_eval(infile.read())
        client = boto3.client('cloudformation',aws_access_key_id=access_key,aws_secret_access_key=secret_key,aws_session_token=session_token,region_name=region)

        response = client.create_stack(
            StackName=stack_name,
            TemplateURL=stack_url,
            Parameters=parameters
        )

When I establish a boto3 client with Cloudformation and call it, I get the error described below. The call works without the parameters, so it's definitely something to do with the parameter file.

    Traceback (most recent call last):
    File "cf_create_stack", line 85, in <module>
        Parameters=parameters
    File "/usr/lib/python2.7/site-packages/botocore/client.py", line 357, in _api_call
        return self._make_api_call(operation_name, kwargs)
    File "/usr/lib/python2.7/site-packages/botocore/client.py", line 661, in _make_api_call
        raise error_class(parsed_response, operation_name)
    botocore.exceptions.ClientError: An error occurred (ValidationError) when calling the CreateStack operation: Template format error: unsupported structure.
3
Have you tried the same thing from CLI? docs.aws.amazon.com/cli/latest/reference/cloudformation/….asr9
Any feedback on the below @swap709? I would like to be 100% sure.Alex Harvey

3 Answers

3
votes

So the TemplateBody param is expecting the content/str of the CloudFormation Template file and not just the filename.

The following should work satisfactorily.

cf_template = open('batch-job-cft.yml').read()
cf_client.create_stack(StackName='Batch Job', TemplateBody=cf_template)

# OR

# Optimal usage would be as below
with open('batch-job-cft.yml', 'r') as cf_file:
    cft_template = cf_file.read()
    cf_client.create_stack(StackName='Batch Job', TemplateBody=cft_template)
1
votes

I believe it is impossible that you got that response based on the code sample you provided.

I think you might have tried TemplateBody rather than TemplateURL?

To reproduce the error message, try this simple example:

#!/usr/bin/env python

import boto3
import ast

parameter_file = 'parameters.json'

client = boto3.client('cloudformation')

with open(parameter_file, 'r') as infile:
    parameters = ast.literal_eval(infile.read())

response = client.create_stack(
    StackName='TestStack',
    TemplateBody='file://cloudformation.yml',
    Parameters=parameters
)

If you place the parameters file and template in the expected locations and run this, you should then see the exact error message you are seeing:

Traceback (most recent call last):
  File "test.py", line 17, in <module>
    Parameters=parameters
  File "/Users/alexharvey/git/home/python-test/virtualenv/lib/python2.7/site-packages/botocore/client.py", line 357, in _api_call                                     
    return self._make_api_call(operation_name, kwargs)
  File "/Users/alexharvey/git/home/python-test/virtualenv/lib/python2.7/site-packages/botocore/client.py", line 661, in _make_api_call                                
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (ValidationError) when calling the CreateStack operation: Template format error: unsupported structure.            

Note that this error:

Template format error: unsupported structure

Comes from the AWS API, not Boto3.

The error is caused when you pass in a file:// URI or URL to the TemplateBody parameter.

Moreover, I believe it is simply impossible to get that response if you really passed anything to TemplateURL.

See also:

  • this related answer, for reproducing this error message using the AWS CLI.
  • API docs where TemplateBody and TemplateURL are documented.
1
votes

Here's how I was able to resolve this:

  1. In order to load the parameter file (which loads as a list of dictionaries), I had the following code:

    with open(parameter_file) as f: parameters=json.load(f)

    for l in parameters: l['UsePreviousValue']=eval('False')

  2. In order to pass the Cloudformation template, I used the following code (basically reading it as a string):

    with open(cloudformation_template) as g: template_body=g.read()

Finally, I passed both variables into the cloudformation client:

response = client.create_stack(
    StackName=stack_name,
    TemplateBody=template_body,
    Parameters=parameters
)

The thing that was going wrong for me was that boto3 'Cloudformation' client expects a 'list of dictionaries' for parameters but expects a 'string' for the cloudformation template.

This is very frustrating and I'm trying to find a way to raise this to AWS.