Skip to content

Commit

Permalink
Prepare 2.0.0
Browse files Browse the repository at this point in the history
- Removed optimistic variable resolution for `Fn::GetAtt` as it was not working properly and caused more issues than it solved. If you rely on `Fn::GetAtt` in your environment variables, define a custom resolution using the `getAttMap` [configuration option](#Configuration-Options).
  • Loading branch information
Andre Rabold committed Jun 4, 2021
1 parent add06c9 commit d08ec6c
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 48 deletions.
32 changes: 18 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ custom:

### Configuration Options

| Option | Default | Description |
| -------------- | ------- | ---------------------------------------------------------------------------------------------------- |
| filename | `.env` | Target file name where to write the environment variables to, relative to the project root. |
| enableOffline | `true` | Evaluate the environment variables when running `sls invoke local` or `sls offline start`. |
| overwrite | `false` | Overwrite the file even if it exists already. |
| refMap | `{}` | A mapping of [resource resolutions](#Custom-Resource-Resoluition) for the `Ref` function |
| getAttMap | `{}` | A mapping of [resource resolutions](#Custom-Resource-Resoluition) for the `Fn::GetAtt` function |
| importValueMap | `{}` | A mapping of [resource resolutions](#Custom-Resource-Resoluition) for the `Fn::ImportValue` function |
| Option | Default | Description |
| -------------- | ------- | ------------------------------------------------------------------------------------------------- |
| filename | `.env` | Target file name where to write the environment variables to, relative to the project root. |
| enableOffline | `true` | Evaluate the environment variables when running `sls invoke local` or `sls offline start`. |
| overwrite | `false` | Overwrite the file even if it exists already. |
| refMap | `{}` | Mapping of [resource resolutions](#Custom-Resource-Resolution) for the `Ref` function |
| getAttMap | `{}` | Mapping of [resource resolutions](#Custom-Resource-Resolution) for the `Fn::GetAtt` function |
| importValueMap | `{}` | Mapping of [resource resolutions](#Custom-Resource-Resolution) for the `Fn::ImportValue` function |

### Custom Resource Resolution

Expand All @@ -128,17 +128,19 @@ The plugin will try its best to resolve resource references like `Ref`, `Fn::Get
custom:
export-env:
refMap:
# Resolve `!Ref MyDbTable` as `mock-myTable`
MyDbTable: "mock-myTable"
# Resolve `!Ref MyDynamoDbTable` as `mock-myTable`
MyDynamoDbTable: "mock-myTable"
getAttMap:
# Resolve `!GetAtt ElasticSearchInstance.DomainEndpoint` as `localhost:9200`
ElasticSearchInstance:
# Resolve `!GetAtt MyElasticSearchInstance.DomainEndpoint` as `localhost:9200`
MyElasticSearchInstance:
DomainEndpoint: "localhost:9200"
importValueMap:
# Resolve `!ImportValue OtherLambdaFunction` as `arn:aws:lambda:us-east-2::function:other-lambda-function`
OtherLambdaFunction: "arn:aws:lambda:us-east-2::function:other-lambda-function"
# Resolve `!ImportValue MyLambdaFunction` as `arn:aws:lambda:us-east-2::function:my-lambda-function`
MyLambdaFunction: "arn:aws:lambda:us-east-2::function:my-lambda-function"
```
> 👉 Generally, it is recommended to avoid the use of intrinsic functions in your environment variables. Often, the same can be achieved by simply predefining a resource name and then manually construct the desired variable values. To share resources between different Serverless services, check out the `${cf:stackName.outputKey}` [variable resolution](https://www.serverless.com/framework/docs/providers/aws/guide/variables/) mechanism.

## Command-Line Options

Running `sls export-env` will, by default, only export _global_ environment variables into your `.env` file (those defined under `provider.environment` in your `serverless.yml`). If you want to generate the `.env` file for a specific function, pass the function name as a command-line argument as follows:
Expand Down Expand Up @@ -173,6 +175,8 @@ sls export-env --function hello --filename .env-hello

### 2.0.0

- Removed optimistic variable resolution for `Fn::GetAtt` as it was not working properly and caused more issues than it solved. If you rely on `Fn::GetAtt` in your environment variables, define a custom resolution using the `getAttMap` [configuration option](#Configuration-Options).

### alpha.1

- Added `--all` command line parameter to merge the environment variables of all functions into a single `.env` file. Please note that the behavior is _undefined_ if functions use conflicting values for the same environment variable name.
Expand Down
4 changes: 0 additions & 4 deletions src/lib/collectFunctionEnvVariables.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ const _ = require("lodash");
*/
function collectFunctionEnvVariables(serverless) {
const functions = _.get(serverless, "service.functions", {});
// const envVars = _.mapValues(
// _.mapKeys(functions, (func) => func.name),
// (func) => func.environment
// );
const envVars = _.mapValues(functions, (func) => func.environment);
return envVars;
}
Expand Down
67 changes: 37 additions & 30 deletions src/lib/resolveCloudFormationEnvVariables.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,43 @@ const BbPromise = require("bluebird"),
NodeEvaluator = require("cfn-resolver-lib");

function resolveGetAtt(refs, resource) {
const Partition = refs["AWS::Partition"];
const Region = refs["AWS::Region"];
const AccountId = refs["AWS::AccountId"];
switch (resource.ResourceType) {
case "AWS::Lambda::Function":
return {
Arn: `arn:${Partition}:lambda:${Region}:${AccountId}:function:${resource.PhysicalResourceId}`,
FunctionName: resource.PhysicalResourceId,
};
case "AWS::SNS::Topic":
return { TopicName: _.last(_.split(resource.PhysicalResourceId, ":")) };
case "AWS::SQS::Queue":
return { QueueName: _.last(_.split(resource.PhysicalResourceId, ":")) };
case "AWS::CloudWatch::Alarm":
return { AlarmName: _.last(_.split(resource.PhysicalResourceId, ":")) };
case "AWS::EC2::Subnet":
return { SubnetId: _.last(_.split(resource.PhysicalResourceId, ":")) };
case "AWS::EC2::VPC":
return { VpcId: _.last(_.split(resource.PhysicalResourceId, ":")) };
case "AWS::S3::Bucket":
return { BucketName: _.last(_.split(resource.PhysicalResourceId, ":")) };
case "AWS::EC2::SecurityGroup":
return { SecurityGroupId: _.last(_.split(resource.PhysicalResourceId, ":")) };
case "AWS::DynamoDB::Table":
return { TableName: _.last(_.split(resource.PhysicalResourceId, ":")) };
case "AWS::IAM::Role":
return { Arn: `arn:${Partition}:iam::${AccountId}:role/${resource.PhysicalResourceId}` };
case "AWS::ApiGateway::RestApi":
return { RootResourceId: resource.PhysicalResourceId };
}
// TODO: While this code was created in good intention (isn't it all?), it doesn't work in the current form.
// There's no AWS API that can help resolve !GetAtt automatically and some attributes are impossible to
// determine without retrieving additional details of the resource, e.g. using an additional API call.
// So, for now, we completely disable this variable resolution mechanism and rely of hardcoding the `getAttMap`
// in the config instead.
// Please note that the code below doesn't work properly.

// const Partition = refs["AWS::Partition"];
// const Region = refs["AWS::Region"];
// const AccountId = refs["AWS::AccountId"];
// switch (resource.ResourceType) {
// case "AWS::Lambda::Function":
// return {
// Arn: `arn:${Partition}:lambda:${Region}:${AccountId}:function:${resource.PhysicalResourceId}`,
// FunctionName: resource.PhysicalResourceId,
// };
// case "AWS::SNS::Topic":
// return { TopicName: _.last(_.split(resource.PhysicalResourceId, ":")) };
// case "AWS::SQS::Queue":
// return { QueueName: _.last(_.split(resource.PhysicalResourceId, ":")) };
// case "AWS::CloudWatch::Alarm":
// return { AlarmName: _.last(_.split(resource.PhysicalResourceId, ":")) };
// case "AWS::EC2::Subnet":
// return { SubnetId: _.last(_.split(resource.PhysicalResourceId, ":")) };
// case "AWS::EC2::VPC":
// return { VpcId: _.last(_.split(resource.PhysicalResourceId, ":")) };
// case "AWS::S3::Bucket":
// return { BucketName: _.last(_.split(resource.PhysicalResourceId, ":")) };
// case "AWS::EC2::SecurityGroup":
// return { SecurityGroupId: _.last(_.split(resource.PhysicalResourceId, ":")) };
// case "AWS::DynamoDB::Table":
// return { TableName: _.last(_.split(resource.PhysicalResourceId, ":")) };
// case "AWS::IAM::Role":
// return { Arn: `arn:${Partition}:iam::${AccountId}:role/${resource.PhysicalResourceId}` };
// case "AWS::ApiGateway::RestApi":
// return { RootResourceId: resource.PhysicalResourceId };
// }

return resource;
}
Expand Down

0 comments on commit d08ec6c

Please sign in to comment.