惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
aimingoo的专栏
aimingoo的专栏
F
Fortinet All Blogs
Blog — PlanetScale
Blog — PlanetScale
GbyAI
GbyAI
MongoDB | Blog
MongoDB | Blog
月光博客
月光博客
The Cloudflare Blog
量子位
T
Tailwind CSS Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
MyScale Blog
MyScale Blog
T
The Blog of Author Tim Ferriss
The GitHub Blog
The GitHub Blog
G
Google Developers Blog
D
DataBreaches.Net
V
Visual Studio Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
U
Unit 42
博客园 - 聂微东
有赞技术团队
有赞技术团队
A
About on SuperTechFans

Nik Ogura

Writing for Generation Ships | Nik Ogura Load-Bearing Humans | Nik Ogura Your Hiring Pipeline Has the Same Bug as Your Deploy Pipeline | Nik Ogura Nice People Who Give Us Money | Nik Ogura Gambling on Failure | Nik Ogura DDCRI: Declarative, Deterministic, Continuously Reconciling Infrastructure | Nik Ogura Stop Holding Out for a Hero | Nik Ogura Don't Paint Yourself Into a Corner | Nik Ogura Most Infrastructure as Code Is Broken — and Reconciliation Is Only Half the Reason | Nik Ogura Continuous Acceptance Tests | Nik Ogura There's More Than One Way to Get Observability Right | Nik Ogura Put Dex In Front of Google OAuth | Nik Ogura Incident Management | Nik Ogura C-Style Thinking vs Go-Style Thinking | Nik Ogura 'Can' vs 'Does' | Nik Ogura Control Repositories | Nik Ogura Trunk-Based Development | Nik Ogura Web3 Is Just Infrastructure With a Hoodie | Nik Ogura "Design Me a Highly Resilient Database" | Nik Ogura Security Is Infrastructure | Nik Ogura Metrics, Logs, Traces, and Events: What's Actually Different | Nik Ogura Distributed Tracing: A Practical Guide | Nik Ogura Prometheus and OpenTelemetry: How They Fit Together | Nik Ogura Puppets and Octopi: Why Top-Down Orchestration Hits a Wall | Nik Ogura The Best Dog Trainer in the World - Or Why Getting Better Isn't Helping | Nik Ogura FluxCD vs ArgoCD: Architectural Comparison | Nik Ogura GitOps | Nik Ogura GitHub Actions Reference Implementation | Nik Ogura Shell Functions | Nik Ogura Engineering Standards | Nik Ogura
Auto Updating AMI's on a Rolling Window with Terraform | ...
2021-02-08 · via Nik Ogura

So, I recently had a connundrum. I want my AWS EC2 ASG’s to continuously update without manual intervention, but I don’t want to ‘poison’ a launch config with a bad AMI image and cause a prod outage when the ASG eventually scales up.

One idea I had was to pre-build my AMI’s, and pre-test the snot out of them before I used them in staging or production. Duh, right?

Sadly, the environments I have are not the environments I want. While I do want to achieve the blessed nirvana of being able to spin up new environments ‘just cos’, test them, and then let them vanish back into the ether, I’m a long way from that enlightened state. There are too many tentacles in this legacy octopus code. sigh.

Instead, I’m forced to rely on my staging enviornment to catch problems. While not perfect, that staging env has been good enough to test prod deploys since before I joined this project. If an AMI has been running in my staging environment without issues for a week or two, the AMI is probably good to go.

Terraform, however, allows me a ‘latest’ on it’s AMI lookup, but not a ‘latest before date X’. What’s a guy to do?

Here’s what I came up with. It’s not particularly pretty, but it appears to work.

First, the basics:

    terraform {
    required_version = ">= 0.12"
    }

    provider "aws" {
    region  = "us-east-1"
    }

Then we define the owner of the AMI, and a pattern for the name. In this case the owner is Canonical, and the image is Ubuntu.

    variable "ami_owner" {
        description = "AWS Account number used to look up AMI's."
        default = "099720109477"
    }

    variable "ami_name" {
        description = "AMI name pattern used to look up AMI's"
        default = "ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"
    }

