I know that you can use stage/environment variables to specify different securityGroupIds/subnetIds for different stages (dev/test/prod) but I have a situation where the Lambda function must be in a VPC when in the dev/test environment (to access private test resources) but should not be in a VPC in production (no need to access the resources and being in the VPC slows down startup performance).
Is there a way to make the whole VPC section conditional based on the stage? I tried leaving the env vars empty or not setting them at all but I get deployment errors when I do that.
Is there any way to do this without having to maintain separate serverless.yml files for dev and prod?
No, there is not way to conditionally include/exclude parts of the serverless.yml, because it’s not a great idea to do so.
While I can totally understand your use-case, what you’re doing may come back to bite you (and often does in larger projects and environments); Since your dev/test environment does not represent your prod environment, it’s conceivable your application will works in dev/test, but have a bug in prod.
That being said (and I feel dirty suggesting this ), you could try to use CFN Conditionals to do what you’ve described. Remember that the resources section of your serverless.yml is merged with the final generated CFN template, so by adding in Conditions to your resources you might be able to do it (e.g. have two function definitions, one for dev/test that is VPC’d, and one for prod that is not).
custom:
allVpcSettings:
prod:
dummyValue: "This is a dummy value that should be ignored"
stage:
vpc:
securityGroupIds:
- securityGroupId1
- securityGroupId2
subnetIds:
- subnetId1
- subnetId2
vpcSettings: &vpcSettings
vpc: ${self:custom.allVpcSettings.${self.provider.stage}.vpc}
provider:
<<: *vpcSettings
# Rest of the provider config
# OR
functions:
hello:
<<: *vpcSettings
# Rest of the function config
It’s mixing stage specific variables with YAML anchors in an attempt to get vpcSettings to contain the right value.
Fwiw I achieved this by doing a slightly modified way to buggy.
I created a settings file called vpc_settings.yml in the base of my project. The structure was as follows:
dev:
vpc_mode_is_disabled: "This is a dummy value so we can have VPC mode running in prod, but not in any other env"
qa:
vpc_mode_is_disabled: "This is a dummy value so we can have VPC mode running in prod, but not in any other env"
uat:
vpc_mode_is_disabled: "This is a dummy value so we can have VPC mode running in prod, but not in any other env"
prod:
securityGroupIds:
- sg-idgoeshere
subnetIds:
- subnet-id1goeshere
- subnet-id2goeshere
- subnet-id3goeshere
The top level keys refer to my stages.
Then, I have serverless.yml for each microservice, in microservices/<name>/serverless.yml. I had the following for provider key.
provider:
vpc: ${file(../../vpc_settings.yml):${self:provider.stage}}
# Rest of your provider config goes here
When you deploy a stage that does not have sec group + subnet in vpc_settings.yml, it will grant the Lambda’s execution role IAM permissions to create and manage EC2 network interface. I believe this is because we are still specifying the vpc key. However, it won’t actually put it into VPC mode because we haven’t specified security group or subnet.
When you deploy a stage that has a fully defined config in vpc_settings.yml, it will work.
This isn’t ideal, and I would recommend that you keep your environments the same, but sometimes you gotta do what you gotta do.
Although we get a warning from serverless for the empty dev environment :
Serverless Warning --------------------------------------
A valid service attribute to satisfy the declaration 'self:custom.vpc.dev' could not be found.