Terraform does not update AWS canary code - amazon-web-services

I have being changing an AWS canary code.
After running terraform apply, I see the updates in the new zip file but in AWS console the code is the old on.
What have I done wrong?
My terraform code:
resource "aws_synthetics_canary" "canary" {
depends_on = [time_sleep.wait_5_minutes]
name = var.name
artifact_s3_location = "s3://${local.artifacts_bucket_and_path}"
execution_role_arn = aws_iam_role.canary_role.arn
handler = "apiCanary.handler"
start_canary = true
zip_file = data.archive_file.source_zip.output_path
runtime_version = "syn-nodejs-puppeteer-3.3"
tags = {
Description = var.description
Entity = var.entity
Service = var.service
}
run_config {
timeout_in_seconds = 300
}
schedule {
expression = "rate(${var.rate_in_minutes} ${var.rate_in_minutes == 1 ? "minute" : "minutes"})"
}
}
I read this but it didn't help me.

I agree with #mjd2 but in the meantime worked around it by manually hashing the lambda source and embedding that hash into the source file name:
locals {
source_code = <whatever your source is>
source_code_hash = sha256(local.source_code)
}
data "archive_file" "canary_lambda" {
type = "zip"
output_path = "/tmp/canary_lambda_${local.source_code_hash}.zip"
source {
content = local.source_code
filename = "nodejs/node_modules/heartbeat.js"
}
}
This way, anytime the source_code is edited a new output filename will be used, triggering a replacement of the archive resource.

This could be a permission issue with your deployment role. Your role must have permission to modify the lambda behind the canary in order to apply the new layer that your zip file change creates.
Unfortunately any errors that occur when applying changes to the lambda are not communicated via terraform, or anywhere in the AWS console, but if it fails then your canary will continue to point to an old version of the lambda, without your code changes.
You should be able to see which version of the lambda your canary is using by checking the "Script location" field on the Configuration tab for your canary. Additionally, if you click in to the script location you will be able to see if you have newer, unpublished versions of the lambda layer available with your code changes in it.
To verify if the failure is a permission issue you need to query your canary via the AWS CLI.
Run aws synthetics get-canary --name <your canary name> and check the Status.StateReason.
If there was a permission issue when attempting to apply your change you should see something along the lines of:
<user> is not authorized to perform: lambda:UpdateFunctionConfiguration on resource: <lamdba arn>
Based on the above you should be able to add any missing permissions to your deployment roles iam policy and try your deployment again.

Hit the same issue. Seems like canary itself is a beta project that made it to production and the terraform resource that manages it also leaves much to be desired. There is no source_code_hash attr like with lambda, so you need to taint the entire canary resource so it gets recreated with any updated code. AWS Canary as of Nov 2022 is not mature at all. It should support integration with slack or at least AWS Chatbot out of the box, but it doesn't. Hopefully AWS team gives it some love because it's terrible as is right now in comparison to NewRelic, Dynatrace, and most other monitoring services that support synthetics.

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!

Error waiting for lambda provisioned concurrency Config(student-poc) to be ready: status reason: FUNCTION_ERROR_INIT_FAILURE)

Does anyone know how to resolve the above error? I'm trying to create an aws lambda, an alias on it and a provisioned concurrency for same. But on terraform apply it gives the error. I checked the forums which suggested to change into version, but I need to work on alias only. Here's my code-
module "student-poc"{
source =
publish = true
// General settings
}
resource "aws_lambda_alias" "live"{
name = "live"
function_name = module.student-poc.function_name
function_version= module.student-poc.version
}
resource "aws_lambda_provisoned_concurreny_config" "live"{
function_name = aws_lambda_alias.live.function_name
provisioned_concurrent_executions = 1
qualifier = aws_lambda_alias.live.name
}
I was finally able to understand, it was because my zipped lambda code had issues, it wasn't running, hence deploying it via terraform couldn't initialise it as well. I had to redeploy with correct zipped files and it worked. Also a tip- add a depends on clause so that it creates an alias first nd then goes for provisioned concurrency.

How to upgrade AWS MQ minor version in terraform in a non destructive manner

