Ohad Elias
Ohad Elias

Reputation: 66

Can't provision EC2 instance with ansible

I am trying to provision an EC2 instance with Ansible installed on an EC2 Ubuntu instance, after provisioning a VPC. This is the contents of my bastion-instance.yml file:

- name: Setup Vprofile Bastion Host
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:
    - name: Import bastion setup variables
      include_vars: vars/bastion_setup

    - name: Import VPC output variables
      include_vars: vars/output_vars

    - name: Create vprofile ec2 keys
      ec2_key:
        name: vprofile-key
        region: "{{region}}"
      register: key_out
    
    - name: Save private key into file bastion-key-pem
      copy: 
        content: "{{key_out.key.private_key}}"
        dest: "./bastion-key.pem"
        mode: 0600
      when: key_out.changed
    
    - name: Create sec grp for bastion hosts
      ec2_group:
        name: Bastion-host-sg
        description: Allow port 22 from everywhere and all port within sg
        region: "{{region}}"
        vpc_id: "{{vpcid}}"
        rules:
          - proto: tcp
            from_port: 22
            to_port: 22
            cidr_ip: "{{MYIP}}"
      register: BastionSG_out
    
    - name: creating Bastion Host
      ec2:
        key_name: vprofile-key
        region: "{{region}}"
        instance_type: t2.micro
        image: "{{bastion_ami}}"
        group_id: "{{BastionSG_out.group_id}}"
        vpc_subnet_id: "{{pubsub1id}}"
        wait: yes #wait until healthcheck is ok
        wait_timeout: 300
        instance_tags:
          Name: "Bastion_host"
          Project: Vprofile
          Owner: DevOps Team
        exact_count: 1 #to make sure we wont create ec2 instance every run
        count_tag: Name #count matched tags
      register: BastionHost_out

When I run this playbook, the security group and key pair are successfully provisioned, but I receive the following error for the instance provisioning:

An exception occurred during task execution. To see the full traceback, use -vvv. The error was: UnknownParameterThe parameter MetadataOptions is not recognized082b8b0f-bf75-409e-985c-386068293e5f fatal: [localhost]: FAILED! => {"boto3_version": "1.20.34", "botocore_version": "1.23.34", "changed": false, "msg": "Instance creation failed: The parameter MetadataOptions is not recognized"}

I tried running a basic testing EC2 instance with the required parameters only, but also got no luck. I don't understand where this MetadataOptions came from since I did not use a parameter with this name.

When running the playbook with -vvv, the following is the log:

The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_ec2_payload_zt20ipz8/ansible_ec2_payload.zip/ansible_collections/amazon/aws/plugins/modules/ec2.py", line 1171, in create_instances
  File "/usr/lib/python3/dist-packages/boto/ec2/connection.py", line 976, in run_instances
    return self.get_object('RunInstances', params, Reservation,
  File "/usr/lib/python3/dist-packages/boto/connection.py", line 1208, in get_object
    raise self.ResponseError(response.status, response.reason, body)
boto.exception.EC2ResponseError: EC2ResponseError: 400 Bad Request
<?xml version="1.0" encoding="UTF-8"?>
<Response><Errors><Error><Code>UnknownParameter</Code><Message>The parameter MetadataOptions is not recognized</Message></Error></Errors><RequestID>f37105b6-56ef-4add-9aed-6a9f78bdb830</RequestID></Response>
fatal: [localhost]: FAILED! => {
    "boto3_version": "1.20.34",
    "botocore_version": "1.23.34",
    "changed": false,
    "invocation": {
        "module_args": {
            "assign_public_ip": null,
            "aws_access_key": null,
            "aws_ca_bundle": null,
            "aws_config": null,
            "aws_secret_key": null,
            "count": 1,
            "count_tag": {
                "Name": "Bastion_host",
                "Owner": "DevOps Team",
                "Project": "Vprofile"
            },
            "debug_botocore_endpoint_logs": false,
            "ebs_optimized": false,
            "ec2_url": null,
            "exact_count": 1,
            "group": null,
            "group_id": [
                "sg-088e3d11d4742c693"
            ],
            "id": null,
            "image": "ami-022e1a32d3f742bd8",
            "instance_ids": null,
            "instance_initiated_shutdown_behavior": "stop",
            "instance_profile_name": null,
            "instance_tags": {
                "Name": "Bastion_host",
                "Owner": "DevOps Team",
                "Project": "Vprofile"
            },
            "instance_type": "t2.micro",
            "kernel": null,
            "key_name": "vprofile-key",
            "monitoring": false,
            "network_interfaces": null,
            "placement_group": null,
            "private_ip": null,
            "profile": null,
            "ramdisk": null,
            "region": "us-east-1",
            "security_token": null,
            "source_dest_check": null,
            "spot_launch_group": null,
            "spot_price": null,
            "spot_type": "one-time",
            "spot_wait_timeout": 600,
            "state": "present",
            "tenancy": "default",
            "termination_protection": null,
            "user_data": null,
            "validate_certs": true,
            "volumes": null,
            "vpc_subnet_id": "subnet-0c755f0ccdf5face2",
            "wait": true,
            "wait_timeout": 300,
            "zone": null
        }
    },
    "msg": "Instance creation failed: The parameter MetadataOptions is not recognized"
}

I searched the internet for a solution to this problem, but didn't find anything similar. I am using Ansible version 2.10. Does anyone have any idea what is causing this issue?

Any help will be appreciated

Upvotes: 3

Views: 1464

Answers (1)

nh2
nh2

Reputation: 25695

Solution

The fix is to set a newer API version. There are many ways to do that:

  • Write a boto config file as described on here. Example:
    [Boto]
    ec2_version = 2016-11-15
    
    You can even do it as a one-liner with an environment variable, e.g. in bash with process substitution:
    BOTO_CONFIG=<(echo '[Boto]\nec2_version = 2016-11-15')
    
  • Upgrade to a current version of boto3.
  • If you're using boto directly as a library, you can set the api_version argument to EC2Connection.

Explanation

The problem is caused by newer AMIs being uploaded with the ImdsSupport="v2.0" setting.

Such newer versions cannot be launched with the older AWS API version 2014-10-01 that boto defaults to:

    APIVersion = boto.config.get('Boto', 'ec2_version', '2014-10-01')

(This is in version 2.49.0 which is the last release before boto was retired in favour of boto3.)

IMDSv2 is important because v1 was insecure (only in situations where an attacker could compel apps running on your EC2 instance to query IMDS for them).

The error

The parameter MetadataOptions is not recognized

is raised by AWS because the enabling of IMDSv2 during the upload of the AMI adds an implicit parameter MetadataOptions during the launch.

This is why you get this error even though you (Ansible) do not pass that parameter to the RunInstances API call.

This is arguably just a bad error message programmed by AWS. It should really say something like:

The parameter MetadataOptions is not recognized (automatically added by the AMI due to the enabling of IMDSv2)

Example fix in code

https://github.com/benaco/nixops/commit/de0b958b37030c4b4b78e3e69908ad0700d6ae57

API versions list

https://github.com/boto/botocore/tree/b5ed2ef6dd45f338b59095b4f4ce34a1488ac1b4/botocore/data/ec2


Thanks to @arianvp for helping me figure this out.

Upvotes: 1

Related Questions