Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality to set a list #92

Closed
aaronmell opened this issue Jul 17, 2018 · 39 comments · Fixed by #1071
Closed

Add functionality to set a list #92

aaronmell opened this issue Jul 17, 2018 · 39 comments · Fixed by #1071

Comments

@aaronmell
Copy link
Contributor

It would be nice if the set command could take a list as an input parameter and turn it into a list in configuration EX

set {
name = "controller.service.loadBalancerSourceRanges"
value = ["${var.internal_ip_addr_cidrs}"]
}

should turn into

loadBalancerSourceRanges:

  • 130.211.204.1/32
  • 130.211.204.2/32

This doesn't solve the problem when you need to build more complex lists though

@sebastien-prudhomme
Copy link

This should work:

set {
  name = "controller.service.loadBalancerSourceRanges"
  value = "{${join(",", var.internal_ip_addr_cidrs)}}"
}

@aaronmell
Copy link
Contributor Author

@sebastien-prudhomme tried this, get the following error

key map "ran" has no value

@sebastien-prudhomme
Copy link

@aaronmell Can you dump the content of var.internal_ip_addr_cidrs?

@BrunoQuaresma
Copy link

The following works for me:

variable "plugins" { default = [
  "kubernetes:1.12.0",
  "workflow-job:2.23",
  "workflow-aggregator:2.5",
  "credentials-binding:1.16",
  "git:3.9.1",
  "blueocean:1.7.1"
]}

resource "helm_release" "jenkins" {
  set {
    name = "Master.InstallPlugins"
    value = "{${join(",", var.plugins)}}"
  }
}

@BrunoQuaresma
Copy link

@sebastien-prudhomme How I supposed to use this?

variable "volumes" { default = [
  {
    type = "HostPath"
    hostPath = "/var/run/docker.sock"
    mountPath = "/var/run/docker.sock"
  }
]}

... 

set {
    name = "Agent.volumes"
    value = "{${join(",", var.volumes)}}"
  }

I got an error:


* helm_release.jenkins: join: only works on flat lists, this list contains elements of type map in:

{${join(",", var.volumes)}}

@sebastien-prudhomme
Copy link

@BrunoQuaresma It works for simple list, not for map

@BrunoQuaresma
Copy link

@sebastien-prudhomme Ok, but how can I work with a list of maps using the plugin? Is it possible?

@sebastien-prudhomme
Copy link

The plugin only accept string but with a "join" you can transform a list a string compatible with Helm list syntax.

What is "Agent.volumes"? Which Helm chart are you using?

@BrunoQuaresma
Copy link

@sebastien-prudhomme
Copy link

@BrunoQuaresma Ok i see, it will be hard to do it with the "set" attribute. Either wait for Terraform 0.12 which can build complex structure, or try to generate portions of files that you can import with the "values" attribute.

@BrunoQuaresma
Copy link

@sebastien-prudhomme I didn't find anything about the "values" attributes in the docs -> https://github.com/mcuadros/terraform-provider-helm/blob/master/docs/README.md

@sebastien-prudhomme
Copy link

There https://github.com/mcuadros/terraform-provider-helm/blob/master/docs/release.md

It's the same thing as using "helm install" with the "--values" option.

@BrunoQuaresma
Copy link

Thanks.

@acrewdson
Copy link

+1 for adding this to the provider

@mogaal
Copy link

mogaal commented Feb 15, 2019

+1 for this. It would be very useful.

@joanayma
Copy link

In case of external-dns chart, list can work as follows:

