-1

longtime lurker first time poster

Looking for some guidance from you all. I'm trying to replicate the aws command to essentially get the parameters (ssm get-parameters-by-path) then loop through the parameters and get them then loop through and put them into a new parameter (ssm put-parameter)

I understand there's a for loop expression in TF but for the life of me I can't put together how I would achieve this. so thanks to the wonderful breakdown below, I've gotten closer! But have this one issue. Code below:

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


data "aws_ssm_parameters_by_path" "parameters" {
  path = "/${var.old_env}"
  recursive = true
}

output "old_params_by_path" {
  value = data.aws_ssm_parameters_by_path.parameters
  sensitive = true
}

locals {
  names = toset(data.aws_ssm_parameters_by_path.parameters.names)
}

data "aws_ssm_parameter" "old_param_name" {
  for_each = local.names
  name = each.key
}

output "old_params_names" {
  value = data.aws_ssm_parameter.old_param_name
  sensitive = true
}

resource "aws_ssm_parameter" "new_params" {
  for_each = local.names
  name = replace(data.aws_ssm_parameter.old_param_name[each.key].name, var.old_env, var.new_env)
  type = data.aws_ssm_parameter.old_param_name[each.key].type
  value = data.aws_ssm_parameter.old_param_name[each.key].value
}

I have another file like how the helpful poster mentioned and created the initial dataset. But what's interesting is that after you create the set after the second set, it overwrites the first set! The idea is that I would be able to tell terraform, I have this current set of SSM parameters and I want you to copy that info (values, type) and create a brand new set of parameters (and not destroy anything that's already there).

Any and all help would be appreciated!

1 Answers1

1

I understand, It's not easy at the beginning. I will try to elaborate step-by-step on how I achieve that.

Anyway, it's nice to include any code, that you tried before, even if doesn't work.

So, firstly I create some example parameters:

# create_parameters.tf
resource "aws_ssm_parameter" "p" {
  count = 3
  name  = "/test/${count.index}/p${count.index}"
  type  = "String"
  value = "test-${count.index}"
}

Then I try to view them:

# example.tf
data "aws_ssm_parameters_by_path" "parameters" {
  path = "/test/"
  recursive = true
}
output "params_by_path" {
  value = data.aws_ssm_parameters_by_path.parameters
  sensitive = true
}

As an output I received: terraform output params_by_path

{
  "arns" = tolist([
    "arn:aws:ssm:eu-central-1:999999999999:parameter/test/0/p0",
    "arn:aws:ssm:eu-central-1:999999999999:parameter/test/1/p1",
    "arn:aws:ssm:eu-central-1:999999999999:parameter/test/2/p2",
  ])
  "id" = "/test/"
  "names" = tolist([
    "/test/0/p0",
    "/test/1/p1",
    "/test/2/p2",
  ])
  "path" = "/test/"
  "recursive" = true
  "types" = tolist([
    "String",
    "String",
    "String",
  ])
  "values" = tolist([
    "test-0",
    "test-1",
    "test-2",
  ])
  "with_decryption" = true
}

aws_ssm_parameters_by_path is unusable without additional processing, so we need to use another data source, to get a suitable object for a copy of provided parameters. n the documentation I found aws_ssm_parameter. However, to use it, I need the full name of the parameter.

List of the parameter names I retrieved in the previous stage, so now only needed is to iterate through them:

# example.tf    
locals {
  names = toset(data.aws_ssm_parameters_by_path.parameters.names)
}

data "aws_ssm_parameter" "param" {
  for_each = local.names
  name = each.key
}
output "params" {
  value = data.aws_ssm_parameter.param
  sensitive = true
}

And as a result, I get: terraform output params

{
  "/test/0/p0" = {
    "arn" = "arn:aws:ssm:eu-central-1:999999999999:parameter/test/0/p0"
    "id" = "/test/0/p0"
    "name" = "/test/0/p0"
    "type" = "String"
    "value" = "test-0"
    "version" = 1
    "with_decryption" = true
  }
  "/test/1/p1" = {
    "arn" = "arn:aws:ssm:eu-central-1:999999999999:parameter/test/1/p1"
    "id" = "/test/1/p1"
    "name" = "/test/1/p1"
    "type" = "String"
    "value" = "test-1"
    "version" = 1
    "with_decryption" = true
  }
  "/test/2/p2" = {
    "arn" = "arn:aws:ssm:eu-central-1:999999999999:parameter/test/2/p2"
    "id" = "/test/2/p2"
    "name" = "/test/2/p2"
    "type" = "String"
    "value" = "test-2"
    "version" = 1
    "with_decryption" = true
  }
}

Each parameter object has been retrieved, so now it is possible to create new parameters - which can be done like this:

# example.tf
resource "aws_ssm_parameter" "new_param" {
  for_each = local.names
  name  = "/new_path${data.aws_ssm_parameter.param[each.key].name}"
  type  = data.aws_ssm_parameter.param[each.key].type
  value = data.aws_ssm_parameter.param[each.key].value
}
ZabielskiGabriel
  • 551
  • 3
  • 12
  • This was an absolutely beautiful explanation. But I guess for me what I'm struggling with is...how do you break down this problem then build your solution? I don't know why I feel like I'm spinning my wheels at times and I get super frustrated. – user2676495 Jul 20 '22 at 16:43
  • Also, do I have to actually build a parameter first before I pull the data down from the original parameter path? – user2676495 Jul 20 '22 at 16:45
  • This gets me to about 95% of the way there but there's one thing I noticed. So the idea is I'm taking the old_params , replacing any reference to the old_path with the new_path. But what ends up happening is it does: /new_path/old_path/param1 I thought I could maybe throw another local in there to do some sort of string manipulation to rip out the old_path from there but that didn't work. Any other thoughts? – user2676495 Jul 20 '22 at 17:26
  • yh, string manipulations - https://www.terraform.io/language/functions/split – ZabielskiGabriel Jul 27 '22 at 16:30
  • @user2676495 May you accept my answer? – ZabielskiGabriel Sep 07 '22 at 15:36