1Password / terraform-provider-onepassword

Use the 1Password Terraform Provider to reference, create, or update items in your 1Password Vaults.
https://developer.1password.com/docs/terraform/
MIT License
324 stars 48 forks source link

Complex examples needed #72

Open pascalrobert opened 1 year ago

pascalrobert commented 1 year ago

We need more complex examples. For example, how to fetch an key from an item? I tried something like this:

data "onepassword_item" "secrets" { vault = data.onepassword_vault.aw.uuid title = "secrets" }

data.onepassword_item.secrets.section[index(data.onepassword_item.secrets.section.*.label, "my-label")]

Didn't work. Also tried with:

data.onepassword_item.secrets.section[index(data.onepassword_item.secrets.section.*.field.label, "my-label")]

Didn't work either. So what's the correct way to get one key out of an item?

scottisloud commented 1 year ago

Hi Pascal, I'm Scott, a solutions architect here at 1Password. First, thank you for raising this issue. You're right, although the standard Terraform syntax for referencing nested attributes applies here in theory, the structure of custom 1Password items can make it a bit thorny. We are always working to improve our documentation and we will look for ways to improve our documentation with respect to retrieving specific keys or values from custom sections.

Now, to your specific issue: I'll have to admit that my Terraform chops are very rough, so my response may not be the most efficient way forward, but I've also reached out to the Secrets Automation developer team to see if they have better suggestions. I also hope I understand your question correctly. It sounds to me as if you'd like to retrieve a specific value from a custom field nested in a section. Is that about right?

If so, here's what worked for me, given the following. I take onepassword_item resource defined in the following way:

resource "onepassword_item" "demo_sections" {
  vault = var.demo_vault

  title    = "Demo Terraform Item with Sections"
  category = "login"
  username = "test@example.com"

  section {
    label = "Terraform_Section"

    field {
      label = "API_KEY"
      type  = "CONCEALED"
      value = "Gr34tP4$$word"
    }

    field {
      label = "HOSTNAME"
      value = "example.com"
    }
  }

  section {
    label = "Terraform Second Section"

    field {
      label = "App Specific Password"
      type  = "CONCEALED"

      password_recipe {
        length  = 40
        symbols = false
      }
    }

    field {
      label = "User"
      value = "demo"
    }
  }
}

And a corresponding data block as follows:

data "onepassword_item" "get_pass_example" {
   vault = var.demo_vault
   uuid  = onepassword_item.demo_sections.uuid
 }

Now I just tested this in terraform console so some tweaks may be required if you're including this in a plan, but the following worked to retrieve the value of the API_KEY field nested in the Terraform_Section section.

data.onepassword_item.get_pass_example.section[index(data.onepassword_item.get_pass_example.section.*.label, "Terraform_Section")].field[0].value

That returns Gr34tP4$$word, as expected.

Now I notice that you are asking for a specific key, rather than values.... if that is actually the case, then you could get an array of keys for the Terraform_Section section:

keys(data.onepassword_item.get_pass_example.section[index(data.onepassword_item.get_pass_example.section.*.label, "Terraform_Section")].field[0])

and a specific key from that array with standard index subscripting on the output from the keys() call. E.g., to get the 0th item:

keys(data.onepassword_item.get_pass_example.section[index(data.onepassword_item.get_pass_example.section.*.label, "Terraform_Section")].field[0])[0]

Now as I said, I'm no Terraform expert myself, so this may be a roundabout way of getting to your destination. I'll pass along any additional suggestions from our developers when available. But hopefully this is a start.

And again, thank you for your suggestions for improving our documentation.

scottisloud commented 1 year ago

Hi Pascal, First of all, I should apologize, since I think I misunderstood your request when you were asking about retrieving a key. I had assumed you meant "key" in the key/value pair sense.

Alas, one of our devs correctly understood your request and provided the following. The syntax to retrieve the value of the secret is nearly similar to my first value-retrieval syntax but is nested in a more useful output block:

Here’s an example terraform file that gets from an item the value of the field title from section off (the item is looked up by item and vault UUIDs)

terraform {
  required_providers {
    onepassword = {
      source = "1Password/onepassword"
      version = "~> 1.1.4"
    }
  }
}

provider "onepassword" {
  url = "http://localhost:8000/"
}

data "onepassword_item" "example" {
  vault = "ar3v2gmnw73p4bhkphr7t6rv44"
  uuid  = "257ueonb6vek5fg4uwzl2qc73y"
}

output "custom_field" {
    value = data.onepassword_item.example.section[index(data.onepassword_item.example.section.*.label, "off")].field[index(data.onepassword_item.example.section[index(data.onepassword_item.example.section.*.label, "off")].field.*.label, "title")].value
}

There may be a slightly less syntactically-verbose way to drill down into that field but this was what we were able to make work most reliably.

I hope that helps you out a bit!

We'll also file an issue with our docs team to explore options for improving our terraform documentation with respect to this type of task.

pascalrobert commented 1 year ago

Hi Scott,

Your reply helped a lot, thanks! I did something like this:

data "onepassword_item" "secrets" {
  vault = data.onepassword_vault.aw.uuid
  title = "secrets"
}

