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

lookup fails on a JSON input #16154

Closed
ofer-velich opened this issue Sep 22, 2017 · 5 comments
Closed

lookup fails on a JSON input #16154

ofer-velich opened this issue Sep 22, 2017 · 5 comments

Comments

@ofer-velich
Copy link

ofer-velich commented Sep 22, 2017

Hi there,

Thank you for opening an issue. Please note that we try to keep the Terraform issue tracker reserved for bug reports and feature requests. For general usage questions, please see: https://www.terraform.io/community.html.

If your issue relates to a specific Terraform provider, please open it in the provider's own repository. The index of providers is at https://github.com/terraform-providers .

Terraform Version

Terraform v0.10.6

Terraform Configuration Files

module A

data "http" "json_data" {
  url = "http://myservice/jsondata"

  # Optional request headers
  request_headers {
    "Accept" = "application/json"
  }
}

output "json_data_key" {
  value = "${lookup(data.http.json_data.body, "mykey")}"
}

main.tf

provider "aws" {
  region = "${var.region}"
  version = "~> 0.1"
}

module "moduleA" {
  source = "../../../terraform-modules/moduleA"
}

resource "aws_instance" "example" {
  ami = "ami-2757f631"
  instance_type = "${module.moduleA.json_data_key}"
}

Debug Output

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

data.http.json_data: Refreshing state...

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + aws_instance.example
      id:                           <computed>
      ami:                          "ami-2757f631"
      associate_public_ip_address:  <computed>
      availability_zone:            <computed>
      ebs_block_device.#:           <computed>
      ephemeral_block_device.#:     <computed>
      instance_state:               <computed>
      instance_type:                "${module.moduleA.json_data_key}"
      ipv6_address_count:           <computed>
      ipv6_addresses.#:             <computed>
      key_name:                     <computed>
      network_interface.#:          <computed>
      network_interface_id:         <computed>
      placement_group:              <computed>
      primary_network_interface_id: <computed>
      private_dns:                  <computed>
      private_ip:                   <computed>
      public_dns:                   <computed>
      public_ip:                    <computed>
      root_block_device.#:          <computed>
      security_groups.#:            <computed>
      source_dest_check:            "true"
      subnet_id:                    <computed>
      tenancy:                      <computed>
      volume_tags.%:                <computed>
      vpc_security_group_ids.#:     <computed>

Expected Behavior

Lookup should not fails ...
The http://myservice/jsondata returns a valid json string
If i look on the plan output with out running the lookup on the returned json data i can see:

instance_type:                "{\"instance_type\":\"c4.large\"}"

I would expect that the json would get decoded to a map object and i will be able to run lookup function on it

@ozbillwang
Copy link

ozbillwang commented Sep 23, 2017

Use the official example (because its url is alive).

data "http" "example" {
  url = "https://checkpoint-api.hashicorp.com/v1/check/terraform"

  # Optional request headers
  request_headers {
    "Accept" = "application/json"
  }
}

# Sample data:
 {\"product\":\"terraform\",\"current_version\":\"0.10.6\",\"current_release\":1505830478,\"current_download_url\":\"https://terraform.io/downloads.html\",\"current_changelog_url\":\"https://github.com/hashicorp/terraform/blob/v0.10.6/CHANGELOG.md\",\"project_website\":\"https://www.terraform.io\",\"alerts\":[]}
output "product" {
  value = "${lookup(data.http.example.body, "product")}"
}

When run with debug option, I see the error. (But it doesn't display in normal mode)

2017/09/23 03:16:50 [DEBUG] ReferenceTransformer: "data.http.example" references: []
2017/09/23 03:16:50 [WARN] Output interpolation "product" failed: At column 3, line 1: lookup: argument 1 should be type map, got type string in:
${lookup(data.http.example.body, "product")}

So the body output is a string only. That's why it doesn't work with your codes. Because lookup function is only used for map

You need to convert the string to map first.

@ozbillwang
Copy link

ozbillwang commented Sep 23, 2017

@apparentlymart
Copy link
Contributor

Hi @ofer-velich!

As @ozbillwang said, the problem here is that the http data source just returns the raw response body string, without decoding it.

At present Terraform does not have a JSON decoder, because the type system for the configuration language can't represent the interface for such a function (it's a function that takes a string and returns any type based on the input).

The good news is that we're currently working on improving the configuration language to support such things, and once done planning to introduce a new function jsondecode that would allow something like the following to work:

# NOT YET IMPLEMENTED and may change before release

data "http" "json_data" {
  url = "http://example.com/jsondata"

  # Optional request headers
  request_headers {
    "Accept" = "application/json"
  }
}

output "json_data_key" {
  value = "${jsondecode(data.http.json_data.body)["mykey"]}"
}

This work is underway, but it's a big project so we're going to be releasing these new features gradually over a few separate releases to reduce risk. We'll have more to share on this soon.

@apparentlymart
Copy link
Contributor

Hi all,

I was going to verify a variant of the original configuration here in Terraform v0.12.0-alpha1, but sadly a build of the http provider was not included in that release, so I had to settle for a placeholder string instead:

locals {
  placeholder_json = <<EOT
{"country_iso":"US"}
EOT
}

output "detected_country" {
  value = jsondecode(local.placeholder_json)["country_iso"]
}
$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

detected_country = US

While I used a local value string here instead of a data source, this jsondecode function would work equally well for data.http.json_data.body as I showed in my previous example.

There's some more discussion about this new jsondecode function over in #10363, which this became essentially a duplicate of.

Since this feature is already merged in master and verified in the alpha release, I'm going to close this. This feature will also be included in the forthcoming v0.12.0 final release. Thanks for sharing this use-case, and thanks for your patience while we laid the groundwork to make this possible.

@apparentlymart apparentlymart added this to the v0.12.0 milestone Oct 29, 2018
@ghost
Copy link

ghost commented Mar 31, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Mar 31, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants