Forum Discussion

mike99's avatar
mike99
Occasional Contributor
2 years ago

"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

  • jerdew's avatar
    jerdew
    New 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" { ... and module "my_other_spn" {, then access them as module.my_spn.op_fields["etc"]

  • mike99's avatar
    mike99
    Occasional 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...

  • jerdew's avatar
    jerdew
    New 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
    }