Vetras
Vetras

Reputation: 1999

Dynamically set the EC2 Instance Type per Environment using ebextensions

I want to create a EC2 instance type t3.medium on all environments and m5.large on production.

I'm using .ebextensions (YAML) like so:

option 1:

Mappings:
  EnvironmentMap:
    "production":
      TheType: "m5.large"
      SecurityGroup: "foo"
      ...
    "staging":
      TheType: "t3.medium"
      SecurityGroup: "bar"
      ...

option_settings:
  aws:autoscaling:launchconfiguration:
    IamInstanceProfile: "aws-elasticbeanstalk-ec2-role"
    InstanceType: !FindInMap
      - EnvironmentMap
      - !Ref 'AWSEBEnvironmentName'
      - TheType
    SecurityGroups:
      - {"Fn::FindInMap": ["EnvironmentMap", {"Ref": "AWSEBEnvironmentName"}, "SecurityGroup"]}

Option 2:

    InstanceType: {"Fn::FindInMap": ["EnvironmentMap", {"Ref": "AWSEBEnvironmentName"}, "EC2InstanceType"]}

Option 3:

    InstanceType:
      - {"Fn::FindInMap": ["EnvironmentMap", {"Ref": "AWSEBEnvironmentName"}, "EC2InstanceType"]}

Results

Option 1 fails with Invalid Yaml (but I took this from this AWS example.

Option 2 and 3 fail with the same problem. The FindInMap function is not "called": Invalid option value: '{"Fn::FindInMap":["EnvironmentMap","EC2InstanceType"]},{"Ref":"AWSEBEnvironmentName"}' (Namespace: 'aws:autoscaling:launchconfiguration', OptionName: 'InstanceType'): Value is not one of the allowed values: [c1.medium, c1.xlarge, c3.2xlarge, .... It tries to interpret the whole function/thing as a string.

For the SecurityGroups property it works, for InstanceType it does not.

I can't do it dynamically and I can't find how to achieve this neither on AWS doc, SO, or anywhere else. I would assume this is simple stuff. What am I missing?


EDIT:

Option 4: using conditionals

Conditions:
  IsProduction: !Equals [ !Ref AWSEBEnvironmentName, production ]

option_settings:

  aws:autoscaling:launchconfiguration:
    InstanceType: !If [ IsProduction, m5.large, t3.medium ]
    SecurityGroups:
      - {"Fn::FindInMap": ["EnvironmentMap", {"Ref": "AWSEBEnvironmentName"}, "SecurityGroup"]}

Error: YAML exception: Invalid Yaml: could not determine a constructor for the tag !Equals in...

But this comes from documentation on conditions and if.


EDIT 2:

I eventually found out that the option InstanceType is obsolute and we should use:

aws:ec2:instances
  InstanceTypes: "t3.medium"

But alas, this does not solve the problem either because I cannot use the replacement functions here as well (Fn:findInMap).

Upvotes: 4

Views: 643

Answers (2)

danbrellis
danbrellis

Reputation: 399

I know this is an old question, but for anyone else searching, instead of trying to dynamically or conditionally set the instance type in an ebextensions config file, you can just entirely omit that config option and instead include the --instance_type flag in the eb create command (https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb3-create.html#eb3-createoptions):

eb create --instance_type t4g.small [...]

Now since your ebextensions has no reference to the instance type/size, when you deploy, the original option won't get overridden. You can change the instance type within the running environment through the web console.

Upvotes: 0

Marcin
Marcin

Reputation: 238747

The reason why FindInMap does not work in option_settings is the fact that only four intrinsic functions are allowed there (from docs):

  • Ref
  • Fn::GetAtt
  • Fn::Join
  • Fn::GetOptionSetting

I'm not convinced that SecurityGroups worked. I think your script failed before FindInMap in SecurityGroups got chance to be evaluated.

However, I tried to find a way using Resources. The closes I got was with the following config file:

Mappings:
  EnvironmentMap:
    production:
      TheType: "t3.medium"
    staging:
      TheType: "t2.small"

Resources:
  AWSEBAutoScalingLaunchConfiguration:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      InstanceType:
        ? "Fn::FindInMap"
        :
          - EnvironmentMap
          - 
            Ref: "AWSEBEnvironmentName"
          - TheType

Although this is a step closer, it ultimately fails as well. The reason is that when EB is jointing our Resources config file with its own template, it produces the following:

"InstanceType": {
  "Ref": "InstanceType", # <--- this should NOT be here :-(
  "Fn::FindInMap": [
    "EnvironmentMap",
    {
      "Ref": "AWSEBEnvironmentName"
    },
    "TheType"
  ]
},

instead of

"InstanceType": {
  "Fn::FindInMap": [
    "EnvironmentMap",
    {
      "Ref": "AWSEBEnvironmentName"
    },
    "TheType"
  ]
},

And this happens because the original InstanceType (before the joint operation) is:

"InstanceType":{"Ref":"InstanceType"},

Therefore, EB instead of replacing InstanceType with our custom InstanceType provided in our config file, it just merges them.

Upvotes: 1

Related Questions