diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 922ee27..d5778e5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @hashicorp/terraform-devex +* @eng-platform diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8bc06fa..1fd3b0a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,11 +59,8 @@ jobs: matrix: # list whatever Terraform versions here you would like to support terraform: - - '1.0.*' - - '1.1.*' - - '1.2.*' - - '1.3.*' - '1.4.*' + - '1.5.*' steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 @@ -76,6 +73,8 @@ jobs: terraform_wrapper: false - run: go mod download - env: + BITWARDEN_CLIENT_ID: ${{ secrets.BITWARDEN_CLIENT_ID }} + BITWARDEN_CLIENT_SECRET: ${{ secrets.BITWARDEN_CLIENT_SECRET }} TF_ACC: "1" run: go test -v -cover ./internal/provider/ timeout-minutes: 10 diff --git a/.gitignore b/.gitignore index fd3ad8e..63af3e5 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,10 @@ website/node_modules website/vendor +# Ignore state files from examples + +examples/**/*.tfstate* + # Test exclusions !command/test-fixtures/**/*.tfstate !command/test-fixtures/**/.terraform/ diff --git a/.golangci.yml b/.golangci.yml index 223cf95..6f92bb8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,6 +13,7 @@ linters: - forcetypeassert - godot - gofmt + - goimports - gosimple - ineffassign - makezero @@ -24,4 +25,4 @@ linters: - unconvert - unparam - unused - - vet \ No newline at end of file + - vet diff --git a/.goreleaser.yml b/.goreleaser.yml index 9bb0aa7..ae124fc 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -41,7 +41,7 @@ checksum: signs: - artifacts: checksum args: - # if you are using this in a GitHub action or some other automated pipeline, you + # if you are using this in a GitHub action or some other automated pipeline, you # need to pass the batch flag to indicate its not interactive. - "--batch" - "--local-user" @@ -58,3 +58,4 @@ release: # draft: true changelog: skip: true + use: github diff --git a/CHANGELOG.md b/CHANGELOG.md index b76e247..d379849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ ## 0.1.0 (Unreleased) FEATURES: + +- Implement a first pass on a Bitwarden Provider that directly integrates with the Bitwarden API. This initial + release focusses on Group and Group Member management. diff --git a/README.md b/README.md index 557af02..7b39683 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,6 @@ -# Terraform Provider Scaffolding (Terraform Plugin Framework) +# Bitwarden Terraform Provider -_This template repository is built on the [Terraform Plugin Framework](https://github.com/hashicorp/terraform-plugin-framework). The template repository built on the [Terraform Plugin SDK](https://github.com/hashicorp/terraform-plugin-sdk) can be found at [terraform-provider-scaffolding](https://github.com/hashicorp/terraform-provider-scaffolding). See [Which SDK Should I Use?](https://developer.hashicorp.com/terraform/plugin/framework-benefits) in the Terraform documentation for additional information._ - -This repository is a *template* for a [Terraform](https://www.terraform.io) provider. It is intended as a starting point for creating Terraform providers, containing: - -- A resource and a data source (`internal/provider/`), -- Examples (`examples/`) and generated documentation (`docs/`), -- Miscellaneous meta files. - -These files contain boilerplate code that you will need to edit to create your own Terraform provider. Tutorials for creating Terraform providers can be found on the [HashiCorp Developer](https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework) platform. _Terraform Plugin Framework specific guides are titled accordingly._ - -Please see the [GitHub template repository documentation](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template) for how to create a new repository from this template on GitHub. - -Once you've written your provider, you'll want to [publish it on the Terraform Registry](https://developer.hashicorp.com/terraform/registry/providers/publishing) so that others can use it. +_This template repository is built on the [Terraform Plugin Framework](https://github.com/hashicorp/terraform-plugin-framework)._ ## Requirements @@ -29,29 +17,42 @@ Once you've written your provider, you'll want to [publish it on the Terraform R go install ``` -## Adding Dependencies +## Using the provider -This provider uses [Go modules](https://github.com/golang/go/wiki/Modules). -Please see the Go documentation for the most up to date information about using Go modules. +This provider is intended to manage users and groups in Bitwarden for an organisation. -To add a new dependency `github.com/author/dependency` to your Terraform provider: +```hcl +provider "bitwarden" { + # Configure the authentication values needed to authenticate with Bitwarden API. + # More information can be found here: https://bitwarden.com/help/public-api/#authentication + client_id = "client_id" + client_secret = "client_api_secret" +} -```shell -go get github.com/author/dependency -go mod tidy +resource "bitwarden_group" "example" { + name = "example" +} ``` -Then commit the changes to `go.mod` and `go.sum`. - -## Using the provider - -Fill this in for each provider - ## Developing the Provider If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above). To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. +If you like to test it out, you will need to create a `~/.terraformrc` file for the `dev_overrides` +```hcl +provider_installation { + + dev_overrides { + "onesignal/bitwarden" = "" + } + + # For all other providers, install them directly from their origin provider + # registries as normal. If you omit this, Terraform will _only_ use + # the dev_overrides block, and so no other providers will be available. + direct {} +} +``` To generate or update documentation, run `go generate`. @@ -60,5 +61,15 @@ In order to run the full suite of Acceptance tests, run `make testacc`. *Note:* Acceptance tests create real resources, and often cost money to run. ```shell +export BITWARDEN_CLIENT_ID="organization.xxxx" +export BITWARDEN_CLIENT_SECRET="xxx" make testacc ``` + + +## TODOs + +- [ ] implement group members +- [ ] gracefully handle 404 when group is manually deleted? How do we do that? +- [ ] ensure urls don't end on trailing /, use validators? +- [ ] Add docs to group resource diff --git a/docs/data-sources/scaffolding_example.md b/docs/data-sources/scaffolding_example.md deleted file mode 100644 index 9c9de1e..0000000 --- a/docs/data-sources/scaffolding_example.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "scaffolding_example Data Source - terraform-provider-scaffolding-framework" -subcategory: "" -description: |- - Example data source ---- - -# scaffolding_example (Data Source) - -Example data source - -## Example Usage - -```terraform -data "scaffolding_example" "example" { - configurable_attribute = "some-value" -} -``` - - -## Schema - -### Optional - -- `configurable_attribute` (String) Example configurable attribute - -### Read-Only - -- `id` (String) Example identifier diff --git a/docs/index.md b/docs/index.md index 8eecef4..79fabf9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,20 +1,23 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "scaffolding-framework Provider" +page_title: "bitwarden Provider" subcategory: "" description: |- - + Bitwarden API Provider, focussing on Group and Group Members for organisations --- -# scaffolding-framework Provider - +# bitwarden Provider +Bitwarden API Provider, focussing on Group and Group Members for organisations ## Example Usage ```terraform -provider "scaffolding" { - # example configuration here +provider "bitwarden" { + # Configure the authentication values needed to authenticate with Bitwarden API. + # More information can be found here: https://bitwarden.com/help/public-api/#authentication + client_id = "organization.xxx" + client_secret = "client_api_secret" } ``` @@ -23,4 +26,7 @@ provider "scaffolding" { ### Optional -- `endpoint` (String) Example provider attribute +- `api_url` (String) The Bitwarden API URL, defaults to `https://api.bitwarden.com`, can also be configured as `BITWARDEN_API_URL`. See [docs](https://bitwarden.com/help/public-api/#endpoints) for more information +- `authentication_url` (String) The Bitwarden Authentication URL, defaults to `https://identity.bitwarden.com/connect/token`, can also be configured as `BITWARDEN_AUTH_URL`. See [docs](https://bitwarden.com/help/public-api/#authentication-endpoints) for more information +- `client_id` (String) The client_id of your organisation, can also be configured as `BITWARDEN_CLIENT_ID`. See [docs](https://bitwarden.com/help/public-api/#authentication) for more information +- `client_secret` (String, Sensitive) The client_secret of your organisation, can also be configured as `BITWARDEN_CLIENT_SECRET`. See [docs](https://bitwarden.com/help/public-api/#authentication) for more information diff --git a/docs/resources/group.md b/docs/resources/group.md new file mode 100644 index 0000000..a3ae466 --- /dev/null +++ b/docs/resources/group.md @@ -0,0 +1,37 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "bitwarden_group Resource - terraform-provider-bitwarden" +subcategory: "" +description: |- + +--- + +# bitwarden_group (Resource) + + + +## Example Usage + +```terraform +resource "bitwarden_group" "example" { + name = "example-group" + access_all = true +} +``` + + +## Schema + +### Required + +- `name` (String) + +### Optional + +- `access_all` (Boolean) +- `external_id` (String) + +### Read-Only + +- `id` (String) The ID of this resource. +- `last_updated` (String) diff --git a/docs/resources/scaffolding_example.md b/docs/resources/scaffolding_example.md deleted file mode 100644 index 47e77ed..0000000 --- a/docs/resources/scaffolding_example.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "scaffolding_example Resource - terraform-provider-scaffolding-framework" -subcategory: "" -description: |- - Example resource ---- - -# scaffolding_example (Resource) - -Example resource - -## Example Usage - -```terraform -resource "scaffolding_example" "example" { - configurable_attribute = "some-value" -} -``` - - -## Schema - -### Optional - -- `configurable_attribute` (String) Example configurable attribute -- `defaulted` (String) Example configurable attribute with default value - -### Read-Only - -- `id` (String) Example identifier diff --git a/examples/README.md b/examples/README.md index 026c42c..a01f3fb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,8 +1,11 @@ # Examples -This directory contains examples that are mostly used for documentation, but can also be run/tested manually via the Terraform CLI. +This directory contains examples that are mostly used for documentation, but can also be run/tested manually via the +Terraform CLI. -The document generation tool looks for files in the following locations by default. All other *.tf files besides the ones mentioned below are ignored by the documentation tool. This is useful for creating examples that can run and/or ar testable even if some parts are not relevant for the documentation. +The document generation tool looks for files in the following locations by default. All other *.tf files besides the +ones mentioned below are ignored by the documentation tool. This is useful for creating examples that can run and/or are +testable even if some parts are not relevant for the documentation. * **provider/provider.tf** example file for the provider index page * **data-sources/`full data source name`/data-source.tf** example file for the named data source page diff --git a/examples/data-sources/scaffolding_example/data-source.tf b/examples/data-sources/scaffolding_example/data-source.tf deleted file mode 100644 index a852489..0000000 --- a/examples/data-sources/scaffolding_example/data-source.tf +++ /dev/null @@ -1,3 +0,0 @@ -data "scaffolding_example" "example" { - configurable_attribute = "some-value" -} diff --git a/examples/provider-install-verification/main.tf b/examples/provider-install-verification/main.tf new file mode 100644 index 0000000..5e4a5ae --- /dev/null +++ b/examples/provider-install-verification/main.tf @@ -0,0 +1,22 @@ +terraform { + required_providers { + bitwarden = { + source = "onesignal/bitwarden" + } + } +} + +# export BITWARDEN_CLIENT_ID="" +# export BITWARDEN_CLIENT_SECRET="" +provider "bitwarden" { +} + + +resource "bitwarden_group" "example" { + name = "example" + access_all = true +} + +output "test" { + value = bitwarden_group.example +} diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf index 942db45..44bd801 100644 --- a/examples/provider/provider.tf +++ b/examples/provider/provider.tf @@ -1,3 +1,6 @@ -provider "scaffolding" { - # example configuration here +provider "bitwarden" { + # Configure the authentication values needed to authenticate with Bitwarden API. + # More information can be found here: https://bitwarden.com/help/public-api/#authentication + client_id = "organization.xxx" + client_secret = "client_api_secret" } diff --git a/examples/resources/bitwarden_group/resource.tf b/examples/resources/bitwarden_group/resource.tf new file mode 100644 index 0000000..04750e4 --- /dev/null +++ b/examples/resources/bitwarden_group/resource.tf @@ -0,0 +1,4 @@ +resource "bitwarden_group" "example" { + name = "example-group" + access_all = true +} diff --git a/examples/resources/scaffolding_example/resource.tf b/examples/resources/scaffolding_example/resource.tf deleted file mode 100644 index 9ae3f57..0000000 --- a/examples/resources/scaffolding_example/resource.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "scaffolding_example" "example" { - configurable_attribute = "some-value" -} diff --git a/go.mod b/go.mod index bbdd72e..b591ea3 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,23 @@ -module github.com/hashicorp/terraform-provider-scaffolding-framework +module terraform-provider-bitwarden -go 1.19 +go 1.21 require ( github.com/hashicorp/terraform-plugin-docs v0.16.0 - github.com/hashicorp/terraform-plugin-framework v1.3.3 - github.com/hashicorp/terraform-plugin-go v0.18.0 + github.com/hashicorp/terraform-plugin-framework v1.4.0 + github.com/hashicorp/terraform-plugin-go v0.19.0 github.com/hashicorp/terraform-plugin-log v0.9.0 - github.com/hashicorp/terraform-plugin-testing v1.4.0 + github.com/hashicorp/terraform-plugin-testing v1.5.1 + golang.org/x/oauth2 v0.12.0 ) require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect github.com/agext/levenshtein v1.2.2 // indirect - github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/cloudflare/circl v1.3.3 // indirect @@ -30,20 +31,20 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.10 // indirect + github.com/hashicorp/go-plugin v1.5.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/hc-install v0.5.2 // indirect - github.com/hashicorp/hcl/v2 v2.17.0 // indirect + github.com/hashicorp/hc-install v0.6.0 // indirect + github.com/hashicorp/hcl/v2 v2.18.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.18.1 // indirect + github.com/hashicorp/terraform-exec v0.19.0 // indirect github.com/hashicorp/terraform-json v0.17.1 // indirect - github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0 // indirect - github.com/hashicorp/terraform-registry-address v0.2.1 // indirect + github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 // indirect + github.com/hashicorp/terraform-registry-address v0.2.2 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect github.com/huandu/xstrings v1.3.2 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/mitchellh/cli v1.1.5 // indirect @@ -60,15 +61,15 @@ require ( github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/zclconf/go-cty v1.13.2 // indirect - golang.org/x/crypto v0.11.0 // indirect - golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect - golang.org/x/mod v0.11.0 // indirect - golang.org/x/net v0.11.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + github.com/zclconf/go-cty v1.14.0 // indirect + golang.org/x/crypto v0.13.0 // indirect + golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.56.1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect + google.golang.org/grpc v1.57.0 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index 00f248c..a32571c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= @@ -5,22 +6,22 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0 github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= +github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= -github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= -github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -31,10 +32,11 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= +github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -63,37 +65,37 @@ github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.10 h1:xUbmA4jC6Dq163/fWcp8P3JuHilrHHMLNRxzGQJ9hNk= -github.com/hashicorp/go-plugin v1.4.10/go.mod h1:6/1TEzT0eQznvI/gV2CM29DLSkAK/e58mUWKVsPaph0= +github.com/hashicorp/go-plugin v1.5.1 h1:oGm7cWBaYIp3lJpx1RUEfLWophprE2EV/KUeqBYo+6k= +github.com/hashicorp/go-plugin v1.5.1/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.5.2 h1:SfwMFnEXVVirpwkDuSF5kymUOhrUxrTq3udEseZdOD0= -github.com/hashicorp/hc-install v0.5.2/go.mod h1:9QISwe6newMWIfEiXpzuu1k9HAGtQYgnSH8H9T8wmoI= -github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY= -github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4= +github.com/hashicorp/hc-install v0.6.0 h1:fDHnU7JNFNSQebVKYhHZ0va1bC6SrPQ8fpebsvNr2w4= +github.com/hashicorp/hc-install v0.6.0/go.mod h1:10I912u3nntx9Umo1VAeYPUUuehk0aRQJYpMwbX5wQA= +github.com/hashicorp/hcl/v2 v2.18.0 h1:wYnG7Lt31t2zYkcquwgKo6MWXzRUDIeIVU5naZwHLl8= +github.com/hashicorp/hcl/v2 v2.18.0/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.18.1 h1:LAbfDvNQU1l0NOQlTuudjczVhHj061fNX5H8XZxHlH4= -github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980= +github.com/hashicorp/terraform-exec v0.19.0 h1:FpqZ6n50Tk95mItTSS9BjeOVUb4eg81SpgVtZNNtFSM= +github.com/hashicorp/terraform-exec v0.19.0/go.mod h1:tbxUpe3JKruE9Cuf65mycSIT8KiNPZ0FkuTE3H4urQg= github.com/hashicorp/terraform-json v0.17.1 h1:eMfvh/uWggKmY7Pmb3T85u86E2EQg6EQHgyRwf3RkyA= github.com/hashicorp/terraform-json v0.17.1/go.mod h1:Huy6zt6euxaY9knPAFKjUITn8QxUFIe9VuSzb4zn/0o= github.com/hashicorp/terraform-plugin-docs v0.16.0 h1:UmxFr3AScl6Wged84jndJIfFccGyBZn52KtMNsS12dI= github.com/hashicorp/terraform-plugin-docs v0.16.0/go.mod h1:M3ZrlKBJAbPMtNOPwHicGi1c+hZUh7/g0ifT/z7TVfA= -github.com/hashicorp/terraform-plugin-framework v1.3.3 h1:D18BlA8gdV4+W8WKhUqxudiYomPZHv94FFzyoSCKC8Q= -github.com/hashicorp/terraform-plugin-framework v1.3.3/go.mod h1:2gGDpWiTI0irr9NSTLFAKlTi6KwGti3AoU19rFqU30o= -github.com/hashicorp/terraform-plugin-go v0.18.0 h1:IwTkOS9cOW1ehLd/rG0y+u/TGLK9y6fGoBjXVUquzpE= -github.com/hashicorp/terraform-plugin-go v0.18.0/go.mod h1:l7VK+2u5Kf2y+A+742GX0ouLut3gttudmvMgN0PA74Y= +github.com/hashicorp/terraform-plugin-framework v1.4.0 h1:WKbtCRtNrjsh10eA7NZvC/Qyr7zp77j+D21aDO5th9c= +github.com/hashicorp/terraform-plugin-framework v1.4.0/go.mod h1:XC0hPcQbBvlbxwmjxuV/8sn8SbZRg4XwGMs22f+kqV0= +github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU= +github.com/hashicorp/terraform-plugin-go v0.19.0/go.mod h1:EhRSkEPNoylLQntYsk5KrDHTZJh9HQoumZXbOGOXmec= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0 h1:I8efBnjuDrgPjNF1MEypHy48VgcTIUY4X6rOFunrR3Y= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0/go.mod h1:cUEP4ly/nxlHy5HzD6YRrHydtlheGvGRJDhiWqqVik4= -github.com/hashicorp/terraform-plugin-testing v1.4.0 h1:DVIXxw7VHZvnwWVik4HzhpC2yytaJ5FpiHxz5debKmE= -github.com/hashicorp/terraform-plugin-testing v1.4.0/go.mod h1:b7Bha24iGrbZQjT+ZE8m9crck1YjdVOZ8mfGCQ19OxA= -github.com/hashicorp/terraform-registry-address v0.2.1 h1:QuTf6oJ1+WSflJw6WYOHhLgwUiQ0FrROpHPYFtwTYWM= -github.com/hashicorp/terraform-registry-address v0.2.1/go.mod h1:BSE9fIFzp0qWsJUUyGquo4ldV9k2n+psif6NYkBRS3Y= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 h1:wcOKYwPI9IorAJEBLzgclh3xVolO7ZorYd6U1vnok14= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0/go.mod h1:qH/34G25Ugdj5FcM95cSoXzUgIbgfhVLXCcEcYaMwq8= +github.com/hashicorp/terraform-plugin-testing v1.5.1 h1:T4aQh9JAhmWo4+t1A7x+rnxAJHCDIYW9kXyo4sVO92c= +github.com/hashicorp/terraform-plugin-testing v1.5.1/go.mod h1:dg8clO6K59rZ8w9EshBmDp1CxTIPu3yA4iaDpX1h5u0= +github.com/hashicorp/terraform-registry-address v0.2.2 h1:lPQBg403El8PPicg/qONZJDC6YlgCVbWDtNmmZKtBno= +github.com/hashicorp/terraform-registry-address v0.2.2/go.mod h1:LtwNbCihUoUZ3RYriyS2wF/lGPB6gF9ICLRtuDk7hSo= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= @@ -102,10 +104,10 @@ github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= @@ -152,7 +154,7 @@ github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= +github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= @@ -171,24 +173,39 @@ github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= -github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0= -github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zclconf/go-cty v1.14.0 h1:/Xrd39K7DXbHzlisFP9c4pHao4yyf+/Ug9LEz+Y/yhc= +github.com/zclconf/go-cty v1.14.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= +golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -197,26 +214,44 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ= -google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= @@ -228,6 +263,5 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/bitwarden/client.go b/internal/bitwarden/client.go new file mode 100644 index 0000000..60f530f --- /dev/null +++ b/internal/bitwarden/client.go @@ -0,0 +1,63 @@ +package bitwarden + +import ( + "context" + "fmt" + "io" + "net/http" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" +) + +type Client interface { + CreateGroup(ctx context.Context, group Group) (*Group, error) + GetGroup(ctx context.Context, id string) (*Group, error) + UpdateGroup(ctx context.Context, id string, group Group) (*Group, error) + DeleteGroup(ctx context.Context, id string) error +} +type client struct { + apiURL string + oauthConfig *clientcredentials.Config +} + +// NewClient creates a new BitWarden API client to interact with the BitWarden Public API +// +// See the BitWarden documentation for more information about the API +// https://bitwarden.com/help/public-api/ +// https://bitwarden.com/help/api/ +func NewClient(_ context.Context, clientID, clientSecret, apiUrl, authUrl string) (Client, error) { + c := &client{ + apiURL: apiUrl, + oauthConfig: &clientcredentials.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + TokenURL: authUrl, + Scopes: []string{"api.organization"}, + AuthStyle: oauth2.AuthStyleInParams, + }, + } + + return c, nil +} + +func (c *client) doRequest(ctx context.Context, req *http.Request) ([]byte, error) { + req.Header.Set("Content-Type", "application/json") + + res, err := c.oauthConfig.Client(ctx).Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", res.StatusCode, body) + } + + return body, err +} diff --git a/internal/bitwarden/groups.go b/internal/bitwarden/groups.go new file mode 100644 index 0000000..4779a26 --- /dev/null +++ b/internal/bitwarden/groups.go @@ -0,0 +1,98 @@ +package bitwarden + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" +) + +type Group struct { + ID string `json:"id"` + Object string `json:"object"` + Name string `json:"name"` + ExternalId string `json:"externalId"` + AccessAll bool `json:"accessAll"` +} + +func (c *client) CreateGroup(ctx context.Context, group Group) (*Group, error) { + rb, err := json.Marshal(group) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/groups", c.apiURL), strings.NewReader(string(rb))) + if err != nil { + return nil, err + } + + body, err := c.doRequest(ctx, req) + if err != nil { + return nil, err + } + + newGroup := Group{} + err = json.Unmarshal(body, &newGroup) + if err != nil { + return nil, err + } + + return &newGroup, nil +} + +func (c *client) GetGroup(ctx context.Context, id string) (*Group, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/groups/%s", c.apiURL, id), nil) + if err != nil { + return nil, err + } + + body, err := c.doRequest(ctx, req) + if err != nil { + return nil, err + } + + newGroup := Group{} + err = json.Unmarshal(body, &newGroup) + if err != nil { + return nil, err + } + + return &newGroup, nil +} + +func (c *client) UpdateGroup(ctx context.Context, id string, group Group) (*Group, error) { + rb, err := json.Marshal(group) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", fmt.Sprintf("%s/groups/%s", c.apiURL, id), strings.NewReader(string(rb))) + if err != nil { + return nil, err + } + + body, err := c.doRequest(ctx, req) + if err != nil { + return nil, err + } + + newGroup := Group{} + err = json.Unmarshal(body, &newGroup) + if err != nil { + return nil, err + } + + return &newGroup, nil +} + +func (c *client) DeleteGroup(ctx context.Context, id string) error { + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/groups/%s", c.apiURL, id), nil) + if err != nil { + return err + } + + _, err = c.doRequest(ctx, req) + + return err +} diff --git a/internal/provider/example_data_source.go b/internal/provider/example_data_source.go deleted file mode 100644 index 585b9d2..0000000 --- a/internal/provider/example_data_source.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "context" - "fmt" - "net/http" - - "github.com/hashicorp/terraform-plugin-framework/datasource" - "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-log/tflog" -) - -// Ensure provider defined types fully satisfy framework interfaces. -var _ datasource.DataSource = &ExampleDataSource{} - -func NewExampleDataSource() datasource.DataSource { - return &ExampleDataSource{} -} - -// ExampleDataSource defines the data source implementation. -type ExampleDataSource struct { - client *http.Client -} - -// ExampleDataSourceModel describes the data source data model. -type ExampleDataSourceModel struct { - ConfigurableAttribute types.String `tfsdk:"configurable_attribute"` - Id types.String `tfsdk:"id"` -} - -func (d *ExampleDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_example" -} - -func (d *ExampleDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { - resp.Schema = schema.Schema{ - // This description is used by the documentation generator and the language server. - MarkdownDescription: "Example data source", - - Attributes: map[string]schema.Attribute{ - "configurable_attribute": schema.StringAttribute{ - MarkdownDescription: "Example configurable attribute", - Optional: true, - }, - "id": schema.StringAttribute{ - MarkdownDescription: "Example identifier", - Computed: true, - }, - }, - } -} - -func (d *ExampleDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { - // Prevent panic if the provider has not been configured. - if req.ProviderData == nil { - return - } - - client, ok := req.ProviderData.(*http.Client) - - if !ok { - resp.Diagnostics.AddError( - "Unexpected Data Source Configure Type", - fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), - ) - - return - } - - d.client = client -} - -func (d *ExampleDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var data ExampleDataSourceModel - - // Read Terraform configuration data into the model - resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := d.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err)) - // return - // } - - // For the purposes of this example code, hardcoding a response value to - // save into the Terraform state. - data.Id = types.StringValue("example-id") - - // Write logs using the tflog package - // Documentation: https://terraform.io/plugin/log - tflog.Trace(ctx, "read a data source") - - // Save data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} diff --git a/internal/provider/example_data_source_test.go b/internal/provider/example_data_source_test.go deleted file mode 100644 index 6f9aa7d..0000000 --- a/internal/provider/example_data_source_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" -) - -func TestAccExampleDataSource(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - // Read testing - { - Config: testAccExampleDataSourceConfig, - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("data.scaffolding_example.test", "id", "example-id"), - ), - }, - }, - }) -} - -const testAccExampleDataSourceConfig = ` -data "scaffolding_example" "test" { - configurable_attribute = "example" -} -` diff --git a/internal/provider/example_resource.go b/internal/provider/example_resource.go deleted file mode 100644 index af8c0fa..0000000 --- a/internal/provider/example_resource.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "context" - "fmt" - "net/http" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-log/tflog" -) - -// Ensure provider defined types fully satisfy framework interfaces. -var _ resource.Resource = &ExampleResource{} -var _ resource.ResourceWithImportState = &ExampleResource{} - -func NewExampleResource() resource.Resource { - return &ExampleResource{} -} - -// ExampleResource defines the resource implementation. -type ExampleResource struct { - client *http.Client -} - -// ExampleResourceModel describes the resource data model. -type ExampleResourceModel struct { - ConfigurableAttribute types.String `tfsdk:"configurable_attribute"` - Defaulted types.String `tfsdk:"defaulted"` - Id types.String `tfsdk:"id"` -} - -func (r *ExampleResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_example" -} - -func (r *ExampleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - // This description is used by the documentation generator and the language server. - MarkdownDescription: "Example resource", - - Attributes: map[string]schema.Attribute{ - "configurable_attribute": schema.StringAttribute{ - MarkdownDescription: "Example configurable attribute", - Optional: true, - }, - "defaulted": schema.StringAttribute{ - MarkdownDescription: "Example configurable attribute with default value", - Optional: true, - Computed: true, - Default: stringdefault.StaticString("example value when not configured"), - }, - "id": schema.StringAttribute{ - Computed: true, - MarkdownDescription: "Example identifier", - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - }, - } -} - -func (r *ExampleResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - // Prevent panic if the provider has not been configured. - if req.ProviderData == nil { - return - } - - client, ok := req.ProviderData.(*http.Client) - - if !ok { - resp.Diagnostics.AddError( - "Unexpected Resource Configure Type", - fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), - ) - - return - } - - r.client = client -} - -func (r *ExampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data *ExampleResourceModel - - // Read Terraform plan data into the model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := r.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create example, got error: %s", err)) - // return - // } - - // For the purposes of this example code, hardcoding a response value to - // save into the Terraform state. - data.Id = types.StringValue("example-id") - - // Write logs using the tflog package - // Documentation: https://terraform.io/plugin/log - tflog.Trace(ctx, "created a resource") - - // Save data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func (r *ExampleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var data *ExampleResourceModel - - // Read Terraform prior state data into the model - resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := r.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err)) - // return - // } - - // Save updated data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func (r *ExampleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var data *ExampleResourceModel - - // Read Terraform plan data into the model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := r.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update example, got error: %s", err)) - // return - // } - - // Save updated data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func (r *ExampleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var data *ExampleResourceModel - - // Read Terraform prior state data into the model - resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := r.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete example, got error: %s", err)) - // return - // } -} - -func (r *ExampleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} diff --git a/internal/provider/example_resource_test.go b/internal/provider/example_resource_test.go deleted file mode 100644 index c5464d0..0000000 --- a/internal/provider/example_resource_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" -) - -func TestAccExampleResource(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - // Create and Read testing - { - Config: testAccExampleResourceConfig("one"), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("scaffolding_example.test", "configurable_attribute", "one"), - resource.TestCheckResourceAttr("scaffolding_example.test", "defaulted", "example value when not configured"), - resource.TestCheckResourceAttr("scaffolding_example.test", "id", "example-id"), - ), - }, - // ImportState testing - { - ResourceName: "scaffolding_example.test", - ImportState: true, - ImportStateVerify: true, - // This is not normally necessary, but is here because this - // example code does not have an actual upstream service. - // Once the Read method is able to refresh information from - // the upstream service, this can be removed. - ImportStateVerifyIgnore: []string{"configurable_attribute", "defaulted"}, - }, - // Update and Read testing - { - Config: testAccExampleResourceConfig("two"), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("scaffolding_example.test", "configurable_attribute", "two"), - ), - }, - // Delete testing automatically occurs in TestCase - }, - }) -} - -func testAccExampleResourceConfig(configurableAttribute string) string { - return fmt.Sprintf(` -resource "scaffolding_example" "test" { - configurable_attribute = %[1]q -} -`, configurableAttribute) -} diff --git a/internal/provider/group_resource.go b/internal/provider/group_resource.go new file mode 100644 index 0000000..00aee36 --- /dev/null +++ b/internal/provider/group_resource.go @@ -0,0 +1,234 @@ +package provider + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + + "terraform-provider-bitwarden/internal/bitwarden" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &groupResource{} + _ resource.ResourceWithConfigure = &groupResource{} + _ resource.ResourceWithImportState = &groupResource{} +) + +// NewGroupResource is a helper function to simplify the provider implementation. +func NewGroupResource() resource.Resource { + return &groupResource{} +} + +// groupResource is the resource implementation. +type groupResource struct { + client *bitwarden.Client +} + +type groupResourceModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + ExternalId types.String `tfsdk:"external_id"` + AccessAll types.Bool `tfsdk:"access_all"` + LastUpdated types.String `tfsdk:"last_updated"` +} + +// Metadata returns the resource type name. +func (r *groupResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_group" +} + +func (r *groupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(bitwarden.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *bitwarden.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = &client +} + +// Schema defines the schema for the resource. +func (r *groupResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + Required: true, + }, + "external_id": schema.StringAttribute{ + Computed: true, + Optional: true, + Default: stringdefault.StaticString(""), + }, + "access_all": schema.BoolAttribute{ + Computed: true, + Optional: true, + Default: booldefault.StaticBool(false), + }, + "last_updated": schema.StringAttribute{ + Computed: true, + }, + }, + } +} + +// Create creates the resource and sets the initial Terraform state. +func (r *groupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan groupResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + group := bitwarden.Group{ + Name: plan.Name.ValueString(), + ExternalId: plan.ExternalId.ValueString(), + AccessAll: plan.AccessAll.ValueBool(), + } + + // Create new group + newGroup, err := (*r.client).CreateGroup(ctx, group) + if err != nil { + resp.Diagnostics.AddError( + "Error creating group", + "Could not create group, unexpected error: "+err.Error(), + ) + return + } + + // TODO: check how we can keep some items hidden from user. Like for example ID, that field is automatically configured + plan.ID = types.StringValue(newGroup.ID) + plan.ExternalId = types.StringValue(newGroup.ExternalId) + plan.Name = types.StringValue(newGroup.Name) + plan.AccessAll = types.BoolValue(newGroup.AccessAll) + plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850)) + + // Set state to fully populated data + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Read refreshes the Terraform state with the latest data. +func (r *groupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state groupResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Get refreshed group value from BitWarden + group, err := (*r.client).GetGroup(ctx, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Error Reading Bitwarden group", + "Could not read Bitwarden group ID "+state.ID.ValueString()+": "+err.Error(), + ) + return + } + + // Overwrite group with refreshed state + state.ID = types.StringValue(group.ID) + state.ExternalId = types.StringValue(group.ExternalId) + state.Name = types.StringValue(group.Name) + state.AccessAll = types.BoolValue(group.AccessAll) + + // Set refreshed state + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Update updates the resource and sets the updated Terraform state on success. +func (r *groupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // Retrieve values from plan + var plan groupResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Generate API request body from plan + group := bitwarden.Group{ + Name: plan.Name.ValueString(), + ExternalId: plan.ExternalId.ValueString(), + AccessAll: plan.AccessAll.ValueBool(), + } + + // Update existing group + newGroup, err := (*r.client).UpdateGroup(ctx, plan.ID.ValueString(), group) + if err != nil { + resp.Diagnostics.AddError( + "Error Updating Bitwarden group", + "Could not update group, unexpected error: "+err.Error(), + ) + return + } + + // Update resource state with updated items and timestamp + plan.ExternalId = types.StringValue(newGroup.ExternalId) + plan.Name = types.StringValue(newGroup.Name) + plan.AccessAll = types.BoolValue(newGroup.AccessAll) + plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850)) + + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Delete deletes the resource and removes the Terraform state on success. +func (r *groupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state groupResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Delete existing group + err := (*r.client).DeleteGroup(ctx, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Error Deleting Bitwarden group", + "Could not delete group, unexpected error: "+err.Error(), + ) + return + } +} + +func (r *groupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/provider/group_resource_test.go b/internal/provider/group_resource_test.go new file mode 100644 index 0000000..ebe6ec9 --- /dev/null +++ b/internal/provider/group_resource_test.go @@ -0,0 +1,62 @@ +package provider + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccGroupResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: testAccGroupResourceConfig("one", "", true), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("bitwarden_group.test", "name", "one"), + resource.TestCheckResourceAttr("bitwarden_group.test", "access_all", "true"), + resource.TestCheckResourceAttrSet("bitwarden_group.test", "id"), + resource.TestCheckResourceAttrSet("bitwarden_group.test", "last_updated"), + ), + }, + // ImportState testing + { + ResourceName: "bitwarden_group.test", + ImportState: true, + ImportStateVerify: true, + // This is not normally necessary, but is here because this + // example code does not have an actual upstream service. + // Once the Read method is able to refresh information from + // the upstream service, this can be removed. + ImportStateVerifyIgnore: []string{"last_updated"}, + }, + //Update and Read testing + { + Config: testAccGroupResourceConfig("two", "external-two", false), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("bitwarden_group.test", "name", "two"), + resource.TestCheckResourceAttr("bitwarden_group.test", "external_id", "external-two"), + resource.TestCheckResourceAttr("bitwarden_group.test", "access_all", "false"), + ), + }, + // Delete testing automatically occurs in TestCase + }, + }) +} + +func testAccGroupResourceConfig(name, externalId string, accessAll bool) string { + builder := strings.Builder{} + builder.WriteString("resource \"bitwarden_group\" \"test\" {\n") + builder.WriteString(fmt.Sprintf("name = %[1]q\n", name)) + builder.WriteString(fmt.Sprintf("access_all = %v\n", accessAll)) + if len(externalId) != 0 { + builder.WriteString(fmt.Sprintf("external_id = %[1]q\n", externalId)) + } + builder.WriteString("}") + + return builder.String() +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index c326755..0ea15a8 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1,85 +1,239 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package provider import ( "context" - "net/http" + "os" "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "terraform-provider-bitwarden/internal/bitwarden" ) -// Ensure ScaffoldingProvider satisfies various provider interfaces. -var _ provider.Provider = &ScaffoldingProvider{} +// Ensure the implementation satisfies the expected interfaces. +var ( + _ provider.Provider = &bitwardenProvider{} +) -// ScaffoldingProvider defines the provider implementation. -type ScaffoldingProvider struct { +// New is a helper function to simplify provider server and testing implementation. +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &bitwardenProvider{ + version: version, + } + } +} + +// bitwardenProvider is the provider implementation. +type bitwardenProvider struct { // version is set to the provider version on release, "dev" when the // provider is built and ran locally, and "test" when running acceptance // testing. version string } -// ScaffoldingProviderModel describes the provider data model. -type ScaffoldingProviderModel struct { - Endpoint types.String `tfsdk:"endpoint"` +// bitwardenProviderModel maps provider schema data to a Go type. +type bitwardenProviderModel struct { + ClientID types.String `tfsdk:"client_id"` + ClientSecret types.String `tfsdk:"client_secret"` + APIUrl types.String `tfsdk:"api_url"` + AuthenticationUrl types.String `tfsdk:"authentication_url"` } -func (p *ScaffoldingProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { - resp.TypeName = "scaffolding" +// Metadata returns the provider type name. +func (p *bitwardenProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "bitwarden" resp.Version = p.version } -func (p *ScaffoldingProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { +// Schema defines the provider-level schema for configuration data. +func (p *bitwardenProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ + MarkdownDescription: "Bitwarden API Provider, focussing on Group and Group Members for organisations", Attributes: map[string]schema.Attribute{ - "endpoint": schema.StringAttribute{ - MarkdownDescription: "Example provider attribute", + "client_id": schema.StringAttribute{ + MarkdownDescription: "The client_id of your organisation, can also be configured as `BITWARDEN_CLIENT_ID`. See [docs](https://bitwarden.com/help/public-api/#authentication) for more information", + Optional: true, + }, + "client_secret": schema.StringAttribute{ + MarkdownDescription: "The client_secret of your organisation, can also be configured as `BITWARDEN_CLIENT_SECRET`. See [docs](https://bitwarden.com/help/public-api/#authentication) for more information", + Optional: true, + Sensitive: true, + }, + "api_url": schema.StringAttribute{ + MarkdownDescription: "The Bitwarden API URL, defaults to `https://api.bitwarden.com`, can also be configured as `BITWARDEN_API_URL`. See [docs](https://bitwarden.com/help/public-api/#endpoints) for more information", + Optional: true, + }, + "authentication_url": schema.StringAttribute{ + MarkdownDescription: "The Bitwarden Authentication URL, defaults to `https://identity.bitwarden.com/connect/token`, can also be configured as `BITWARDEN_AUTH_URL`. See [docs](https://bitwarden.com/help/public-api/#authentication-endpoints) for more information", Optional: true, }, }, } } -func (p *ScaffoldingProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { - var data ScaffoldingProviderModel +// Configure prepares a Bitwarden API client for data sources and resources. +func (p *bitwardenProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + // Retrieve provider data from configuration + var config bitwardenProviderModel + diags := req.Config.Get(ctx, &config) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + if config.ClientID.IsUnknown() { + resp.Diagnostics.AddAttributeError( + path.Root("client_id"), + "Unknown Bitwarden client_id", + "The provider cannot create the Bitwarden API client as there is an unknown configuration value for the Bitwarden Client ID. "+ + "Either target apply the source of the value first, set the value statically in the configuration, or use the BITWARDEN_CLIENT_ID environment variable.", + ) + } - resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if config.ClientSecret.IsUnknown() { + resp.Diagnostics.AddAttributeError( + path.Root("client_secret"), + "Unknown Bitwarden client_secret", + "The provider cannot create the Bitwarden API client as there is an unknown configuration value for the Bitwarden client_secret. "+ + "Either target apply the source of the value first, set the value statically in the configuration, or use the BITWARDEN_CLIENT_SECRET environment variable.", + ) + } + + if config.APIUrl.IsUnknown() { + resp.Diagnostics.AddAttributeError( + path.Root("api_url"), + "Unknown Bitwarden API URL", + "The provider cannot create the Bitwarden API client as there is an unknown configuration value for the Bitwarden API URL. "+ + "Either target apply the source of the value first, set the value statically in the configuration, or use the BITWARDEN_API_URL environment variable.", + ) + } + + if config.AuthenticationUrl.IsUnknown() { + resp.Diagnostics.AddAttributeError( + path.Root("authentication_url"), + "Unknown Bitwarden Authentication URL", + "The provider cannot create the Bitwarden API client as there is an unknown configuration value for the Bitwarden Authentication URL. "+ + "Either target apply the source of the value first, set the value statically in the configuration, or use the BITWARDEN_AUTHENTICATION_URL environment variable.", + ) + } if resp.Diagnostics.HasError() { return } - // Configuration values are now available. - // if data.Endpoint.IsNull() { /* ... */ } + // Default values to environment variables, but override + // with Terraform configuration value if set. + clientId := os.Getenv("BITWARDEN_CLIENT_ID") + clientSecret := os.Getenv("BITWARDEN_CLIENT_SECRET") + apiUrl := os.Getenv("BITWARDEN_API_URL") + authUrl := os.Getenv("BITWARDEN_AUTHENTICATION_URL") - // Example client configuration for data sources and resources - client := http.DefaultClient - resp.DataSourceData = client - resp.ResourceData = client -} + if !config.ClientID.IsNull() { + clientId = config.ClientID.ValueString() + } -func (p *ScaffoldingProvider) Resources(ctx context.Context) []func() resource.Resource { - return []func() resource.Resource{ - NewExampleResource, + if !config.ClientSecret.IsNull() { + clientSecret = config.ClientSecret.ValueString() + } + + if !config.APIUrl.IsNull() { + apiUrl = config.APIUrl.ValueString() + } else if len(apiUrl) == 0 { + apiUrl = "https://api.bitwarden.com/public" + } + + if !config.AuthenticationUrl.IsNull() { + authUrl = config.AuthenticationUrl.ValueString() + } else if len(authUrl) == 0 { + authUrl = "https://identity.bitwarden.com/connect/token" + } + + // If any of the expected configurations are missing, return + // errors with provider-specific guidance. + if clientId == "" { + resp.Diagnostics.AddAttributeError( + path.Root("client_id"), + "Missing Bitwarden client_id", + "The provider cannot create the Bitwarden API client as there is a missing or empty value for the Bitwarden client_id. "+ + "Set the client_id value in the configuration or use the BITWARDEN_CLIENT_ID environment variable. "+ + "If either is already set, ensure the value is not empty.", + ) } -} -func (p *ScaffoldingProvider) DataSources(ctx context.Context) []func() datasource.DataSource { - return []func() datasource.DataSource{ - NewExampleDataSource, + if clientSecret == "" { + resp.Diagnostics.AddAttributeError( + path.Root("client_secret"), + "Missing Bitwarden client_secret", + "The provider cannot create the Bitwarden API client as there is a missing or empty value for the Bitwarden client_secret. "+ + "Set the client_secret value in the configuration or use the BITWARDEN_CLIENT_SECRET environment variable. "+ + "If either is already set, ensure the value is not empty.", + ) } + + if apiUrl == "" { + resp.Diagnostics.AddAttributeError( + path.Root("api_url"), + "Missing Bitwarden API URL", + "The provider cannot create the Bitwarden API client as there is a missing or empty value for the Bitwarden API URL. "+ + "Set the api_url value in the configuration or use the BITWARDEN_API_URL environment variable. "+ + "If either is already set, ensure the value is not empty.", + ) + } + + if authUrl == "" { + resp.Diagnostics.AddAttributeError( + path.Root("authentication_url"), + "Missing Bitwarden Authentication URL", + "The provider cannot create the Bitwarden API client as there is a missing or empty value for the Bitwarden Authentication URL. "+ + "Set the authentication_url value in the configuration or use the BITWARDEN_AUTHENTICATION_URL environment variable. "+ + "If either is already set, ensure the value is not empty.", + ) + } + + if resp.Diagnostics.HasError() { + return + } + + ctx = tflog.SetField(ctx, "bitwarden_client_id", clientId) + ctx = tflog.SetField(ctx, "bitwarden_api_url", apiUrl) + ctx = tflog.SetField(ctx, "bitwarden_authentication_url", authUrl) + ctx = tflog.SetField(ctx, "bitwarden_client_secret", clientSecret) + ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "bitwarden_client_secret") + + tflog.Debug(ctx, "Creating Bitwarden API client") + + // Create a new Bitwarden client using the configuration values + client, err := bitwarden.NewClient(ctx, clientId, clientSecret, apiUrl, authUrl) + if err != nil { + resp.Diagnostics.AddError( + "Unable to Create Bitwarden API Client", + "An unexpected error occurred when creating the Bitwarden API client. "+ + "If the error is not clear, please contact the provider developers.\n\n"+ + "Bitwarden Client Error: "+err.Error(), + ) + return + } + + // Make the Bitwarden client available during DataSource and Resource + // type Configure methods. + resp.DataSourceData = client + resp.ResourceData = client } -func New(version string) func() provider.Provider { - return func() provider.Provider { - return &ScaffoldingProvider{ - version: version, - } +// DataSources defines the data sources implemented in the provider. +func (p *bitwardenProvider) DataSources(_ context.Context) []func() datasource.DataSource { + return nil +} + +// Resources defines the resources implemented in the provider. +func (p *bitwardenProvider) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + NewGroupResource, } } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index ef6599b..7972125 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -15,11 +15,8 @@ import ( // CLI command executed to create a provider server to which the CLI can // reattach. var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ - "scaffolding": providerserver.NewProtocol6WithError(New("test")()), + "bitwarden": providerserver.NewProtocol6WithError(New("test")()), } -func testAccPreCheck(t *testing.T) { - // You can add code here to run prior to any test case execution, for example assertions - // about the appropriate environment variables being set are common to see in a pre-check - // function. +func testAccPreCheck(_ *testing.T) { } diff --git a/main.go b/main.go index e3d16b2..4d99c4c 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,8 @@ import ( "log" "github.com/hashicorp/terraform-plugin-framework/providerserver" - "github.com/hashicorp/terraform-provider-scaffolding-framework/internal/provider" + + "terraform-provider-bitwarden/internal/provider" ) // Run "go generate" to format example terraform files and generate the docs for the registry/website @@ -38,8 +39,7 @@ func main() { flag.Parse() opts := providerserver.ServeOpts{ - // TODO: Update this string with the published name of your provider. - Address: "registry.terraform.io/hashicorp/scaffolding", + Address: "registry.terraform.io/onesignal/terraform-provider-bitwarden", Debug: debug, }