3

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).

Vetras
  • 1,609
  • 22
  • 40

1 Answers1

1

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.

Marcin
  • 215,873
  • 14
  • 235
  • 294
  • thanks for the answer. i learned something already. I would assume this is a basic operation. Use smaller, cheaper EC2 on dev, use a bigger faster EC2 on prod. How come this is so hard? How are we supposed to achieve the same effect then ? – Vetras May 28 '20 at 08:33
  • 1
    @Vetras I don't have answer currently. I also tried using [Fn::GetOptionSetting](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/ebextensions-functions.html#ebextensions-functions-getoptionsetting) but had no luck. Maybe there is other way, simpler. I don't know for now. Sorry. – Marcin May 28 '20 at 08:37
  • @Vetras As a side note, if you used CloudFormation for provisioning your EB env, you could do it easily there, not from `.ebextensions`. – Marcin May 28 '20 at 08:55
  • Right now the whole project is ebextensions and is a bit large. I am planning on cloning this into terraform sometime soon. Thanks for the help. – Vetras May 28 '20 at 13:19