I have been playing around with using the terraform aws provider to create managed services such as Amazon MQ. I found this worked nicely overall but when testing changing the minor version (5.15.9 -> 5.15.10) this is a destructive process which would result in data loss if this was being performed on a live service.
I have read the documentation for the provisioner and did not any further config that would allow this to happen without destroying and recreating the broker(s). The only option I could see would be to manually update via AWS directly or allow minor updates to happen automatically.
Any feedback on if this can be done via Terraform? Otherwise I would imagine the best process might be some automation or manual pipeline using the AWS CLI to do upgrades but then the terraform state will be stale I would presume.
Edit:
See terraform for creating the broker below, I have removed content related to secret manager, security groups, subnets etc as it seemed to add bulk and not be related to the issue directly.
resource "aws_mq_broker" "broker" {
broker_name = "${var.env}_${var.team}_broker"
engine_type = "ActiveMQ"
engine_version = "5.15.9"
deployment_mode = "${lookup(var.team_env_config, "${var.env}_${var.team}_deployment_mode")}"
host_instance_type = "${lookup(var.team_env_config, "${var.env}_${var.team}_instance_type")}"
auto_minor_version_upgrade = false
}
When testing the upgrade I just changed 5.15.9 -> 5.15.10
The output is as follows:
deployment_mode: "SINGLE_INSTANCE" => "SINGLE_INSTANCE"
engine_type: "ActiveMQ" => "ActiveMQ"
engine_version: "5.15.9" => "5.15.10" (forces new resource)
That is just a snipped but as you can see the engine version change forces a new resource.

Can you clone an AWS lambda?

Cloning for different environments. Staging/QA/PROD/DEV etc.
Is there a quick an easy way to clone my lambdas, give a different name, and adjust configurations from there?
You will need to recreate your Lambda Functions in the new account. Go to lambda function click on Action and export your function .
Download a deployment package (your code and libraries), and/or an AWS
Serverless Application Model (SAM) file that defines your function,
its events sources, and permissions.
You or others who you share this file with can use AWS CloudFormation
to deploy and manage a similar serverless application. Learn more
about how to deploy a serverless application with AWS CloudFormation.
This is an example of terraform code(Infrastructure as Code) which can be used to stamp out same lambdas within different environment dev/prod.
If you have a look at this bit of code function_name = "${var.environment}-first_lambda" it will be clear as to how the name of the function is prefixed with environments like dev/prod etc.
This variable can be passed in at terraform command execution time eg TF_VAR_environment="dev" terraform apply or defaulted in the variables.tf or passed in using *.tfvars
#main.tf
resource "aws_lambda_function" "first_lambda" {
function_name = "${var.environment}-first_lambda"
filename = "${data.archive_file.first_zip.output_path}"
source_code_hash = "${data.archive_file.first_zip.output_base64sha256}"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "first_lambda.lambda_handler"
runtime = "python3.6"
timeout = 15
environment {
variables = {
value_one = "some value_one"
}
}
}
# variables.tf
variable "environment" {
type = "string"
description = "The name of the environment within the project"
default = "dev"
}

Exporting AWS Data Pipeline as CloudFormation template to use it in Terraform

I'm trying to export existing AWS Data Pipeline task to Terraform infrastructure somehow.
Accordingly, to this issue, there is no direct support for Data Pipelines, but it still seems achievable using CloudFormation templates (terraform resource).
The problem is that I cannot find a way to export existing pipeline into CloudFormation template.
Exporting the pipeline with its specific definition syntax won't work as I've not found a way to include this definition into CloudFormation. CloudFormer does not support exporting pipelines either.
Does anybody know how to export a pipeline to CloudFormation or any other way to get AWS Data Pipeline automated with Terraform?
Thank you for your help!
UPD [Jul. 2019]: Some progress has been made in the terraform repository. aws_datapipeline_pipeline resource has been implemented, but it is not yet clear how to use it. Merged pull request
Original answer:
As a solution to this problem, I've come up with a node.js script, which covers my use case. In addition, I've created a Terraform module to be used in Terraform configuration.
Here is the link to the gist with the code
Will copy usage examples here.
Command Line:
node converter-cli.js ./template.json "Data Pipeline Cool Name" "Data Pipeline Cool Description" "true" >> cloudformation.json
Terraform:
module "some_cool_pipeline" {
source = "./pipeline"
name = "cool-pipeline"
description = "The best pipeline!"
activate = true
template = "${file("./cool-pipeline-template.json")}"
values = {
myDatabase = "some_database",
myUsername = "${var.db_user}",
myPassword = "${var.db_password}",
myTableName = "some_table",
}
}