Next, we pull a list of the images

    data "aws_ami_ids" "bases" {
        filter {
            name   = "name"
            values = [var.ami_name]
        }

        filter {
            name   = "virtualization-type"
            values = ["hvm"]
        }

        owners = [var.ami_owner]
    }

Terraform is funny. I can get a list of the ID’s, but it’s just a bunch of strings. I have to look up the actual AMI’s themselves thusly:

    data "aws_ami" "base" {
        count = length(data.aws_ami_ids.bases.ids)
        owners = [var.ami_owner]
        filter {
            name = "image-id"
            values = [data.aws_ami_ids.bases.ids["${count.index}"]]
        }
    }

Now I can get down and dirty. We use locals to declare local variables I can use later in actual resources.

Where this gets really wild is, since I can’t convert to unix time, and I can only use operators like ’<’ on numbers, I have to ‘format’ the numbers in such a way that I can do a purely numeric comparison on them to build my rolling time window.

Then I can sort the keys, which works in lexicographic order - and turns out just fine given the numeric dates, and reverse it, cos I want the latest.

Finally I can pull the first element off the numeric list and pull lookup the ami id of that element. Voila!

    locals {
        // window - 2 weeks ago in hours.  Terraform can do hours, but not 'days'.
        time_window = "-336h"

        // 2 weeks prior to the moment terraform is run, formatted as a pure number.
        two_weeks_ago = formatdate("YYYYMMDDhhmmss", timeadd(timestamp(), local.time_window))

        // all images.  This hash is just so I can check my work visually.
        all_amis = {for i in data.aws_ami.base : formatdate("YYYYMMDDhhmmss", i.creation_date) => i.id}

        // images more than 2 weeks old.  We only add them to the hash if the date is less than right now.  I make a hash, cos while I'm going to sort on the creation date, I eventually need to return the ID of the ami.
        older_amis = {for i in data.aws_ami.base : i.creation_date => i.id if formatdate("YYYYMMDDhhmmss", i.creation_date) < formatdate("YYYYMMDDhhmmss", timeadd(timestamp(), local.time_window))}

        // sort the keys, which are the dates, then reverse it so the newest is at the top
        dates = reverse(sort(keys(local.older_amis)))

        // take the top element.  That will be the newest within the list.
        latest_date = local.dates[0]

        // set the value in a variable I can use elsewhere.
        ami_id = local.older_amis[local.latest_date]

    }

I make outputs so I can see what I did:

    output "timeWindow" {
        value = local.time_window
    }

    output "twoWeeksAgo" {
        value = local.two_weeks_ago
    }

    output "allAmis" {
        value = local.all_amis
    }

    output "olderAmis" {
        value = local.older_amis
    }

    output "ami_id" {
        value = local.ami_id
    }

