using Cloudformation ref with awscli in userData - aws-cli

"aws ec2 create-tags --resources xxxxxx --tags Key=Team,Value=everybody --region { \"Ref\" : \"region\" } --out text\n"
The above line/command I am using in my Cloudformation userData, It is not getting executed , I am getting the following error when I debugged :
aws: error: argument --region: Invalid choice, valid choices are:
ap-southeast-1 | us-gov-west-1
ap-northeast-1 | eu-west-1
fips-us-gov-west-1 | us-west-1
us-west-2 | us-east-1
cn-north-1 | ap-southeast-2
sa-east-1
My region name is taken as input parameter for Cloudformation script. Thats why I used ref in --region option.
Is this wrong ?
Is it possible to use ref with awscli commands in Cloudformation ?
Thanks

The UserData is a string in your Cloud Formation template, and therefore the {"Ref": "region"} is not expanded so the literal {"Ref": "region"} is being passed to the --region argument.
you could try
{"Fn::Join": [" ", ["aws ec2 create-tags --resources xxxxxx --tags Key=Team,Value=everybody --region", {"Ref": "region"}, "--out text\n"]]}
The docs provide information on the Fn::Join function http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html
Also this example template shows UserData including a Ref for the region parameter
https://s3.amazonaws.com/cloudformation-templates-us-east-1/vpc_single_instance_in_subnet.template

Related

Get most recent/newest image from ECR using terraform data source

I have an ECR repository named workflow and in this repository, there is 5 image pushed using GitHub action.
Now I have a terraform workflow that will just use the image from ECR and using this ECR image builds the ECS container definition.
so now I want to fetch the latest image with the tag whatever it would be...
I tried the below thing
data "aws_ecr_repository" "example" {
name = "workflow"
}
and then
"image": "${data.aws_ecr_repository.example.repository_url}"
but here I only get the Url for the repo without a tag
so how can I pass here the latest or newest image with the tag?
As terraform is not capable for this thing and you want to use still terraform in you are workplace then you can use terraform as an external data source
resource "aws_ecs_task_definition" "snipe-main" {
container_definitions = <<TASK_DEFINITION
[
{
"image":"${data.aws_ecr_repository.example.repository_url}:${data.external.current_image.result["image_tag"]}"
}
]
TASK_DEFINITION
}
data "external" "current_image" {
program = ["bash", "./ecs-task.sh"]
}
output "get_new_tag" {
value = data.external.current_image.result["image_tag"]
}
cat ECS-task.sh
#!/bin/bash
set -e
imageTag=$(aws ecr describe-images --repository-name <<here your repo name>> --query 'sort_by(imageDetails,& imagePushedAt)[-1].imageTags[0]')
imageTag=`sed -e 's/^"//' -e 's/"$//' <<<"$imageTag"`
jq -n --arg imageTag "$imageTag" '{"image_tag":$imageTag}'
exit 0
I was looking for the same, look if this documentation suites you
https://registry.terraform.io/providers/hashicorp/aws/2.34.0/docs/data-sources/ecr_image
it includes a way to obtain the image:
data "aws_ecr_image" "service_image" {
repository_name = "my/service"
image_tag = "latest"
}
the problem of that is that "image_uri" isnt in the resource.
There is an open issue in Github about it:
https://github.com/hashicorp/terraform-provider-aws/pull/24526
Meanwhile you can use this format for the url:
"${var.aws_account_id}.dkr.ecr.${var.region}.amazonaws.com/${var.project_name}:${var.latest-Tag}"

AWS cli --query is not returning expected JMES path

aws route53 list-hosted-zones --profile myprofile
is returning
{
"HostedZones": [
{
"Id": "/hostedzone/Z0874178161VQMKVVVJBT",
"Name": "mydomain.org",
"CallerReference": "RISWorkflow-RD:62a669b6-5465-4741-bb96-671e0be70b10",
"Config": {
"Comment": "HostedZone created by Route53 Registrar",
"PrivateZone": false
},
"ResourceRecordSetCount": 4
}
]
}
I want to get the Id value and only the ID value.
based on https://jmespath.org/tutorial.html
I think that the first command below should work but I cannot get the value of Id. I pasted the output into the JmesPath site and it thinks the first line should work
aws route53 list-hosted-zones --profile admin1 --query HostedZones[0].Id
zsh: no matches found: HostedZones[0].Id
aws route53 list-hosted-zones --profile admin1 --query HostedZones.Id
null
aws route53 list-hosted-zones --profile admin1 --query Id
null
You can get the id with
aws route53 list-hosted-zones --profile admin1 --query 'HostedZones[].{ID: Id}'

