using parameter store values in apprunner - amazon-web-services

I'm using aws copilot but i think i can generalize this question to apprunner. trying to get envvars set from Parameter Store but having no luck. the left is my aws copilot manifest yml, i saw examples of setting things this way. it results in an apprunner configed on the right. in production it seems these are interpreted as literals and not as parameter store values
any idea on how to properly connect apprunner to parameter store?

Unfortunately currently App Runner doesn't provide an intrinsic way for integrating with SSM parameter store like what ECS does. As a result, Copilot doesn't support secret section for Request-driven service as well (refer to Copilot doc here). As for environment variables, they are what you define in the manifest and will be injected as literals.
However, there is a workaround in Copilot allowing your app to use secrets stored in SSM parameter store. You can specify an addon template (e.g., policy.yaml) and put it in the copilot/${svc name}/addons/ local directory with the following template allowing the App Runner service to be able to retrieve from SSM parameter store:
Parameters:
App:
Type: String
Description: Your application's name.
Env:
Type: String
Description: The environment name your service, job, or workflow is being deployed to.
Name:
Type: String
Description: The name of the service, job, or workflow being deployed.
Resources:
MySSMPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: SSMActions
Effect: Allow
Action:
- "ssm:GetParameters"
Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*'
Outputs:
MySSMPolicyArn:
Value: !Ref MySSMPolicy
After that, in your code by using AWS SDK you can call AWS SSM API to retrieve any secrets you defined before. Let me know if you have any more questions!

Related

UI5 web application deployed by cloudformation, how to create SSM parameters so they can be accessible later?

I have a UI5 web application which has no access to AWS Parameter store. However, it is deployed by CloudFormation, by an account which has this access - I already collect some values from SSM during deployment and use them as parameters for resources which are being deployed by template.yml file.
However, now I need some of these parameters not only during deployment, but also during actual run of the app. Does someone familiar with AWS and UI5 know, how can I store these values during deployment so I can use them later, during the run of the web application? Thank you.
If you can edit the CloudFormation template then you can add resources of type AWS:SSM:Parameter to the template which will create SSM Parameters as part of the deployment.
Example:
Resources:
BasicParameter:
Type: AWS::SSM::Parameter
Properties:
Name: /my/basic/parameter
Type: String
Value: my-basic-parameter-value
Description: A description of the parameter

Can I force CloudFormation to resolve values from Secrets Manager?

In the following (abbreviated CloudFormation template), I am trying to configure an AWS Lambda function to get a value from AWS Secrets Manager injected into its environment:
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
SECRET_KEY: !Sub '{{resolve:secretsmanager:${Secret}:SecretString:KEY}}'
Secret:
Type: AWS::SecretsManager::Secret
Properties:
Name: 'very-secret-thing'
SecretString: '{"KEY":"dummy"}'
When creating a stack using this template, everything comes up as expected. I then go and change the value of the secret outside of CloudFormation, as I would not really want the secret checked into source control. This is totally possible, and the documentation implies, that the secret's value will not be touched subsequent CloudFormation stack updates, as long as I avoid changing the dummy value for SecretString in the template.
So, after setting the actual secret in the AWS Console, I need to trigger a redeploy of the Lambda function, so that the new secret value will be resolved by CloudFormation and set in the function's environment. How do I do that?
Executing aws cloudformation deploy fails with the message: No changes to deploy.
I suspect CloudFormation is comparing the "raw" version of the template with what was deployed last, without first resolving the references to Secrets Manager. Is that the case? And is there some trick to force earlier dereferencing?
Footnote: I am well aware that using Secrets Manager this way will cause the secret value to be visible in the AWS Lambda Console, and that getting the value from Secrets Manager at runtime would be the more secure approach. That just happens to be out-of-scope for what I am hoping to do.
You can artificially change something else on the AWS::Serverless::Function resource to force it to update when you do your deployment.
Say, for example, a timestamp:
Parameters:
DeployTimestamp: { Type: String }
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
SECRET_KEY: !Sub '{{resolve:secretsmanager:${Secret}:SecretString:KEY}}'
SECRET_KEY_UPDATED: !Ref DeployTimestamp
Assuming you do your deployment from a script, then you'd do something like aws cloudformation deploy --parameter-overrides "DeployTimestamp=$(date)" to change the value each time.
The downside to this, of course, is that the function will update every deployment, even if the secret hasn't updated. If that bothers you, you could get fancier and inject aws secretsmanager describe-secret --query LastChangedDate as a parameter instead of the current time.
You say that reading the value in the Lambda is out of scope, but that is really the correct solution. It not only improves security, but allows the Lambda to pickup the latest value when the secret is rotated.
If you read the secret outside the handler (that is to say, during initialization), the number of reads is minimized. If this is a java lambda connecting to a database, you could also use the secrets manager jdbc wrapper which will automatically fetch the secret.
Just to add for other readers who find this page, AWS published a solution to reference the version ID of the secret at the end of the key.
Resources:
SG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: '{{resolve:secretsmanager:mysecret:SecretString:MyKey::ab01234c-5d67-89ef-01gh-2ijk345l6m78}}'
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
CidrIp: 0.0.0.0/0
Now the thing is, you'll apparently have to do an API call to figure out what the versionId is since it's not on the console (which doesn't make any sense but it's AWS...).

