├── .github
├── ISSUE_TEMPLATE
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── release.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── docs
├── data-sources
│ ├── group.md
│ ├── item_common.md
│ ├── item_credit_card.md
│ ├── item_document.md
│ ├── item_identity.md
│ ├── item_login.md
│ ├── item_password.md
│ ├── item_secure_note.md
│ ├── item_software_license.md
│ ├── user.md
│ └── vault.md
├── index.md
└── resources
│ ├── group.md
│ ├── group_member.md
│ ├── item_common.md
│ ├── item_credit_card.md
│ ├── item_document.md
│ ├── item_identity.md
│ ├── item_login.md
│ ├── item_password.md
│ ├── item_secure_note.md
│ ├── item_software_license.md
│ └── vault.md
├── examples
├── .gitignore
├── credit_card
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
├── document
│ ├── main.tf
│ ├── output.tf
│ ├── test.txt
│ └── variables.tf
├── group
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
├── identity
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
├── login
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
├── main.tf
├── password
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
├── reward_program
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
├── secure_note
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
├── software_license
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
├── user
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
└── vault
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
├── go.mod
├── go.sum
├── main.go
├── onepassword
├── data_group.go
├── data_item_common.go
├── data_item_credit_card.go
├── data_item_document.go
├── data_item_identity.go
├── data_item_login.go
├── data_item_password.go
├── data_item_secure_note.go
├── data_item_software_license.go
├── data_user.go
├── data_vault.go
├── group.go
├── group_test.go
├── helper.go
├── item.go
├── provider.go
├── provider_test.go
├── resource_group.go
├── resource_group_member.go
├── resource_group_member_test.go
├── resource_item_common.go
├── resource_item_credit_card.go
├── resource_item_document.go
├── resource_item_identity.go
├── resource_item_login.go
├── resource_item_password.go
├── resource_item_secure_note.go
├── resource_item_software_license.go
├── resource_vault.go
├── section.go
├── user.go
└── vault.go
└── terraform-registry-manifest.json
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: anasinnyk
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Fixes #
2 |
3 | ## Proposed Changes
4 |
5 | -
6 | -
7 | -
8 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # This GitHub action can publish assets for release when a tag is created.
2 | # Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0).
3 | #
4 | # This uses an action (crazy-max/ghaction-import-gpg) that assumes you set your
5 | # private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE`
6 | # secret. If you would rather own your own GPG handling, please fork this action
7 | # or use an alternative one for key handling.
8 | #
9 | # You will need to pass the `--batch` flag to `gpg` in your signing step
10 | # in `goreleaser` to indicate this is being used in a non-interactive mode.
11 | #
12 | name: release
13 | on:
14 | create:
15 | tags:
16 | - 'v*'
17 | jobs:
18 | goreleaser:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v3
23 | with:
24 | fetch-depth: 0
25 |
26 | - name: Set up Go
27 | uses: actions/setup-go@v3
28 | with:
29 | go-version: '>=1.18.0'
30 |
31 | - name: Import GPG key
32 | id: import_gpg
33 | uses: crazy-max/ghaction-import-gpg@v4
34 | with:
35 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
36 | passphrase: ${{ secrets.PASSPHRASE }}
37 |
38 | - name: Run GoReleaser
39 | uses: goreleaser/goreleaser-action@v2
40 | with:
41 | version: latest
42 | args: release --rm-dist
43 | env:
44 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/**/*
2 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | linters:
2 | enable-all: true
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | # Visit https://goreleaser.com for documentation on how to customize this
2 | # behavior.
3 | before:
4 | hooks:
5 | # this is just an example and not a requirement for provider building/publishing
6 | - go mod tidy
7 | builds:
8 | - env:
9 | # goreleaser does not work with CGO, it could also complicate
10 | # usage by users in CI/CD systems like Terraform Cloud where
11 | # they are unable to install libraries.
12 | - CGO_ENABLED=0
13 | mod_timestamp: '{{ .CommitTimestamp }}'
14 | flags:
15 | - -trimpath
16 | ldflags:
17 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}'
18 | goos:
19 | - freebsd
20 | - windows
21 | - linux
22 | - darwin
23 | goarch:
24 | - amd64
25 | - '386'
26 | - arm
27 | - arm64
28 | ignore:
29 | - goos: darwin
30 | goarch: '386'
31 | binary: '{{ .ProjectName }}_v{{ .Version }}'
32 | archives:
33 | - format: zip
34 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
35 | checksum:
36 | extra_files:
37 | - glob: 'terraform-registry-manifest.json'
38 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
39 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
40 | algorithm: sha256
41 | signs:
42 | - artifacts: checksum
43 | args:
44 | # if you are using this is a GitHub action or some other automated pipeline, you
45 | # need to pass the batch flag to indicate its not interactive.
46 | - "--batch"
47 | - "--local-user"
48 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key
49 | - "--output"
50 | - "${signature}"
51 | - "--detach-sign"
52 | - "${artifact}"
53 | release:
54 | extra_files:
55 | - glob: 'terraform-registry-manifest.json'
56 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
57 | # If you want to manually examine the release before its live, uncomment this line:
58 | # draft: true
59 | changelog:
60 | skip: true
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at andriy.nas@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Terraform OnePassword Provider
2 |
3 | [](https://golangci.com/r/github.com/anasinnyk/terraform-provider-1password)
4 | [](https://travis-ci.com/anasinnyk/terraform-provider-1password)
5 |
6 | ## Usage
7 |
8 | [See the docs for usage information](./docs).
9 |
--------------------------------------------------------------------------------
/docs/data-sources/group.md:
--------------------------------------------------------------------------------
1 | # onepassword_group
2 |
3 | This resource can load groups from your 1Password account.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_group" "this" {
9 | name = "exist-group"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) group name.
16 |
17 | ## Attribute Reference
18 |
19 | In addition to the above arguments, the following attributes are exported:
20 |
21 | * `id` - group id.
22 | * `state` - current state of the group. "A" for active, "D" for deleted.
23 |
--------------------------------------------------------------------------------
/docs/data-sources/item_common.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_common
2 |
3 | This resource can load any other item without required fields like Database/Membership/Wireless Router/Driver License/Outdoor License/Passport/Email Account/Reward Program/Social Security Number/Bank Account/Server in your 1password account.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_item_common" "this" {
9 | name = "some-element-from-vault"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) your item title.
16 | * `template` - (Required) your item category. Can be one of the next value `Database`, `Membership`, `Wireless Router`, `Driver License`, `Outdoor License`, `Passport`, `Email Account`, `Reward Program`, `Social Security Number`, `Bank Account`, `Server`.
17 | * `vault` - (Optional) link to your vault, can be id (recommended) or name. If it's empty, it creates to default vault.
18 | * `notes` - (Optional) note for this item.
19 | * `tags` - (Optional) array of strings with any tag, for grouping your 1password item.
20 | * `section` - (Optional) it's a block with additional information available in any other item type.
21 |
22 | The `section` block support:
23 |
24 | * `name` - (Optional) section title.
25 | * `field` - (Optional) field in section.
26 |
27 | The `field` block support:
28 |
29 | * `name` - (Optional) field title.
30 | * `string` - (Optional) if you have a text field use string.
31 | * `url` - (Optional) if you have a URL field type (checks if URL is correct).
32 | * `phone` - (Optional) if you have a phone number filed type.
33 | * `email` - (Optional) if you have a email field type.
34 | * `date` - (Optional) if you have a date field type should use a UNIXTIME.
35 | * `month_year` - (Optional) if you have a month year field type, credit card expiration for example, use 6 number in next format `YYYYMM`.
36 | * `totp` - (Optional) if you have a one time password you can save url in this type and 1password client can generate totp for you.
37 | * `concealed` - (Optional) if you have a sensitive infromation, you can save it in this field type, it looks like a password.
38 | * `sex` - (Optional) text field with information about geander, possible next vaules `male`,`female`.
39 | * `card_type` - (Optional) text field with information about credit card type, possible next vaules `mc`, `visa`, `amex`, `diners`, `carteblanche`, `discover`, `jcb`, `maestro`, `visaelectron`, `laser`, `unionpay`.
40 | * `reference` - (Optional) not supported yet. Potentially we can store reference between different items.
41 | * `address` - (Optional) it's a address block.
42 |
43 | *Note: MUST be one of there `string`,`url`,`phone`,`email`,`date`,`month_year`,`totp`,`concealed`,`address`,`sex`,`card_type`,`reference`.*
44 |
45 | The `address` block support:
46 |
47 | * `street` - (Optional) street information.
48 | * `country` - (Optional) ISO2 country code.
49 | * `state` - (Optional) state name.
50 | * `region` - (Optional) region name.
51 | * `city` - (Optional) city name.
52 | * `zip` - (Optional) zip code.
53 |
54 | ## Attribute Reference
55 |
56 | In addition to the above arguments, the following attributes are exported:
57 |
58 | * `id` - item id.
59 |
--------------------------------------------------------------------------------
/docs/data-sources/item_credit_card.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_credit_card
2 |
3 | ## Example Usage
4 |
5 | ```hcl
6 | data "onepassword_item_credit_card" "this" {
7 | name = "credit_card-from-vault"
8 | }
9 | ```
10 |
11 | ## Argument Reference
12 |
13 | * `name` - (Required) your credit card title.
14 | * `vault` - (Optional) see details in onepassword_item_common.
15 | * `main` - (Optional) block of card data.
16 | * `notes` - (Optional) see details in onepassword_item_common.
17 | * `tags` - (Optional) see details in onepassword_item_common.
18 | * `section` - (Optional) see details in onepassword_item_common.
19 |
20 | The `main` block support:
21 |
22 | * `cardholder` - (Optional) store card holder name.
23 | * `type` - (Optional) store card type value. see details in onepassword_item_common -> section -> field type card_type.
24 | * `number` - (Optional) store 16 digit card numner.
25 | * `cvv` - (Optional) sensitive data with your cvv card code.
26 | * `expiry_date` - (Optional) store your exprite date in month year format. see details in onepassword_item_common -> section -> field type card_type
27 | * `valid_from` - (Optional) store date when your card was publish in month year format. see details in onepassword_item_common -> section -> field type card_type
28 |
29 | ## Attribute Reference
30 |
31 | In addition to the above arguments, the following attributes are exported:
32 |
33 | * `id` - credit card id.
34 |
--------------------------------------------------------------------------------
/docs/data-sources/item_document.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_document
2 |
3 | This resource can load any document from 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_item_document" "this" {
9 | name = "some-document-from-vault"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) your document title.
16 | * `field_path` - (Required) path to your document, which will be upload to 1password.
17 | * `vault` - (Optional) see details in onepassword_item_common.
18 | * `notes` - (Optional) see details in onepassword_item_common.
19 | * `tags` - (Optional) see details in onepassword_item_common.
20 | * `section` - (Optional) see details in onepassword_item_common.
21 |
22 | ## Attribute Reference
23 |
24 | In addition to the above arguments, the following attributes are exported:
25 |
26 | * `id` - document id.
27 | * `content` - document content.
28 |
--------------------------------------------------------------------------------
/docs/data-sources/item_identity.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_identity
2 |
3 | This resource can load any identity from 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_item_identity" "this" {
9 | name = "some-identity-from-vault"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) your identity title.
16 | * `vault` - (Optional) see details in onepassword_item_common.
17 | * `notes` - (Optional) see details in onepassword_item_common.
18 | * `tags` - (Optional) see details in onepassword_item_common.
19 | * `section` - (Optional) see details in onepassword_item_common.
20 | * `identification` - (Optional)
21 | * `address` - (Optional)
22 | * `internet` - (Optional)
23 |
24 | The `identification` block support:
25 |
26 | * `firstname` - (Optional)
27 | * `initial` - (Optional)
28 | * `lastname` - (Optional)
29 | * `sex` - (Optional)
30 | * `birth_date` - (Optional)
31 | * `occupation` - (Optional)
32 | * `company` - (Optional)
33 | * `department` - (Optional)
34 | * `job_title` - (Optional)
35 |
36 | The `address` block support:
37 |
38 | * `address` - (Optional)
39 | * `default_phone` - (Optional)
40 | * `home_phone` - (Optional)
41 | * `cell_phone` - (Optional)
42 | * `business_phone` - (Optional)
43 |
44 | The `internet` block support:
45 |
46 | * `username` - (Optional)
47 | * `email` - (Optional)
48 |
49 | ## Attribute Reference
50 |
51 | In addition to the above arguments, the following attributes are exported:
52 |
53 | * `id` - identity id.
54 |
--------------------------------------------------------------------------------
/docs/data-sources/item_login.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_login
2 |
3 | This resource can load any login from 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_item_login" "this" {
9 | name = "some-login-from-vault"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) your login title.
16 | * `username` - (Optional) from this login.
17 | * `password` - (Optional) from this login.
18 | * `url` - (Optional) url for website from this login.
19 | * `vault` - (Optional) see details in onepassword_item_common.
20 | * `notes` - (Optional) see details in onepassword_item_common.
21 | * `tags` - (Optional) see details in onepassword_item_common.
22 | * `section` - (Optional) see details in onepassword_item_common.
23 |
24 | ## Attribute Reference
25 |
26 | In addition to the above arguments, the following attributes are exported:
27 |
28 | * `id` - login id.
29 |
--------------------------------------------------------------------------------
/docs/data-sources/item_password.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_password
2 |
3 | This resource can load any password from 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_item_password" "this" {
9 | name = "some-password-from-vault"
10 | }
11 | ```
12 | ## Argument Reference
13 |
14 | * `name` - (Required) your password title.
15 | * `password` - (Optional) store password here.
16 | * `url` - (Optional) url for website from this password.
17 | * `notes` - (Optional) see details in onepassword_item_common.
18 | * `vault` - (Optional) see details in onepassword_item_common.
19 | * `tags` - (Optional) see details in onepassword_item_common.
20 | * `section` - (Optional) see details in onepassword_item_common.
21 |
22 | ## Attribute Reference
23 |
24 | In addition to the above arguments, the following attributes are exported:
25 |
26 | * `id` - password id.
27 |
--------------------------------------------------------------------------------
/docs/data-sources/item_secure_note.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_secure_note
2 |
3 | This resource can load any secure note from 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_item_secure_note" "this" {
9 | name = "some-secure-note-from-vault"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) your secure note title.
16 | * `vault` - (Optional) see details in onepassword_item_common.
17 | * `notes` - (Optional) see details in onepassword_item_common (main field for this type).
18 | * `tags` - (Optional) see details in onepassword_item_common.
19 | * `section` - (Optional) see details in onepassword_item_common.
20 |
21 | ## Attribute Reference
22 |
23 | In addition to the above arguments, the following attributes are exported:
24 |
25 | * `id` - secure note id.
26 |
--------------------------------------------------------------------------------
/docs/data-sources/item_software_license.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_software_license
2 |
3 | This resource can load any software license from 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_item_software_license" "this" {
9 | name = "software-license-from-vault"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) your software license title.
16 | * `license_key` - (Optional) store your license key here.
17 | * `vault` - (Optional) see details in onepassword_item_common.
18 | * `notes` - (Optional) see details in onepassword_item_common.
19 | * `tags` - (Optional) see details in onepassword_item_common.
20 | * `section` - (Optional) see details in onepassword_item_common.
21 |
22 | ## Attribute Reference
23 |
24 | In addition to the above arguments, the following attributes are exported:
25 |
26 | * `id` - software license id.
27 |
--------------------------------------------------------------------------------
/docs/data-sources/user.md:
--------------------------------------------------------------------------------
1 | # onepassword_user
2 |
3 | This resource can read user data in your 1Password account.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_user" "this" {
9 | email = "example@example.com"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `email` - (Required) user email address.
16 |
17 | ## Attribute Reference
18 |
19 | In addition to the above arguments, the following attributes are exported:
20 |
21 | * `id` - user id.
22 | * `firstname` - User first name.
23 | * `lastname` - User last name.
24 | * `state` - Current user state. "A" for Active, "S" for Suspended.
25 |
--------------------------------------------------------------------------------
/docs/data-sources/vault.md:
--------------------------------------------------------------------------------
1 | # onepassword_vault
2 |
3 | This resource can load vaults from your 1password account.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | data "onepassword_vault" "this" {
9 | name = "exist-vault"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) vault name.
16 |
17 | ## Attribute Reference
18 |
19 | In addition to the above arguments, the following attributes are exported:
20 |
21 | * `id` - vault id.
22 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Provider
2 |
3 | Terraform provider for 1password usage with your infrastructure, for example you can share password from your admin panel via some vault in you 1password company account. This provider is based on 1Password CLI client version 1.4.0, but you can rewrite it by env variable `OP_VERSION`.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | provider "onepassword" {
9 | email = "john.smith@example.com"
10 | password = "super secret master password"
11 | secret_key = "A3-XXXXXX-XXXXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
12 | subdomain = "company"
13 | }
14 | ```
15 |
16 | ## Argument Reference
17 |
18 | The following arguments are supported:
19 |
20 | * `email` - (Optional) your email address in 1password or via env variable `OP_EMAIL`.
21 | * `password` - (Optional) your master password from 1password or via env variable `OP_PASSWORD`.
22 | * `secret_key` - (Optional) secret key which you can download after registration or via env variable `OP_SECRET_KEY`.
23 | * `subdomain` - (Optional) If you use corporate account you must fill subdomain form your 1password site. Defaults to `my` or via env variable `OP_SUBDOMAIN`.
24 |
25 | If `email`, `password` and `secret_key` is not set through the arguments or env variables, then the env variable `OP_SESSION_` is checked for existence. If set it will be assumed to be a valid session token and used while executing the `op` commands. Note that any dash `-` character within `subdomain` will be substituted upon `OP_SESSION_` env variable evaluation (e.g, if `subdomain=team-foo`, `OP_SESSION_team_foo` will be looked up).
26 |
--------------------------------------------------------------------------------
/docs/resources/group.md:
--------------------------------------------------------------------------------
1 | # onepassword_group
2 |
3 | This resource can create groups in your 1Password account.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | resource "onepassword_group" "this" {
9 | name = "new-group"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) group name.
16 |
17 | ## Attribute Reference
18 |
19 | In addition to the above arguments, the following attributes are exported:
20 |
21 | * `id` - group id.
22 | * `state` - current state of the group. "A" for active, "D" for deleted.
23 |
24 | ## Import
25 |
26 | 1Password Groups can be imported using the `id`, e.g.
27 |
28 | ```
29 | terraform import onepassword_group.group 7kalogoe3kirwf5aizotkbzrpq
30 | ```
31 |
--------------------------------------------------------------------------------
/docs/resources/group_member.md:
--------------------------------------------------------------------------------
1 | # onepassword_group_member
2 |
3 | This resource can manage group membership within a 1Password group.
4 |
5 | ## Example Usage
6 |
7 | ### Resource
8 |
9 | ```hcl
10 | resource "onepassword_group" "group" {
11 | group = "new-group"
12 | }
13 |
14 | data "onepassword_user" "user" {
15 | email = "example@example.com"
16 | }
17 |
18 | resource "onepassword_group_member" "example" {
19 | group = onepassword_group.group.id
20 | user = data.onepassword_user.user.id
21 | }
22 | ```
23 |
24 | ## Argument Reference
25 |
26 | * `group` - (Required) group id.
27 | * `user` - (Required) user id.
28 |
29 | ## Attribute Reference
30 |
31 | In addition to the above arguments, the following attributes are exported:
32 |
33 | * `id` - (Required) internal membership identifier.
34 |
35 | ## Import
36 |
37 | 1Password Group Members can be imported using the `id`, which consists of the group ID and user ID separated by a hyphen, e.g.
38 |
39 | ```
40 | terraform import onepassword_group_member.example fmownretj6zdobn2cnjtqqyrae-KDLG56VTIJDXXBXC2KKCPHNHHI
41 | ```
42 |
43 | **Note: this is case sensitive, and matches the case provided by 1Password.**
44 |
--------------------------------------------------------------------------------
/docs/resources/item_common.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_common
2 |
3 | This resource can create any other item without required fields like Database/Membership/Wireless Router/Driver License/Outdoor License/Passport/Email Account/Reward Program/Social Security Number/Bank Account/Server in your 1password account.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | resource "onepassword_item_common" "this" {
9 | name = "Coupone"
10 | vault = var.vault_id
11 |
12 | template = "Reward Program"
13 |
14 | section {
15 | field {
16 | name = "company name"
17 | string = "MacPaw"
18 | }
19 |
20 | field {
21 | name = "member name"
22 | string = "anasinnyk"
23 | }
24 |
25 | field {
26 | name = "member ID"
27 | string = "123"
28 | }
29 |
30 | field {
31 | name = "PIN"
32 | concealed = "123456qQ"
33 | }
34 | }
35 |
36 | section {
37 | name = "More Information"
38 |
39 | field {
40 | name = "member ID (additional)"
41 | string = "321"
42 | }
43 |
44 | field {
45 | name = "customer service phone"
46 | phone = "+38 (000) 000 0000"
47 | }
48 |
49 | field {
50 | name = "phone for reservations"
51 | phone = "+38 (000) 000 0000"
52 | }
53 |
54 | field {
55 | name = "website"
56 | url = "https://groupon.com"
57 | }
58 |
59 | field {
60 | name = "member since"
61 | month_year = 201903
62 | }
63 | }
64 | }
65 | ```
66 |
67 | ## Argument Reference
68 |
69 | * `name` - (Required) your item title.
70 | * `template` - (Required) your item category. Can be one of the next value `Database`, `Membership`, `Wireless Router`, `Driver License`, `Outdoor License`, `Passport`, `Email Account`, `Reward Program`, `Social Security Number`, `Bank Account`, `Server`.
71 | * `vault` - (Optional) link to your vault, can be id (recommended) or name. If it's empty, it creates to default vault.
72 | * `notes` - (Optional) note for this item.
73 | * `tags` - (Optional) array of strings with any tag, for grouping your 1password item.
74 | * `section` - (Optional) it's a block with additional information available in any other item type.
75 |
76 | The `section` block support:
77 |
78 | * `name` - (Optional) section title.
79 | * `field` - (Optional) field in section.
80 |
81 | The `field` block support:
82 |
83 | * `name` - (Optional) field title.
84 | * `string` - (Optional) if you have a text field use string.
85 | * `url` - (Optional) if you have a URL field type (checks if URL is correct).
86 | * `phone` - (Optional) if you have a phone number filed type.
87 | * `email` - (Optional) if you have a email field type.
88 | * `date` - (Optional) if you have a date field type should use a UNIXTIME.
89 | * `month_year` - (Optional) if you have a month year field type, credit card expiration for example, use 6 number in next format `YYYYMM`.
90 | * `totp` - (Optional) if you have a one time password you can save url in this type and 1password client can generate totp for you.
91 | * `concealed` - (Optional) if you have a sensitive infromation, you can save it in this field type, it looks like a password.
92 | * `sex` - (Optional) text field with information about geander, possible next vaules `male`,`female`.
93 | * `card_type` - (Optional) text field with information about credit card type, possible next vaules `mc`, `visa`, `amex`, `diners`, `carteblanche`, `discover`, `jcb`, `maestro`, `visaelectron`, `laser`, `unionpay`.
94 | * `reference` - (Optional) not supported yet. Potentially we can store reference between different items.
95 | * `address` - (Optional) it's a address block.
96 |
97 | *Note: MUST be one of there `string`,`url`,`phone`,`email`,`date`,`month_year`,`totp`,`concealed`,`address`,`sex`,`card_type`,`reference`.*
98 |
99 | The `address` block support:
100 |
101 | * `street` - (Optional) street information.
102 | * `counrty` - (Optional) ISO2 country code.
103 | * `state` - (Optional) state name.
104 | * `region` - (Optional) region name.
105 | * `city` - (Optional) city name.
106 | * `zip` - (Optional) zip code.
107 |
108 | ## Attribute Reference
109 |
110 | In addition to the above arguments, the following attributes are exported:
111 |
112 | * `id` - item id.
113 |
--------------------------------------------------------------------------------
/docs/resources/item_credit_card.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_credit_card
2 |
3 | ## Example Usage
4 |
5 | ```hcl
6 | resource "onepassword_item_credit_card" "this" {
7 | name = "Default Visa"
8 | vault = var.vault_id
9 |
10 | main {
11 | cardholder = "John Smith"
12 | type = "visa"
13 | number = "4111 1111 1111 1111"
14 | cvv = "1111"
15 | expiry_date = 202205
16 | valid_from = 201805
17 | }
18 | }
19 | ```
20 |
21 | ## Argument Reference
22 |
23 | * `name` - (Required) your credit card title.
24 | * `vault` - (Optional) see details in onepassword_item_common.
25 | * `main` - (Optional) block of card data.
26 | * `notes` - (Optional) see details in onepassword_item_common.
27 | * `tags` - (Optional) see details in onepassword_item_common.
28 | * `section` - (Optional) see details in onepassword_item_common.
29 |
30 | The `main` block support:
31 |
32 | * `cardholder` - (Optional) store card holder name.
33 | * `type` - (Optional) store card type value. see details in onepassword_item_common -> section -> field type card_type.
34 | * `number` - (Optional) store 16 digit card numner.
35 | * `cvv` - (Optional) sensitive data with your cvv card code.
36 | * `expiry_date` - (Optional) store your exprite date in month year format. see details in onepassword_item_common -> section -> field type card_type
37 | * `valid_from` - (Optional) store date when your card was publish in month year format. see details in onepassword_item_common -> section -> field type card_type
38 |
39 | ## Attribute Reference
40 |
41 | In addition to the above arguments, the following attributes are exported:
42 |
43 | * `id` - credit card id.
44 |
--------------------------------------------------------------------------------
/docs/resources/item_document.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_document
2 |
3 | This resource can create any document for 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | resource "onepassword_item_document" "this" {
9 | name = "document-name"
10 | vault = var.vault_id
11 | file_path = "${path.module}/test.txt"
12 | }
13 | ```
14 |
15 | ## Argument Reference
16 |
17 | * `name` - (Required) your document title.
18 | * `field_path` - (Required) path to your document, which will be upload to 1password.
19 | * `vault` - (Optional) see details in onepassword_item_common.
20 | * `notes` - (Optional) see details in onepassword_item_common.
21 | * `tags` - (Optional) see details in onepassword_item_common.
22 | * `section` - (Optional) see details in onepassword_item_common.
23 |
24 | ## Attribute Reference
25 |
26 | In addition to the above arguments, the following attributes are exported:
27 |
28 | * `id` - document id.
29 | * `content` - document content.
30 |
--------------------------------------------------------------------------------
/docs/resources/item_identity.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_identity
2 |
3 | This resource can create any identity for 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | resource "onepassword_item_identity" "this" {
9 | name = "Andrii Nasinnyk"
10 | vault = var.vault_id
11 |
12 | identification {
13 | firstname = "Andrii"
14 | initial = "AN#24"
15 | lastname = "Nasinnyk"
16 | sex = "male"
17 | birth_date = 575553660 //unix-time
18 | occupation = "Play Basketball"
19 | company = "HarshPhil"
20 | department = "Guards"
21 | job_title = "Point Guard"
22 | }
23 |
24 | address {
25 | address {
26 | city = "Kyiv"
27 | street = "11 Line"
28 | country = "ua"
29 | zip = "46000"
30 | region = "Dniprovskii"
31 | state = "Kyiv"
32 | }
33 |
34 | default_phone = "+38 (000) 000 0000"
35 | home_phone = "+38 (000) 000 0000"
36 | cell_phone = "+38 (000) 000 0000"
37 | business_phone = "+38 (000) 000 0000"
38 | }
39 |
40 | internet {
41 | username = "anasinnyk"
42 | email = "andriy.nas@gmail.com"
43 | }
44 | }
45 | ```
46 |
47 | ## Argument Reference
48 |
49 | * `name` - (Required) your identity title.
50 | * `vault` - (Optional) see details in onepassword_item_common.
51 | * `notes` - (Optional) see details in onepassword_item_common.
52 | * `tags` - (Optional) see details in onepassword_item_common.
53 | * `section` - (Optional) see details in onepassword_item_common.
54 | * `identification` - (Optional)
55 | * `address` - (Optional)
56 | * `internet` - (Optional)
57 |
58 | The `identification` block support:
59 |
60 | * `firstname` - (Optional)
61 | * `initial` - (Optional)
62 | * `lastname` - (Optional)
63 | * `sex` - (Optional)
64 | * `birth_date` - (Optional)
65 | * `occupation` - (Optional)
66 | * `company` - (Optional)
67 | * `department` - (Optional)
68 | * `job_title` - (Optional)
69 |
70 | The `address` block support:
71 |
72 | * `address` - (Optional)
73 | * `default_phone` - (Optional)
74 | * `home_phone` - (Optional)
75 | * `cell_phone` - (Optional)
76 | * `business_phone` - (Optional)
77 |
78 | The `internet` block support:
79 |
80 | * `username` - (Optional)
81 | * `email` - (Optional)
82 |
83 | ## Attribute Reference
84 |
85 | In addition to the above arguments, the following attributes are exported:
86 |
87 | * `id` - identity id.
88 |
--------------------------------------------------------------------------------
/docs/resources/item_login.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_login
2 |
3 | This resource can create any login for 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | resource "onepassword_item_login" "this" {
9 | name = "login-title"
10 | username = "some-user-name"
11 | password = "123456qQ"
12 | url = "https://example.com"
13 | vault = var.vault_id
14 | }
15 | ```
16 |
17 | ## Argument Reference
18 |
19 | * `name` - (Required) your login title.
20 | * `username` - (Optional) from this login.
21 | * `password` - (Optional) from this login.
22 | * `url` - (Optional) url for website from this login.
23 | * `vault` - (Optional) see details in onepassword_item_common.
24 | * `notes` - (Optional) see details in onepassword_item_common.
25 | * `tags` - (Optional) see details in onepassword_item_common.
26 | * `section` - (Optional) see details in onepassword_item_common.
27 |
28 | ## Attribute Reference
29 |
30 | In addition to the above arguments, the following attributes are exported:
31 |
32 | * `id` - login id.
33 |
--------------------------------------------------------------------------------
/docs/resources/item_password.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_password
2 |
3 | This resource can create any password for 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | resource "onepassword_item_password" "this" {
9 | name = "login-title"
10 | password = "123456qQ"
11 | url = "https://example.com"
12 | vault = var.vault_id
13 | }
14 | ```
15 |
16 | ## Argument Reference
17 |
18 | * `name` - (Required) your password title.
19 | * `password` - (Optional) store password here.
20 | * `url` - (Optional) url for website from this password.
21 | * `notes` - (Optional) see details in onepassword_item_common.
22 | * `vault` - (Optional) see details in onepassword_item_common.
23 | * `tags` - (Optional) see details in onepassword_item_common.
24 | * `section` - (Optional) see details in onepassword_item_common.
25 |
26 | ## Attribute Reference
27 |
28 | In addition to the above arguments, the following attributes are exported:
29 |
30 | * `id` - password id.
31 |
--------------------------------------------------------------------------------
/docs/resources/item_secure_note.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_secure_note
2 |
3 | This resource can create any secure note for 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | resource "onepassword_item_secure_note" "this" {
9 | name = "secure-note-title"
10 | notes = <<>>
15 | vault = var.vault_id
16 | }
17 | ```
18 |
19 | ## Argument Reference
20 |
21 | * `name` - (Required) your secure note title.
22 | * `vault` - (Optional) see details in onepassword_item_common.
23 | * `notes` - (Optional) see details in onepassword_item_common (main field for this type).
24 | * `tags` - (Optional) see details in onepassword_item_common.
25 | * `section` - (Optional) see details in onepassword_item_common.
26 |
27 | ## Attribute Reference
28 |
29 | In addition to the above arguments, the following attributes are exported:
30 |
31 | * `id` - secure note id.
32 |
--------------------------------------------------------------------------------
/docs/resources/item_software_license.md:
--------------------------------------------------------------------------------
1 | # onepassword_item_software_license
2 |
3 | This resource can create any software license for 1password.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | resource "onepassword_item_software_license" "this" {
9 | name = "software-license-title"
10 | vault = var.vault_id
11 | license_key = "SOME-SECURE-SOWTWARE-LICENSE-KEY"
12 | }
13 | ```
14 |
15 | ## Argument Reference
16 |
17 | * `name` - (Required) your software license title.
18 | * `license_key` - (Optional) store your license key here.
19 | * `vault` - (Optional) see details in onepassword_item_common.
20 | * `notes` - (Optional) see details in onepassword_item_common.
21 | * `tags` - (Optional) see details in onepassword_item_common.
22 | * `section` - (Optional) see details in onepassword_item_common.
23 |
24 | ## Attribute Reference
25 |
26 | In addition to the above arguments, the following attributes are exported:
27 |
28 | * `id` - software license id.
29 |
--------------------------------------------------------------------------------
/docs/resources/vault.md:
--------------------------------------------------------------------------------
1 | # onepassword_vault
2 |
3 | This resource can create vaults in your 1password account.
4 |
5 | ## Example Usage
6 |
7 | ```hcl
8 | resource "onepassword_vault" "this" {
9 | name = "new-vault"
10 | }
11 | ```
12 |
13 | ## Argument Reference
14 |
15 | * `name` - (Required) vault name.
16 |
17 | ## Attribute Reference
18 |
19 | In addition to the above arguments, the following attributes are exported:
20 |
21 | * `id` - vault id.
22 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | .terraform/
2 | terraform.*
3 | crash.log
4 |
--------------------------------------------------------------------------------
/examples/credit_card/main.tf:
--------------------------------------------------------------------------------
1 | resource "onepassword_item_credit_card" "this" {
2 | name = "Default Visa"
3 | vault = var.vault_id
4 |
5 | main {
6 | cardholder = "John Smith"
7 | type = "visa"
8 | number = "4111 1111 1111 1111"
9 | cvv = "1111"
10 | expiry_date = 202205
11 | valid_from = 201805
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/credit_card/output.tf:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/credit_card/variables.tf:
--------------------------------------------------------------------------------
1 | variable "vault_id" {
2 | type = string
3 | }
4 |
--------------------------------------------------------------------------------
/examples/document/main.tf:
--------------------------------------------------------------------------------
1 | resource "onepassword_item_document" "this" {
2 | name = var.new_document_name
3 | vault = var.vault_id
4 | file_path = "${path.module}/test.txt"
5 | }
6 |
--------------------------------------------------------------------------------
/examples/document/output.tf:
--------------------------------------------------------------------------------
1 | output "new_document" {
2 | value = "${onepassword_item_document.this.content}"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/document/test.txt:
--------------------------------------------------------------------------------
1 | test
2 |
--------------------------------------------------------------------------------
/examples/document/variables.tf:
--------------------------------------------------------------------------------
1 | variable "new_document_name" {
2 | default = "newDoc"
3 | }
4 |
5 | variable "vault_id" {
6 | type = string
7 | }
8 |
--------------------------------------------------------------------------------
/examples/group/main.tf:
--------------------------------------------------------------------------------
1 | data "onepassword_group" "this" {
2 | name = var.exist_group_name
3 | }
4 |
5 | resource "onepassword_group" "this" {
6 | name = var.new_group_name
7 | }
8 |
--------------------------------------------------------------------------------
/examples/group/output.tf:
--------------------------------------------------------------------------------
1 | output "team" {
2 | value = data.onepassword_group.this.id
3 | }
4 |
5 | output "new" {
6 | value = onepassword_group.this.id
7 | }
8 |
--------------------------------------------------------------------------------
/examples/group/variables.tf:
--------------------------------------------------------------------------------
1 | variable "exist_group_name" {
2 | default = "Team Members"
3 | }
4 |
5 | variable "new_group_name" {
6 | default = "New"
7 | }
8 |
--------------------------------------------------------------------------------
/examples/identity/main.tf:
--------------------------------------------------------------------------------
1 | resource "onepassword_item_identity" "this" {
2 | name = "Andrii Nasinnyk"
3 | vault = var.vault_id
4 |
5 | identification {
6 | firstname = "Andrii"
7 | initial = "AN#24"
8 | lastname = "Nasinnyk"
9 | sex = "male"
10 | birth_date = 575553660 //unix-time
11 | occupation = "Play Basketball"
12 | company = "HarshPhil"
13 | department = "Guards"
14 | job_title = "Point Guard"
15 | }
16 |
17 | address {
18 | address {
19 | city = "Kyiv"
20 | street = "11 Line"
21 | country = "ua"
22 | zip = "46000"
23 | region = "Dniprovskii"
24 | state = "Kyiv"
25 | }
26 |
27 | default_phone = "+38 (000) 000 0000"
28 | home_phone = "+38 (000) 000 0000"
29 | cell_phone = "+38 (000) 000 0000"
30 | business_phone = "+38 (000) 000 0000"
31 | }
32 |
33 | internet {
34 | username = "anasinnyk"
35 | email = "andriy.nas@gmail.com"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/examples/identity/output.tf:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/identity/variables.tf:
--------------------------------------------------------------------------------
1 | variable "vault_id" {
2 | type = string
3 | }
4 |
--------------------------------------------------------------------------------
/examples/login/main.tf:
--------------------------------------------------------------------------------
1 | resource "onepassword_item_login" "this" {
2 | name = var.login
3 | username = var.login
4 | password = var.password
5 | url = var.website
6 | vault = var.vault_id
7 | }
8 |
--------------------------------------------------------------------------------
/examples/login/output.tf:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/login/variables.tf:
--------------------------------------------------------------------------------
1 | variable "password" {
2 | type = string
3 | }
4 |
5 | variable "login" {
6 | type = string
7 | }
8 |
9 | variable "website" {
10 | type = string
11 | }
12 |
13 | variable "vault_id" {
14 | type = string
15 | }
16 |
--------------------------------------------------------------------------------
/examples/main.tf:
--------------------------------------------------------------------------------
1 | provider "onepassword" {
2 | email = "your@email.here" // or use environment variable OP_EMAIL
3 | password = "super-master-password-here" // or use environment variable OP_PASSWORD
4 | secret_key = "secret-key-from-pdf-document" // or use environment variable OP_SECRET_KEY
5 | subdomain = "company-domain" // skip it or use my if you use personal 1password account or use environment variable OP_SUBDOMAIN
6 | }
7 |
8 | provider "random" {
9 | version = "~> 2.3"
10 | }
11 |
12 | terraform {
13 | required_version = ">= 0.12"
14 | }
15 |
16 | resource "random_string" "password" {
17 | length = "32"
18 | }
19 |
20 | module "group" {
21 | source = "./group"
22 | }
23 |
24 | module "user" {
25 | source = "./user"
26 | email = "example@example.com"
27 | }
28 |
29 | module "vault" {
30 | source = "./vault"
31 | }
32 |
33 | module "document" {
34 | source = "./document"
35 | vault_id = module.vault.new
36 | }
37 |
38 | module "login" {
39 | source = "./login"
40 | login = "anasinnyk"
41 | password = random_string.password.result
42 | website = "https://terraform.io"
43 | vault_id = module.vault.new
44 | }
45 |
46 | module "secret_note" {
47 | source = "./secure_note"
48 | secret = random_string.password.result
49 | vault_id = module.vault.new
50 | }
51 |
52 | module "password" {
53 | source = "./password"
54 | password = random_string.password.result
55 | vault_id = module.vault.new
56 | }
57 |
58 | module "software_license" {
59 | source = "./software_license"
60 | license_key = random_string.password.result
61 | vault_id = module.vault.new
62 | }
63 |
64 | module "credit_card" {
65 | source = "./credit_card"
66 | vault_id = module.vault.new
67 | }
68 |
69 | module "identity" {
70 | source = "./identity"
71 | vault_id = module.vault.new
72 | }
73 |
74 | module "reward_program" {
75 | source = "./reward_program"
76 | vault_id = module.vault.new
77 | }
78 |
--------------------------------------------------------------------------------
/examples/password/main.tf:
--------------------------------------------------------------------------------
1 | resource "onepassword_item_password" "this" {
2 | name = "password"
3 | password = var.password
4 | vault = var.vault_id
5 | }
6 |
--------------------------------------------------------------------------------
/examples/password/output.tf:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/password/variables.tf:
--------------------------------------------------------------------------------
1 | variable "password" {
2 | type = string
3 | }
4 |
5 | variable "vault_id" {
6 | type = string
7 | }
8 |
--------------------------------------------------------------------------------
/examples/reward_program/main.tf:
--------------------------------------------------------------------------------
1 | resource "onepassword_item_common" "this" {
2 | name = "Coupone"
3 | vault = var.vault_id
4 |
5 | template = "Reward Program"
6 |
7 | section {
8 | field {
9 | name = "company name"
10 | string = "MacPaw"
11 | }
12 |
13 | field {
14 | name = "member name"
15 | string = "anasinnyk"
16 | }
17 |
18 | field {
19 | name = "member ID"
20 | string = "123"
21 | }
22 |
23 | field {
24 | name = "PIN"
25 | concealed = "123456qQ"
26 | }
27 | }
28 |
29 | section {
30 | name = "More Information"
31 |
32 | field {
33 | name = "member ID (additional)"
34 | string = "321"
35 | }
36 |
37 | field {
38 | name = "customer service phone"
39 | phone = "+38 (000) 000 0000"
40 | }
41 |
42 | field {
43 | name = "phone for reservations"
44 | phone = "+38 (000) 000 0000"
45 | }
46 |
47 | field {
48 | name = "website"
49 | url = "https://groupon.com"
50 | }
51 |
52 | field {
53 | name = "member since"
54 | month_year = 201903
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/examples/reward_program/output.tf:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/reward_program/variables.tf:
--------------------------------------------------------------------------------
1 | variable "vault_id" {
2 | type = string
3 | }
4 |
--------------------------------------------------------------------------------
/examples/secure_note/main.tf:
--------------------------------------------------------------------------------
1 | resource "onepassword_item_secure_note" "this" {
2 | name = "secure_note"
3 | notes = var.secret
4 | vault = var.vault_id
5 | }
6 |
--------------------------------------------------------------------------------
/examples/secure_note/output.tf:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/secure_note/variables.tf:
--------------------------------------------------------------------------------
1 | variable "secret" {
2 | type = string
3 | }
4 |
5 | variable "vault_id" {
6 | type = string
7 | }
8 |
--------------------------------------------------------------------------------
/examples/software_license/main.tf:
--------------------------------------------------------------------------------
1 | resource "onepassword_item_software_license" "this" {
2 | name = "software-license"
3 | vault = var.vault_id
4 |
5 | main {
6 | license_key = var.license_key
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/software_license/output.tf:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/software_license/variables.tf:
--------------------------------------------------------------------------------
1 | variable "license_key" {
2 | type = string
3 | }
4 |
5 | variable "vault_id" {
6 | type = string
7 | }
8 |
--------------------------------------------------------------------------------
/examples/user/main.tf:
--------------------------------------------------------------------------------
1 | data onepassword_user this {
2 | email = var.email
3 | }
4 |
--------------------------------------------------------------------------------
/examples/user/output.tf:
--------------------------------------------------------------------------------
1 | output user {
2 | value = data.onepassword_user.this
3 | }
4 |
--------------------------------------------------------------------------------
/examples/user/variables.tf:
--------------------------------------------------------------------------------
1 | variable email {
2 | description = "Email address of the user"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/vault/main.tf:
--------------------------------------------------------------------------------
1 | data "onepassword_vault" "this" {
2 | name = var.exist_vault_name
3 | }
4 |
5 | resource "onepassword_vault" "this" {
6 | name = var.new_vault_name
7 | }
8 |
--------------------------------------------------------------------------------
/examples/vault/output.tf:
--------------------------------------------------------------------------------
1 | output "personal" {
2 | value = "${data.onepassword_vault.this.id}"
3 | }
4 |
5 | output "new" {
6 | value = "${onepassword_vault.this.id}"
7 | }
8 |
--------------------------------------------------------------------------------
/examples/vault/variables.tf:
--------------------------------------------------------------------------------
1 | variable "exist_vault_name" {
2 | default = "Personal"
3 | }
4 |
5 | variable "new_vault_name" {
6 | default = "New"
7 | }
8 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/anasinnyk/terraform-provider-1password
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/Masterminds/semver v1.5.0
7 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
8 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.16.0
9 | github.com/kalaspuffar/base64url v0.0.0-20171121144659-483af17b794c
10 | )
11 |
12 | require (
13 | github.com/agext/levenshtein v1.2.2 // indirect
14 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
15 | github.com/fatih/color v1.7.0 // indirect
16 | github.com/golang/protobuf v1.5.2 // indirect
17 | github.com/google/go-cmp v0.5.8 // indirect
18 | github.com/hashicorp/errwrap v1.0.0 // indirect
19 | github.com/hashicorp/go-hclog v1.2.0 // indirect
20 | github.com/hashicorp/go-multierror v1.1.1 // indirect
21 | github.com/hashicorp/go-plugin v1.4.3 // indirect
22 | github.com/hashicorp/go-uuid v1.0.3 // indirect
23 | github.com/hashicorp/go-version v1.4.0 // indirect
24 | github.com/hashicorp/hcl/v2 v2.12.0 // indirect
25 | github.com/hashicorp/logutils v1.0.0 // indirect
26 | github.com/hashicorp/terraform-plugin-go v0.9.0 // indirect
27 | github.com/hashicorp/terraform-plugin-log v0.4.0 // indirect
28 | github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 // indirect
29 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect
30 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
31 | github.com/mattn/go-colorable v0.1.4 // indirect
32 | github.com/mattn/go-isatty v0.0.10 // indirect
33 | github.com/mitchellh/copystructure v1.2.0 // indirect
34 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect
35 | github.com/mitchellh/go-wordwrap v1.0.0 // indirect
36 | github.com/mitchellh/mapstructure v1.5.0 // indirect
37 | github.com/mitchellh/reflectwalk v1.0.2 // indirect
38 | github.com/oklog/run v1.0.0 // indirect
39 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
40 | github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
41 | github.com/vmihailenco/tagparser v0.1.1 // indirect
42 | github.com/zclconf/go-cty v1.10.0 // indirect
43 | golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect
44 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
45 | golang.org/x/text v0.3.5 // indirect
46 | google.golang.org/appengine v1.6.6 // indirect
47 | google.golang.org/genproto v0.0.0-20200711021454-869866162049 // indirect
48 | google.golang.org/grpc v1.45.0 // indirect
49 | google.golang.org/protobuf v1.28.0 // indirect
50 | )
51 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
4 | github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
5 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
6 | github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
7 | github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
8 | github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
9 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
10 | github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
11 | github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I=
12 | github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
13 | github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
14 | github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
15 | github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
16 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
17 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
18 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
19 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
20 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
21 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
22 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
23 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
24 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
25 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
26 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
27 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
28 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
29 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
30 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
31 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
32 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
33 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
34 | github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
35 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
36 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
37 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
38 | github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
39 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
40 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
41 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
42 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
43 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
44 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
45 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
46 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
47 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
48 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
49 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
50 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
51 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
52 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
53 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
54 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
55 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
56 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
57 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
58 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
59 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
60 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
61 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
62 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
63 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
64 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
65 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
66 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
67 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
68 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
69 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
70 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
71 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI=
72 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs=
73 | github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
74 | github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
75 | github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
76 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
77 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
78 | github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
79 | github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
80 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
81 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
82 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
83 | github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
84 | github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
85 | github.com/hashicorp/hcl/v2 v2.12.0 h1:PsYxySWpMD4KPaoJLnsHwtK5Qptvj/4Q6s0t4sUxZf4=
86 | github.com/hashicorp/hcl/v2 v2.12.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
87 | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
88 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
89 | github.com/hashicorp/terraform-plugin-go v0.9.0 h1:FvLY/3z4SNVatPZdoFcyrlNbCar+WyyOTv5X4Tp+WZc=
90 | github.com/hashicorp/terraform-plugin-go v0.9.0/go.mod h1:EawBkgjBWNf7jiKnVoyDyF39OSV+u6KUX+Y73EPj3oM=
91 | github.com/hashicorp/terraform-plugin-log v0.4.0 h1:F3eVnm8r2EfQCe2k9blPIiF/r2TT01SHijXnS7bujvc=
92 | github.com/hashicorp/terraform-plugin-log v0.4.0/go.mod h1:9KclxdunFownr4pIm1jdmwKRmE4d6HVG2c9XDq47rpg=
93 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.16.0 h1:9fjPgCenJqnbjo95SDcbJ+YdLyEC1N35cwKWcRWhJTQ=
94 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.16.0/go.mod h1:hLa0sTiySU/AWEgV2GxJh0/pQIqcCmm30IPja9N9lTg=
95 | github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 h1:1FGtlkJw87UsTMg5s8jrekrHmUPUJaMcu6ELiVhQrNw=
96 | github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896/go.mod h1:bzBPnUIkI0RxauU8Dqo+2KrZZ28Cf48s8V6IHt3p4co=
97 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0=
98 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=
99 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
100 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
101 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
102 | github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
103 | github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
104 | github.com/kalaspuffar/base64url v0.0.0-20171121144659-483af17b794c h1:ZkUizoGcMSTBf//ceQsABr48qCn5jKDbcJzl/tF0rTE=
105 | github.com/kalaspuffar/base64url v0.0.0-20171121144659-483af17b794c/go.mod h1:TB01veSUodJmp2SAYhLtMRGSfQnbx0PSoCsIpY4Tp6I=
106 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
107 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
108 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
109 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
110 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
111 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
112 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
113 | github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
114 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
115 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
116 | github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
117 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
118 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
119 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
120 | github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
121 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
122 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
123 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
124 | github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
125 | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
126 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
127 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
128 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
129 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
130 | github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=
131 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
132 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
133 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
134 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
135 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
136 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
137 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
138 | github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
139 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
140 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
141 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
142 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
143 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
144 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
145 | github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
146 | github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
147 | github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
148 | github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U=
149 | github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
150 | github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
151 | github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
152 | github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
153 | github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
154 | github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
155 | github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0=
156 | github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
157 | github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
158 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
159 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
160 | golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
161 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
162 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
163 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
164 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
165 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
166 | golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
167 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
168 | golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
169 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
170 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
171 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
172 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
173 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
174 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
175 | golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
176 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
177 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
178 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
179 | golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs=
180 | golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
181 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
182 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
183 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
184 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
185 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
186 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
187 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
188 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
189 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
190 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
191 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
192 | golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
193 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
194 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
195 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
196 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
197 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
198 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
199 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
200 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
201 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
202 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
203 | golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
204 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
205 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
206 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
207 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
208 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
209 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
210 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
211 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
212 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
213 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
214 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
215 | google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
216 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
217 | google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
218 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
219 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
220 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
221 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
222 | google.golang.org/genproto v0.0.0-20200711021454-869866162049 h1:YFTFpQhgvrLrmxtiIncJxFXeCyq84ixuKWVCaCAi9Oc=
223 | google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
224 | google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
225 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
226 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
227 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
228 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
229 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
230 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
231 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
232 | google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
233 | google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
234 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
235 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
236 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
237 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
238 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
239 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
240 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
241 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
242 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
243 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
244 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
245 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
246 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
247 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
248 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
249 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
250 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
251 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
252 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
253 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
254 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
255 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
256 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
257 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/anasinnyk/terraform-provider-1password/onepassword"
5 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
6 | "github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
7 | )
8 |
9 | func main() {
10 | plugin.Serve(&plugin.ServeOpts{
11 | ProviderFunc: func() *schema.Provider {
12 | return onepassword.Provider()
13 | },
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/onepassword/data_group.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceGroup() *schema.Resource {
6 | return &schema.Resource{
7 | ReadContext: resourceGroupRead,
8 | Schema: resourceGroup().Schema,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/onepassword/data_item_common.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceItemCommon() *schema.Resource {
6 | s := resourceItemCommon().Schema
7 | s["template"].Required = false
8 | s["template"].Optional = true
9 |
10 | return &schema.Resource{
11 | ReadContext: resourceItemCommonRead,
12 | Schema: s,
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/onepassword/data_item_credit_card.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceItemCreditCard() *schema.Resource {
6 | return &schema.Resource{
7 | ReadContext: resourceItemCreditCardRead,
8 | Schema: resourceItemCreditCard().Schema,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/onepassword/data_item_document.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceItemDocument() *schema.Resource {
6 | s := resourceItemDocument().Schema
7 | delete(s, "file_path")
8 |
9 | return &schema.Resource{
10 | ReadContext: resourceItemDocumentRead,
11 | Schema: s,
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/onepassword/data_item_identity.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceItemIdentity() *schema.Resource {
6 | return &schema.Resource{
7 | ReadContext: resourceItemIdentityRead,
8 | Schema: resourceItemIdentity().Schema,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/onepassword/data_item_login.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceItemLogin() *schema.Resource {
6 | return &schema.Resource{
7 | ReadContext: resourceItemLoginRead,
8 | Schema: resourceItemLogin().Schema,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/onepassword/data_item_password.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceItemPassword() *schema.Resource {
6 | return &schema.Resource{
7 | ReadContext: resourceItemPasswordRead,
8 | Schema: resourceItemPassword().Schema,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/onepassword/data_item_secure_note.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceItemSecureNote() *schema.Resource {
6 | return &schema.Resource{
7 | ReadContext: resourceItemSecureNoteRead,
8 | Schema: resourceItemSecureNote().Schema,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/onepassword/data_item_software_license.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceItemSoftwareLicense() *schema.Resource {
6 | return &schema.Resource{
7 | ReadContext: resourceItemSoftwareLicenseRead,
8 | Schema: resourceItemSoftwareLicense().Schema,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/onepassword/data_user.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8 | )
9 |
10 | func dataSourceUser() *schema.Resource {
11 | return &schema.Resource{
12 | ReadContext: dataSourceUserRead,
13 | Schema: map[string]*schema.Schema{
14 | "email": {
15 | Type: schema.TypeString,
16 | Required: true,
17 | },
18 | "firstname": {
19 | Type: schema.TypeString,
20 | Optional: true,
21 | },
22 | "lastname": {
23 | Type: schema.TypeString,
24 | Optional: true,
25 | },
26 | "state": {
27 | Type: schema.TypeString,
28 | Optional: true,
29 | },
30 | },
31 | }
32 | }
33 |
34 | func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
35 | m := meta.(*Meta)
36 | v, err := m.onePassClient.ReadUser(getIDEmail(d))
37 | if err != nil {
38 | return diag.FromErr(err)
39 | }
40 |
41 | d.SetId(v.UUID)
42 | if err := d.Set("email", v.Email); err != nil {
43 | return diag.FromErr(err)
44 | }
45 | if err := d.Set("firstname", v.FirstName); err != nil {
46 | return diag.FromErr(err)
47 | }
48 | if err := d.Set("lastname", v.LastName); err != nil {
49 | return diag.FromErr(err)
50 | }
51 | if err := d.Set("state", v.State); err != nil {
52 | return diag.FromErr(err)
53 | }
54 | return nil
55 | }
56 |
--------------------------------------------------------------------------------
/onepassword/data_vault.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
4 |
5 | func dataSourceVault() *schema.Resource {
6 | return &schema.Resource{
7 | ReadContext: resourceVaultRead,
8 | Schema: resourceVault().Schema,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/onepassword/group.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | const (
9 | // GroupResource is 1Password's internal designator for Groups
10 | GroupResource = "group"
11 |
12 | // GroupStateActive indicates an Active Group
13 | GroupStateActive = "A"
14 |
15 | // GroupStateDeleted indicates a Deleted Group
16 | GroupStateDeleted = "D"
17 | )
18 |
19 | // Group represents a 1Password Group resource
20 | type Group struct {
21 | UUID string
22 | Name string
23 | State string
24 | }
25 |
26 | // ReadGroup gets an existing 1Password Group
27 | func (o *OnePassClient) ReadGroup(id string) (*Group, error) {
28 | group := &Group{}
29 | res, err := o.runCmd(opPasswordGet, GroupResource, id)
30 | if err != nil {
31 | return nil, err
32 | }
33 | if err = json.Unmarshal(res, group); err != nil {
34 | return nil, err
35 | }
36 | return group, nil
37 | }
38 |
39 | // ListGroupMembers lists the existing Users in a given Group
40 | func (o *OnePassClient) ListGroupMembers(id string) ([]User, error) {
41 | users := []User{}
42 | if id == "" {
43 | return users, fmt.Errorf("Must provide an identifier to list group members")
44 | }
45 |
46 | res, err := o.runCmd(opPasswordList, "users", "--"+GroupResource, id)
47 | if err != nil {
48 | return nil, err
49 | }
50 | if err = json.Unmarshal(res, &users); err != nil {
51 | return nil, err
52 | }
53 | return users, nil
54 | }
55 |
56 | // CreateGroup creates a new 1Password Group
57 | func (o *OnePassClient) CreateGroup(v *Group) (*Group, error) {
58 | args := []string{opPasswordCreate, GroupResource, v.Name}
59 | res, err := o.runCmd(args...)
60 | if err != nil {
61 | return nil, err
62 | }
63 | if err = json.Unmarshal(res, v); err != nil {
64 | return nil, err
65 | }
66 | return v, nil
67 | }
68 |
69 | // CreateGroupMember adds a User to a Group
70 | func (o *OnePassClient) CreateGroupMember(groupID string, userID string) error {
71 | args := []string{opPasswordAdd, UserResource, userID, groupID}
72 | _, err := o.runCmd(args...)
73 | return err
74 | }
75 |
76 | // UpdateGroup updates an existing 1Password Group
77 | func (o *OnePassClient) UpdateGroup(id string, v *Group) error {
78 | args := []string{opPasswordEdit, GroupResource, id, "--name=" + v.Name}
79 | _, err := o.runCmd(args...)
80 | return err
81 | }
82 |
83 | // DeleteGroup deletes a 1Password Group
84 | func (o *OnePassClient) DeleteGroup(id string) error {
85 | return o.Delete(GroupResource, id)
86 | }
87 |
88 | // DeleteGroupMember removes a User from a Group
89 | func (o *OnePassClient) DeleteGroupMember(groupID string, userID string) error {
90 | args := []string{opPasswordRemove, UserResource, userID, groupID}
91 | _, err := o.runCmd(args...)
92 | return err
93 | }
94 |
--------------------------------------------------------------------------------
/onepassword/group_test.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | "testing"
7 | )
8 |
9 | func TestOnePassClient_ReadGroup(t *testing.T) {
10 | type fields struct {
11 | runCmd func() (string, error)
12 | }
13 | type args struct {
14 | id string
15 | }
16 | tests := []struct {
17 | name string
18 | fields fields
19 | args args
20 | wantExecResults []string
21 | want *Group
22 | wantErr bool
23 | }{
24 | {
25 | name: "success",
26 | fields: fields{
27 | runCmd: func() (string, error) {
28 | return `{ "uuid": "uniq", "name": "foo" }`, nil
29 | },
30 | },
31 | args: args{id: "uniq"},
32 | wantExecResults: []string{"op", "get", "group", "uniq", "--session="},
33 | want: &Group{UUID: "uniq", Name: "foo"},
34 | },
35 | {
36 | name: "bad json",
37 | fields: fields{
38 | runCmd: func() (string, error) {
39 | return `This was supposed to be JSON`, nil
40 | },
41 | },
42 | args: args{id: "uniq"},
43 | wantExecResults: []string{"op", "get", "group", "uniq", "--session="},
44 | wantErr: true,
45 | },
46 | {
47 | name: "error",
48 | fields: fields{
49 | runCmd: func() (string, error) {
50 | return ``, fmt.Errorf("oops")
51 | },
52 | },
53 | args: args{id: "uniq"},
54 | wantExecResults: []string{"op", "get", "group", "uniq", "--session="},
55 | wantErr: true,
56 | },
57 | }
58 | for _, tt := range tests {
59 | t.Run(tt.name, func(t *testing.T) {
60 | config := &mockOnePassConfig{
61 | runCmd: tt.fields.runCmd,
62 | }
63 | o := mockOnePassClient(config)
64 |
65 | got, err := o.ReadGroup(tt.args.id)
66 | if (err != nil) != tt.wantErr {
67 | t.Errorf("OnePassClient.ReadGroup() error = %v, wantErr %v", err, tt.wantErr)
68 | return
69 | }
70 | if !reflect.DeepEqual(got, tt.want) {
71 | t.Errorf("OnePassClient.ReadGroup() = %v, want %v", got, tt.want)
72 | }
73 | if !reflect.DeepEqual(config.execCommandResults, tt.wantExecResults) {
74 | t.Errorf("OnePassClient.ReadGroup() = %v, want %v", config.execCommandResults, tt.wantExecResults)
75 | }
76 | })
77 | }
78 | }
79 |
80 | func TestOnePassClient_CreateGroup(t *testing.T) {
81 | type fields struct {
82 | runCmd func() (string, error)
83 | }
84 | type args struct {
85 | v *Group
86 | }
87 | tests := []struct {
88 | name string
89 | fields fields
90 | args args
91 | wantExecResults []string
92 | want *Group
93 | wantErr bool
94 | }{
95 | {
96 | name: "success",
97 | fields: fields{
98 | runCmd: func() (string, error) {
99 | return `{ "uuid": "uniq", "name": "foo" }`, nil
100 | },
101 | },
102 | args: args{v: &Group{Name: "foo"}},
103 | wantExecResults: []string{"op", "create", "group", "foo", "--session="},
104 | want: &Group{UUID: "uniq", Name: "foo"},
105 | },
106 | {
107 | name: "bad json",
108 | fields: fields{
109 | runCmd: func() (string, error) {
110 | return `This was supposed to be JSON`, nil
111 | },
112 | },
113 | args: args{v: &Group{Name: "foo"}},
114 | wantExecResults: []string{"op", "create", "group", "foo", "--session="},
115 | wantErr: true,
116 | },
117 | {
118 | name: "error",
119 | fields: fields{
120 | runCmd: func() (string, error) {
121 | return ``, fmt.Errorf("oops")
122 | },
123 | },
124 | args: args{v: &Group{Name: "foo"}},
125 | wantExecResults: []string{"op", "create", "group", "foo", "--session="},
126 | wantErr: true,
127 | },
128 | }
129 | for _, tt := range tests {
130 | t.Run(tt.name, func(t *testing.T) {
131 | config := &mockOnePassConfig{
132 | runCmd: tt.fields.runCmd,
133 | }
134 | o := mockOnePassClient(config)
135 |
136 | got, err := o.CreateGroup(tt.args.v)
137 | if (err != nil) != tt.wantErr {
138 | t.Errorf("OnePassClient.CreateGroup() error = %v, wantErr %v", err, tt.wantErr)
139 | return
140 | }
141 | if !reflect.DeepEqual(got, tt.want) {
142 | t.Errorf("OnePassClient.CreateGroup() = %v, want %v", got, tt.want)
143 | }
144 | if !reflect.DeepEqual(config.execCommandResults, tt.wantExecResults) {
145 | t.Errorf("OnePassClient.CreateGroup() = %v, want %v", config.execCommandResults, tt.wantExecResults)
146 | }
147 | })
148 | }
149 | }
150 |
151 | func TestOnePassClient_UpdateGroup(t *testing.T) {
152 | type fields struct {
153 | runCmd func() (string, error)
154 | }
155 | type args struct {
156 | id string
157 | v *Group
158 | }
159 | tests := []struct {
160 | name string
161 | fields fields
162 | args args
163 | wantExecResults []string
164 | want *Group
165 | wantErr bool
166 | }{
167 | {
168 | name: "success",
169 | fields: fields{
170 | runCmd: func() (string, error) {
171 | return `{ "uuid": "uniq", "name": "foo" }`, nil
172 | },
173 | },
174 | args: args{
175 | id: "uniq",
176 | v: &Group{Name: "foo"},
177 | },
178 | wantExecResults: []string{"op", "edit", "group", "uniq", "--name=foo", "--session="},
179 | want: &Group{UUID: "uniq", Name: "foo"},
180 | },
181 | {
182 | name: "error",
183 | fields: fields{
184 | runCmd: func() (string, error) {
185 | return ``, fmt.Errorf("oops")
186 | },
187 | },
188 | args: args{
189 | id: "uniq",
190 | v: &Group{Name: "foo"},
191 | },
192 | wantExecResults: []string{"op", "edit", "group", "uniq", "--name=foo", "--session="},
193 | wantErr: true,
194 | },
195 | }
196 | for _, tt := range tests {
197 | t.Run(tt.name, func(t *testing.T) {
198 | config := &mockOnePassConfig{
199 | runCmd: tt.fields.runCmd,
200 | }
201 | o := mockOnePassClient(config)
202 |
203 | err := o.UpdateGroup(tt.args.id, tt.args.v)
204 | if (err != nil) != tt.wantErr {
205 | t.Errorf("OnePassClient.UpdateGroup() error = %v, wantErr %v", err, tt.wantErr)
206 | return
207 | }
208 | if !reflect.DeepEqual(config.execCommandResults, tt.wantExecResults) {
209 | t.Errorf("OnePassClient.UpdateGroup() = %v, want %v", config.execCommandResults, tt.wantExecResults)
210 | }
211 | })
212 | }
213 | }
214 |
215 | func TestOnePassClient_DeleteGroup(t *testing.T) {
216 | type fields struct {
217 | runCmd func() (string, error)
218 | }
219 | type args struct {
220 | id string
221 | }
222 | tests := []struct {
223 | name string
224 | fields fields
225 | args args
226 | wantExecResults []string
227 | want *Group
228 | wantErr bool
229 | }{
230 | {
231 | name: "success",
232 | fields: fields{
233 | runCmd: func() (string, error) {
234 | return `{ "uuid": "uniq", "name": "foo" }`, nil
235 | },
236 | },
237 | args: args{id: "uniq"},
238 | wantExecResults: []string{"op", "delete", "group", "uniq", "--session="},
239 | want: &Group{UUID: "uniq", Name: "foo"},
240 | },
241 | {
242 | name: "error",
243 | fields: fields{
244 | runCmd: func() (string, error) {
245 | return ``, fmt.Errorf("oops")
246 | },
247 | },
248 | args: args{id: "uniq"},
249 | wantExecResults: []string{"op", "delete", "group", "uniq", "--session="},
250 | wantErr: true,
251 | },
252 | }
253 | for _, tt := range tests {
254 | t.Run(tt.name, func(t *testing.T) {
255 | config := &mockOnePassConfig{
256 | runCmd: tt.fields.runCmd,
257 | }
258 | o := mockOnePassClient(config)
259 |
260 | err := o.DeleteGroup(tt.args.id)
261 | if (err != nil) != tt.wantErr {
262 | t.Errorf("OnePassClient.DeleteGroup() error = %v, wantErr %v", err, tt.wantErr)
263 | return
264 | }
265 | if !reflect.DeepEqual(config.execCommandResults, tt.wantExecResults) {
266 | t.Errorf("OnePassClient.DeleteGroup() = %v, want %v", config.execCommandResults, tt.wantExecResults)
267 | }
268 | })
269 | }
270 | }
271 |
272 | func TestOnePassClient_ListGroupMembers(t *testing.T) {
273 | type fields struct {
274 | runCmd func() (string, error)
275 | }
276 | type args struct {
277 | id string
278 | }
279 | tests := []struct {
280 | name string
281 | fields fields
282 | args args
283 | wantExecResults []string
284 | want []User
285 | wantErr bool
286 | }{
287 | {
288 | name: "success",
289 | fields: fields{
290 | runCmd: func() (string, error) {
291 | return `[ { "uuid": "uniq", "firstname": "Testy", "lastname": "Testerton" } ]`, nil
292 | },
293 | },
294 | args: args{id: "uniq"},
295 | wantExecResults: []string{"op", "list", "users", "--group", "uniq", "--session="},
296 | want: []User{{UUID: "uniq", FirstName: "Testy", LastName: "Testerton"}},
297 | },
298 | {
299 | name: "error",
300 | fields: fields{
301 | runCmd: func() (string, error) {
302 | return ``, fmt.Errorf("oops")
303 | },
304 | },
305 | args: args{id: "uniq"},
306 | wantExecResults: []string{"op", "list", "users", "--group", "uniq", "--session="},
307 | wantErr: true,
308 | },
309 | {
310 | name: "error-missing-id",
311 | args: args{id: ""},
312 | want: []User{},
313 | wantErr: true,
314 | },
315 | }
316 | for _, tt := range tests {
317 | t.Run(tt.name, func(t *testing.T) {
318 | config := &mockOnePassConfig{
319 | runCmd: tt.fields.runCmd,
320 | }
321 | o := mockOnePassClient(config)
322 |
323 | got, err := o.ListGroupMembers(tt.args.id)
324 | if (err != nil) != tt.wantErr {
325 | t.Errorf("OnePassClient.ListGroupMembers() error = %v, wantErr %v", err, tt.wantErr)
326 | return
327 | }
328 | if !reflect.DeepEqual(got, tt.want) {
329 | t.Errorf("OnePassClient.ListGroupMembers() = %v, want %v", got, tt.want)
330 | }
331 | if !reflect.DeepEqual(config.execCommandResults, tt.wantExecResults) {
332 | t.Errorf("OnePassClient.ListGroupMembers() exec = %v, want %v", config.execCommandResults, tt.wantExecResults)
333 | }
334 | })
335 | }
336 | }
337 |
338 | func TestOnePassClient_CreateGroupMember(t *testing.T) {
339 | type fields struct {
340 | runCmd func() (string, error)
341 | }
342 | type args struct {
343 | userID string
344 | groupID string
345 | }
346 | tests := []struct {
347 | name string
348 | fields fields
349 | args args
350 | wantExecResults []string
351 | wantErr bool
352 | }{
353 | {
354 | name: "success",
355 | fields: fields{
356 | runCmd: func() (string, error) {
357 | return `{ }`, nil
358 | },
359 | },
360 | args: args{userID: "userName", groupID: "groupName"},
361 | wantExecResults: []string{"op", "add", "user", "groupName", "userName", "--session="},
362 | },
363 | {
364 | name: "error",
365 | fields: fields{
366 | runCmd: func() (string, error) {
367 | return ``, fmt.Errorf("oops")
368 | },
369 | },
370 | args: args{userID: "userName", groupID: "groupName"},
371 | wantExecResults: []string{"op", "add", "user", "groupName", "userName", "--session="},
372 | wantErr: true,
373 | },
374 | }
375 | for _, tt := range tests {
376 | t.Run(tt.name, func(t *testing.T) {
377 | config := &mockOnePassConfig{
378 | runCmd: tt.fields.runCmd,
379 | }
380 | o := mockOnePassClient(config)
381 |
382 | err := o.CreateGroupMember(tt.args.userID, tt.args.groupID)
383 | if (err != nil) != tt.wantErr {
384 | t.Errorf("OnePassClient.ListGroupMembers() error = %v, wantErr %v", err, tt.wantErr)
385 | return
386 | }
387 | if !reflect.DeepEqual(config.execCommandResults, tt.wantExecResults) {
388 | t.Errorf("OnePassClient.ListGroupMembers() exec = %v, want %v", config.execCommandResults, tt.wantExecResults)
389 | }
390 | })
391 | }
392 | }
393 |
394 | func TestOnePassClient_DeleteGroupMember(t *testing.T) {
395 | type fields struct {
396 | runCmd func() (string, error)
397 | }
398 | type args struct {
399 | userID string
400 | groupID string
401 | }
402 | tests := []struct {
403 | name string
404 | fields fields
405 | args args
406 | wantExecResults []string
407 | wantErr bool
408 | }{
409 | {
410 | name: "success",
411 | fields: fields{
412 | runCmd: func() (string, error) {
413 | return `{ }`, nil
414 | },
415 | },
416 | args: args{userID: "userName", groupID: "groupName"},
417 | wantExecResults: []string{"op", "remove", "user", "groupName", "userName", "--session="},
418 | },
419 | {
420 | name: "error",
421 | fields: fields{
422 | runCmd: func() (string, error) {
423 | return ``, fmt.Errorf("oops")
424 | },
425 | },
426 | args: args{userID: "userName", groupID: "groupName"},
427 | wantExecResults: []string{"op", "remove", "user", "groupName", "userName", "--session="},
428 | wantErr: true,
429 | },
430 | }
431 | for _, tt := range tests {
432 | t.Run(tt.name, func(t *testing.T) {
433 | config := &mockOnePassConfig{
434 | runCmd: tt.fields.runCmd,
435 | }
436 | o := mockOnePassClient(config)
437 |
438 | err := o.DeleteGroupMember(tt.args.userID, tt.args.groupID)
439 | if (err != nil) != tt.wantErr {
440 | t.Errorf("OnePassClient.ListGroupMembers() error = %v, wantErr %v", err, tt.wantErr)
441 | return
442 | }
443 | if !reflect.DeepEqual(config.execCommandResults, tt.wantExecResults) {
444 | t.Errorf("OnePassClient.ListGroupMembers() exec = %v, want %v", config.execCommandResults, tt.wantExecResults)
445 | }
446 | })
447 | }
448 | }
449 |
--------------------------------------------------------------------------------
/onepassword/helper.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "crypto/rand"
5 | "fmt"
6 | "net/url"
7 | "regexp"
8 | "strings"
9 |
10 | "github.com/hashicorp/go-cty/cty"
11 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13 | )
14 |
15 | func stringDiag() schema.SchemaValidateDiagFunc {
16 | return func(v interface{}, path cty.Path) diag.Diagnostics {
17 | var diags diag.Diagnostics
18 | val, ok := v.(string)
19 | if !ok {
20 | diags = append(diags, diag.Diagnostic{
21 | Severity: diag.Error,
22 | Summary: "Value is not a string",
23 | Detail: fmt.Sprintf("Value is not a string (type = %T)", val),
24 | AttributePath: path,
25 | })
26 | }
27 | return diags
28 | }
29 | }
30 |
31 | func emailValidateDiag() schema.SchemaValidateDiagFunc {
32 | return func(v interface{}, path cty.Path) diag.Diagnostics {
33 | diags := stringDiag()(v, path)
34 | val, _ := v.(string)
35 |
36 | emailRegexp := regexp.MustCompile(
37 | "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}" +
38 | "[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$",
39 | )
40 | if len(diags) == 0 && !emailRegexp.MatchString(val) {
41 | diags = append(diags, diag.Diagnostic{
42 | Severity: diag.Error,
43 | Summary: "Value is not email",
44 | Detail: fmt.Sprintf("%s is not email", val),
45 | AttributePath: path,
46 | })
47 | }
48 |
49 | return diags
50 | }
51 | }
52 |
53 | func urlValidateDiag() schema.SchemaValidateDiagFunc {
54 | return func(v interface{}, path cty.Path) diag.Diagnostics {
55 | diags := stringDiag()(v, path)
56 | val, _ := v.(string)
57 | if len(diags) == 0 {
58 | _, err := url.ParseRequestURI(val)
59 | if err != nil {
60 | diags = append(diags, diag.Diagnostic{
61 | Severity: diag.Error,
62 | Summary: "Value is not URL",
63 | Detail: fmt.Sprintf("%s is not an URL", val),
64 | AttributePath: path,
65 | })
66 | }
67 | }
68 | return diags
69 | }
70 | }
71 |
72 | func stringInSliceDiag(ss []string, empty bool) schema.SchemaValidateDiagFunc {
73 | return func(v interface{}, path cty.Path) diag.Diagnostics {
74 | diags := stringDiag()(v, path)
75 | val, _ := v.(string)
76 | if len(diags) == 0 {
77 | if (!empty || val != "") && !stringInSlice(val, ss) {
78 | diags = append(diags, diag.Diagnostic{
79 | Severity: diag.Error,
80 | Summary: "Value has incorect value",
81 | Detail: fmt.Sprintf("%s one from next list (%s)", val, strings.Join(ss,",")),
82 | AttributePath: path,
83 | })
84 | }
85 | }
86 | return diags
87 | }
88 | }
89 |
90 | func stringInSlice(a string, list []string) bool {
91 | for _, b := range list {
92 | if b == a {
93 | return true
94 | }
95 | }
96 | return false
97 | }
98 |
99 | func fieldNumber() (string, error) {
100 | b := make([]byte, 16)
101 | if _, err := rand.Read(b); err != nil {
102 | return "", err
103 | }
104 | return strings.ToUpper(fmt.Sprintf("%x", b)), nil
105 | }
106 |
--------------------------------------------------------------------------------
/onepassword/item.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "errors"
7 | "fmt"
8 | "strings"
9 |
10 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12 | "github.com/kalaspuffar/base64url"
13 | )
14 |
15 | const ItemResource = "item"
16 | const DocumentResource = "document"
17 |
18 | type SectionFieldType string
19 | type FieldType string
20 | type Category string
21 |
22 | const (
23 | FieldPassword FieldType = "P"
24 | FieldText FieldType = "T"
25 | )
26 |
27 | const (
28 | LoginCategory Category = "Login"
29 | IdentityCategory Category = "Identity"
30 | DatabaseCategory Category = "Database"
31 | MembershipCategory Category = "Membership"
32 | WirelessRouterCategory Category = "Wireless Router"
33 | SecureNoteCategory Category = "Secure Note"
34 | SoftwareLicenseCategory Category = "Software License"
35 | CreditCardCategory Category = "Credit Card"
36 | DriverLicenseCategory Category = "Driver License"
37 | OutdoorLicenseCategory Category = "Outdoor License"
38 | PassportCategory Category = "Passport"
39 | EmailAccountCategory Category = "Email Account"
40 | PasswordCategory Category = "Password"
41 | RewardProgramCategory Category = "Reward Program"
42 | SocialSecurityNumberCategory Category = "Social Security Number"
43 | BankAccountCategory Category = "Bank Account"
44 | DocumentCategory Category = "Document"
45 | ServerCategory Category = "Server"
46 | UnknownCategory Category = "UNKNOWN"
47 | )
48 |
49 | const (
50 | TypeSex SectionFieldType = "menu"
51 | TypeCard SectionFieldType = "cctype"
52 | TypeAddress SectionFieldType = "address"
53 | TypeString SectionFieldType = "string"
54 | TypeURL SectionFieldType = "URL"
55 | TypeEmail SectionFieldType = "email"
56 | TypeDate SectionFieldType = "date"
57 | TypeMonthYear SectionFieldType = "monthYear"
58 | TypeConcealed SectionFieldType = "concealed"
59 | TypePhone SectionFieldType = "phone"
60 | TypeReference SectionFieldType = "reference"
61 | )
62 |
63 | type Address struct {
64 | City string `json:"city"`
65 | Country string `json:"country"`
66 | Region string `json:"region"`
67 | State string `json:"state"`
68 | Street string `json:"street"`
69 | Zip string `json:"zip"`
70 | }
71 |
72 | type Item struct {
73 | UUID string `json:"uuid"`
74 | Template string `json:"templateUUID"`
75 | Vault string `json:"vaultUUID"`
76 | Overview Overview `json:"overview"`
77 | Details Details `json:"details"`
78 | }
79 |
80 | type Details struct {
81 | Notes string `json:"notesPlain"`
82 | Password string `json:"password"`
83 | Fields []Field `json:"fields"`
84 | Sections []Section `json:"sections"`
85 | }
86 |
87 | type Section struct {
88 | Name string `json:"name"`
89 | Title string `json:"title"`
90 | Fields []SectionField `json:"fields"`
91 | }
92 |
93 | type SectionField struct {
94 | Type SectionFieldType `json:"k"`
95 | Text string `json:"t"`
96 | Value interface{} `json:"v"`
97 | N string `json:"n"`
98 | A Annotation `json:"a"`
99 | Inputs map[string]string `json:"inputTraits"`
100 | }
101 |
102 | type SectionGroup struct {
103 | Selector string
104 | Name string
105 | Fields map[string]string
106 | }
107 |
108 | type Annotation struct {
109 | generate string
110 | guarded string
111 | multiline string
112 | clipboardFilter string
113 | }
114 |
115 | type Field struct {
116 | Type FieldType `json:"type"`
117 | Designation string `json:"designation"`
118 | Name string `json:"name"`
119 | Value string `json:"value"`
120 | }
121 |
122 | type Overview struct {
123 | Title string `json:"title"`
124 | URL string `json:"url"`
125 | Tags []string `json:"tags"`
126 | }
127 |
128 | func (o *OnePassClient) ReadItem(id string, vaultID string) (*Item, error) {
129 | item := &Item{}
130 | args := []string{
131 | opPasswordGet,
132 | ItemResource,
133 | id,
134 | }
135 |
136 | if vaultID != "" {
137 | args = append(args, fmt.Sprintf("--vault=%s", vaultID))
138 | }
139 | res, err := o.runCmd(args...)
140 | if err != nil {
141 | return nil, err
142 | }
143 | if err = json.Unmarshal(res, item); err != nil {
144 | return nil, err
145 | }
146 | return item, nil
147 | }
148 |
149 | func Category2Template(c Category) string {
150 | switch c {
151 | case LoginCategory:
152 | return "001"
153 | case IdentityCategory:
154 | return "004"
155 | case PasswordCategory:
156 | return "005"
157 | case PassportCategory:
158 | return "106"
159 | case DatabaseCategory:
160 | return "102"
161 | case ServerCategory:
162 | return "010"
163 | case DriverLicenseCategory:
164 | return "103"
165 | case OutdoorLicenseCategory:
166 | return "104"
167 | case SoftwareLicenseCategory:
168 | return "100"
169 | case EmailAccountCategory:
170 | return "111"
171 | case RewardProgramCategory:
172 | return "107"
173 | case WirelessRouterCategory:
174 | return "109"
175 | case DocumentCategory:
176 | return "006"
177 | case BankAccountCategory:
178 | return "101"
179 | case SocialSecurityNumberCategory:
180 | return "108"
181 | case CreditCardCategory:
182 | return "002"
183 | case SecureNoteCategory:
184 | return "003"
185 | case MembershipCategory:
186 | return "105"
187 | default:
188 | return "000"
189 | }
190 | }
191 |
192 | func Template2Category(t string) Category {
193 | switch t {
194 | case "001":
195 | return LoginCategory
196 | case "004":
197 | return IdentityCategory
198 | case "005":
199 | return PasswordCategory
200 | case "106":
201 | return PassportCategory
202 | case "102":
203 | return DatabaseCategory
204 | case "010":
205 | return ServerCategory
206 | case "103":
207 | return DriverLicenseCategory
208 | case "104":
209 | return OutdoorLicenseCategory
210 | case "100":
211 | return SoftwareLicenseCategory
212 | case "111":
213 | return EmailAccountCategory
214 | case "107":
215 | return RewardProgramCategory
216 | case "109":
217 | return WirelessRouterCategory
218 | case "006":
219 | return DocumentCategory
220 | case "101":
221 | return BankAccountCategory
222 | case "108":
223 | return SocialSecurityNumberCategory
224 | case "002":
225 | return CreditCardCategory
226 | case "003":
227 | return SecureNoteCategory
228 | case "105":
229 | return MembershipCategory
230 | default:
231 | return UnknownCategory
232 | }
233 | }
234 |
235 | func (o *OnePassClient) CreateItem(v *Item) error {
236 | details, err := json.Marshal(v.Details)
237 | if err != nil {
238 | return err
239 | }
240 | detailsHash := base64url.Encode(details)
241 | template := Template2Category(v.Template)
242 | if template == UnknownCategory {
243 | return errors.New("unknown template id " + v.Template)
244 | }
245 |
246 | args := []string{
247 | opPasswordCreate,
248 | ItemResource,
249 | string(template),
250 | detailsHash,
251 | }
252 |
253 | if v.Vault != "" {
254 | args = append(args, fmt.Sprintf("--vault=%s", v.Vault))
255 | }
256 |
257 | if v.Overview.Title != "" {
258 | args = append(args, fmt.Sprintf("--title=%s", v.Overview.Title))
259 | }
260 |
261 | if v.Overview.URL != "" {
262 | args = append(args, fmt.Sprintf("--url=%s", v.Overview.URL))
263 | }
264 |
265 | if len(v.Overview.Tags) > 0 {
266 | args = append(args, fmt.Sprintf("--tags=%s", strings.Join(v.Overview.Tags, ",")))
267 | }
268 |
269 | res, err := o.runCmd(args...)
270 | if err == nil {
271 | if id, err := getResultID(res); err == nil {
272 | v.UUID = id
273 | }
274 | }
275 | return err
276 | }
277 |
278 | func (o *OnePassClient) ReadDocument(id string) (string, error) {
279 | content, err := o.runCmd(
280 | opPasswordGet,
281 | DocumentResource,
282 | id,
283 | )
284 | return string(content), err
285 | }
286 |
287 | func (o *OnePassClient) CreateDocument(v *Item, filePath string) error {
288 | args := []string{
289 | opPasswordCreate,
290 | DocumentResource,
291 | filePath,
292 | fmt.Sprintf("--title=%s", v.Overview.Title),
293 | fmt.Sprintf("--tags=%s", strings.Join(v.Overview.Tags, ",")),
294 | }
295 |
296 | if v.Vault != "" {
297 | args = append(args, fmt.Sprintf("--vault=%s", v.Vault))
298 | }
299 |
300 | res, err := o.runCmd(args...)
301 | if err == nil {
302 | if id, err := getResultID(res); err == nil {
303 | v.UUID = id
304 | }
305 | }
306 | return err
307 | }
308 |
309 | func resourceItemDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
310 | m := meta.(*Meta)
311 | err := m.onePassClient.DeleteItem(getID(d))
312 | if err == nil {
313 | d.SetId("")
314 | return nil
315 | }
316 | return diag.FromErr(err)
317 | }
318 |
319 | func (o *OnePassClient) DeleteItem(id string) error {
320 | return o.Delete(ItemResource, id)
321 | }
322 |
323 | func ProcessField(srcFields []SectionField) []map[string]interface{} {
324 | fields := make([]map[string]interface{}, 0, len(srcFields))
325 | for _, field := range srcFields {
326 | f := map[string]interface{}{
327 | "name": field.Text,
328 | }
329 | var key string
330 | switch field.Type {
331 | case TypeSex:
332 | key = "sex"
333 | case TypeURL:
334 | key = "url"
335 | case TypeMonthYear:
336 | key = "month_year"
337 | case TypeCard:
338 | key = "card_type"
339 | case TypeConcealed:
340 | if strings.HasPrefix(field.N, "TOTP_") {
341 | key = "totp"
342 | } else {
343 | key = "concealed"
344 | }
345 | default:
346 | key = string(field.Type)
347 | }
348 | f[key] = field.Value
349 | fields = append(fields, f)
350 | }
351 | return fields
352 | }
353 |
354 | func ProcessSections(srcSections []Section) []map[string]interface{} {
355 | sections := make([]map[string]interface{}, 0, len(srcSections))
356 | for _, section := range srcSections {
357 | sections = append(sections, map[string]interface{}{
358 | "name": section.Title,
359 | "field": ProcessField(section.Fields),
360 | })
361 | }
362 | return sections
363 | }
364 |
365 | func parseSectionFromSchema(sections []Section, d *schema.ResourceData, groups []SectionGroup) error {
366 | leftSections := []Section{}
367 | for _, section := range sections {
368 | var use bool
369 | for _, group := range groups {
370 | if section.Name == group.Selector {
371 | use = true
372 | var leftFields []SectionField
373 | src := map[string]interface{}{
374 | "title": section.Title,
375 | }
376 | for _, field := range section.Fields {
377 | found := false
378 | for k, f := range group.Fields {
379 | if f == field.N {
380 | src[k] = field.Value
381 | found = true
382 | continue
383 | }
384 | }
385 | if !found {
386 | leftFields = append(leftFields, field)
387 | }
388 | }
389 | src["field"] = ProcessField(leftFields)
390 | if err := d.Set(group.Name, []interface{}{src}); err != nil {
391 | return err
392 | }
393 | }
394 | }
395 | if !use {
396 | leftSections = append(leftSections, section)
397 | }
398 | }
399 | return d.Set("section", ProcessSections(leftSections))
400 | }
401 |
--------------------------------------------------------------------------------
/onepassword/provider.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "archive/zip"
5 | "context"
6 | "encoding/json"
7 | "errors"
8 | "fmt"
9 | "io"
10 | "log"
11 | "net/http"
12 | "os"
13 | "os/exec"
14 | "path/filepath"
15 | "runtime"
16 | "strings"
17 | "sync"
18 |
19 | "github.com/Masterminds/semver"
20 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
21 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
22 | )
23 |
24 | var version string = "1.4.0"
25 |
26 | func Provider() *schema.Provider {
27 | return &schema.Provider{
28 | Schema: map[string]*schema.Schema{
29 | "email": {
30 | Type: schema.TypeString,
31 | Optional: true,
32 | DefaultFunc: schema.EnvDefaultFunc("OP_EMAIL", nil),
33 | Description: "Set account email address",
34 | },
35 | "password": {
36 | Type: schema.TypeString,
37 | Optional: true,
38 | DefaultFunc: schema.EnvDefaultFunc("OP_PASSWORD", nil),
39 | Description: "Set account password",
40 | },
41 | "secret_key": {
42 | Type: schema.TypeString,
43 | Optional: true,
44 | DefaultFunc: schema.EnvDefaultFunc("OP_SECRET_KEY", nil),
45 | Description: "Set account secret key",
46 | },
47 | "subdomain": {
48 | Type: schema.TypeString,
49 | Optional: true,
50 | DefaultFunc: func() (interface{}, error) {
51 | if v := os.Getenv("OP_SUBDOMAIN"); v != "" {
52 | return v, nil
53 | }
54 | return "my", nil
55 | },
56 | Description: "Set alternative subdomain for 1password. From [subdomain].1password.com",
57 | },
58 | },
59 | ResourcesMap: map[string]*schema.Resource{
60 | "onepassword_group": resourceGroup(),
61 | "onepassword_group_member": resourceGroupMember(),
62 | "onepassword_item_common": resourceItemCommon(),
63 | "onepassword_item_software_license": resourceItemSoftwareLicense(),
64 | "onepassword_item_identity": resourceItemIdentity(),
65 | "onepassword_item_password": resourceItemPassword(),
66 | "onepassword_item_credit_card": resourceItemCreditCard(),
67 | "onepassword_item_secure_note": resourceItemSecureNote(),
68 | "onepassword_item_document": resourceItemDocument(),
69 | "onepassword_item_login": resourceItemLogin(),
70 | "onepassword_vault": resourceVault(),
71 | },
72 | DataSourcesMap: map[string]*schema.Resource{
73 | "onepassword_group": dataSourceGroup(),
74 | "onepassword_user": dataSourceUser(),
75 | "onepassword_item_common": dataSourceItemCommon(),
76 | "onepassword_item_software_license": dataSourceItemSoftwareLicense(),
77 | "onepassword_item_identity": dataSourceItemIdentity(),
78 | "onepassword_item_password": dataSourceItemPassword(),
79 | "onepassword_item_credit_card": dataSourceItemCreditCard(),
80 | "onepassword_item_secure_note": dataSourceItemSecureNote(),
81 | "onepassword_item_document": dataSourceItemDocument(),
82 | "onepassword_item_login": dataSourceItemLogin(),
83 | "onepassword_vault": dataSourceVault(),
84 | },
85 | ConfigureContextFunc: providerConfigure,
86 | }
87 | }
88 |
89 | func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
90 | return NewMeta(d)
91 | }
92 |
93 | const (
94 | opPasswordAdd = "add"
95 | opPasswordCreate = "create"
96 | opPasswordEdit = "edit"
97 | opPasswordDelete = "delete"
98 | opPasswordGet = "get"
99 | opPasswordList = "list"
100 | opPasswordRemove = "remove"
101 | )
102 |
103 | type OnePassClient struct {
104 | Password string
105 | Email string
106 | SecretKey string
107 | Subdomain string
108 | PathToOp string
109 | Session string
110 | execCommand func(string, ...string) *exec.Cmd // Can be overridden for mocking purposes
111 | mutex *sync.Mutex
112 | }
113 |
114 | type Meta struct {
115 | data *schema.ResourceData
116 | onePassClient *OnePassClient
117 | }
118 |
119 | func NewMeta(d *schema.ResourceData) (*Meta, diag.Diagnostics) {
120 | m := &Meta{data: d}
121 | client, err := m.NewOnePassClient()
122 | if err != nil {
123 | return m, diag.FromErr(err)
124 | }
125 | m.onePassClient = client
126 | return m, nil
127 | }
128 |
129 | func unzip(src string, dest string) error {
130 | r, err := zip.OpenReader(src)
131 | if err != nil {
132 | return err
133 | }
134 | defer r.Close()
135 |
136 | for _, f := range r.File {
137 | traversableCheck := strings.Split(f.Name, "..")
138 | fpath := filepath.Join(dest, traversableCheck[len(traversableCheck)-1])
139 | if err != nil {
140 | return err
141 | }
142 | if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
143 | return fmt.Errorf("%s: illegal file path", fpath)
144 | }
145 | if f.FileInfo().IsDir() {
146 | if err := os.MkdirAll(fpath, os.ModePerm); err != nil {
147 | return err
148 | }
149 | continue
150 | }
151 | if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
152 | return err
153 | }
154 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
155 | if err != nil {
156 | return err
157 | }
158 | rc, err := f.Open()
159 | if err != nil {
160 | return err
161 | }
162 | _, err = io.Copy(outFile, rc)
163 | outFile.Close()
164 | rc.Close()
165 |
166 | if err != nil {
167 | return err
168 | }
169 | }
170 | return nil
171 | }
172 |
173 | func findExistingOPClient() (string, error) {
174 | o, err := exec.Command("op", "--version").Output()
175 |
176 | if err != nil {
177 | return "", fmt.Errorf("Trouble calling: op\nOutput: %s", o)
178 | }
179 |
180 | c, err := semver.NewConstraint(">= " + version)
181 | if err != nil {
182 | return "", err
183 | }
184 |
185 | v, err := semver.NewVersion(strings.TrimSuffix(string(o), "\n"))
186 | if err != nil {
187 | return "", fmt.Errorf("[%s]", string(o))
188 | }
189 |
190 | if c.Check(v) {
191 | return "op", nil
192 | }
193 |
194 | return "", fmt.Errorf("op version needs to be equal or greater than: %s", version)
195 | }
196 |
197 | func installOPClient() (string, error) {
198 | if os.Getenv("OP_VERSION") != "" {
199 | semVer, err := semver.NewVersion(os.Getenv("OP_VERSION"))
200 | if err != nil {
201 | return "", err
202 | }
203 | version = semVer.String()
204 | }
205 | if runtime.GOOS == "darwin" {
206 | return "", fmt.Errorf("Unable to automatically install v%s of the op client. Please install manually from https://app-updates.agilebits.com/product_history/CLI", version)
207 | }
208 |
209 | binZip := fmt.Sprintf("/tmp/op_%s.zip", version)
210 | if _, err := os.Stat(binZip); os.IsNotExist(err) {
211 | resp, err := http.Get(fmt.Sprintf(
212 | "https://cache.agilebits.com/dist/1P/op/pkg/v%s/op_%s_%s_v%s.zip",
213 | version,
214 | runtime.GOOS,
215 | runtime.GOARCH,
216 | version,
217 | ))
218 | if err != nil {
219 | return "", fmt.Errorf("Could not retrieve zipped op release: %w", err)
220 | }
221 | defer resp.Body.Close()
222 |
223 | out, err := os.Create(binZip)
224 | if err != nil {
225 | return "", fmt.Errorf("Could not create temp file for op client: %w", err)
226 | }
227 | defer out.Close()
228 | if _, err = io.Copy(out, resp.Body); err != nil {
229 | return "", fmt.Errorf("Could not copy zip contents to temp file for op client: %w", err)
230 | }
231 | if err := unzip(binZip, "/tmp/terraform-provider-onepassword/"+version); err != nil {
232 | return "", fmt.Errorf("Could not unzip temp file for op client: %w", err)
233 | }
234 | }
235 | return "/tmp/terraform-provider-onepassword/" + version + "/op", nil
236 | }
237 |
238 | func (m *Meta) NewOnePassClient() (*OnePassClient, error) {
239 | bin, err := findExistingOPClient()
240 | if err != nil {
241 | bin, err = installOPClient()
242 | if err != nil {
243 | return nil, err
244 | }
245 | }
246 |
247 | subdomain := m.data.Get("subdomain").(string)
248 | email := m.data.Get("email").(string)
249 | password := m.data.Get("password").(string)
250 | secretKey := m.data.Get("secret_key").(string)
251 | session := ""
252 |
253 | if email == "" || password == "" || secretKey == "" {
254 | email = ""
255 | password = ""
256 | secretKey = ""
257 |
258 | var sessionKeyName string
259 | if strings.Contains(subdomain, "-") {
260 | sessionKeyName = "OP_SESSION_" + strings.ReplaceAll(subdomain, "-", "_")
261 | } else {
262 | sessionKeyName = "OP_SESSION_" + subdomain
263 | }
264 | session = os.Getenv(sessionKeyName)
265 |
266 | if session == "" {
267 | return nil, fmt.Errorf("email, password or secret_key is empty and environment variable %s is not set",
268 | sessionKeyName)
269 | }
270 | }
271 |
272 | op := &OnePassClient{
273 | Email: email,
274 | Password: password,
275 | SecretKey: secretKey,
276 | Subdomain: subdomain,
277 | PathToOp: bin,
278 | Session: session,
279 | execCommand: exec.Command,
280 | mutex: &sync.Mutex{},
281 | }
282 |
283 | if session != "" {
284 | return op, nil
285 | }
286 | if err := op.SignIn(); err != nil {
287 | return nil, err
288 | }
289 | return op, nil
290 | }
291 |
292 | func (o *OnePassClient) SignIn() error {
293 | cmd := exec.Command(o.PathToOp, "signin", o.Subdomain, o.Email, o.SecretKey, "--output=raw")
294 | stdin, err := cmd.StdinPipe()
295 | if err != nil {
296 | return err
297 | }
298 | go func() {
299 | defer stdin.Close()
300 | if _, err := io.WriteString(stdin, fmt.Sprintf("%s\n", o.Password)); err != nil {
301 | log.Println("[ERROR] ", err)
302 | }
303 | }()
304 |
305 | session, err := cmd.CombinedOutput()
306 | if err != nil {
307 | return errors.New(fmt.Sprintf("Cannot signin: %s\nExit code: %s", string(session), err))
308 | }
309 |
310 | o.Session = string(session)
311 | return nil
312 | }
313 |
314 | func (o *OnePassClient) runCmd(args ...string) ([]byte, error) {
315 | args = append(args, fmt.Sprintf("--session=%s", strings.Trim(o.Session, "\n")))
316 | o.mutex.Lock()
317 | cmd := o.execCommand(o.PathToOp, args...)
318 | defer o.mutex.Unlock()
319 | res, err := cmd.CombinedOutput()
320 | if err != nil {
321 | err = fmt.Errorf("some error in command %v\nError: %s\nOutput: %s", args[:len(args)-1], err, res)
322 | }
323 | return res, err
324 | }
325 |
326 | func getResultID(r []byte) (string, error) {
327 | result := &Resource{}
328 | if err := json.Unmarshal(r, result); err != nil {
329 | return "", err
330 | }
331 | return result.UUID, nil
332 | }
333 |
334 | type Resource struct {
335 | UUID string `json:"uuid"`
336 | }
337 |
338 | func getID(d *schema.ResourceData) string {
339 | if d.Id() != "" {
340 | return d.Id()
341 | }
342 | return d.Get("name").(string)
343 | }
344 |
345 | func getIDEmail(d *schema.ResourceData) string {
346 | if d.Id() != "" {
347 | return d.Id()
348 | }
349 | return d.Get("email").(string)
350 | }
351 |
352 | func (o *OnePassClient) Delete(resource string, id string) error {
353 | _, err := o.runCmd(opPasswordDelete, resource, id)
354 | return err
355 | }
356 |
--------------------------------------------------------------------------------
/onepassword/provider_test.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "os/exec"
5 | "strings"
6 | "sync"
7 | )
8 |
9 | type mockOnePassConfig struct {
10 | runCmd func() (string, error)
11 | execCommandResults []string // Populated when execCommand is executed so you can assert against the args passed in
12 | }
13 |
14 | func mockOnePassClient(params *mockOnePassConfig) *OnePassClient {
15 | ret := &OnePassClient{
16 | PathToOp: "op",
17 | mutex: &sync.Mutex{},
18 | }
19 |
20 | if params.runCmd != nil {
21 | ret.execCommand = func(binary string, args ...string) *exec.Cmd {
22 | params.execCommandResults = append([]string{binary}, args...)
23 |
24 | out, err := params.runCmd()
25 | if err != nil {
26 | return exec.Command("sh", "-c", "echo "+strings.ReplaceAll(err.Error(), `"`, `\"`)+" && false")
27 | }
28 | return exec.Command("sh", "-c", "echo "+strings.ReplaceAll(out, `"`, `\"`))
29 | }
30 | }
31 |
32 | return ret
33 | }
34 |
--------------------------------------------------------------------------------
/onepassword/resource_group.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceGroup() *schema.Resource {
12 | return &schema.Resource{
13 | ReadContext: resourceGroupRead,
14 | CreateContext: resourceGroupCreate,
15 | UpdateContext: resourceGroupUpdate,
16 | DeleteContext: resourceGroupDelete,
17 | Importer: &schema.ResourceImporter{
18 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
19 | if err := resourceGroupRead(ctx, d, meta); err.HasError() {
20 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
21 | }
22 | return []*schema.ResourceData{d}, nil
23 | },
24 | },
25 | Schema: map[string]*schema.Schema{
26 | "name": {
27 | Type: schema.TypeString,
28 | Required: true,
29 | },
30 | "state": {
31 | Type: schema.TypeString,
32 | Computed: true,
33 | },
34 | },
35 | }
36 | }
37 |
38 | func resourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
39 | m := meta.(*Meta)
40 | v, err := m.onePassClient.ReadGroup(getID(d))
41 | if err != nil {
42 | return diag.FromErr(err)
43 | } else if v.State == GroupStateDeleted {
44 | d.SetId("")
45 | return nil
46 | }
47 |
48 | d.SetId(v.UUID)
49 | if err := d.Set("name", v.Name); err != nil {
50 | return diag.FromErr(err)
51 | }
52 |
53 | err = d.Set("state", v.State)
54 | if err != nil {
55 | return diag.FromErr(err)
56 | }
57 | return nil
58 | }
59 |
60 | func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
61 | m := meta.(*Meta)
62 | _, err := m.onePassClient.CreateGroup(&Group{
63 | Name: d.Get("name").(string),
64 | })
65 | if err != nil {
66 | return diag.FromErr(err)
67 | }
68 | return resourceGroupRead(ctx, d, meta)
69 | }
70 |
71 | func resourceGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
72 | m := meta.(*Meta)
73 | err := m.onePassClient.DeleteGroup(getID(d))
74 | if err == nil {
75 | d.SetId("")
76 | return nil
77 | }
78 | return diag.FromErr(err)
79 | }
80 |
81 | func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
82 | m := meta.(*Meta)
83 |
84 | g := &Group{
85 | Name: d.Get("name").(string),
86 | }
87 |
88 | if err := m.onePassClient.UpdateGroup(getID(d), g); err != nil {
89 | return diag.FromErr(err)
90 | }
91 | return resourceGroupRead(ctx, d, meta)
92 | }
93 |
--------------------------------------------------------------------------------
/onepassword/resource_group_member.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "strings"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10 | )
11 |
12 | func resourceGroupMember() *schema.Resource {
13 | return &schema.Resource{
14 | ReadContext: resourceGroupMemberRead,
15 | CreateContext: resourceGroupMemberCreate,
16 | DeleteContext: resourceGroupMemberDelete,
17 | Importer: &schema.ResourceImporter{
18 | StateContext: schema.ImportStatePassthroughContext,
19 | },
20 | Schema: map[string]*schema.Schema{
21 | "group": {
22 | Type: schema.TypeString,
23 | ForceNew: true,
24 | Required: true,
25 | },
26 | "user": {
27 | Type: schema.TypeString,
28 | ForceNew: true,
29 | Required: true,
30 | },
31 | },
32 | }
33 | }
34 |
35 | func resourceGroupMemberRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
36 | groupID, userID, err := resourceGroupMemberExtractID(d.Id())
37 | if err != nil {
38 | return diag.FromErr(err)
39 | }
40 |
41 | m := meta.(*Meta)
42 | v, err := m.onePassClient.ListGroupMembers(groupID)
43 | if err != nil {
44 | return diag.FromErr(err)
45 | }
46 |
47 | var found string
48 | for _, member := range v {
49 | if member.UUID == userID {
50 | found = member.UUID
51 | }
52 | }
53 |
54 | if found == "" {
55 | d.SetId("")
56 | return nil
57 | }
58 |
59 | d.SetId(resourceGroupMemberBuildID(groupID, found))
60 | d.Set("group", groupID)
61 | d.Set("user", found)
62 | return nil
63 | }
64 |
65 | func resourceGroupMemberCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
66 | m := meta.(*Meta)
67 | err := m.onePassClient.CreateGroupMember(
68 | d.Get("group").(string),
69 | d.Get("user").(string),
70 | )
71 | if err != nil {
72 | return diag.FromErr(err)
73 | }
74 |
75 | d.SetId(resourceGroupMemberBuildID(d.Get("group").(string), d.Get("user").(string)))
76 | return resourceGroupMemberRead(ctx, d, meta)
77 | }
78 |
79 | func resourceGroupMemberDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
80 | groupID, userID, err := resourceGroupMemberExtractID(d.Id())
81 | if err != nil {
82 | return diag.FromErr(err)
83 | }
84 |
85 | m := meta.(*Meta)
86 | err = m.onePassClient.DeleteGroupMember(
87 | groupID,
88 | userID,
89 | )
90 | if err != nil {
91 | return diag.FromErr(err)
92 | }
93 |
94 | d.SetId("")
95 | return nil
96 | }
97 |
98 | // resourceGroupMemberBuildID will conjoin the group ID and user ID into a single string
99 | // This is used as the resource ID.
100 | //
101 | // Note that user ID is being lowercased. Some operations require this user ID to be uppercased.
102 | // Use the resourceGroupMemberExtractID function to correctly reverse this encoding.
103 | func resourceGroupMemberBuildID(groupID, userID string) string {
104 | return strings.ToLower(groupID + "-" + strings.ToLower(userID))
105 | }
106 |
107 | // resourceGroupMemberExtractID will split the group ID and user ID from a given resource ID
108 | //
109 | // Note that user ID is being uppercased. Some operations require this user ID to be uppercased.
110 | func resourceGroupMemberExtractID(id string) (groupID, userID string, err error) {
111 | spl := strings.Split(id, "-")
112 | if len(spl) != 2 {
113 | return "", "", fmt.Errorf("Improperly formatted group member string. The format \"groupid-userid\" is expected")
114 | }
115 | return spl[0], strings.ToUpper(spl[1]), nil
116 | }
117 |
--------------------------------------------------------------------------------
/onepassword/resource_group_member_test.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import "testing"
4 |
5 | func Test_resourceGroupMemberBuildID(t *testing.T) {
6 | want := "v3zk6wiptl42r7cmzbmf23unny-tgkw5a3cpbcu5end3lld3wckxi"
7 | got := resourceGroupMemberBuildID("v3zk6wiptl42r7cmzbmf23unny", "TGKW5A3CPBCU5END3LLD3WCKXI")
8 |
9 | if want != got {
10 | t.Error("Did not correctly conjoin the group and user IDs: " + got)
11 | }
12 | }
13 |
14 | func Test_resourceGroupMemberExtractID(t *testing.T) {
15 | wantGroup := "v3zk6wiptl42r7cmzbmf23unny"
16 | wantUser := "TGKW5A3CPBCU5END3LLD3WCKXI"
17 | gotGroup, gotUser, err := resourceGroupMemberExtractID("v3zk6wiptl42r7cmzbmf23unny-tgkw5a3cpbcu5end3lld3wckxi")
18 |
19 | if err != nil {
20 | t.Error(err)
21 | } else if wantGroup != gotGroup {
22 | t.Error("Did not correctly extract the group ID: " + gotGroup)
23 | } else if wantUser != gotUser {
24 | t.Error("Did not correctly extract the user ID: " + gotUser)
25 | }
26 |
27 | // Test malformed ID
28 | _, _, err = resourceGroupMemberExtractID("totally not the right id")
29 | if err == nil {
30 | t.Error("Error was not returned from malformed id")
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/onepassword/resource_item_common.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceItemCommon() *schema.Resource {
12 | return &schema.Resource{
13 | ReadContext: resourceItemCommonRead,
14 | CreateContext: resourceItemCommonCreate,
15 | DeleteContext: resourceItemDelete,
16 | Importer: &schema.ResourceImporter{
17 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
18 | if err := resourceItemCommonRead(ctx, d, meta); err.HasError() {
19 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
20 | }
21 | return []*schema.ResourceData{d}, nil
22 | },
23 | },
24 | Schema: map[string]*schema.Schema{
25 | "name": {
26 | Type: schema.TypeString,
27 | Optional: true,
28 | ForceNew: true,
29 | },
30 | "notes": {
31 | Type: schema.TypeString,
32 | Optional: true,
33 | ForceNew: true,
34 | },
35 | "template": {
36 | Type: schema.TypeString,
37 | Required: true,
38 | ForceNew: true,
39 | ValidateDiagFunc: stringInSliceDiag([]string{
40 | string(DatabaseCategory),
41 | string(MembershipCategory),
42 | string(WirelessRouterCategory),
43 | string(DriverLicenseCategory),
44 | string(OutdoorLicenseCategory),
45 | string(PassportCategory),
46 | string(EmailAccountCategory),
47 | string(RewardProgramCategory),
48 | string(SocialSecurityNumberCategory),
49 | string(BankAccountCategory),
50 | string(ServerCategory),
51 | }, false),
52 | },
53 | "tags": {
54 | Type: schema.TypeList,
55 | Optional: true,
56 | ForceNew: true,
57 | Elem: &schema.Schema{Type: schema.TypeString},
58 | },
59 | "vault": {
60 | Type: schema.TypeString,
61 | Optional: true,
62 | ForceNew: true,
63 | },
64 | "section": {
65 | Type: schema.TypeList,
66 | Optional: true,
67 | ForceNew: true,
68 | Elem: sectionSchema(),
69 | },
70 | },
71 | }
72 | }
73 |
74 | func resourceItemCommonRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
75 | m := meta.(*Meta)
76 | vaultID := d.Get("vault").(string)
77 | v, err := m.onePassClient.ReadItem(getID(d), vaultID)
78 | if err != nil {
79 | return diag.FromErr(err)
80 | }
81 |
82 | d.SetId(v.UUID)
83 | if err := d.Set("name", v.Overview.Title); err != nil {
84 | return diag.FromErr(err)
85 | }
86 | if err := d.Set("notes", v.Details.Notes); err != nil {
87 | return diag.FromErr(err)
88 | }
89 | if err := d.Set("tags", v.Overview.Tags); err != nil {
90 | return diag.FromErr(err)
91 | }
92 | if err := d.Set("vault", v.Vault); err != nil {
93 | return diag.FromErr(err)
94 | }
95 | if err := d.Set("template", string(Template2Category(v.Template))); err != nil {
96 | return diag.FromErr(err)
97 | }
98 | if err := d.Set("section", ProcessSections(v.Details.Sections)); err != nil {
99 | return diag.FromErr(err)
100 | }
101 | return nil
102 | }
103 |
104 | func resourceItemCommonCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
105 | item := &Item{
106 | Vault: d.Get("vault").(string),
107 | Template: Category2Template(Category(d.Get("template").(string))),
108 | Overview: Overview{
109 | Title: d.Get("name").(string),
110 | Tags: ParseTags(d),
111 | },
112 | Details: Details{
113 | Notes: d.Get("notes").(string),
114 | Sections: ParseSections(d),
115 | },
116 | }
117 | m := meta.(*Meta)
118 | err := m.onePassClient.CreateItem(item)
119 | if err != nil {
120 | return diag.FromErr(err)
121 | }
122 | d.SetId(item.UUID)
123 | return nil
124 | }
125 |
--------------------------------------------------------------------------------
/onepassword/resource_item_credit_card.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceItemCreditCard() *schema.Resource {
12 | return &schema.Resource{
13 | ReadContext: resourceItemCreditCardRead,
14 | CreateContext: resourceItemCreditCardCreate,
15 | DeleteContext: resourceItemDelete,
16 | Importer: &schema.ResourceImporter{
17 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
18 | if err := resourceItemCreditCardRead(ctx, d, meta); err.HasError() {
19 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
20 | }
21 | return []*schema.ResourceData{d}, nil
22 | },
23 | },
24 | Schema: map[string]*schema.Schema{
25 | "name": {
26 | Type: schema.TypeString,
27 | Optional: true,
28 | ForceNew: true,
29 | },
30 | "tags": {
31 | Type: schema.TypeList,
32 | Optional: true,
33 | ForceNew: true,
34 | Elem: &schema.Schema{Type: schema.TypeString},
35 | },
36 | "vault": {
37 | Type: schema.TypeString,
38 | Optional: true,
39 | ForceNew: true,
40 | },
41 | "notes": {
42 | Type: schema.TypeString,
43 | Optional: true,
44 | ForceNew: true,
45 | },
46 | "main": {
47 | Type: schema.TypeList,
48 | Optional: true,
49 | ForceNew: true,
50 | MaxItems: 1,
51 | Elem: &schema.Resource{
52 | Schema: map[string]*schema.Schema{
53 | "title": {
54 | Type: schema.TypeString,
55 | Optional: true,
56 | ForceNew: true,
57 | },
58 | "cardholder": {
59 | Type: schema.TypeString,
60 | Optional: true,
61 | ForceNew: true,
62 | },
63 | "type": {
64 | Type: schema.TypeString,
65 | Optional: true,
66 | ForceNew: true,
67 | ValidateDiagFunc: stringInSliceDiag([]string{
68 | "mc",
69 | "visa",
70 | "amex",
71 | "diners",
72 | "carteblanche",
73 | "discover",
74 | "jcb",
75 | "maestro",
76 | "visaelectron",
77 | "laser",
78 | "unionpay",
79 | }, true),
80 | },
81 | "number": {
82 | Type: schema.TypeString,
83 | Optional: true,
84 | ForceNew: true,
85 | },
86 | "cvv": {
87 | Type: schema.TypeString,
88 | Optional: true,
89 | ForceNew: true,
90 | Sensitive: true,
91 | },
92 | "expiry_date": {
93 | Type: schema.TypeInt,
94 | Optional: true,
95 | ForceNew: true,
96 | },
97 | "valid_from": {
98 | Type: schema.TypeInt,
99 | Optional: true,
100 | ForceNew: true,
101 | },
102 | "field": sectionSchema().Schema["field"],
103 | },
104 | },
105 | },
106 | "section": {
107 | Type: schema.TypeList,
108 | Optional: true,
109 | ForceNew: true,
110 | Elem: sectionSchema(),
111 | },
112 | },
113 | }
114 | }
115 |
116 | func resourceItemCreditCardRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
117 | m := meta.(*Meta)
118 | vaultID := d.Get("vault").(string)
119 | v, err := m.onePassClient.ReadItem(getID(d), vaultID)
120 | if err != nil {
121 | return diag.FromErr(err)
122 | }
123 | if v.Template != Category2Template(CreditCardCategory) {
124 | return diag.FromErr(errors.New("item is not from " + string(CreditCardCategory)))
125 | }
126 |
127 | d.SetId(v.UUID)
128 | if err := d.Set("name", v.Overview.Title); err != nil {
129 | return diag.FromErr(err)
130 | }
131 | if err := d.Set("tags", v.Overview.Tags); err != nil {
132 | return diag.FromErr(err)
133 | }
134 | if err := d.Set("vault", v.Vault); err != nil {
135 | return diag.FromErr(err)
136 | }
137 | if err := d.Set("notes", v.Details.Notes); err != nil {
138 | return diag.FromErr(err)
139 | }
140 | if err := parseSectionFromSchema(v.Details.Sections, d, []SectionGroup{
141 | {
142 | Name: "main",
143 | Selector: "",
144 | Fields: map[string]string{
145 | "cardholder": "cardholder",
146 | "number": "ccnum",
147 | "type": "type",
148 | "cvv": "cvv",
149 | "expiry_date": "expiry",
150 | "valid_from": "validFrom",
151 | },
152 | },
153 | }); err != nil {
154 | return diag.FromErr(err)
155 | }
156 | return nil
157 | }
158 |
159 | func resourceItemCreditCardCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
160 | main := d.Get("main").([]interface{})[0].(map[string]interface{})
161 | item := &Item{
162 | Vault: d.Get("vault").(string),
163 | Template: Category2Template(CreditCardCategory),
164 | Details: Details{
165 | Notes: d.Get("notes").(string),
166 | Sections: append(
167 | []Section{
168 | {
169 | Title: main["title"].(string),
170 | Name: "",
171 | Fields: append([]SectionField{
172 | {
173 | Type: "string",
174 | Text: "cardholder",
175 | Value: main["cardholder"].(string),
176 | N: "cardholder",
177 | A: Annotation{
178 | guarded: "yes",
179 | },
180 | Inputs: map[string]string{
181 | "autocapitalization": "Words",
182 | "keyboard": "Default",
183 | },
184 | },
185 | {
186 | Type: "cctype",
187 | Text: "type",
188 | Value: main["type"].(string),
189 | N: "type",
190 | A: Annotation{
191 | guarded: "yes",
192 | },
193 | },
194 | {
195 | Type: "string",
196 | Text: "number",
197 | Value: main["number"].(string),
198 | N: "ccnum",
199 | A: Annotation{
200 | guarded: "yes",
201 | clipboardFilter: "0123456789",
202 | },
203 | Inputs: map[string]string{
204 | "keyboard": "NumberPad",
205 | },
206 | },
207 | {
208 | Type: "concealed",
209 | Text: "verification number",
210 | Value: main["cvv"].(string),
211 | N: "cvv",
212 | A: Annotation{
213 | guarded: "yes",
214 | generate: "off",
215 | },
216 | Inputs: map[string]string{
217 | "keyboard": "NumberPad",
218 | },
219 | },
220 | {
221 | Type: "monthYear",
222 | Text: "expiry date",
223 | Value: main["expiry_date"].(int),
224 | N: "expiry",
225 | A: Annotation{
226 | guarded: "yes",
227 | },
228 | },
229 | {
230 | Type: "monthYear",
231 | Text: "valid from",
232 | Value: main["valid_from"].(int),
233 | N: "validFrom",
234 | A: Annotation{
235 | guarded: "yes",
236 | },
237 | },
238 | }, ParseFields(main)...),
239 | },
240 | },
241 | ParseSections(d)...,
242 | ),
243 | },
244 | Overview: Overview{
245 | Title: d.Get("name").(string),
246 | Tags: ParseTags(d),
247 | },
248 | }
249 | m := meta.(*Meta)
250 | err := m.onePassClient.CreateItem(item)
251 | if err != nil {
252 | return diag.FromErr(err)
253 | }
254 | d.SetId(item.UUID)
255 | return nil
256 | }
257 |
--------------------------------------------------------------------------------
/onepassword/resource_item_document.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceItemDocument() *schema.Resource {
12 | return &schema.Resource{
13 | ReadContext: resourceItemDocumentRead,
14 | CreateContext: resourceItemDocumentCreate,
15 | DeleteContext: resourceItemDelete,
16 | Importer: &schema.ResourceImporter{
17 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
18 | if err := resourceItemDocumentRead(ctx, d, meta); err.HasError() {
19 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
20 | }
21 | return []*schema.ResourceData{d}, nil
22 | },
23 | },
24 | Schema: map[string]*schema.Schema{
25 | "name": {
26 | Type: schema.TypeString,
27 | Optional: true,
28 | ForceNew: true,
29 | },
30 | "tags": {
31 | Type: schema.TypeList,
32 | Optional: true,
33 | ForceNew: true,
34 | Elem: &schema.Schema{Type: schema.TypeString},
35 | },
36 | "vault": {
37 | Type: schema.TypeString,
38 | ForceNew: true,
39 | Optional: true,
40 | },
41 | "file_path": {
42 | Type: schema.TypeString,
43 | ForceNew: true,
44 | Required: true,
45 | },
46 | "content": {
47 | Type: schema.TypeString,
48 | Computed: true,
49 | Optional: true,
50 | },
51 | },
52 | }
53 | }
54 |
55 | func resourceItemDocumentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
56 | m := meta.(*Meta)
57 | vaultID := d.Get("vault").(string)
58 | v, err := m.onePassClient.ReadItem(getID(d), vaultID)
59 | if err != nil {
60 | return diag.FromErr(err)
61 | }
62 | if v.Template != Category2Template(DocumentCategory) {
63 | return diag.FromErr(errors.New("item is not from " + string(DocumentCategory)))
64 | }
65 |
66 | d.SetId(v.UUID)
67 | if err := d.Set("name", v.Overview.Title); err != nil {
68 | return diag.FromErr(err)
69 | }
70 | if err := d.Set("tags", v.Overview.Tags); err != nil {
71 | return diag.FromErr(err)
72 | }
73 | if err := d.Set("vault", v.Vault); err != nil {
74 | return diag.FromErr(err)
75 | }
76 |
77 | content, err := m.onePassClient.ReadDocument(v.UUID)
78 | if err != nil {
79 | return diag.FromErr(err)
80 | }
81 | if err := d.Set("content", content); err != nil {
82 | return diag.FromErr(err)
83 | }
84 | return nil
85 | }
86 |
87 | func resourceItemDocumentCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
88 | item := &Item{
89 | Vault: d.Get("vault").(string),
90 | Template: Category2Template(DocumentCategory),
91 | Overview: Overview{
92 | Title: d.Get("name").(string),
93 | Tags: ParseTags(d),
94 | },
95 | }
96 | m := meta.(*Meta)
97 | err := m.onePassClient.CreateDocument(item, d.Get("file_path").(string))
98 | if err != nil {
99 | return diag.FromErr(err)
100 | }
101 | content, err := m.onePassClient.ReadDocument(item.UUID)
102 | if err != nil {
103 | return diag.FromErr(err)
104 | }
105 |
106 | d.SetId(item.UUID)
107 | if err := d.Set("content", content); err != nil {
108 | return diag.FromErr(err)
109 | }
110 | return nil
111 | }
112 |
--------------------------------------------------------------------------------
/onepassword/resource_item_identity.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceItemIdentity() *schema.Resource {
12 | addressSchema := sectionSchema().Schema["field"].Elem.(*schema.Resource).Schema["address"]
13 | addressSchema.ConflictsWith = []string{}
14 |
15 | return &schema.Resource{
16 | ReadContext: resourceItemIdentityRead,
17 | CreateContext: resourceItemIdentityCreate,
18 | DeleteContext: resourceItemDelete,
19 | Importer: &schema.ResourceImporter{
20 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
21 | if err := resourceItemIdentityRead(ctx, d, meta); err.HasError() {
22 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
23 | }
24 | return []*schema.ResourceData{d}, nil
25 | },
26 | },
27 | Schema: map[string]*schema.Schema{
28 | "name": {
29 | Type: schema.TypeString,
30 | Optional: true,
31 | ForceNew: true,
32 | },
33 | "tags": {
34 | Type: schema.TypeList,
35 | Optional: true,
36 | ForceNew: true,
37 | Elem: &schema.Schema{Type: schema.TypeString},
38 | },
39 | "vault": {
40 | Type: schema.TypeString,
41 | Optional: true,
42 | ForceNew: true,
43 | },
44 | "notes": {
45 | Type: schema.TypeString,
46 | Optional: true,
47 | ForceNew: true,
48 | },
49 | "identification": {
50 | Type: schema.TypeList,
51 | Optional: true,
52 | ForceNew: true,
53 | MaxItems: 1,
54 | Elem: &schema.Resource{
55 | Schema: map[string]*schema.Schema{
56 | "title": {
57 | Type: schema.TypeString,
58 | Optional: true,
59 | ForceNew: true,
60 | Default: "Identification",
61 | },
62 | "firstname": {
63 | Type: schema.TypeString,
64 | Optional: true,
65 | ForceNew: true,
66 | },
67 | "initial": {
68 | Type: schema.TypeString,
69 | Optional: true,
70 | ForceNew: true,
71 | },
72 | "lastname": {
73 | Type: schema.TypeString,
74 | Optional: true,
75 | ForceNew: true,
76 | },
77 | "sex": {
78 | Type: schema.TypeString,
79 | Optional: true,
80 | ForceNew: true,
81 | ValidateDiagFunc: stringInSliceDiag([]string{"male", "female"}, true),
82 | },
83 | "birth_date": {
84 | Type: schema.TypeInt,
85 | Optional: true,
86 | ForceNew: true,
87 | },
88 | "occupation": {
89 | Type: schema.TypeString,
90 | Optional: true,
91 | ForceNew: true,
92 | },
93 | "company": {
94 | Type: schema.TypeString,
95 | Optional: true,
96 | ForceNew: true,
97 | },
98 | "department": {
99 | Type: schema.TypeString,
100 | Optional: true,
101 | ForceNew: true,
102 | },
103 | "job_title": {
104 | Type: schema.TypeString,
105 | Optional: true,
106 | ForceNew: true,
107 | },
108 | "field": sectionSchema().Schema["field"],
109 | },
110 | },
111 | },
112 | "address": {
113 | Type: schema.TypeList,
114 | Optional: true,
115 | ForceNew: true,
116 | MaxItems: 1,
117 | Elem: &schema.Resource{
118 | Schema: map[string]*schema.Schema{
119 | "title": {
120 | Type: schema.TypeString,
121 | Optional: true,
122 | ForceNew: true,
123 | Default: "Address",
124 | },
125 | "address": addressSchema,
126 | "default_phone": {
127 | Type: schema.TypeString,
128 | Optional: true,
129 | ForceNew: true,
130 | },
131 | "home_phone": {
132 | Type: schema.TypeString,
133 | Optional: true,
134 | ForceNew: true,
135 | },
136 | "cell_phone": {
137 | Type: schema.TypeString,
138 | Optional: true,
139 | ForceNew: true,
140 | },
141 | "business_phone": {
142 | Type: schema.TypeString,
143 | Optional: true,
144 | ForceNew: true,
145 | },
146 | "field": sectionSchema().Schema["field"],
147 | },
148 | },
149 | },
150 | "internet": {
151 | Type: schema.TypeList,
152 | Optional: true,
153 | ForceNew: true,
154 | MaxItems: 1,
155 | Elem: &schema.Resource{
156 | Schema: map[string]*schema.Schema{
157 | "title": {
158 | Type: schema.TypeString,
159 | Optional: true,
160 | ForceNew: true,
161 | Default: "Internet Details",
162 | },
163 | "username": {
164 | Type: schema.TypeString,
165 | Optional: true,
166 | ForceNew: true,
167 | },
168 | "email": {
169 | Type: schema.TypeString,
170 | Optional: true,
171 | ForceNew: true,
172 | ValidateDiagFunc: emailValidateDiag(),
173 | },
174 | "field": sectionSchema().Schema["field"],
175 | },
176 | },
177 | },
178 | "section": {
179 | Type: schema.TypeList,
180 | Optional: true,
181 | ForceNew: true,
182 | Elem: sectionSchema(),
183 | },
184 | },
185 | }
186 | }
187 |
188 | func resourceItemIdentityRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
189 | m := meta.(*Meta)
190 | vaultID := d.Get("vault").(string)
191 | v, err := m.onePassClient.ReadItem(getID(d), vaultID)
192 | if err != nil {
193 | return diag.FromErr(err)
194 | }
195 | if v.Template != Category2Template(IdentityCategory) {
196 | return diag.FromErr(errors.New("item is not from " + string(IdentityCategory)))
197 | }
198 |
199 | d.SetId(v.UUID)
200 | if err := d.Set("name", v.Overview.Title); err != nil {
201 | return diag.FromErr(err)
202 | }
203 | if err := d.Set("tags", v.Overview.Tags); err != nil {
204 | return diag.FromErr(err)
205 | }
206 | if err := d.Set("vault", v.Vault); err != nil {
207 | return diag.FromErr(err)
208 | }
209 | if err := d.Set("notes", v.Details.Notes); err != nil {
210 | return diag.FromErr(err)
211 | }
212 | if err := parseSectionFromSchema(v.Details.Sections, d, []SectionGroup{
213 | {
214 | Name: "identification",
215 | Selector: "name",
216 | Fields: map[string]string{
217 | "firstname": "firstname",
218 | "initial": "initial",
219 | "lastname": "lastname",
220 | "sex": "sex",
221 | "birth_date": "birthdate",
222 | "occupation": "occupation",
223 | "company": "company",
224 | "department": "department",
225 | "job_title": "jobtitle",
226 | },
227 | },
228 | {
229 | Name: "address",
230 | Selector: "address",
231 | Fields: map[string]string{
232 | "address": "address",
233 | "default_phone": "defphone",
234 | "home_phone": "homephone",
235 | "cell_phone": "cellphone",
236 | "business_phone": "busphone",
237 | },
238 | },
239 | {
240 | Name: "internet",
241 | Selector: "internet",
242 | Fields: map[string]string{
243 | "username": "username",
244 | "email": "email",
245 | },
246 | },
247 | }); err != nil {
248 | return diag.FromErr(err)
249 | }
250 | return nil
251 | }
252 |
253 | func resourceItemIdentityCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
254 | main := d.Get("identification").([]interface{})[0].(map[string]interface{})
255 | address := d.Get("address").([]interface{})[0].(map[string]interface{})
256 | internet := d.Get("internet").([]interface{})[0].(map[string]interface{})
257 | item := &Item{
258 | Vault: d.Get("vault").(string),
259 | Template: Category2Template(IdentityCategory),
260 | Details: Details{
261 | Notes: d.Get("notes").(string),
262 | Sections: append(
263 | []Section{
264 | {
265 | Title: main["title"].(string),
266 | Name: "name",
267 | Fields: append([]SectionField{
268 | {
269 | Type: "string",
270 | Text: "firstname",
271 | Value: main["firstname"].(string),
272 | N: "firstname",
273 | A: Annotation{
274 | guarded: "yes",
275 | },
276 | Inputs: map[string]string{
277 | "autocapitalization": "Words",
278 | },
279 | },
280 | {
281 | Type: "string",
282 | Text: "initial",
283 | Value: main["initial"].(string),
284 | N: "initial",
285 | A: Annotation{
286 | guarded: "yes",
287 | },
288 | Inputs: map[string]string{
289 | "autocapitalization": "Words",
290 | },
291 | },
292 | {
293 | Type: "string",
294 | Text: "lastname",
295 | Value: main["lastname"].(string),
296 | N: "lastname",
297 | A: Annotation{
298 | guarded: "yes",
299 | },
300 | Inputs: map[string]string{
301 | "autocapitalization": "Words",
302 | },
303 | },
304 | {
305 | Type: "menu",
306 | Text: "sex",
307 | Value: main["sex"].(string),
308 | N: "sex",
309 | A: Annotation{
310 | guarded: "yes",
311 | },
312 | },
313 | {
314 | Type: "date",
315 | Text: "birth date",
316 | Value: main["birth_date"].(int),
317 | N: "birthdate",
318 | A: Annotation{
319 | guarded: "yes",
320 | },
321 | },
322 | {
323 | Type: "string",
324 | Text: "occupation",
325 | Value: main["occupation"].(string),
326 | N: "occupation",
327 | A: Annotation{
328 | guarded: "yes",
329 | },
330 | Inputs: map[string]string{
331 | "autocapitalization": "Words",
332 | },
333 | },
334 | {
335 | Type: "string",
336 | Text: "company",
337 | Value: main["company"].(string),
338 | N: "company",
339 | A: Annotation{
340 | guarded: "yes",
341 | },
342 | Inputs: map[string]string{
343 | "autocapitalization": "Words",
344 | },
345 | },
346 | {
347 | Type: "string",
348 | Text: "department",
349 | Value: main["department"].(string),
350 | N: "department",
351 | A: Annotation{
352 | guarded: "yes",
353 | },
354 | Inputs: map[string]string{
355 | "autocapitalization": "Words",
356 | },
357 | },
358 | {
359 | Type: "string",
360 | Text: "job title",
361 | Value: main["job_title"].(string),
362 | N: "jobtitle",
363 | A: Annotation{
364 | guarded: "yes",
365 | },
366 | Inputs: map[string]string{
367 | "autocapitalization": "Words",
368 | },
369 | },
370 | }, ParseFields(main)...),
371 | },
372 | {
373 | Title: address["title"].(string),
374 | Name: "address",
375 | Fields: append([]SectionField{
376 | {
377 | Type: "address",
378 | Text: "address",
379 | Value: address["address"].(map[string]interface{}),
380 | N: "address",
381 | A: Annotation{
382 | guarded: "yes",
383 | },
384 | Inputs: map[string]string{
385 | "autocapitalization": "Sentences",
386 | },
387 | },
388 | {
389 | Type: "phone",
390 | Text: "default phone",
391 | Value: address["default_phone"].(string),
392 | N: "defphone",
393 | A: Annotation{
394 | guarded: "yes",
395 | },
396 | },
397 | {
398 | Type: "phone",
399 | Text: "home",
400 | Value: address["home_phone"].(string),
401 | N: "homephone",
402 | A: Annotation{
403 | guarded: "yes",
404 | },
405 | },
406 | {
407 | Type: "phone",
408 | Text: "cell",
409 | Value: address["cell_phone"].(string),
410 | N: "cellphone",
411 | A: Annotation{
412 | guarded: "yes",
413 | },
414 | },
415 | {
416 | Type: "phone",
417 | Text: "business",
418 | Value: address["business_phone"].(string),
419 | N: "busphone",
420 | A: Annotation{
421 | guarded: "yes",
422 | },
423 | },
424 | }, ParseFields(address)...),
425 | },
426 | {
427 | Title: internet["title"].(string),
428 | Name: "internet",
429 | Fields: append([]SectionField{
430 | {
431 | Type: "string",
432 | Text: "username",
433 | Value: internet["username"].(string),
434 | N: "username",
435 | A: Annotation{
436 | guarded: "yes",
437 | },
438 | Inputs: map[string]string{
439 | "autocapitalization": "Sentences",
440 | },
441 | },
442 | {
443 | Type: "string",
444 | Text: "email",
445 | Value: internet["email"].(string),
446 | N: "email",
447 | A: Annotation{
448 | guarded: "yes",
449 | },
450 | Inputs: map[string]string{
451 | "keyboard": "EmailAddress",
452 | },
453 | },
454 | }, ParseFields(internet)...),
455 | },
456 | },
457 | ParseSections(d)...,
458 | ),
459 | },
460 | Overview: Overview{
461 | Title: d.Get("name").(string),
462 | Tags: ParseTags(d),
463 | },
464 | }
465 | m := meta.(*Meta)
466 | err := m.onePassClient.CreateItem(item)
467 | if err != nil {
468 | return diag.FromErr(err)
469 | }
470 | d.SetId(item.UUID)
471 | return nil
472 | }
473 |
--------------------------------------------------------------------------------
/onepassword/resource_item_login.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceItemLogin() *schema.Resource {
12 | return &schema.Resource{
13 | ReadContext: resourceItemLoginRead,
14 | CreateContext: resourceItemLoginCreate,
15 | DeleteContext: resourceItemDelete,
16 | Importer: &schema.ResourceImporter{
17 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
18 | if err := resourceItemLoginRead(ctx, d, meta); err.HasError() {
19 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
20 | }
21 | return []*schema.ResourceData{d}, nil
22 | },
23 | },
24 | Schema: map[string]*schema.Schema{
25 | "name": {
26 | Type: schema.TypeString,
27 | Optional: true,
28 | ForceNew: true,
29 | },
30 | "username": {
31 | Type: schema.TypeString,
32 | Optional: true,
33 | ForceNew: true,
34 | },
35 | "password": {
36 | Type: schema.TypeString,
37 | Optional: true,
38 | Sensitive: true,
39 | ForceNew: true,
40 | },
41 | "notes": {
42 | Type: schema.TypeString,
43 | Optional: true,
44 | ForceNew: true,
45 | },
46 | "tags": {
47 | Type: schema.TypeList,
48 | Optional: true,
49 | ForceNew: true,
50 | Elem: &schema.Schema{Type: schema.TypeString},
51 | },
52 | "vault": {
53 | Type: schema.TypeString,
54 | Optional: true,
55 | ForceNew: true,
56 | },
57 | "section": {
58 | Type: schema.TypeList,
59 | Optional: true,
60 | ForceNew: true,
61 | Elem: sectionSchema(),
62 | },
63 | "url": {
64 | Type: schema.TypeString,
65 | Optional: true,
66 | ForceNew: true,
67 | ValidateDiagFunc: urlValidateDiag(),
68 | },
69 | },
70 | }
71 | }
72 |
73 | func resourceItemLoginRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
74 | m := meta.(*Meta)
75 | vaultID := d.Get("vault").(string)
76 | v, err := m.onePassClient.ReadItem(getID(d), vaultID)
77 | if err != nil {
78 | return diag.FromErr(err)
79 | }
80 | if v.Template != Category2Template(LoginCategory) {
81 | return diag.FromErr(errors.New("item is not from " + string(LoginCategory)))
82 | }
83 |
84 | d.SetId(v.UUID)
85 | if err := d.Set("name", v.Overview.Title); err != nil {
86 | return diag.FromErr(err)
87 | }
88 | if err := d.Set("url", v.Overview.URL); err != nil {
89 | return diag.FromErr(err)
90 | }
91 | if err := d.Set("notes", v.Details.Notes); err != nil {
92 | return diag.FromErr(err)
93 | }
94 | if err := d.Set("tags", v.Overview.Tags); err != nil {
95 | return diag.FromErr(err)
96 | }
97 | if err := d.Set("vault", v.Vault); err != nil {
98 | return diag.FromErr(err)
99 | }
100 | for _, field := range v.Details.Fields {
101 | if field.Name == "username" {
102 | if err := d.Set("username", field.Value); err != nil {
103 | return diag.FromErr(err)
104 | }
105 | }
106 | if field.Name == "password" {
107 | if err := d.Set("password", field.Value); err != nil {
108 | return diag.FromErr(err)
109 | }
110 | }
111 | }
112 | if err := d.Set("section", ProcessSections(v.Details.Sections)); err != nil {
113 | return diag.FromErr(err)
114 | }
115 | return nil
116 | }
117 |
118 | func resourceItemLoginCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
119 | item := &Item{
120 | Vault: d.Get("vault").(string),
121 | Template: Category2Template(LoginCategory),
122 | Overview: Overview{
123 | Title: d.Get("name").(string),
124 | URL: d.Get("url").(string),
125 | Tags: ParseTags(d),
126 | },
127 | Details: Details{
128 | Notes: d.Get("notes").(string),
129 | Fields: []Field{
130 | {
131 | Name: "username",
132 | Designation: "username",
133 | Value: d.Get("username").(string),
134 | Type: FieldText,
135 | },
136 | {
137 | Name: "password",
138 | Designation: "password",
139 | Value: d.Get("password").(string),
140 | Type: FieldPassword,
141 | },
142 | },
143 | Sections: ParseSections(d),
144 | },
145 | }
146 | m := meta.(*Meta)
147 | err := m.onePassClient.CreateItem(item)
148 | if err != nil {
149 | return diag.FromErr(err)
150 | }
151 | d.SetId(item.UUID)
152 | return nil
153 | }
154 |
--------------------------------------------------------------------------------
/onepassword/resource_item_password.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceItemPassword() *schema.Resource {
12 | return &schema.Resource{
13 | ReadContext: resourceItemPasswordRead,
14 | CreateContext: resourceItemPasswordCreate,
15 | DeleteContext: resourceItemDelete,
16 | Importer: &schema.ResourceImporter{
17 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
18 | if err := resourceItemPasswordRead(ctx, d, meta); err.HasError() {
19 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
20 | }
21 | return []*schema.ResourceData{d}, nil
22 | },
23 | },
24 | Schema: map[string]*schema.Schema{
25 | "name": {
26 | Type: schema.TypeString,
27 | Optional: true,
28 | ForceNew: true,
29 | },
30 | "password": {
31 | Type: schema.TypeString,
32 | Optional: true,
33 | Sensitive: true,
34 | ForceNew: true,
35 | },
36 | "notes": {
37 | Type: schema.TypeString,
38 | Optional: true,
39 | ForceNew: true,
40 | },
41 | "tags": {
42 | Type: schema.TypeList,
43 | Optional: true,
44 | ForceNew: true,
45 | Elem: &schema.Schema{Type: schema.TypeString},
46 | },
47 | "vault": {
48 | Type: schema.TypeString,
49 | Optional: true,
50 | ForceNew: true,
51 | },
52 | "section": {
53 | Type: schema.TypeList,
54 | Optional: true,
55 | ForceNew: true,
56 | Elem: sectionSchema(),
57 | },
58 | "url": {
59 | Type: schema.TypeString,
60 | Optional: true,
61 | ForceNew: true,
62 | ValidateDiagFunc: urlValidateDiag(),
63 | },
64 | },
65 | }
66 | }
67 |
68 | func resourceItemPasswordRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
69 | m := meta.(*Meta)
70 | vaultID := d.Get("vault").(string)
71 | v, err := m.onePassClient.ReadItem(getID(d), vaultID)
72 | if err != nil {
73 | diag.FromErr(err)
74 | }
75 | if v.Template != Category2Template(PasswordCategory) {
76 | diag.FromErr(errors.New("item is not from " + string(PasswordCategory)))
77 | }
78 |
79 | d.SetId(v.UUID)
80 | if err := d.Set("name", v.Overview.Title); err != nil {
81 | diag.FromErr(err)
82 | }
83 | if err := d.Set("url", v.Overview.URL); err != nil {
84 | diag.FromErr(err)
85 | }
86 | if err := d.Set("notes", v.Details.Notes); err != nil {
87 | diag.FromErr(err)
88 | }
89 | if err := d.Set("tags", v.Overview.Tags); err != nil {
90 | diag.FromErr(err)
91 | }
92 | if err := d.Set("vault", v.Vault); err != nil {
93 | diag.FromErr(err)
94 | }
95 | if err := d.Set("password", v.Details.Password); err != nil {
96 | diag.FromErr(err)
97 | }
98 | if err := d.Set("section", ProcessSections(v.Details.Sections)); err != nil {
99 | diag.FromErr(err)
100 | }
101 | return nil
102 | }
103 |
104 | func resourceItemPasswordCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
105 | item := &Item{
106 | Vault: d.Get("vault").(string),
107 | Template: Category2Template(PasswordCategory),
108 | Overview: Overview{
109 | Title: d.Get("name").(string),
110 | URL: d.Get("url").(string),
111 | Tags: ParseTags(d),
112 | },
113 | Details: Details{
114 | Notes: d.Get("notes").(string),
115 | Password: d.Get("password").(string),
116 | Sections: ParseSections(d),
117 | },
118 | }
119 | m := meta.(*Meta)
120 | err := m.onePassClient.CreateItem(item)
121 | if err != nil {
122 | return diag.FromErr(err)
123 | }
124 | d.SetId(item.UUID)
125 | return nil
126 | }
127 |
--------------------------------------------------------------------------------
/onepassword/resource_item_secure_note.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceItemSecureNote() *schema.Resource {
12 | return &schema.Resource{
13 | ReadContext: resourceItemSecureNoteRead,
14 | CreateContext: resourceItemSecureNoteCreate,
15 | DeleteContext: resourceItemDelete,
16 | Importer: &schema.ResourceImporter{
17 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
18 | if err := resourceItemSecureNoteRead(ctx, d, meta); err.HasError() {
19 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
20 | }
21 | return []*schema.ResourceData{d}, nil
22 | },
23 | },
24 | Schema: map[string]*schema.Schema{
25 | "name": {
26 | Type: schema.TypeString,
27 | Optional: true,
28 | ForceNew: true,
29 | },
30 | "tags": {
31 | Type: schema.TypeList,
32 | Optional: true,
33 | ForceNew: true,
34 | Elem: &schema.Schema{Type: schema.TypeString},
35 | },
36 | "vault": {
37 | Type: schema.TypeString,
38 | Optional: true,
39 | ForceNew: true,
40 | },
41 | "notes": {
42 | Type: schema.TypeString,
43 | Optional: true,
44 | ForceNew: true,
45 | },
46 | "section": {
47 | Type: schema.TypeList,
48 | Optional: true,
49 | ForceNew: true,
50 | Elem: sectionSchema(),
51 | },
52 | },
53 | }
54 | }
55 |
56 | func resourceItemSecureNoteRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
57 | m := meta.(*Meta)
58 | vaultID := d.Get("vault").(string)
59 | v, err := m.onePassClient.ReadItem(getID(d), vaultID)
60 | if err != nil {
61 | return diag.FromErr(err)
62 | }
63 | if v.Template != Category2Template(SecureNoteCategory) {
64 | return diag.FromErr(errors.New("item is not from " + string(SecureNoteCategory)))
65 | }
66 |
67 | d.SetId(v.UUID)
68 | if err := d.Set("name", v.Overview.Title); err != nil {
69 | return diag.FromErr(err)
70 | }
71 | if err := d.Set("tags", v.Overview.Tags); err != nil {
72 | return diag.FromErr(err)
73 | }
74 | if err := d.Set("vault", v.Vault); err != nil {
75 | return diag.FromErr(err)
76 | }
77 | if err := d.Set("notes", v.Details.Notes); err != nil {
78 | return diag.FromErr(err)
79 | }
80 | if err := d.Set("section", ProcessSections(v.Details.Sections)); err != nil {
81 | return diag.FromErr(err)
82 | }
83 | return nil
84 | }
85 |
86 | func resourceItemSecureNoteCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
87 | item := &Item{
88 | Vault: d.Get("vault").(string),
89 | Template: Category2Template(SecureNoteCategory),
90 | Details: Details{
91 | Notes: d.Get("notes").(string),
92 | Sections: ParseSections(d),
93 | },
94 | Overview: Overview{
95 | Title: d.Get("name").(string),
96 | Tags: ParseTags(d),
97 | },
98 | }
99 | m := meta.(*Meta)
100 | err := m.onePassClient.CreateItem(item)
101 | if err != nil {
102 | return diag.FromErr(err)
103 | }
104 | d.SetId(item.UUID)
105 | return nil
106 | }
107 |
--------------------------------------------------------------------------------
/onepassword/resource_item_software_license.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceItemSoftwareLicense() *schema.Resource {
12 | return &schema.Resource{
13 | ReadContext: resourceItemSoftwareLicenseRead,
14 | CreateContext: resourceItemSoftwareLicenseCreate,
15 | DeleteContext: resourceItemDelete,
16 | Importer: &schema.ResourceImporter{
17 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
18 | if err := resourceItemSoftwareLicenseRead(ctx, d, meta); err.HasError() {
19 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
20 | }
21 | return []*schema.ResourceData{d}, nil
22 | },
23 | },
24 | Schema: map[string]*schema.Schema{
25 | "name": {
26 | Type: schema.TypeString,
27 | Optional: true,
28 | ForceNew: true,
29 | },
30 | "tags": {
31 | Type: schema.TypeList,
32 | Optional: true,
33 | ForceNew: true,
34 | Elem: &schema.Schema{Type: schema.TypeString},
35 | },
36 | "vault": {
37 | Type: schema.TypeString,
38 | Optional: true,
39 | ForceNew: true,
40 | },
41 | "notes": {
42 | Type: schema.TypeString,
43 | Optional: true,
44 | ForceNew: true,
45 | },
46 | "main": {
47 | Type: schema.TypeList,
48 | Optional: true,
49 | ForceNew: true,
50 | MaxItems: 1,
51 | Elem: &schema.Resource{
52 | Schema: map[string]*schema.Schema{
53 | "title": {
54 | Type: schema.TypeString,
55 | Optional: true,
56 | ForceNew: true,
57 | },
58 | "license_key": {
59 | Type: schema.TypeString,
60 | Optional: true,
61 | ForceNew: true,
62 | },
63 | "field": sectionSchema().Schema["field"],
64 | },
65 | },
66 | },
67 | "section": {
68 | Type: schema.TypeList,
69 | Optional: true,
70 | ForceNew: true,
71 | Elem: sectionSchema(),
72 | },
73 | },
74 | }
75 | }
76 |
77 | func resourceItemSoftwareLicenseRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
78 | m := meta.(*Meta)
79 | vaultID := d.Get("vault").(string)
80 | v, err := m.onePassClient.ReadItem(getID(d), vaultID)
81 | if err != nil {
82 | return diag.FromErr(err)
83 | }
84 | if v.Template != Category2Template(SoftwareLicenseCategory) {
85 | return diag.FromErr(errors.New("item is not from " + string(SoftwareLicenseCategory)))
86 | }
87 |
88 | d.SetId(v.UUID)
89 | if err := d.Set("name", v.Overview.Title); err != nil {
90 | return diag.FromErr(err)
91 | }
92 | if err := d.Set("tags", v.Overview.Tags); err != nil {
93 | return diag.FromErr(err)
94 | }
95 | if err := d.Set("vault", v.Vault); err != nil {
96 | return diag.FromErr(err)
97 | }
98 | if err := d.Set("notes", v.Details.Notes); err != nil {
99 | return diag.FromErr(err)
100 | }
101 | if err := parseSectionFromSchema(v.Details.Sections, d, []SectionGroup{
102 | {
103 | Name: "main",
104 | Selector: "",
105 | Fields: map[string]string{
106 | "license_key": "reg_code",
107 | },
108 | },
109 | }); err != nil {
110 | return diag.FromErr(err)
111 | }
112 | return nil
113 | }
114 |
115 | func resourceItemSoftwareLicenseCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
116 | main := d.Get("main").([]interface{})[0].(map[string]interface{})
117 | item := &Item{
118 | Vault: d.Get("vault").(string),
119 | Template: Category2Template(SoftwareLicenseCategory),
120 | Details: Details{
121 | Notes: d.Get("notes").(string),
122 | Sections: append(
123 | []Section{
124 | {
125 | Title: main["title"].(string),
126 | Name: "",
127 | Fields: append([]SectionField{
128 | {
129 | Type: "string",
130 | Text: "license key",
131 | Value: main["license_key"].(string),
132 | N: "reg_code",
133 | A: Annotation{
134 | guarded: "yes",
135 | multiline: "yes",
136 | },
137 | },
138 | }, ParseFields(main)...),
139 | },
140 | },
141 | ParseSections(d)...,
142 | ),
143 | },
144 | Overview: Overview{
145 | Title: d.Get("name").(string),
146 | Tags: ParseTags(d),
147 | },
148 | }
149 | m := meta.(*Meta)
150 | err := m.onePassClient.CreateItem(item)
151 | if err != nil {
152 | return diag.FromErr(err)
153 | }
154 | d.SetId(item.UUID)
155 | return nil
156 | }
157 |
--------------------------------------------------------------------------------
/onepassword/resource_vault.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9 | )
10 |
11 | func resourceVault() *schema.Resource {
12 | return &schema.Resource{
13 | ReadContext: resourceVaultRead,
14 | CreateContext: resourceVaultCreate,
15 | DeleteContext: resourceVaultDelete,
16 | Importer: &schema.ResourceImporter{
17 | StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
18 | if err := resourceVaultRead(ctx, d, meta); err.HasError() {
19 | return []*schema.ResourceData{d}, errors.New(err[0].Summary)
20 | }
21 | return []*schema.ResourceData{d}, nil
22 | },
23 | },
24 | Schema: map[string]*schema.Schema{
25 | "name": {
26 | Type: schema.TypeString,
27 | ForceNew: true,
28 | Required: true,
29 | },
30 | },
31 | }
32 | }
33 |
34 | func resourceVaultRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
35 | m := meta.(*Meta)
36 | v, err := m.onePassClient.ReadVault(getID(d))
37 | if err != nil {
38 | return diag.FromErr(err)
39 | }
40 |
41 | d.SetId(v.UUID)
42 | if err := d.Set("name", v.Name); err != nil {
43 | return diag.FromErr(err)
44 | }
45 | return nil
46 | }
47 |
48 | func resourceVaultCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
49 | m := meta.(*Meta)
50 | _, err := m.onePassClient.CreateVault(&Vault{
51 | Name: d.Get("name").(string),
52 | })
53 | if err != nil {
54 | return diag.FromErr(err)
55 | }
56 | return resourceVaultRead(ctx, d, meta)
57 | }
58 |
59 | func resourceVaultDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
60 | m := meta.(*Meta)
61 | err := m.onePassClient.DeleteVault(getID(d))
62 | if err == nil {
63 | d.SetId("")
64 | return nil
65 | }
66 | return diag.FromErr(err)
67 | }
68 |
--------------------------------------------------------------------------------
/onepassword/section.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "reflect"
5 | "strings"
6 |
7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8 | )
9 |
10 | func sectionSchema() *schema.Resource {
11 | return &schema.Resource{
12 | Schema: map[string]*schema.Schema{
13 | "name": {
14 | Type: schema.TypeString,
15 | ForceNew: true,
16 | Optional: true,
17 | },
18 | "field": {
19 | Type: schema.TypeList,
20 | ForceNew: true,
21 | Optional: true,
22 | Elem: &schema.Resource{
23 | Schema: map[string]*schema.Schema{
24 | "name": {
25 | Type: schema.TypeString,
26 | ForceNew: true,
27 | Optional: true,
28 | },
29 | "string": {
30 | Type: schema.TypeString,
31 | Optional: true,
32 | ForceNew: true,
33 | },
34 | "url": {
35 | Type: schema.TypeString,
36 | Optional: true,
37 | ForceNew: true,
38 | ValidateDiagFunc: urlValidateDiag(),
39 | },
40 | "phone": {
41 | Type: schema.TypeString,
42 | ForceNew: true,
43 | Optional: true,
44 | },
45 | "reference": {
46 | Type: schema.TypeString,
47 | ForceNew: true,
48 | Optional: true,
49 | },
50 | "sex": {
51 | Type: schema.TypeString,
52 | ForceNew: true,
53 | Optional: true,
54 | ValidateDiagFunc: stringInSliceDiag([]string{"female", "male"}, true),
55 | },
56 | "card_type": {
57 | Type: schema.TypeString,
58 | Optional: true,
59 | ForceNew: true,
60 | ValidateDiagFunc: stringInSliceDiag([]string{
61 | "mc",
62 | "visa",
63 | "amex",
64 | "diners",
65 | "carteblanche",
66 | "discover",
67 | "jcb",
68 | "maestro",
69 | "visaelectron",
70 | "laser",
71 | "unionpay",
72 | }, true),
73 | },
74 | "email": {
75 | Type: schema.TypeString,
76 | Optional: true,
77 | ForceNew: true,
78 | ValidateDiagFunc: emailValidateDiag(),
79 | },
80 | "date": {
81 | Type: schema.TypeInt,
82 | Optional: true,
83 | ForceNew: true,
84 | },
85 | "month_year": {
86 | Type: schema.TypeInt,
87 | Optional: true,
88 | ForceNew: true,
89 | Description: "Item login section field value for month year.",
90 | },
91 | "totp": {
92 | Type: schema.TypeString,
93 | Optional: true,
94 | Sensitive: true,
95 | ForceNew: true,
96 | },
97 | "concealed": {
98 | Type: schema.TypeString,
99 | Optional: true,
100 | Sensitive: true,
101 | ForceNew: true,
102 | },
103 | "address": {
104 | Type: schema.TypeList,
105 | Optional: true,
106 | ForceNew: true,
107 | Elem: &schema.Resource{
108 | Schema: map[string]*schema.Schema{
109 | "country": {
110 | Type: schema.TypeString,
111 | Optional: true,
112 | ForceNew: true,
113 | },
114 | "city": {
115 | Type: schema.TypeString,
116 | Optional: true,
117 | ForceNew: true,
118 | },
119 | "region": {
120 | Type: schema.TypeString,
121 | Optional: true,
122 | ForceNew: true,
123 | },
124 | "state": {
125 | Type: schema.TypeString,
126 | Optional: true,
127 | ForceNew: true,
128 | },
129 | "street": {
130 | Type: schema.TypeString,
131 | Optional: true,
132 | ForceNew: true,
133 | },
134 | "zip": {
135 | Type: schema.TypeString,
136 | Optional: true,
137 | ForceNew: true,
138 | },
139 | },
140 | },
141 | },
142 | },
143 | },
144 | },
145 | },
146 | }
147 | }
148 |
149 | func ParseTags(d *schema.ResourceData) []string {
150 | tSrc := d.Get("tags").([]interface{})
151 | tags := make([]string, 0, len(tSrc))
152 | for _, tag := range tSrc {
153 | tags = append(tags, tag.(string))
154 | }
155 | return tags
156 | }
157 |
158 | func ParseField(fl map[string]interface{}) SectionField {
159 | f := SectionField{
160 | Text: fl["name"].(string),
161 | }
162 | for key, val := range fl {
163 | if key == "name" {
164 | continue
165 | }
166 |
167 | isNotEmptyString := reflect.TypeOf(val).String() == "string" && val != ""
168 | isNotEmptyInt := reflect.TypeOf(val).String() == "int" && val != 0
169 | isNotEmptyAddress := strings.HasPrefix(reflect.TypeOf(val).String(), "map") && len(val.(map[string]interface{})) != 0
170 |
171 | if isNotEmptyString || isNotEmptyInt || isNotEmptyAddress {
172 | f.N = f.Text
173 | if val, err := fieldNumber(); err == nil {
174 | f.N = val
175 | }
176 | f.Value = val
177 | switch key {
178 | case "sex":
179 | f.Type = TypeSex
180 | case "totp":
181 | f.Type = TypeConcealed
182 | f.N = "TOTP_" + f.N
183 | case "month_year":
184 | f.Type = TypeMonthYear
185 | case "url":
186 | f.Type = TypeURL
187 | case "card_type":
188 | f.Type = TypeCard
189 | default:
190 | f.Type = SectionFieldType(key)
191 | }
192 | }
193 | }
194 | return f
195 | }
196 |
197 | func ParseFields(s map[string]interface{}) []SectionField {
198 | fields := []SectionField{}
199 | for _, field := range s["field"].([]interface{}) {
200 | fl := field.(map[string]interface{})
201 | fields = append(fields, ParseField(fl))
202 | }
203 | return fields
204 | }
205 |
206 | func ParseSections(d *schema.ResourceData) []Section {
207 | sections := []Section{}
208 | for _, section := range d.Get("section").([]interface{}) {
209 | s := section.(map[string]interface{})
210 | secName := s["name"].(string)
211 | if val, err := fieldNumber(); err == nil {
212 | secName = val
213 | }
214 | sections = append(sections, Section{
215 | Title: s["name"].(string),
216 | Name: "Section_" + secName,
217 | Fields: ParseFields(s),
218 | })
219 | }
220 | return sections
221 | }
222 |
--------------------------------------------------------------------------------
/onepassword/user.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "encoding/json"
5 | )
6 |
7 | const (
8 | // UserResource is 1Password's internal designator for Users
9 | UserResource = "user"
10 |
11 | // UserStateActive indicates an Active User
12 | UserStateActive = "A"
13 |
14 | // UserStateSuspended indicates a Suspended User
15 | UserStateSuspended = "S"
16 | )
17 |
18 | // User represents a 1Password User resource
19 | type User struct {
20 | UUID string
21 | Email string
22 | FirstName string
23 | LastName string
24 | State string
25 | }
26 |
27 | // ReadUser gets an existing 1Password User
28 | // This supports multiple id parameter values, including "First Last", "Email", and "UUID".
29 | func (o *OnePassClient) ReadUser(id string) (*User, error) {
30 | user := &User{}
31 | res, err := o.runCmd(opPasswordGet, UserResource, id)
32 | if err != nil {
33 | return nil, err
34 | }
35 | if err = json.Unmarshal(res, user); err != nil {
36 | return nil, err
37 | }
38 | return user, nil
39 | }
40 |
--------------------------------------------------------------------------------
/onepassword/vault.go:
--------------------------------------------------------------------------------
1 | package onepassword
2 |
3 | import (
4 | "encoding/json"
5 | )
6 |
7 | const VaultResource = "vault"
8 |
9 | type Vault struct {
10 | UUID string
11 | Name string
12 | }
13 |
14 | func (o *OnePassClient) ReadVault(id string) (*Vault, error) {
15 | vault := &Vault{}
16 | res, err := o.runCmd(opPasswordGet, VaultResource, id)
17 | if err != nil {
18 | return nil, err
19 | }
20 | if err = json.Unmarshal(res, vault); err != nil {
21 | return nil, err
22 | }
23 | return vault, nil
24 | }
25 |
26 | func (o *OnePassClient) CreateVault(v *Vault) (*Vault, error) {
27 | args := []string{opPasswordCreate, VaultResource, v.Name}
28 | res, err := o.runCmd(args...)
29 | if err != nil {
30 | return nil, err
31 | }
32 | if err = json.Unmarshal(res, v); err != nil {
33 | return nil, err
34 | }
35 | return v, nil
36 | }
37 |
38 | func (o *OnePassClient) DeleteVault(id string) error {
39 | return o.Delete(VaultResource, id)
40 | }
41 |
--------------------------------------------------------------------------------
/terraform-registry-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "metadata": {
4 | "protocol_versions": [
5 | "5.0"
6 | ]
7 | }
8 | }
--------------------------------------------------------------------------------