Terraform aws_lambda_function Requires Docker Image In ECR

I have a module that creates all the infrastructure needed for a lambda including the ECR that stores the image:
resource "aws_ecr_repository" "image_storage" {
name = "${var.project}/${var.environment}/lambda"
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
}
resource "aws_lambda_function" "executable" {
function_name = var.function_name
image_uri = "${aws_ecr_repository.image_storage.repository_url}:latest"
package_type = "Image"
role = aws_iam_role.lambda.arn
}
The problem with this of course is that it fails because when aws_lambda_function runs the repository is there but the image is not: the image is uploaded using my CI/CD.
So this is a chicken egg problem. Terraform is supposed to only be used for infrastructure so I cannot/should not use it to upload an image (even a dummy one) but I cannot instantiate the infrastructure unless the image is uploaded in between repository and lambda creation steps.
The only solution I can think of is to create ECR separately from the lambda and then somehow link it as an existing aws resource in my lambda but that seems kind of clumsy.
Any suggestions?
I ended up using the following solution where a dummy image is uploaded as part resource creation.
resource "aws_ecr_repository" "listing" {
name = "myLambda"
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
provisioner "local-exec" {
command = <<-EOT
docker pull alpine
docker tag alpine dummy_container
docker push dummy_container
EOT
}
}
Building off #przemek-lach's answer plus #halloei's comment, I wanted to post a fully-working ECR repository that gets provisioned with a dummy image
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
}
}

AWS CLI to filter images based on tags

I want to get AMI's which are not having a specific tag using AWS CLI. I do not know how to use not included filter.
Tried this BUT getting error.
aws ec2 describe-images --profile prod --owners asfhjaf --filter "Name=name,Values=UAT" "Name=tag-key,Values!=Delete" --query 'Images[*].[ImageId,CreationDate]'
One of the options that you can try is to use --query parameter along with --fitlers.
First filter key Name
Then query where not sometag
aws ec2 describe-images --owners asfhjaf --filters Name=tag-key,Values=Name --query 'Images[].Tags[?Value != `sometagvalue`]'
output
[
[
{
"Key": "Name",
"Value": "demo"
}
],
[],
]
or to remove the empty array
aws ec2 describe-images --owners asfhjaf --filters Name=tag-key,Values=Name --query 'Images[].Tags[?Value != `sometagvalue`]' | jq '[ .[] | select(length > 0) ]'
To print Image ID and creation Date
aws ec2 describe-images --owners abcd --filters Name=tag-key,Values=Name --query 'Images[].{Tags:Tags[?Value != `sensu`],ImageId:ImageId,CreationDate:CreationDate}' | jq '[ .[].Tags | select(length > 0) ]'

How to pass aws provider credentials to null_resource local-exec provisioner?

Has anyone come up with a decent way to do this?
In short, you have a provider "aws", configured via env vars or profile, with or without sts, it doesn't matter. Maybe you have several.
Now you want to call out to the aws cli because something isn't well implemented in the aws provider. In my case, I need to generate and upload some sensitive information directly to an S3 bucket that I do not want in the state file. In any case, it was s3 sync, so the action is idempotent.
However, there appears to be no way to pass the provider credentials - permanent, env var, profile to temporary sts - to a null_resource clause:
provider "aws" {
# set using explicit setting or profile or however
alias = "myaws"
}
resource "null_resource" "cli" {
provisioner "local-exec" {
command = "aws <do something>"
environment {
# happy to pass AWS_PROFILE or AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY here...
# if there were a way to retrieve it from the "myaws" provider
}
}
}
You can pass an AWS role_arn into a local-exec script. For example:
variable "aws_role" {
type = string
description = "AWS role for local exec to assume"
default = "arn:aws:iam::123456789012:role/DBMigrateRole"
}
resource "null_resource" "call-db-migrate" {
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
command = <<EOF
set -e
CREDENTIALS=(`aws sts assume-role \
--role-arn ${var.aws_role} \
--role-session-name "db-migration-cli" \
--query "[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]" \
--output text`)
unset AWS_PROFILE
export AWS_DEFAULT_REGION=us-east-1
export AWS_ACCESS_KEY_ID="$${CREDENTIALS[0]}"
export AWS_SECRET_ACCESS_KEY="$${CREDENTIALS[1]}"
export AWS_SESSION_TOKEN="$${CREDENTIALS[2]}"
aws sts get-caller-identity
EOF
}
}
Credit to https://github.com/hashicorp/terraform-provider-aws/issues/8242#issuecomment-586687360 .

Resources