An AWS Lambda microservice providing limited read-only access to MIP Cloud.
This microservice is designed to retrieve data using the MIP Cloud API and present the data in a useful format.
Formats available:
| API Route | Description |
|---|---|
| /accounts | A dictionary mapping the chart of program accounts to their friendly names. |
| /balances | A CSV listing GL account balances, appropriate for FloQast consumption. |
| /tags | A list of valid tag values for either CostCenter or CostCenterOther. |
Since we reach out to a third-party API across the internet, responses are cached to minimize interaction with the API and mitigate potential environmental issues (e.g. packet loss).
In the event of a cache miss in Cloudfront, an API gateway request will trigger the lambda, which will read login credentials from secure parameters in SSM, query MIPS for the latest chart of accounts, and return a JSON mapping of the data to be stored in Cloudfront for a default of one day.
In the event of a cache hit, Cloudfront will return the cached value without triggering an API gateway event.
By default, the lambda will process the chart of accounts received to remove inactive codes, deduplicate the significant portion of active codes, and add a "No Program" entry mapped to a code configured in a template parameter. Each of these actions can be toggled with query-string parameters, except for deduplication.
Specific account codes from the chart of accounts can be ignored globally with
the CodesToOmit template parameter. Remaining codes will be returned in
numeric order as either a list of strings or a json dictionary depending on the
API route.
When retrieving a CSV of current balances, the full chart of accounts will be processed according to query-string parameters, and then collated with MIP trial balance data.
During the first week of the month, the balance will be calculated for the previous month; otherwise a month-to-date balance will be calculated.
User credentials for logging in to the finance system are stored as secure
parameters with a configurable prefix. By default, the prefix is
/lambda/mipsSecret, resulting the following required secure parameters:
/lambda/mipsSecret/user/lambda/mipsSecret/pass
The following template parameters are used to configure CloudFormation resources
| Template Parameter | Description |
|---|---|
| CacheTTL | TTL value for CloudFront cache objects. |
| SsmAliasPrefix | Prepend this value to the Alias Name of the KMS key created. |
| SsmKeyAdminArn | Grant KMS admin access to these ARNs for managing secure secrets. |
The following template parameters are set as environment variables in the lambda environment:
| Template Parameter | Environment Variable | Description |
|---|---|---|
| MipOrganization | MipOrg | Log in to this organization in the finance system. |
| SsmParamPrefix | SsmPath | Path prefix for required secure parameters. |
| CodesToOmit | CodesToOmit | List of numeric codes to remove from output. |
| NoProgramCode | NoProgramCode | Numeric code to use for "No Program" entry. |
| OtherCode | OtherCode | Numeric code to use for "Other" entry. |
Several query-string parameters are shared by the endpoints to configure the
list of accounts in the response output. All query string parameters except for
year_to_date effect the /accounts and /tags endpoints. The only parameters
that effect the /balances endpoint are show_inactive_codes and
target_date.
| Query String Parameter | Allowed Values | Default | Supported Endpoints |
|---|---|---|---|
| show_other_code | Boolean value | False | /accounts, /tags |
| hide_no_program_code | Boolean value | False | /accounts, /tags |
| show_inactive_codes | Boolean value | False | /accounts, /balances, /tags |
| priority_codes | Comma-separated list of numeric codes | Empty list | /accounts, /tags |
| limit | Integer | 0 (unlimited) | /accounts, /tags |
| target_date | ISO 8601 string | Today | /balances |
Boolean values: any value other than "no", "off", or "false" will be interpreted as true, including the empty string.
A show_other_code parameter is available to optionally include an "Other"
entry in the output with a value from the OtherCode parameter. Defining any
non-false value for this parameter will include the code.
A hide_no_program_code parameter is available to prevent the default addition
of a "No Program" entry, otherwise it will be added with the value from the
NoProgramCode parameter. Defining any non-false value for this parameter will
prevent the addition of a "No Program" entry.
A show_inactive_codes parameter is available to prevent the default removal of
inactive codes. If this parameter is set to any non-false value, inactive codes
will be included in the output.
By default, the output is ordered by numeric code. A priority_codes parameter
is available to select specific programs to prioritize to the beginning of the
output regardless of their code; OtherCode andNoProgramCode are always
prioritized when included. Example value: 123456,654321.
A limit parameter is available to restrict the number of items returned. This
value must be a positive integer, a value of zero disables the parameter.
A target_date parameter is available to calculate a balance period from a day
other than today.
The CloudFormation template will output all available endpoint URLs for
triggering the lambda, e.g.: https://abcxyz.cloudfront.net/accounts
https://abcxyz.cloudfront.net/tags
These URLs can be also constructed by appending the API Gateway paths to the CloudFormation domain.
The CloudFormation template also outputs the origin URL behind the CloudFront distribution for debugging purposes.
The /accounts endpoint will return a json string representing a dictionary
mapping numeric account codes to their friendly names.
E.g.:
/accounts?show_inactive_codes=on
{
"000000": "No Program",
"123456": "Duplicate 1",
"54321": "Inactive",
"990300": "Platform Infrastructure"
}The /tags endpoint will return a json string representing a list of valid
values for CostCenter tags. A valid CostCenter tag value takes the form
'{Friendly Name} / {Numeric Code}'.
E.g.:
/tags?show_other_code=on
[
"No Program / 000000",
"Other / 000001",
"Duplicate 1 / 123456",
"Platform Infrastructure / 990300"
]This lambda is expected to run less frequently than the 15-minute expiry for lambda environments, which means most lambda runs will require a cold start. We also expect the third-party API data to change much less frequently than the lambda is called. And so we add a CloudFront cache to store the lambda responses, reducing our reliance on the third-party API and minimizing the impact of the lambda cold starts by calling the lambda less frequently.
If a bad response has been cached, it may need to be manually invalidated through Cloudfront.
In addition to a web cache storing responses sent from the lambda, we also have a write-through cache of the successful responses received from the upstream API written to an S3 object in order to add fault tolerance in the event of upstream API failures. And bucket versioning for the cache object gives us the ability to access any previous successful upstream API responses, historic data that may be useful during disaster recovery. Alternatively, during upstream API outages any arbitrary cache object written to S3 by a cloud admin would be treated as authoritative upstream information.
For more information on S3 versioning workflows, see How S3 Versioning Works and Restoring Previous Versions.
Contributions are welcome.
Run pipenv install --dev to install both production and development
requirements, and pipenv shell to activate the virtual environment. For more
information see the pipenv docs.
After activating the virtual environment, run pre-commit install to install
the pre-commit git hook.
First, make any needed updates to the base requirements in Pipfile, then use
pipenv to regenerate both Pipfile.lock and requirements.txt.
$ pipenv update --devWe use pipenv to control versions in testing, but sam relies on
requirements.txt directly for building the lambda artifact, so we dynamically
generate requirements.txt from Pipfile.lock before building the artifact.
The file must be created in the CodeUri directory specified in
template.yaml.
$ pipenv requirements > requirements.txtAdditionally, pre-commit manages its own requirements.
$ pre-commit autoupdateUse a Lambda-like docker container to build the Lambda artifact
$ sam build --use-containerTests are defined in the tests folder in this project, and dependencies are
managed with pipenv. Install the development dependencies and run the tests
using coverage.
$ pipenv run coverage run -m pytest tests/ -svvAutomated testing will upload coverage results to Coveralls.
Running integration tests requires docker
$ sam local invoke Function --event events/event.jsonDeployments are sent to the
Sage cloudformation repository
which requires permissions to upload to Sage
bootstrap-awss3cloudformationbucket-19qromfd235z9 and
essentials-awss3lambdaartifactsbucket-x29ftznj6pqw buckets.
sam package --template-file .aws-sam/build/template.yaml \
--s3-bucket essentials-awss3lambdaartifactsbucket-x29ftznj6pqw \
--output-template-file .aws-sam/build/lambda-mips-api.yaml
aws s3 cp .aws-sam/build/lambda-mips-api.yaml s3://bootstrap-awss3cloudformationbucket-19qromfd235z9/lambda-mips-api/master/Publishing the lambda makes it available in your AWS account. It will be accessible in the serverless application repository.
sam publish --template .aws-sam/build/lambda-mips-api.yamlMaking the lambda publicly accessible makes it available in the global AWS serverless application repository
aws serverlessrepo put-application-policy \
--application-id <lambda ARN> \
--statements Principals=*,Actions=DeployCreate the following sceptre file config/prod/lambda-mips-api.yaml
template:
type: http
url: "https://PUBLISH_BUCKET.s3.amazonaws.com/lambda-mips-api/VERSION/lambda-mips-api.yaml"
stack_name: "lambda-mips-api"
stack_tags:
Department: "Platform"
Project: "Infrastructure"
OwnerEmail: "[email protected]"Install the lambda using sceptre:
sceptre --var "profile=my-profile" --var "region=us-east-1" launch prod/lambda-mips-api.yamlSteps to deploy from AWS console.
- Login to AWS
- Access the serverless application repository -> Available Applications
- Select application to install
- Enter Application settings
- Click Deploy
We have setup our CI to automate a releases. To kick off the process just create a tag (i.e 0.0.1) and push to the repo. The tag must be the same number as the current version in template.yaml. Our CI will do the work of deploying and publishing the lambda.
