Updating Lambda using CDK doesn't deploy latest image - amazon-web-services

Using the AWS C# CDK.
I get a docker image from an ECR repository & then create a lambda function using it.
The problem is that when I run the CDK, it clearly creates CloudFormation that updates the function. Within the AWS console, the latest image is then shown under "Image > Image URI". However the behaviour of my lambda clearly shows that the latest image has NOT been deployed.
If I click "Deploy New Image", leave everything as normal & click Save, my Lambda then shows that it is updating & then the behaviour of my lambda is as expected (latest image).
Unsure where I'm going wrong:
var dockerImageCode = DockerImageCode.FromEcr(ecrRepositoryContainingImage);
var dockerImageFunction = new DockerImageFunction(this,
Constants.LAMBDA_ID,
new DockerImageFunctionProps()
{
Code = dockerImageCode,
Description = versionString,
Vpc = foundationStackVpc,
SecurityGroups = new ISecurityGroup[]
{
securityStackVpcSecurityGroup
},
Timeout = Duration.Seconds(30),
MemorySize = 512
});
It is almost like, my lambda gets updated & shows that it is apparently pointing at the correct image within ECR. However the reality is, that it is not actually deployed.
Edit: A temporary fix is to ensure that rather than pushing a new image:latest image to ECR, I now call it image:buildnumber. It seems that even if the image in ECR is underlyingly different & cdk has supposedly updated the lambda image reference to the newly uploaded one in ECR, it doesn't actually redeploy/consider a change has occurred worthy of redeployment when the old image tag & new image tag are both named the same, in this case latest. Now since the build number will always be different & thus the new image tag will always be different to the previous one, this is deemed enough of a change for the lambda to be redeployed properly.

When using API fromEcr, you can specify EcrImageCodeProps with specified image tag.
See doc for detail.

The tag: latest did not work for me also. I think an easy way is to use SSM in the CodeBuild
'aws ssm put-parameter --name FhrEcrImageTagDemo --type String --value ${CODEBUILD_RESOLVED_SOURCE_VERSION} --overwrite'
Then in CDK lambda
code: aws_lambda.Code.fromEcrImage(
aws_ecr.Repository.fromRepositoryName(
this,
'id',
'ecrRepositoryName',
),
{
tag: aws_ssm.StringParameter.valueForStringParameter(
this,
'parameterName'
)
}
)
Another potential solution is using the exported variables and override parameters in this example class TagParameterContainerImage. It works for ecs but not sure for lambda and ecr.

Related

"Force" docker image creation in Terraform with docker_registry_image (kreuzwerker/docker)