AWS Serverless, CloudFormation : Error, Trying to populate non string value into a string for variable

I am using serverless framework for deploying my application on AWS Cloud.
https://serverless.com/
I want to use the value of AWS Account ID in my serverless.yml file and I want to export the acccount ID as the environment variable, so that it can be accessed from Lambda functions.
Based on the value of this lambda function, I want to create some resources ( like IAM roles, etc.), which refer to this accountId variable.
But when I try to deploy the stack, I get the below error,
Trying to populate non string value into a string for variable
${self:custom.accountId}. Please make sure the value of the property
is a string.
My Serverless.yml file is as below
custom:
accountId : !Ref "AWS::AccountId"
provider:
name: aws
runtime: go1.x
stage: dev
region: us-east-1
environment:
ACCOUNT_ID : ${self:custom.accountId}
myRoleArn: arn:aws:iam::${self:custom.accountId}:role/xxxxxxxx
Is there any way to refer to the value of the Account Id in the serverless.yml file?
You can't reference AWS::AccountId in your serverless.yml, because it doesn't quite translate when creating the CloudFormation template.
The workaround is to use the serverless plugin Pseudo Parameters.
You can install the plugin using npm.
npm install serverless-pseudo-parameters
You will also need to add the plugin to the serverless.yml file.
plugins:
- serverless-pseudo-parameters
Then you can reference your AccountId with #{AWS::AccountId}
functions:
helloWorld:
handler: index.handler
events:
- http:
path: /
method: get
environment:
ACCOUNT_ID : #{AWS::AccountId}
Note that the reference begins with a hash instead of a dollar sign.

How can I nest a serverless Step Function / State Machine / Lambda build into an existing AWS CloudFormation ElasticBeanstalk Application?

I have written a service using AWS Step Functions. I would like to integrate this into our applications existing Elastic Beanstalk development process, wherein we have distinct dev, staging, and production applications. Each of these stages have app-specific environment variables, which I would like to pull into my Lambda functions as well.
I am not presently using SAM but I can port over if necessary to accomplish this.
The following is a simplified configuration mirroring my serverless.yml file.
service:
name: small-service
plugins:
- serverless-webpack
- serverless-step-functions
- serverless-pseudo-parameters
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: us-east-2
iamRoleStatements:
- Effect: "Allow"
Action:
- "s3:*"
Resource: { "Fn::Join": ["", ["arn:aws:s3:::S3-bucket-name", "/*" ] ] }
functions:
connect:
handler: handler.connect
stepFunctions:
stateMachines:
smallService:
name: small-service-${self:provider.stage}
definition:
Comment: Service that connects to things
StartAt: Connect
States:
Connect:
Type: Task
Resource: arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${self:provider.stage}-connect
End: true
How can I dynamically deploy the step functions into different beanstalk applications? How can I access ElasticBeanstalk environment properties from within Step Functions?
Is there a better way to import environment .env variables dynamically into a Serverless application outside of EB? We are integrating the service into a larger AWS applications development workflow, is there is a more "serverless" way of doing this?
Move your environment variables into SSM Parameter Store. Then you can either
reference SSM parameters in your serverless.yaml, or
fetch SSM parameters in the beginning of each Lambda invocation (see e.g. here)
Note that the former method requires re-deploying your Lambda to receive the latest SSM parameters whereas the latter always fetches the latest parameter values.

Access SSM parameters from parameter store

I have SAM template which deploys few lambdas and I would like to use some parameters I created in SSM parameters store.
I created 2 parameters for my tests:
/test/param which is a simple string
/test/param/encrypt which contains the same string as /test/param but is encrypted by a KMS key
In my SAM template, I'm trying to get the the value of /test/params by following this blog post. Here is a snipper of my template:
Parameters:
AuthPasswordPublic:
Type: AWS::SSM::Parameter::Value<String>
NoEcho: true
MinLength: 8
Description: Password for the "public" part of the website
Default: /test/param
...
Resources:
Auth:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs8.10
Handler: auth.handler
CodeUri: ./dist
Environment:
Variables:
PASSWORD_PUBLIC: !Ref AuthPasswordPublic
SEED: !Ref AuthSeed
Events:
GetResource:
Type: Api
Properties:
Path: /auth
Method: post
This should theoretically works when deployed onto AWS. However, I would like to test it locally first. I'm already aws-sam-local and my credentials are properly configured on my local machine as I'm able to use the AWS CLI. But when running this locally, the value of the envvar PASSWORD_PUBLIC is empty. I tested both the plain text en encrypted SSM parameters but the results are the same.
I would suspect that aws-sam-cli does not support SSM parameters yet but couldn't find any information about that online, nor on the GitHub issues/PR. Any ideas of what is going on here?
aws-sam-cli uses the docker-lambda container, which according to the docs creates:
A sandboxed local environment that replicates the live AWS Lambda environment almost identically...
This means that components such as AWS SSM are not re-created within the docker container. You can check the open Github issue here.
So you may have to resort to retrieving the SSM parameters from the host (with aws cli configured), and pass them into the container when invoking sam-cli:
PASSWORD_PUBLIC=$(aws ssm get-parameter --with-decryption --name "/test/param/encrypt") sam local start-api