├── .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 | [![GolangCI](https://golangci.com/badges/github.com/anasinnyk/terraform-provider-1password.svg)](https://golangci.com/r/github.com/anasinnyk/terraform-provider-1password) 4 | [![Build Status](https://travis-ci.com/anasinnyk/terraform-provider-1password.svg?branch=master)](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 reserva​tions" 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 | } --------------------------------------------------------------------------------