I am developing series of lambdas that are using docker images. The first step is to create them and registering in AWS ECR (not sure if everything I am doing is ok, so any advice is welcomed :-) ):
terraform {
...
required_providers {
docker = {
source = "kreuzwerker/docker"
version = ">= 2.12"
}
}
}
resource aws_ecr_repository lambda_repo {
name = "lambda"
}
resource docker_registry_image lambda_image {
name = "<account_id>.dkr.ecr.<region>.amazonaws.com/lambda:latest"
build {
context = "./code/lambda"
}
depends_on = [
aws_ecr_repository.lambda_repo
]
keep_remotely = true
}
resource aws_lambda_function lambda {
...
image_uri = "<account_id>.dkr.ecr.<region>.amazonaws.com/lambda:latest"
source_code_hash = docker_registry_image.lambda_image.sha256_digest
...
}
So with this code:
docker_registry_image > lambda_image : build the image and uploaded it in AWS
aws_lambda_function > lambda : if the image "lambda:latest" the lambda is updated with the new code
The problem I have is how to "force" docker_registry_image > lambda_image to rebuild the image and update the "lambda:latest" when the Dockerfile or app.py (the main code that is added in the file) has changed. Also I am not sure if this is the way to build the images.
Thanks!!
I was stuck with the exact same problem, and was disappointed to find your question hadn't been answered. I struggled a good bit, but I just clicked late tonight and got mine working.
The problem is incorrect thinking based on bad Docker habits (guilty of the same here!):
latest is a bad habit: it's based on tag mutability, which isn't how docker was designed, and pulling latest is non-deterministic, anyway - you never know what you're going to get. Usually, latest will pull the most recent version on a docker pull.
More on tag immutability: as developers, when encountering a small bug, we often quickly rebuild and overwrite the existing image with the same tag because "nobody will know, and it's just a small fix".
Thinking of the Lambda code files as something with state that should trigger a Terraform replace - the code files are not a resource, they're an asset for creating the Lambda.
Here is the better way to think about this:
docker_registry_image and the kreusewerker/docker provider are based on tag immutability.
docker_registry_image gets "replaced" in Terraform state (you'll see that in the Terraform plan when you try it), but the
effect in your ECR repository is to add a new image with a the next
sequential tag number, not to replace the actual image as one
usually thinks with Terraform.
Each change to your Lambda source code files requires a new image build with a new tag number.
If you find your images piling up on you, then you need a lifecycle policy to automate managing that.
Looking at your code, here is the solution:
Create a new variable called image_tag:
variable "image_tag" {
default = 1
}
Modify your docker_registry_image so that it uses the image_tag variable (also touching up the docker_registry_image name so you're not doing to much error-prone string building):
resource docker_registry_image lambda_image {
name = "${aws_ecr_repository.lambda_repo.repository_url}:${var.image_tag}"
build {
context = "./code/lambda"
}
...
}
Modify your aws_lambda_function. Change the image_uri to the name of the docker_registry_image so that those two are never out of sync:
resource aws_lambda_function lambda {
image_uri = docker_registry_image.lambda_image.name
source_code_hash = docker_registry_image.lambda_image.sha256_digest
}
Each time you change the code in the Lambda's source file(s), increment the image_tag variable by 1. Then try a terraform plan and you'll see that the docker_registry_image and aws_lambda_function will be replaced. A good exercise, would be to look at your ECR repo and Lambda function in the console while you do this. You'll see the images appearing in your ECR repo, and the Lambda function's image uri being updated with the new image_tag.
If you don't like how your image versions are piling up in your ECR repo, look into implementing a ecr_lifecycle_policy
Hope this helps. I sure feel a whole lot better tonight!

Terraform: Is it possible to define an empty image based lambda?

When defining a lambda of package_type = Zip, it's possible to create a dummy temp.zip file and set it as the lambda's filename.
When created, the lambda will basically have an empty zip, which can later be replaced by something like a continuous delivery pipeline that pushes an artifact to it.
I've noticed this pattern used at work.
However, I'm playing with lambda container images for something personal.
I set it package_type = Image, and set other required arguments (per the Terraform docs). But when I run terraform apply, I get an error saying the lambda's image_uri argument must be set.
What if I don't have an image built yet? Is there some equivalent technique to satisfy the image_uri requirement, to essentially create an "empty" lambda, which I later plan to update via a CD pipeline?
Been looking around but have not yet found a solution.
What if I don't have an image built yet?
Then you can't create container lambda. You have to provide some image url. It can be dummy image that does nothing, but it must preexist before you can create such a lambda function.
Then later you can update the dummy image with something else.
Yes you can! This question has already been answered here, which I've copied below
data "aws_ecr_authorization_token" "token" {}
resource "aws_ecr_repository" "repository" {
name = "lambda-${local.name}-${local.environment}"
image_tag_mutability = "MUTABLE"
tags = local.common_tags
image_scanning_configuration {
scan_on_push = true
}
lifecycle {
ignore_changes = all
}
provisioner "local-exec" {
# This is a 1-time execution to put a dummy image into the ECR repo, so
# terraform provisioning works on the lambda function. Otherwise there is
# a chicken-egg scenario where the lambda can't be provisioned because no
# image exists in the ECR
command = <<EOF
docker login ${data.aws_ecr_authorization_token.token.proxy_endpoint} -u AWS -p ${data.aws_ecr_authorization_token.token.password}
docker pull alpine
docker tag alpine ${aws_ecr_repository.repository.repository_url}:SOME_TAG
docker push ${aws_ecr_repository.repository.repository_url}:SOME_TAG
EOF
}
}

Setting up Fargate Task Definition in CDK

I am trying to set up a task definition for a Fargate container running from the latest image in a ECR repository. However, I can really get it to work. I am having this code so far:
const Repository = new ecr.Repository(this, 'Repository');
const TaskDefinition = new ecs.FargateTaskDefinition(this, "TaskDefinition", {})
TaskDefinition.addContainer("Container", {
image : ecs.EcrImage.fromEcrRepository(Repository, "latest")
})
However when I run it, I get Argument of type 'Repository' is not assignable to parameter of type 'IRepository'. I am trying to follow the documentation and have tried writing it a few different ways, but always the same error.

AWS CDK create Lambda from image

I am a new to AWS world. I am working on a project to build a server less application and as part of that I have created 4 lambda which works fine.
Next I am trying to create a deployment pipe line using CDK; below is what I am trying to do.
create an docker image with code that includes all lambda code
create 4 different lambdas from same image just override the CMD in docker image and mention the lambda handler
I have setup CDK locally and able to create stack, everything works fine.
Below is my code snippet
--create the docker image
asset_img = aws_ecr_assets.DockerImageAsset(
self,
"test_image",
directory=os.path.join(os.getcwd(), "../mysrc")
)
--create lambda from docker image
aws_lambda.DockerImageFunction(
self,
function_name="gt_from_image",
code=_lambda.DockerImageCode.from_ecr(
self,
repository=asset_img.repository,
tag="latest")
)
Below is the error I am getting
TypeError: from_ecr() got multiple values for argument 'repository'
I am not sure how I can reference the image that was created and define the lambda.
Solved: Below is the solution
asset_img = _asset.DockerImageAsset(self, "test_image", directory=os.path.join(os.getcwd(), "../gt"))
_lambda.DockerImageFunction(self, id='gt_from_image', function_name="gt_from_image_Fn",
code=_lambda.DockerImageCode.from_ecr(
repository=asset_img.repository,
tag=asset_img.source_hash))
From the documentation for DockerImageCode.from_ecr(), it does not expect a scope argument, so your self argument is what is causing the error.
Another issue is that DockerImageAsset will not tag the image as latest, as that is against AWS best practices.
The easy way to achieve what you are doing is to use
DockerImageCode.from_image_asset().

AWS: Delete lambda layer still retains layer version history

I am deploying a AWS Lambda layer using aws cli:
aws lambda publish-layer-version --layer-name my_layer --zip-file fileb://my_layer.zip
I delete it using
VERSION=$(aws lambda list-layer-versions --layer-name my_layer | jq '.LayerVersions[0].Version'
aws lambda delete-layer-version --layer-name my_layer --version-number $VERSION
Deletes successfully, ensured no other version of the layer exists.
aws lambda list-layer-versions --layer-name my_layer
>
{
"LayerVersions": []
}
Upon next publishing of the layer, it still retains history of previous version. From what I read if no layer version exists and no reference exists, the version history should be gone, but I don't see that. Anybody have a solution to HARD delete the layer with its version?
I have the same problem. What I'm trying to achieve is to "reset" the version count to 1 in order to match code versioning and tags on my repo. Currently, the only way I found is to publish a new layer with a new name.
I think AWS Lambda product lacks of features that help (semantic) versioning.
Currently there's no way to do this. Layer Versions are immutable, they cannot be updated or modified, you can only delete and publish new layer versions. Once a layer version is 'stamped' -- there is no way (AFAIK) that you can go back and get back that layer version.
It might be that after a while (weeks...months?) AWS delete it's memory of the layer version, but as it stands, the version number assigned to any deleted layer cannot be assumed by any new layer.
I ran into similar problem with layer versions. Do you have suggestions for simple way out instead of writing code to check available versions out there and pick latest one...
I am facing the same issue, As there was no aws-cli command to delete the layer itself,I had delete all versions of my lambda-layer using:
aws lambda delete-layer-version --layer-name test_layer --version-number 1
After deleting all versions of the layer, the layer was not showing in aws lambda layers page,So I thought its successfully deleted.
But to my surprise,AWS stills keeps the data about our deleted layers(at-least the last version for sure), when you try create a lambda layer with your previous deleted lambda name using aws gui or cli, the version will not start from 1, instead it starts from last_version of assumed to be deleted layer.