locals {
  secrets_bd = data.onepassword_item.secrets.section[index(data.onepassword_item.secrets.section.*.label, "BD")]
}

local.secrets_bd.field[index(local.secrets_bd.field.*.label, "majbds-mdp")].value
``` 

It does work, except if the label or the section name contains a space in it, or the label goes over a specific length, I get errors like this:

│ Error: Error in function call │ │ on asg.tf line 95, in module "gabarit_lancement": │ 95: mp_bd_datadog : local.secrets_bd.field[index(local.secrets_bd.field.*.label, "awDatabase-datadog-mdp")].value, │ ├──────────────── │ │ while calling index(list, value) │ │ local.secrets_bd.field is list of object with 7 elements │ │ Call to function "index" failed: item not found.

edif2008 commented 1 year ago

Heey @pascalrobert,

Glad to see that the suggested snippet that we've suggested worked for you and you even went beyond and made it friendlier. I really appreciate it and we should document such example in the repo as well.

Also, I appreciate that you've identified some scenarios in which the provider doesn't work as expected. I will raise them with my team and try to look into them. We will keep you posted when we have new insight for you regarding these.

sshipway commented 1 year ago

Thanks for this, I was looking for an example of how to use a custom section. My problem now is that I haven't seen how to set a custom field of type DATE - the resource completes fine, but the field never appears in the 1password database.

  section {
    label = "Custom"
    field {
      label = "Expiry"
      type  = "DATE"
      value = formatdate("YYYY-MM-DD",expirytimestamp)
    }
  }
scholdan commented 1 year ago

Hello,

After update to 1.2.0 this no longer works:

resource "onepassword_item" "vm-admin" {
  vault    = data.onepassword_vault.vault.uuid
  title    = lower(var.vm-name)
  category = "login"
  username = "deploy"
  section {
    label = "XXX Data"
    field {
      label   = "Deployment notes"
      purpose = "NOTES"
      value   = "Local admin generated by Terraform"
    }
    field {
      label = "IP"
      type  = "STRING"
      value = phpipam_address.newip.ip_address
    }
  }
  tags = ["terraform", "auto-created", "local-admin", "Linux"]
  password_recipe {
    length  = 40
    symbols = false
  }
}

It does create the "XXX Data" section, but without any data in it.

image

edif2008 commented 1 year ago

Hey @scholdan, We've just merged a fix for that with https://github.com/1Password/terraform-provider-onepassword/pull/100 and we're planning to make a release with this fix soon.

edif2008 commented 1 year ago

And the fix has been pushed with the 1.2.1 release. So you should be able to create fields within a section again @scholdan. 😄

mrkhachaturov commented 5 months ago

@pascalrobert @scottisloud, thank you for providing your examples!

I decided to show how I adapted it for my needs. Below is an example of how to retrieve specific data from item.

CleanShot 2024-06-02 at 00 41 35@2x

# Retrieve your vault using the specified name
data "onepassword_vault" "devops_vault" {
  name = var.op_vault
}

# Retrieve the item "Proxmox PVE01" from the main vault
data "onepassword_item" "proxmox_pve01" {
  vault = data.onepassword_vault.devops_vault.uuid
  title = "Proxmox PVE01"
}

locals {
  # Store the section labeled "API" from item  "Proxmox PVE01" in a local variable for shortened string references
  section_api = data.onepassword_item.proxmox_pve01.section[index(data.onepassword_item.proxmox_pve01.section.*.label, "API")]

  # Store the field value of the section "API"  in a local variable for shortened string references
  pve01_api = local.section_api.field

  # Extract the API URL field value from the "API" section of the Proxmox PVE01 item and store it in a local variable
  pm_api_url = local.pve01_api[index(local.pve01_api.*.label, "pm_api_url")].value

  # Extract the API token secret field value from the "API" section of the Proxmox PVE01 item and store it in a local variable
  pm_api_token_secret = local.pve01_api[index(local.pve01_api.*.label, "pm_api_token_secret")].value

  # Extract the API token ID field value from the "API" section of the Proxmox PVE01 item and store it in a local variable
  pm_api_token_id = local.pve01_api[index(local.pve01_api.*.label, "pm_api_token_id")].value

}

# Output the Proxmox PVE01 API URL
output "pm_api_url" {
  value = local.pm_api_url
}

# Output the Proxmox PVE01 API token secret
output "pm_api_token_secret" {
  value = local.pm_api_token_secret

}

# Output the Proxmox PVE01 API token ID
output "pm_api_token_id" {
  value = local.pm_api_token_id
}  
danslinky commented 3 months ago

@mrkhachaturov thank you for the nudge. using a map can improve readability.

locals {
  section_api = data.onepassword_item.proxmox_pve01.section[index(data.onepassword_item.proxmox_pve01.section.*.label, "API")]

  pve01_api = { for field in local.section_api.field : field.label => field.value }

  pm_api_url           = local.pve01_api["pm_api_url"]
  pm_api_token_secret  = local.pve01_api["pm_api_token_secret"]
  pm_api_token_id      = local.pve01_api["pm_api_token_id"]
}