Getting started with 1Password for your growing team, or refining your setup? Our Secured Success quickstart guide is for you.
Forum Discussion
mike99
2 years agoOccasional Contributor
"Correct" way to read custom fields in ```onepassword_item``` data source
Hi,
We're looking at migrating from HashiCorp Vault to 1Password as a secret provider for our terraform projects.
At present we have something like this:
```
provider "vault" {
address = "https://vault.mydomain.internal"
}
data "vault_generic_secret" "azure" {
path = "secret/path/to/my/secret"
}
provider "azurerm" {
client_id = data.vault_generic_secret.azure.data["clientid"]
tenant_id = data.vault_generic_secret.azure.data["tenant_id"]
client_secret = data.vault_generic_secret.azure.data["client_secret"]
subscription_id = data.vault_generic_secret.azure.data["subscription_id"]
features {}
}
```
The best I've ben able to put together with the onepassword provider is this:
```
provider "onepassword" {
# uses the OP_SERVICE_ACCOUNT_TOKEN environment variable for authentication.
# also looks in the system path for the 1password cli (op.exe / op)
}
data "onepassword_item" "my_spn" {
vault = "my-vault"
title = "my-spn"
}
provider "azurerm" {
tenant_id = [for field in data.onepassword_item.my_spn.section[0].field : field.value if field.label == "tenant_id"][0]
client_id = [for field in data.onepassword_item.my_spn.section[0].field : field.value if field.label == "client_id"][0]
client_secret = [for field in data.onepassword_item.my_spn.section[0].field : field.value if field.label == "client_secret"][0]
subscription_id = [for field in data.onepassword_item.my_spn.section[0].field : field.value if field.label == "subscription_id "][0]
features {}
}
```
Have I got completely the wrong end of the stick and there's an easier way to access custom fields?
I've used the 1Password Github actions and the 1Password CLI and they both support a much simpler syntax so I was hoping for something similar in the terraform provider - e.g. something like:
Here's the GitHub Actions and OP cli syntax for comparison...
provider "azurerm" {
client_id = data.onepassword_item.my_spn.field["client_id"]
tenant_id = data.onepassword_item.my_spn.field["tenant_id"]
client_secret = data.onepassword_item.my_spn.field["client_secret"]
subscription_id = data.onepassword_item.my_spn.field["subscription_id"]
features {}
}
github actions
- name: load secrets
id: onepassword
uses: 1password/load-secrets-action@v2
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: "${{ secrets.ONEPASSWORD_SERVICE_ACCOUNT_TOKEN }}"
MY_SPN: "op://my-vault/my-spn/client_id"
op cli
C:\> op read "op://my-vault/my-spn/client_id"
Thanks,
Mike
1Password Version: Not Provided
Extension Version: Not Provided
OS Version: Not Provided
Browser: Not Provided
3 Replies
- jerdewNew Contributor
I thought the same at first, but the issue comment helped me understand how a 'module' was gonna help me out. The key part is here:
```
module "test_item" {
source = "./op_password"vault_uuid = data.onepassword_vault.vault.uuid
title = "Test"
}
this is how my updated code now looks
data "onepassword_item" "password" {
vault = data.onepassword_vault.vault.uuid
title = var.title
}locals {
op_fields = { for f in data.onepassword_item.password.section[0].field : f.label => {
id = f.id
purpose = f.purpose
type = f.type
value = f.value
}
}
...
```The 'title' is the name of the 1password item, so you can add
module "my_spn" { ...
andmodule "my_other_spn" {
, then access them asmodule.my_spn.op_fields["etc"]
- mike99Occasional Contributor
jerdew - nice, although that looks like it would get quite busy if you have multiple onepassword items that you want to reference in the same project.
For completeness, I went my original approach in the end:
provider "azurerm" {
tenant_id = [for field in data.onepassword_item.my_spn.section[0].field : field.value if field.label == "tenant_id"][0]
.. etc ...
}
I also created a local variable like you did for referencing secrets in resources:
locals {
my_spn = {
tenant_id = [for field in data.onepassword_item.my_spn.section[0].field : field.value if field.label == "tenant_id"][0]
client_id = [for field in data.onepassword_item.my_spn.section[0].field : field.value if field.label == "client_id"][0]
client_secret = [for field in data.onepassword_item.my_spn.section[0].field : field.value if field.label == "client_secret"][0]
}
}
and then
some_resource_property = local.my_spn.tenant_id
I didn't seem to be able to reference the locals in a provider block so I had to duplicate the long-hand version in the azurerm provider. I might take another look as your example looks like it does that ok...
- jerdewNew Contributor
I found this issue comment helpful: https://github.com/1Password/terraform-provider-onepassword/issues/117#issuecomment-2059385999
and ended up with
```
data "onepassword_item" "terraform" {
vault = "MyVault"
title = "MyTerraformItem"
}locals {
op_terraform_fields = { for f in data.onepassword_item.terraform.section[0].field : f.label => {
id = f.id
purpose = f.purpose
type = f.type
value = f.value
}
}# accessed like so: local.op_terraform_sections["Did You Get"].fields["that thing I sent you"].value
op_terraform_sections = { for s in data.onepassword_item.terraform.section : s.label => {
id = s.id
fields = { for f in s.field : f.label => {
id = f.id
purpose = f.purpose
type = f.type
value = f.value
} }
} if s.label != "" }
}
```and then the 'fields' one is the sectionless section found at [0], and used by me as
provider "aws" {
region = "us-east-1"
access_key = local.op_terraform_fields["ACCESS_KEY"].value
secret_key = local.op_terraform_fields["SECRET_ACCESS_KEY"].value
}