Run terraform apply on this and you get something like:

    Outputs:

    allAmis = {
    "20180426220041" = "ami-7ad76705"
    "20180522173154" = "ami-432eb53c"
    "20180613234855" = "ami-85f9b8fa"
    "20180622194657" = "ami-5cc39523"
    "20180725160717" = "ami-b04847cf"
    "20180807104617" = "ami-920b10ed"
    "20180815001804" = "ami-0b425589c7bb7663d"
    "20180828123510" = "ami-07917569e2c4a2b6a"
    "20180912210417" = "ami-0ac019f4fcb7cb7e6"
    "20181013151116" = "ami-0977029b5b13f3d08"
    "20181106153828" = "ami-05aa248bfb1c99d0f"
    "20181126175259" = "ami-0d2505740b82f7948"
    "20181203192256" = "ami-0edd3706ab2e952c4"
    "20190117172225" = "ami-0b86cfbff176b7d3a"
    "20190123024731" = "ami-012fd5eb46f56731f"
    "20190210210246" = "ami-07073342279f98d28"
    "20190213124822" = "ami-0a313d6098716f372"
    "20190321160109" = "ami-07025b83b4379007e"
    "20190403221623" = "ami-0fba9b33b5304d8b4"
    "20190501114518" = "ami-0273df992a343e0d6"
    "20190515091552" = "ami-024a64a6685d05041"
    "20190531214333" = "ami-079f96ce4a4a7e1c7"
    "20190618052823" = "ami-095192256fe1477ad"
    "20190627214607" = "ami-026c8acd92718196b"
    "20190725194907" = "ami-07d0cf3af28718ef8"
    "20190819181119" = "ami-064a0193585662d74"
    "20190912044329" = "ami-024582e76075564db"
    "20190918222933" = "ami-05ecb1463f8f1ee4b"
    "20191002232841" = "ami-04b9e92b5572fa0d1"
    "20191010221339" = "ami-0607bfda7f358db2f"
    "20191021222116" = "ami-0d5ae5525eb033d0a"
    "20191113192803" = "ami-00a208c7cdba991ea"
    "20200115003046" = "ami-07ebfd5b3428b6f4d"
    "20200204185151" = "ami-046842448f9e74e7d"
    "20200312205548" = "ami-0238c6e72a7e906fc"
    "20200317210532" = "ami-055df5de4f42cf331"
    "20200324205616" = "ami-0a4f4704a9146742a"
    "20200409164413" = "ami-085925f297f89fce1"
    "20200507134600" = "ami-05801d0a3c8e4c443"
    "20200611211230" = "ami-025201fa53cf4d031"
    "20200611213648" = "ami-064fc5eec45384288"
    "20200611220538" = "ami-0ac80df6eff0e70b5"
    "20200710160608" = "ami-0dc45e3d9be6ab7b5"
    "20200716200445" = "ami-03a2cbdcd9e7d1955"
    "20200730152850" = "ami-07df16d0682f1fa59"
    "20200810202931" = "ami-0bcc094591f354be2"
    "20200824172451" = "ami-0c34018d0aabaef93"
    "20200903194723" = "ami-06b263d6ceff0b3dd"
    "20200908074438" = "ami-0817d428a6fb68645"
    "20200917161755" = "ami-06b33ea0e4b6334bc"
    "20200923231125" = "ami-013da1cc4ae87618c"
    "20201014160212" = "ami-038e35de01603d84e"
    "20201026191624" = "ami-00ddb0e5626798373"
    "20201112195935" = "ami-08b277333b9511393"
    "20201123222013" = "ami-0b893eef6e21b60a1"
    "20201201195255" = "ami-01c132a30955dafbb"
    "20201211122251" = "ami-053adf54573f777cf"
    "20210105200639" = "ami-0d0032af1da6905c7"
    "20210114205942" = "ami-01101ef9882f9c4bb"
    "20210120170000" = "ami-007e8beb808004fdc"
    "20210128195439" = "ami-02fe94dee086c0c37"
    }
    ami_id = ami-007e8beb808004fdc
    olderAmis = {
    "2018-04-26T22:00:41.000Z" = "ami-7ad76705"
    "2018-05-22T17:31:54.000Z" = "ami-432eb53c"
    "2018-06-13T23:48:55.000Z" = "ami-85f9b8fa"
    "2018-06-22T19:46:57.000Z" = "ami-5cc39523"
    "2018-07-25T16:07:17.000Z" = "ami-b04847cf"
    "2018-08-07T10:46:17.000Z" = "ami-920b10ed"
    "2018-08-15T00:18:04.000Z" = "ami-0b425589c7bb7663d"
    "2018-08-28T12:35:10.000Z" = "ami-07917569e2c4a2b6a"
    "2018-09-12T21:04:17.000Z" = "ami-0ac019f4fcb7cb7e6"
    "2018-10-13T15:11:16.000Z" = "ami-0977029b5b13f3d08"
    "2018-11-06T15:38:28.000Z" = "ami-05aa248bfb1c99d0f"
    "2018-11-26T17:52:59.000Z" = "ami-0d2505740b82f7948"
    "2018-12-03T19:22:56.000Z" = "ami-0edd3706ab2e952c4"
    "2019-01-17T17:22:25.000Z" = "ami-0b86cfbff176b7d3a"
    "2019-01-23T02:47:31.000Z" = "ami-012fd5eb46f56731f"
    "2019-02-10T21:02:46.000Z" = "ami-07073342279f98d28"
    "2019-02-13T12:48:22.000Z" = "ami-0a313d6098716f372"
    "2019-03-21T16:01:09.000Z" = "ami-07025b83b4379007e"
    "2019-04-03T22:16:23.000Z" = "ami-0fba9b33b5304d8b4"
    "2019-05-01T11:45:18.000Z" = "ami-0273df992a343e0d6"
    "2019-05-15T09:15:52.000Z" = "ami-024a64a6685d05041"
    "2019-05-31T21:43:33.000Z" = "ami-079f96ce4a4a7e1c7"
    "2019-06-18T05:28:23.000Z" = "ami-095192256fe1477ad"
    "2019-06-27T21:46:07.000Z" = "ami-026c8acd92718196b"
    "2019-07-25T19:49:07.000Z" = "ami-07d0cf3af28718ef8"
    "2019-08-19T18:11:19.000Z" = "ami-064a0193585662d74"
    "2019-09-12T04:43:29.000Z" = "ami-024582e76075564db"
    "2019-09-18T22:29:33.000Z" = "ami-05ecb1463f8f1ee4b"
    "2019-10-02T23:28:41.000Z" = "ami-04b9e92b5572fa0d1"
    "2019-10-10T22:13:39.000Z" = "ami-0607bfda7f358db2f"
    "2019-10-21T22:21:16.000Z" = "ami-0d5ae5525eb033d0a"
    "2019-11-13T19:28:03.000Z" = "ami-00a208c7cdba991ea"
    "2020-01-15T00:30:46.000Z" = "ami-07ebfd5b3428b6f4d"
    "2020-02-04T18:51:51.000Z" = "ami-046842448f9e74e7d"
    "2020-03-12T20:55:48.000Z" = "ami-0238c6e72a7e906fc"
    "2020-03-17T21:05:32.000Z" = "ami-055df5de4f42cf331"
    "2020-03-24T20:56:16.000Z" = "ami-0a4f4704a9146742a"
    "2020-04-09T16:44:13.000Z" = "ami-085925f297f89fce1"
    "2020-05-07T13:46:00.000Z" = "ami-05801d0a3c8e4c443"
    "2020-06-11T21:12:30.000Z" = "ami-025201fa53cf4d031"
    "2020-06-11T21:36:48.000Z" = "ami-064fc5eec45384288"
    "2020-06-11T22:05:38.000Z" = "ami-0ac80df6eff0e70b5"
    "2020-07-10T16:06:08.000Z" = "ami-0dc45e3d9be6ab7b5"
    "2020-07-16T20:04:45.000Z" = "ami-03a2cbdcd9e7d1955"
    "2020-07-30T15:28:50.000Z" = "ami-07df16d0682f1fa59"
    "2020-08-10T20:29:31.000Z" = "ami-0bcc094591f354be2"
    "2020-08-24T17:24:51.000Z" = "ami-0c34018d0aabaef93"
    "2020-09-03T19:47:23.000Z" = "ami-06b263d6ceff0b3dd"
    "2020-09-08T07:44:38.000Z" = "ami-0817d428a6fb68645"
    "2020-09-17T16:17:55.000Z" = "ami-06b33ea0e4b6334bc"
    "2020-09-23T23:11:25.000Z" = "ami-013da1cc4ae87618c"
    "2020-10-14T16:02:12.000Z" = "ami-038e35de01603d84e"
    "2020-10-26T19:16:24.000Z" = "ami-00ddb0e5626798373"
    "2020-11-12T19:59:35.000Z" = "ami-08b277333b9511393"
    "2020-11-23T22:20:13.000Z" = "ami-0b893eef6e21b60a1"
    "2020-12-01T19:52:55.000Z" = "ami-01c132a30955dafbb"
    "2020-12-11T12:22:51.000Z" = "ami-053adf54573f777cf"
    "2021-01-05T20:06:39.000Z" = "ami-0d0032af1da6905c7"
    "2021-01-14T20:59:42.000Z" = "ami-01101ef9882f9c4bb"
    "2021-01-20T17:00:00.000Z" = "ami-007e8beb808004fdc"
    }
    timeWindow = -336h
    twoWeeksAgo = 20210125214712

Q.E.D