resource "helm_release" "externaldns" {
  name  = "externaldns"
  chart = "stable/external-dns"
  set {
    name  = "sources"
    value = "{service, ingress}"
  }
  set {
    name  = "domainFilters"
    value = "{${local.dns_zone_name}}"
  }

Hope it works for others. I think it's for golang spec.

@etwillbefine
Copy link

@joanayma is it really "working"? Which versions are you running? I have the same issue with external-dns but when I use this syntax I get a comma separated list in the domain filter which does not work. It needs to be multiple --domain-filter arguments/an array.

@joshuaspence
Copy link

It would be great if set could work with list and map values, performing the necessary conversion (i.e. from ["x", "y", "z"] to {x,y,z}).

@abelal83
Copy link

for anyone else that comes across this I ended up with
values.yaml

controller:
  service:
    annotations:
      service.beta.kubernetes.io/azure-dns-label-name: ""

main.tf

  set_string {
    name  = "controller.service.annotations.service\\.beta\\.kubernetes\\.io/azure-dns-label-name"
    value = "${var.domain_name_label}"
  }

@theothermike
Copy link

theothermike commented Apr 17, 2020

Here's a work around I found for setting deeply nested values less painfully (tf 12+ only)

resource helm_release my-app {
  name = "my-app"
  namespace = "my-app-ns"
  chart = "/path/to/chart"

  values = [
    file("/path/to/default/values.yaml"),
    yamlencode(yamldecode(file("/path/to/env/specific/values.yaml"),["optional_multipackage_yaml_key"]_
    yamlencode(
        {
          some = {
            deeply = [
               { nested_option = data.foobar.baz.result }
            ]
          }
        }
      })
  ]
}

@tomaszgawlik
Copy link

I've managed to solve the issue, but creating yaml file on the fly from local complex object:

locals {
    helmChartValues = {
        someProperty= {
            someArray = [
                "a", "b"
            ]
        }
        someOtherProperty = {
            a = "b"
        }
    }
}

resource "local_file" "foo" {
    content     = yamlencode(local.helmChartValues)
    filename = "${path.module}/values.yaml"
}

resource "helm_release" "this" {
  name       = "helm-release"
  chart      = "ChartPath"

  values = [
      local_file.chart_values.content
  ]
}

@biswarup1290dass
Copy link

When I set an array value for a helm_release resource from my TF code like below:

set {
    name  = "ObjectIds"
    value = "{${join(",", local.rbac_config.group_oid_list)}}"
}

My TF Plan shows the value being passed like whats shown below:

 + set {
      + name  = "ObjectIds"
      + value = "{"Id1-xxxxxxxxxxx,Id2-yyyyyyyyyyyyyyyyyyy,Id3-zzzzzzzzzzzzzzzz"}"
    }

Why I need the format to be like this?

When the helm chart is installed manually from the command line using helm install, it throws an error if i specify --set ObjectIds={Id1-xxxxxxxx,Id2-yyyyyyyy,Id3-zzzzzz}

Helm Error: Error: This command needs 2 arguments: release name, chart path

Fix: It works just fine when I specify --set ObjectIds={"Id1-xxxxxxxx,Id2-yyyyyyyy,Id3-zzzzzz"}.
So I want the Terraform code to parse the value as value = "{"Id1-xxxxxxxx,Id2-yyyyyyyy,Id3-zzzzzz"}" instead of value = "{Id1-xxxxxxxx,Id2-yyyyyyyy,Id3-zzzzzz}"

Things I have tried:

1. Doesn't works:

set {
    name  = "ObjectIds"
    value = "{\"${join(",", local.rbac_config.group_oid_list)}\"}"
}

Failue/Error : TF Plan parses the value as

+ value = "{\"Id1-xxxxxxxx,Id2-yyyyyyyy,Id3-zzzzzz\"}"

2. Doesn't works:

set {
    name  = "ObjectIds"
    value = format("\"%s\"", join(",", local.rbac_config.group_oid_list))
}

Failue/Error : TF Plan parses the value as

+ value = "{\"Id1-xxxxxxxx,Id2-yyyyyyyy,Id3-zzzzzz\"}"

@venky999
Copy link

any update on working with list of maps

@venky999
Copy link

venky999 commented Jan 21, 2021

it works like below as well

locals {
    helmChartValues1 = {
        someProperty1= {
            someArray1 = [
                "a", "b"
            ]
        }
        someOtherProperty1 = {
            a = "b"
        }
    }

    helmChartValues2 = {
        someProperty2= {
            someArray2 = [
                "x", "y"
            ]
        }
        someOtherProperty2 = {
            p = "r"
        }
    }
}

resource "helm_release" "this" {
  name       = "helm-release"
  chart      = "ChartPath"

  values = [
      yamlencode(local.helmChartValues1),
      yamlencode(local.helmChartValues2)
  ]
}


@Type1J
Copy link

Type1J commented Feb 4, 2021

I'd really like to set the set block take a list or map as it's value attribute and "do the right thing". I don't want to have to escape strings so that everything will work as if I'm working on the command line because Terraform has these types that Helm wants already. The values list in the helm_release resource, and the command literals as strings are working work arounds for now, but setting values individually with the set block should work with non-string types, or at least maps and lists.

@Type1J
Copy link

Type1J commented Feb 4, 2021

I see that this has been marked as "breaking change".

Terraform attempts to parse strings from the command line when the variable that the string defines is typed as map or list in the variable definition. If a type were added to the set block, then an undefined or auto type could preserve the current behavior, while map or list could accept either the parsed string (as it does now), or a Terraform typed value.

As a bonus, a type of string would auto-escape the { and } so that a string that begins and ends with { and } could be used.

All cases would work properly, while a type of auto would preserve current behavior.

@omerfsen
Copy link

+1

1 similar comment
@Mark1626
Copy link

Mark1626 commented Mar 4, 2021

+1

@rafaelnpaiva
Copy link

rafaelnpaiva commented May 7, 2021

any updates ?
I would like to send a list of objects to the helm without doing the yamlencode ugly .
Is it possible now after 3 years ?
Something like this :

variable "example" {
   default = [ 
                {  
                 "name": string
                 "ips": list
                }
     ]
}

set { 
   name = example 
   values = var.example   

}

@Type1J
Copy link

Type1J commented May 17, 2021

I've done quite a bit of structure gymnastics to get the data that I need into the chart from Terraform. This really needs to be fixed. The yaml encoding that other comments have stated only works when the array values are not maps.

@mknapcok
Copy link

mknapcok commented Jul 7, 2021

Hi, I have a same problem as @Type1J
I'm trying to set a list of maps (eg. k8s tolerations). In values file it looks like:

tolerations:
  - key: "role"
    operator: "Equal"
    value: "nodes-on-demand"
    effect: "NoSchedule"
  - key: "role"
    operator: "Equal"
    value: "nodes-other"
    effect: "NoSchedule"

but I have a problem to define it with:

set {
    name  = "tolerations"
    value = ...
  }

@rafaelnpaiva
Copy link

rafaelnpaiva commented Jul 7, 2021

Maybe it can help you .
I did a workaround that works for me , look :
PS: look the values = [yamlencode({
runners = var.runners

})]

resource "helm_release" "cfrunner" {
  count            = var.deploy_cfrunner ? 1 : 0
  name             = "codefresh-runner"
  chart            = "${path.module}/helm/codefresh-runner"
  create_namespace = true
  namespace        = "codefresh"

  values = [yamlencode({
    runners = var.runners

  })]


  set {
    name  = "cf_api_token"
    value = var.cf_api_token
  }

And the var.runners is :

variable "runners" {
  description = "Codefresh Runners to be installed"
  type = list(object({
    name                 = string
    namespace            = string
    ebs_size             = string
  }))
  default = []
}

to populate it in some main.tf just use a list of object :

runners = [  { 
        name: xyz
        namespace: yyy
        ebs_size: 111 
     },
       { 
      name: abc
        namespace: zzz
        ebs_size: 222 
     }, 

 ] 

Worked smoothly for me . I'm setting up the entire list of objects to my values .
And my runners inside values.yaml is waiting for a list of object like yours :

runners: 
    - name : xyz
      namespace: yyy
      ebs_size: 111
   - name: abc
     namespace: zzz
     ebs_size: 222

@mknapcok
Copy link

mknapcok commented Jul 7, 2021

Looks great, thanks. I'll test it out tomorrow.

@tomwidmer
Copy link

The workaround of using the values field is only usable if your values aren't sensitive. I need either this issue fixed or #546 - without one of them, I believe it's impossible to pass complex sensitive values to a helm chart (in my case I have a map of usernames to passwords I'm trying to pass).

@rafaelnpaiva
Copy link

rafaelnpaiva commented Aug 11, 2021

aren't u able to populate it as strings base64 ?
You could do some strategy , like script to populate terraform.tfvars -> terraform -> helm chart ?
Like:
{
user: rpaiva
password: base64
}
To use your password base64 you can create secret.yaml in your helm chart of type opaque and manage it directly referring it on deployment.yaml ( mount volume , refer the secret ,, so on ... )
Or use vault sidecar in your helm chart instead populate secret using terraform https://www.hashicorp.com/blog/injecting-vault-secrets-into-kubernetes-pods-via-a-sidecar ...

@nitrocode
Copy link

I hit an issue with the golang spec if my value itself has a comma in it.

locals {
  tokens = [
    "trusted_cluster:aaaa",
    # orig value that works in helm `values` but shows up as sensitive if aaaa is token
    "proxy,db,kube,app,node:aaaa",
    # single quoted, fails to deploy
    #"'proxy,db,kube,app,node':aaaa",
    # escaped double quotes, fails to deploy
    #"\"proxy,db,kube,app,node\":aaaa",
    # tried escaping commas, fails to plan
    #"proxy\,db\,kube\,app\,node:aaaa",
  ]
}

resource "helm_release" "default" {
  # ...

  set {
    name  = "tokens"
    value = "{${local.tokens}}"
  }
}

Which results in this incorrect value

      + set {
          + name  = "tokens"
          + type  = "auto"
          + value = "{proxy,db,kube,app,node:aaaa,trusted_cluster:aaaa}"
        }

Not sure how to test this out in the golang playground.

How does one insert a list item containing a comma in this format without it breaking?

@nitrocode
Copy link

Nevermind. I got it to work by using double backslashes per comma.

locals {
  tokens = [
    "trusted_cluster:aaaa",
    "proxy\\,db\\,kube\\,app\\,node:aaaa",
  ]
}

@weitzjdevk
Copy link

This is an example how you would add an ingress host:

Given a values.yaml:

server:
  dev:
    enabled: true
  image:
    repository: "vault"
    tag: "1.9.3"
  ingress:
    enabled: true
    annotations:
      kubernetes.io/ingress.class: nginx
    hosts:
      - host: sampledomain.example.org

We will:

  • overwrite the first host entry
  • add an addtional host entry
resource "helm_release" "vault" {
  name             = "myreleasename"
  create_namespace = true
  namespace        = "mynamespace"
  repository       = "https://helm.releases.hashicorp.com"
  chart            = "vault"
  version          = "0.9.1"

  values = [
    file("${path.module}/values.yaml"),
  ]

  # overwrite first host
  set {
    name  = "server.ingress.hosts[0].host"
    value = "overwrittendomain.example.org"
  }

  # add another host
  set {
    name  = "server.ingress.hosts[1].host"
    value = "additionaldomain.example.org"
  }
}

Result yaml:

spec:
  rules:
    - host: "overwrittendomain.example.org"
      http:
        paths:
....

    - host: "additionaldomain.example.org"
      http:
        paths:
       ....

@northonheld
Copy link

northonheld commented Mar 8, 2022

Thanks @weitzjdevk for your example, it helped me to solve another problem I was having installing and configuring istio in AKS.

I was having trouble passing different values to the same value, but doing it a little differently worked!!! I didn't need to create a values.yaml file in my use case.

resource "helm_release" "istio_ingress" {
    name         = "istio-ingress"
    repository   = "https://istio-release.storage.googleapis.com/charts"
    chart        = "gateway"
    namespace    = "istio-ingress"
    
    set {
        name  = "service.ports[0].name"
        value = "status-port"
    }
    set {
        name  = "service.ports[0].port"
        value = "15021"
    }
    set {
        name  = "service.ports[0].targetPort"
        value = "15021"
    }
   set {
        name  = "service.ports[1].name"
        value = "http2"
    }
    set {
        name  = "service.ports[1].port"
        value = "80"
    }
    set {
        name  = "service.ports[1].targetPort"
        value = "80"
    }
    set {
        name  = "service.ports[1].nodePort"
        value = "31380"
    }

My Terraform version is 1.1.5

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.