AWS CloudFormation – Configuring Two Network Interfaces with EIP

amazon-cloudformationamazon-web-services

I'm an AWS newbie. Trying to create an EC2 Instance with two network interfaces, one should be public and the other private.

Here is what I've tried in my YAML template:

  1. Create a VPC with two subnets
  2. Create an Instance with just the private network interface
  3. Create the second (public) network interface as another YAML entity
  4. Attach the second network interface to the instance
  5. Create an EIP and associate it with the second network interface

The public network interface has to be created as a separate entity because I need to !Ref it in the EIP association stanza.

Properties:
[...]
  Host1:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: 'ami-02541b8af977f6cdd' # Amazon Linux x86
      InstanceType: 't2.micro'
      AvailabilityZone:  !Select [0, !GetAZs '']
      KeyName: !Ref KeyName
      NetworkInterfaces:
      - SubnetId: !Ref SubnetPrivate
        DeleteOnTermination: true
        DeviceIndex: '0'
        GroupSet:
        - !Ref SecurityGroup
      Tags:
      - Key: Name
        Value: 'simple - host1'
  Host1Eth1:
    Type: 'AWS::EC2::NetworkInterface'
    Properties:
      SubnetId: !Ref SubnetPublic
      GroupSet:
      - !Ref SecurityGroup
      Tags:
      - Key: Name
        Value: 'simple - host1 eth1'
  Host1Eth1Attachment:
    Type: 'AWS::EC2::NetworkInterfaceAttachment'
    Properties:
      DeleteOnTermination: true
      DeviceIndex: 1
      NetworkInterfaceId: !Ref Host1Eth1
      InstanceId: !Ref Host1
  ElasticIP:
    Type: 'AWS::EC2::EIP'
    Properties:
      Domain: vpc
    DependsOn: VPCGatewayAttachment
  ElasticIPAssociation:
    Type: 'AWS::EC2::EIPAssociation'
    Properties:
      EIP: !Ref ElasticIP
      InstanceId: !Ref Host1
      NetworkInterfaceId: !Ref Host1Eth1

The complete YAML file is available here: https://gist.github.com/kmansoft/6b90b33bcf91eb0d493414ed6c3d9754

Problem:

Once the stack gets created, the Instance has the EIP listed as its Public IP in the Console and there is a DNS name for it. However, I am not able to connect to the instance using SSH (either by host name or by IP).

Strangely, if I omit the EIP from the YAML file and create and associate it manually using the AWS Console, then everything works.

Any suggestions for how to make the public EIP work using YAML?


Update: I've tried to add DependsOn: Host1Eth1Attachment to ElasticIPAssociation, to make sure the ordering is the same as when I associate the EIP in the Console.

Got this error:

There are multiple interfaces attached to instance 'i-020622d0cf13e5185'. Please specify an interface ID for the operation instead.

even though my ElasticIPAssociation does specify a NetworkInterfaceId (and an InstanceId).


Update 2: I've tried changing my ElasticIPAssociation to be like this:

  ElasticIPAssociation:
    Type: 'AWS::EC2::EIPAssociation'
    Properties:
      EIP: !Ref ElasticIP
      NetworkInterfaceId: !Ref Host1Eth1
    DependsOn: Host1Eth1Attachment

removing the InstanceId. Got a different error:

InstanceId not found in properties

This is weird, the documentation for AWS::EC2::EIPAssociation says that InstanceId is "Required: Conditional" – "For EC2-VPC, you can specify either the instance ID or the network interface ID, but not both."

Looks like a CloudFormation bug?


Update 3

This – from EC2 documentation – works.

  PublicEIPAssociate:
    Type: 'AWS::EC2::EIPAssociation'
    Properties:
      AllocationId: !GetAtt PublicEIP.AllocationId
      NetworkInterfaceId: !Ref InterfacePublic

Note that there is no InstanceId here (which wouldn't have worked because the Instance has two network interfaces).

Also note this uses AllocationId and not EIP to refer to the EIP.

The docs say this about AllocationId: "This is required for EC2-VPC". For EIP it says "This is required for EC2-Classic". I am using EC2-VPC so switching to AllocationId made sense and finally worked.

Best Answer

After a quick test I think you may have found a bug. I could be wrong, I didn't spend long on it. The easiest way to report this bug is to to go the EIPAssociation CloudFormation page and give feedback. Offer to email in your example, or reference this question on SF.

You're right that the docs say specify and InstanceID OR a NetworkInterfaceID but when you supply just the interface ID it asks for the Instance ID. If you add the InstanceID it tells you to remove it.

Small point, according to the docs the NetworkInterfaceID of the EIP needs to reference "The ID of the network interface". The return value of !Ref to a AWS::EC2::NetworkInterface is the name so you need to get the ID attribute. That would be important if there wasn't a bug in CloudFormation.

ElasticIPAssociation:
  Type: 'AWS::EC2::EIPAssociation'
  Properties:
    EIP: !Ref ElasticIP
    InstanceId: !Ref Host1
    NetworkInterfaceId: !GetAtt Host1Eth1.Id