Salt: AWS Security groups cannot be specified along with network interfaces

Created on 20 Jul 2015  路  11Comments  路  Source: saltstack/salt

When specifying a new network interface in AWS you cannot specify

    securitygroup:
      - mysecuritygroup
    network_interfaces:
      - DeviceIndex: 0
        PrivateIpAddresses:
          - Primary: True
        #auto assign public ip (not EIP)
        AssociatePublicIpAddress: True
        SubnetId: subnet-xxxx

Error code:

[ERROR   ] AWS Response Status Code and Error: [400 400 Client Error: Bad Request] {'Errors': {'Error': {'Message': 'Network interfaces and an instance-level security groups may not be specified on the same request', 'Code': 'InvalidParameterCombination'}}, 'RequestID': '8f35e3fd-9dfe-49d9-8efd-b4944c2ba9ff'}

Might be good to clarify documentation for AWS network interfaces or create a code fix for this to allow specification of groups and interfaces on separate calls

Bug P3 RIoT Salt-Cloud severity-medium

Most helpful comment

So the workaround that I discovered is to specify the security group by its id within the network interface:

    network_interfaces:
      - DeviceIndex: 0
        PrivateIpAddresses:
          - Primary: True
        #auto assign public ip (not EIP)
        AssociatePublicIpAddress: True
        SubnetId: subnet-xxxx
        SecurityGroupId: 
          - sg-xxxxxx

However this limits my use-case where I'd like to specify network connections (specifically subnets) and then extend to change the security groups for various machine types

All 11 comments

@Enquier, thanks for the report. What is the output of salt --versions-report?

         Salt: 2015.5.2
         Python: 2.7.5 (default, Jun 24 2015, 00:41:19)
         Jinja2: 2.7.2
       M2Crypto: 0.21.1
 msgpack-python: 0.4.6
   msgpack-pure: Not Installed
       pycrypto: 2.6.1
        libnacl: Not Installed
         PyYAML: 3.10
          ioflo: Not Installed
          PyZMQ: 14.3.1
           RAET: Not Installed
            ZMQ: 3.2.5
           Mako: Not Installed

So the workaround that I discovered is to specify the security group by its id within the network interface:

    network_interfaces:
      - DeviceIndex: 0
        PrivateIpAddresses:
          - Primary: True
        #auto assign public ip (not EIP)
        AssociatePublicIpAddress: True
        SubnetId: subnet-xxxx
        SecurityGroupId: 
          - sg-xxxxxx

However this limits my use-case where I'd like to specify network connections (specifically subnets) and then extend to change the security groups for various machine types

@Enquier, thanks for the extra info.

Looking at the following, you are specifying a network device ID (presumably because of the "AssociatePublicIp") thus cannot specify SecurityGroupIds (VPC) when specifying NetworkInterfaces. You have to put the SecurityGroupIds on the network interfaces. SecurityGroups is only for Ec2, not VPC(Need to verify this).

One of the ways I see around this is to automatically associate public IP's on the specified subnet. This would require you specify 'subnetid' without the use of network interfaces.

http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html#cfn-ec2-instance-securitygroupids

def _create_eni_if_necessary(interface):
    '''
    Create an Elastic Interface if necessary and return a Network Interface Specification
    '''
    if 'NetworkInterfaceId' in interface and interface['NetworkInterfaceId'] is not None:
        return {'DeviceIndex': interface['DeviceIndex'],
                'NetworkInterfaceId': interface['NetworkInterfaceId']}

    params = {'Action': 'DescribeSubnets'}
    subnet_query = aws.query(params,
                             return_root=True,
                             location=get_location(),
                             provider=get_provider(),
                             opts=__opts__,
                             sigver='4')
    found = False

    for subnet_query_result in subnet_query:
        if 'item' in subnet_query_result:
            if isinstance(subnet_query_result['item'], dict):
                for key, value in subnet_query_result['item'].iteritems():
                    if key == "subnetId":
                        if value == interface['SubnetId']:
                            found = True
                            break
            else:
                for subnet in subnet_query_result['item']:
                    if subnet['subnetId'] == interface['SubnetId']:
                        found = True
                        break

    if not found:
        raise SaltCloudConfigError(
            'No such subnet <{0}>'.format(interface['SubnetId'])
        )

    params = {'SubnetId': interface['SubnetId']}

    for k in ('Description', 'PrivateIpAddress',
              'SecondaryPrivateIpAddressCount'):
        if k in interface:
            params[k] = interface[k]

    for k in ('PrivateIpAddresses', 'SecurityGroupId'):
        if k in interface:
            params.update(_param_from_config(k, interface[k]))

    params['Action'] = 'CreateNetworkInterface'

    result = aws.query(params,
                       return_root=True,
                       location=get_location(),
                       provider=get_provider(),
                       opts=__opts__,
                       sigver='4')

    eni_desc = result[1]
    if not eni_desc or not eni_desc.get('networkInterfaceId'):
        raise SaltCloudException('Failed to create interface: {0}'.format(result))

    eni_id = eni_desc.get('networkInterfaceId')
    log.debug(
        'Created network interface {0} inst {1}'.format(
            eni_id, interface['DeviceIndex']
        )
    )

    associate_public_ip = interface.get('AssociatePublicIpAddress', False)
    if isinstance(associate_public_ip, str):
        # Assume id of EIP as value
        _associate_eip_with_interface(eni_id, associate_public_ip)
    elif interface.get('associate_eip'):
        _associate_eip_with_interface(eni_id, interface.get('associate_eip'))
    elif interface.get('allocate_new_eip') or associate_public_ip:
        _new_eip = _request_eip(interface)
        _associate_eip_with_interface(eni_id, _new_eip)
    elif interface.get('allocate_new_eips'):
        addr_list = _list_interface_private_addresses(eni_desc)
        eip_list = []
        for idx, addr in enumerate(addr_list):
            eip_list.append(_request_eip(interface))
        for idx, addr in enumerate(addr_list):
            _associate_eip_with_interface(eni_id, eip_list[idx], addr)

Actually I primarily am specifying a network_interface for the subnetID. Certain subnets within my VPC are connected to my local network and route traffic differently. I would prefer to specify the subnet without the network interface but reading the documentation I was unable to determine how to do so.

http://docs.saltstack.com/en/latest/topics/cloud/aws.html#simple-launching-into-a-vpc

Reading that, you should be able to specify 'subnetid' without the use of network interfaces.

Guess I didn't read the documentation as thoroughly as I thought! I'll give it a whirl and report back

If you're no longer running into issues, I think this can be closed.

I was able to confirm @AkhterAli 's solution works. This issue can be closed

Worked for me also thanks for the help everyone.

Was this page helpful?
0 / 5 - 0 ratings