"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
Comments
-
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 }
0 -
@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...
0 -
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"]
0