From 4f0509c55d5090060fb11fb120d261692a2d1a3e Mon Sep 17 00:00:00 2001 From: Jeff Malnick Date: Tue, 22 Jun 2021 21:07:07 -0700 Subject: [PATCH] Add credential library resource for Vault (#114) * Integrate with Vault to retrieve and manage per session credentials Co-authored-by: Louis Ruch --- docs/resources/credential_library_vault.md | 86 +++++ docs/resources/credential_store_vault.md | 72 ++++ docs/resources/target.md | 20 + .../import.sh | 1 + .../resource.tf | 43 +++ .../boundary_credential_store_vault/import.sh | 1 + .../resource.tf | 22 ++ .../resources/boundary_target/resource.tf | 19 + go.mod | 15 +- go.sum | 98 +++-- internal/provider/const.go | 14 +- internal/provider/provider.go | 30 +- internal/provider/provider_test.go | 2 +- .../resource_credential_library_vault.go | 237 ++++++++++++ .../resource_credential_library_vault_test.go | 154 ++++++++ .../resource_credential_store_vault.go | 346 ++++++++++++++++++ .../resource_credential_store_vault_test.go | 191 ++++++++++ internal/provider/resource_group.go | 2 - internal/provider/resource_target.go | 48 ++- internal/provider/resource_target_test.go | 131 ++++++- 20 files changed, 1433 insertions(+), 99 deletions(-) create mode 100644 docs/resources/credential_library_vault.md create mode 100644 docs/resources/credential_store_vault.md create mode 100644 examples/resources/boundary_credential_library_vault/import.sh create mode 100644 examples/resources/boundary_credential_library_vault/resource.tf create mode 100644 examples/resources/boundary_credential_store_vault/import.sh create mode 100644 examples/resources/boundary_credential_store_vault/resource.tf create mode 100644 internal/provider/resource_credential_library_vault.go create mode 100644 internal/provider/resource_credential_library_vault_test.go create mode 100644 internal/provider/resource_credential_store_vault.go create mode 100644 internal/provider/resource_credential_store_vault_test.go diff --git a/docs/resources/credential_library_vault.md b/docs/resources/credential_library_vault.md new file mode 100644 index 00000000..f0a3a1f8 --- /dev/null +++ b/docs/resources/credential_library_vault.md @@ -0,0 +1,86 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "boundary_credential_library_vault Resource - terraform-provider-boundary" +subcategory: "" +description: |- + The credential library for Vault resource allows you to configure a Boundary credential library for Vault. +--- + +# boundary_credential_library_vault (Resource) + +The credential library for Vault resource allows you to configure a Boundary credential library for Vault. + +## Example Usage + +```terraform +resource "boundary_scope" "org" { + name = "organization_one" + description = "My first scope!" + scope_id = "global" + auto_create_admin_role = true + auto_create_default_role = true +} + +resource "boundary_scope" "project" { + name = "project_one" + description = "My first scope!" + scope_id = boundary_scope.org.id + auto_create_admin_role = true +} + +resource "boundary_credential_store_vault" "foo" { + name = "foo" + description = "My first Vault credential store!" + address = "127.0.0.1" + token = "s.0ufRo6XEGU2jOqnIr7OlFYP5" + scope_id = boundary_scope.project.id +} + +resource "boundary_credential_library_vault" "foo" { + name = "foo" + description = "My first Vault credential library!" + credential_store_id = boundary_credential_store_vault.foo.id + path = "my/secret/foo" + http_method = "GET" +} + +resource "boundary_credential_library_vault" "bar" { + name = "bar" + description = "My second Vault credential library!" + credential_store_id = boundary_credential_store_vault.foo.id + path = "my/secret/bar" + http_method = "POST" + request_body = < +## Schema + +### Required + +- **credential_store_id** (String) The ID of the credential store that this library belongs to. +- **path** (String) The Vault path to query + +### Optional + +- **description** (String) The Vault credential library description. +- **http_method** (String) The HTTP method to use when contacting Vault +- **http_request_body** (String) The raw string to use in HTTP request to Vault +- **name** (String) The Vault credential library name. Defaults to the resource name. + +### Read-Only + +- **id** (String) The ID of the Vault credential library. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import boundary_credential_library_vault.foo +``` diff --git a/docs/resources/credential_store_vault.md b/docs/resources/credential_store_vault.md new file mode 100644 index 00000000..62e4f5a8 --- /dev/null +++ b/docs/resources/credential_store_vault.md @@ -0,0 +1,72 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "boundary_credential_store_vault Resource - terraform-provider-boundary" +subcategory: "" +description: |- + The credential store for Vault resource allows you to configure a Boundary credential store for Vault. +--- + +# boundary_credential_store_vault (Resource) + +The credential store for Vault resource allows you to configure a Boundary credential store for Vault. + +## Example Usage + +```terraform +resource "boundary_scope" "org" { + name = "organization_one" + description = "My first scope!" + scope_id = "global" + auto_create_admin_role = true + auto_create_default_role = true +} + +resource "boundary_scope" "project" { + name = "project_one" + description = "My first scope!" + scope_id = boundary_scope.org.id + auto_create_admin_role = true +} + +resource "boundary_credential_store_vault" "example" { + name = "foo" + description = "My first Vault credential store!" + address = "127.0.0.1" + token = "s.0ufRo6XEGU2jOqnIr7OlFYP5" + scope_id = boundary_scope.project.id +} +``` + + +## Schema + +### Required + +- **address** (String) The address to Vault server +- **scope_id** (String) The scope for this credential store +- **token** (String, Sensitive) The Vault token + +### Optional + +- **ca_cert** (String) The Vault CA certificate to use +- **client_certificate** (String) The Vault client certificate +- **client_certificate_key** (String, Sensitive) The Vault client certificate key +- **description** (String) The Vault credential store description. +- **name** (String) The Vault credential store name. Defaults to the resource name. +- **namespace** (String) The namespace within Vault to use +- **tls_server_name** (String) The Vault TLS server name +- **tls_skip_verify** (Boolean) Whether or not to skip TLS verification + +### Read-Only + +- **client_certificate_key_hmac** (String) The Vault client certificate key hmac +- **id** (String) The ID of the Vault credential store. +- **token_hmac** (String) The Vault token hmac + +## Import + +Import is supported using the following syntax: + +```shell +terraform import boundary_credential_store_vault.foo +``` diff --git a/docs/resources/target.md b/docs/resources/target.md index cf8b7fbb..d412a7c2 100644 --- a/docs/resources/target.md +++ b/docs/resources/target.md @@ -33,6 +33,22 @@ resource "boundary_scope" "project" { auto_create_admin_role = true } +resource "boundary_credential_store_vault" "foo" { + name = "vault_store" + description = "My first Vault credential store!" + address = "http://localhost:55001" + token = "s.0ufRo6XEGU2jOqnIr7OlFYP5" + scope_id = boundary_scope.project.id +} + +resource "boundary_credential_library_vault" "foo" { + name = "vault_library" + description = "My first Vault credential library!" + credential_store_id = boundary_credential_store_vault.foo.id + path = "database/creds/opened" + http_method = "GET" +} + resource "boundary_host_catalog" "foo" { name = "test" description = "test catalog" @@ -72,6 +88,9 @@ resource "boundary_target" "foo" { host_set_ids = [ boundary_host_set.foo.id ] + credential_library_ids = [ + boundary_credential_library_vault.foo.id + ] } ``` @@ -85,6 +104,7 @@ resource "boundary_target" "foo" { ### Optional +- **credential_library_ids** (Set of String) A list of credential library ID's. - **default_port** (Number) The default port for this target. - **description** (String) The target description. - **host_set_ids** (Set of String) A list of host set ID's. diff --git a/examples/resources/boundary_credential_library_vault/import.sh b/examples/resources/boundary_credential_library_vault/import.sh new file mode 100644 index 00000000..f169aa70 --- /dev/null +++ b/examples/resources/boundary_credential_library_vault/import.sh @@ -0,0 +1 @@ +terraform import boundary_credential_library_vault.foo diff --git a/examples/resources/boundary_credential_library_vault/resource.tf b/examples/resources/boundary_credential_library_vault/resource.tf new file mode 100644 index 00000000..bf672365 --- /dev/null +++ b/examples/resources/boundary_credential_library_vault/resource.tf @@ -0,0 +1,43 @@ +resource "boundary_scope" "org" { + name = "organization_one" + description = "My first scope!" + scope_id = "global" + auto_create_admin_role = true + auto_create_default_role = true +} + +resource "boundary_scope" "project" { + name = "project_one" + description = "My first scope!" + scope_id = boundary_scope.org.id + auto_create_admin_role = true +} + +resource "boundary_credential_store_vault" "foo" { + name = "foo" + description = "My first Vault credential store!" + address = "127.0.0.1" + token = "s.0ufRo6XEGU2jOqnIr7OlFYP5" + scope_id = boundary_scope.project.id +} + +resource "boundary_credential_library_vault" "foo" { + name = "foo" + description = "My first Vault credential library!" + credential_store_id = boundary_credential_store_vault.foo.id + path = "my/secret/foo" + http_method = "GET" +} + +resource "boundary_credential_library_vault" "bar" { + name = "bar" + description = "My second Vault credential library!" + credential_store_id = boundary_credential_store_vault.foo.id + path = "my/secret/bar" + http_method = "POST" + request_body = < diff --git a/examples/resources/boundary_credential_store_vault/resource.tf b/examples/resources/boundary_credential_store_vault/resource.tf new file mode 100644 index 00000000..87a1ddb5 --- /dev/null +++ b/examples/resources/boundary_credential_store_vault/resource.tf @@ -0,0 +1,22 @@ +resource "boundary_scope" "org" { + name = "organization_one" + description = "My first scope!" + scope_id = "global" + auto_create_admin_role = true + auto_create_default_role = true +} + +resource "boundary_scope" "project" { + name = "project_one" + description = "My first scope!" + scope_id = boundary_scope.org.id + auto_create_admin_role = true +} + +resource "boundary_credential_store_vault" "example" { + name = "foo" + description = "My first Vault credential store!" + address = "127.0.0.1" + token = "s.0ufRo6XEGU2jOqnIr7OlFYP5" + scope_id = boundary_scope.project.id +} diff --git a/examples/resources/boundary_target/resource.tf b/examples/resources/boundary_target/resource.tf index cd667e37..723d32d9 100644 --- a/examples/resources/boundary_target/resource.tf +++ b/examples/resources/boundary_target/resource.tf @@ -18,6 +18,22 @@ resource "boundary_scope" "project" { auto_create_admin_role = true } +resource "boundary_credential_store_vault" "foo" { + name = "vault_store" + description = "My first Vault credential store!" + address = "http://localhost:55001" + token = "s.0ufRo6XEGU2jOqnIr7OlFYP5" + scope_id = boundary_scope.project.id +} + +resource "boundary_credential_library_vault" "foo" { + name = "vault_library" + description = "My first Vault credential library!" + credential_store_id = boundary_credential_store_vault.foo.id + path = "database/creds/opened" + http_method = "GET" +} + resource "boundary_host_catalog" "foo" { name = "test" description = "test catalog" @@ -57,4 +73,7 @@ resource "boundary_target" "foo" { host_set_ids = [ boundary_host_set.foo.id ] + credential_library_ids = [ + boundary_credential_library_vault.foo.id + ] } diff --git a/go.mod b/go.mod index e99f902d..a8859e84 100644 --- a/go.mod +++ b/go.mod @@ -13,17 +13,15 @@ require ( github.com/agext/levenshtein v1.2.3 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.545 // indirect github.com/aws/aws-sdk-go v1.35.4 // indirect - github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/circonus-labs/circonusllhist v0.1.4 // indirect github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe // indirect - github.com/coreos/go-oidc v2.2.1+incompatible // indirect github.com/golang/snappy v0.0.2 // indirect - github.com/hashicorp/boundary v0.2.2 - github.com/hashicorp/boundary/api v0.0.10 - github.com/hashicorp/boundary/sdk v0.0.4 - github.com/hashicorp/cap v0.0.0-20210518163718-e72205e8eaae + github.com/hashicorp/boundary v0.3.1-0.20210623022626-df35699c4ef2 + github.com/hashicorp/boundary/api v0.0.12-0.20210623022626-df35699c4ef2 + github.com/hashicorp/boundary/sdk v0.0.5-0.20210623022626-df35699c4ef2 + github.com/hashicorp/cap v0.1.0 github.com/hashicorp/go-immutable-radix v1.3.0 // indirect - github.com/hashicorp/go-kms-wrapping v0.6.1 + github.com/hashicorp/go-kms-wrapping v0.6.2 github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl/v2 v2.6.0 // indirect github.com/hashicorp/terraform-plugin-docs v0.4.0 @@ -33,11 +31,10 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/oklog/run v1.1.0 // indirect github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect - github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect github.com/prometheus/common v0.14.0 // indirect github.com/prometheus/procfs v0.2.0 // indirect github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c // indirect + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.62.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index fe86158d..c87e281b 100644 --- a/go.sum +++ b/go.sum @@ -158,10 +158,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-metrics v0.3.4/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.3.6 h1:x/tmtOF9cDBoXH7XoAGOz2qqm1DknFD1590XmD/DUJ8= -github.com/armon/go-metrics v0.3.6/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.3.8 h1:oOxq3KPj0WhCuy50EhzwiyMyG2ovRQZpZLXQuOh2a/M= -github.com/armon/go-metrics v0.3.8/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18= +github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= 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= @@ -198,8 +196,6 @@ github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= -github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc= github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= @@ -246,8 +242,6 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= -github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-oidc/v3 v3.0.0 h1:/mAA0XMgYJw2Uqm7WKGCsKnjitE/+A0FFbOmiRJm7LQ= github.com/coreos/go-oidc/v3 v3.0.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -261,6 +255,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= @@ -310,10 +305,9 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DP github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.11.0 h1:l4iX0RqNnx/pU7rY2DB/I+znuYY0K3x6Ywac6EIr0PA= github.com/fatih/color v1.11.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/favadi/protoc-go-inject-tag v1.1.0/go.mod h1:13goAxKedbu5IbfI0n2wIKh1CCgZOwPNZQd0igDWvko= @@ -364,8 +358,9 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= @@ -441,8 +436,9 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-metrics-stackdriver v0.2.0 h1:rbs2sxHAPn2OtUj9JdR/Gij1YKGl0BTVD0augB+HEjE= github.com/google/go-metrics-stackdriver v0.2.0/go.mod h1:KLcPyp3dWJAFD+yHisGlJSZktIsTjb50eB72U2YZ9K0= @@ -490,18 +486,16 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0 h1:IvO4FbbQL6n3v3M1rQNobZ61SGL0gJLdvKA5KETM7Xs= github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0/go.mod h1:d2gYTOTUQklu06xp0AJYYmRdTVU1VKrqhkYfYag2L08= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/boundary v0.2.1 h1:I4O1BrqR1R6O449k1b7FGCOFXwgRKxhZoK8CltvsBoo= -github.com/hashicorp/boundary v0.2.1/go.mod h1:FwX0UmYaj3OFlKrhhaMcT8FvEZk/y/1AnZgxsk4xQQM= -github.com/hashicorp/boundary v0.2.2 h1:Gel7cjlcild1AZGVfR+4d28MmqnTQ6F/CTOAyEAFyis= -github.com/hashicorp/boundary v0.2.2/go.mod h1:Gd+jRjPuPqFHZLlA7+llzK4BlHxilp37cJDp7xswAGU= -github.com/hashicorp/boundary/api v0.0.10 h1:WdKR5dZ7FvAW0QG05jyKWt9ESu1vT/4E2LIDiSVJ1ZI= -github.com/hashicorp/boundary/api v0.0.10/go.mod h1:mZfLjpD0hU2YMopuobjh+ZmWMRLl9sooiPr46gi64i4= -github.com/hashicorp/boundary/sdk v0.0.4 h1:l3uuXOL5ZQwYc63Bm8sLY36XGUlIN/4Q2r2SqkmGTyw= +github.com/hashicorp/boundary v0.3.1-0.20210623022626-df35699c4ef2 h1:hJ8qTqevchi/REQy7JEDS1phXlAWs+2j1Kl8b3XfCiI= +github.com/hashicorp/boundary v0.3.1-0.20210623022626-df35699c4ef2/go.mod h1:wTfjDEGdal4/942BfnJ5yXDjB+LFhsrYY0aah4Q6FOk= +github.com/hashicorp/boundary/api v0.0.11/go.mod h1:mZfLjpD0hU2YMopuobjh+ZmWMRLl9sooiPr46gi64i4= +github.com/hashicorp/boundary/api v0.0.12-0.20210623022626-df35699c4ef2 h1:UTlcgB8eCeyPX7HrsmyNmsBHTuLM9/ZNgwvEMYgrd2s= +github.com/hashicorp/boundary/api v0.0.12-0.20210623022626-df35699c4ef2/go.mod h1:mZfLjpD0hU2YMopuobjh+ZmWMRLl9sooiPr46gi64i4= github.com/hashicorp/boundary/sdk v0.0.4/go.mod h1:MQ8FJhseJrKpwvFDTn+AUAcJ1vdk1NKUxk7DxQgH7oQ= -github.com/hashicorp/cap v0.0.0-20210408110729-0dd65efe5473 h1:qEikB8/ychrgsll9feuGjVLt0qo5oktfyTWi7BLsUrg= -github.com/hashicorp/cap v0.0.0-20210408110729-0dd65efe5473/go.mod h1:hjMYKmFvk9AOL69NbqrahCHoMPZHRWqvRcdr7ciPU7g= -github.com/hashicorp/cap v0.0.0-20210518163718-e72205e8eaae h1:8tmylghjLoNNQdJdDddF54L38LakfoC/YfxJ7dGsAIE= -github.com/hashicorp/cap v0.0.0-20210518163718-e72205e8eaae/go.mod h1:VfBvK2ULRyqsuqAnjgZl7HJ7/CGMC7ro4H5eXiZuun8= +github.com/hashicorp/boundary/sdk v0.0.5-0.20210623022626-df35699c4ef2 h1:CPkNe8ipxlewl9UJ8GABAEBzm2ira5F9B8VK7CpkBTI= +github.com/hashicorp/boundary/sdk v0.0.5-0.20210623022626-df35699c4ef2/go.mod h1:MQ8FJhseJrKpwvFDTn+AUAcJ1vdk1NKUxk7DxQgH7oQ= +github.com/hashicorp/cap v0.1.0 h1:uBDfu9NDvmotza/mJW6vtQId+VYid9ztlTnDCW6YUWU= +github.com/hashicorp/cap v0.1.0/go.mod h1:VfBvK2ULRyqsuqAnjgZl7HJ7/CGMC7ro4H5eXiZuun8= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -511,8 +505,9 @@ github.com/hashicorp/dbassert v0.0.0-20200930125617-6218396928df/go.mod h1:+B5eZ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-bexpr v0.1.7 h1:z48qzCgJkvdnMO/LDy3XHNyCyxnHiFGx9uTKLv0jW2Y= -github.com/hashicorp/go-bexpr v0.1.7/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/hashicorp/eventlogger v0.0.0-20210523164657-c216620e1746/go.mod h1:LG0lqGlYKC9FD5m5Umh7FW8SeJlciNUZv400J4+j094= +github.com/hashicorp/go-bexpr v0.1.8 h1:ETfuLF1bBAuHW/Qg6l1xCdV8WJ7lfatLtJ1N1w0IsfE= +github.com/hashicorp/go-bexpr v0.1.8/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -531,8 +526,6 @@ github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39 github.com/hashicorp/go-hclog v0.13.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.15.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.0 h1:uCeOEwSWGMwhJUdpUjk+1cVKIEfGu2/1nFXukimi2MU= -github.com/hashicorp/go-hclog v0.16.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.1 h1:IVQwpTGNRRIHafnTs2dQLIk4ENtneRIEEJWOVDqz99o= github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -540,8 +533,9 @@ github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE= github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping v0.5.12/go.mod h1:yVIWtGOTh/cdGc++/NOlXLus0hJ19Lz4iFrpF6WsZh4= -github.com/hashicorp/go-kms-wrapping v0.6.1 h1:QwG2nGi3BXa8xpU5BRWKY5z6Xg1aqAErC9tAvDfMA3Y= github.com/hashicorp/go-kms-wrapping v0.6.1/go.mod h1:lxD7e9q7ZyCtDEP+tnMevsEvw3M0gmZnneAgv8BaO1Q= +github.com/hashicorp/go-kms-wrapping v0.6.2 h1:ryj5jVvecszFG4yL4/0jQxGK6g1LCNVLWRH8ZXjrYbY= +github.com/hashicorp/go-kms-wrapping v0.6.2/go.mod h1:aUzqzg9WEm5mEcQcNI431QLmxKzGL80cvwGQyVSP15Y= github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= @@ -555,7 +549,6 @@ github.com/hashicorp/go-plugin v1.4.0/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ3 github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs= github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= @@ -596,8 +589,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/shared-secure-libs v0.0.4/go.mod h1:xhtA0FH6AYYFOy0sir7u0O0zzdLi7ofU6oWxy+gjnuc= -github.com/hashicorp/shared-secure-libs v0.0.5 h1:qHd+wxqgvW+INIMU0uzF/Sw3+iaqAOk5SrcXzsThlvY= -github.com/hashicorp/shared-secure-libs v0.0.5/go.mod h1:xhtA0FH6AYYFOy0sir7u0O0zzdLi7ofU6oWxy+gjnuc= +github.com/hashicorp/shared-secure-libs v0.0.6 h1:QH22a9VbBP3b7v/e5In7uHTanimGPzq4oj0QHtVHhWI= +github.com/hashicorp/shared-secure-libs v0.0.6/go.mod h1:PH51kNtHA6kYV5Z8E3TOJM634iyp7IIxAMfLh0Dp35o= github.com/hashicorp/terraform-exec v0.12.0/go.mod h1:SGhto91bVRlgXQWcJ5znSz+29UZIa8kpBbkGwQ+g9E8= github.com/hashicorp/terraform-exec v0.13.3 h1:R6L2mNpDGSEqtLrSONN8Xth0xYwNrnEVzDz6LF/oJPk= github.com/hashicorp/terraform-exec v0.13.3/go.mod h1:SSg6lbUsVB3DmFyCPjBPklqf6EYGX0TlQ6QTxOlikDU= @@ -612,8 +605,9 @@ github.com/hashicorp/terraform-plugin-sdk/v2 v2.6.1 h1:OZ+Q7irJBDhb71XzMSPGJvTIW github.com/hashicorp/terraform-plugin-sdk/v2 v2.6.1/go.mod h1:72j8cKfs9IirGhPMXJJWLTvRUK4zATtrCOvs2avDlo8= github.com/hashicorp/vault/api v1.0.5-0.20200514164214-89b1987e38c2/go.mod h1:P8A7gn1a6j/7qQ4zOaWI6FC/Q9jQWEBRTOwZH33tC9o= github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= -github.com/hashicorp/vault/api v1.0.5-0.20200805123347-1ef507638af6 h1:ChHpobQse3kjVX7GmbpUFVqaMTf4lb1gcLrJltsUmS4= github.com/hashicorp/vault/api v1.0.5-0.20200805123347-1ef507638af6/go.mod h1:R3Umvhlxi2TN7Ex2hzOowyeNb+SfbVWI973N+ctaFMk= +github.com/hashicorp/vault/api v1.1.0 h1:QcxC7FuqEl0sZaIjcXB/kNEeBa0DH5z57qbWBvZwLC4= +github.com/hashicorp/vault/api v1.1.0/go.mod h1:R3Umvhlxi2TN7Ex2hzOowyeNb+SfbVWI973N+ctaFMk= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/vault/sdk v0.1.14-0.20200514144402-4bfac290c352/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= github.com/hashicorp/vault/sdk v0.1.14-0.20200514164214-89b1987e38c2/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= @@ -762,8 +756,6 @@ github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.6.0/go.mod h1:4vXEAYvW1fRQ2/FhZ78H73A60MHw1geSm145z2mdY1g= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= -github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -900,7 +892,6 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/oracle/oci-go-sdk v12.5.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= -github.com/ory/dockertest/v3 v3.6.0 h1:I6KNJ6izxGduLACQii2SP/g7GN0JM9Xfaik6aAVaw6Y= github.com/ory/dockertest/v3 v3.6.0/go.mod h1:4ZOpj8qBUmh8fcBSVzkH2bws2s91JdGvHUqan4GHEuQ= github.com/ory/dockertest/v3 v3.6.5 h1:mhNKFeVEHuvaYW+/u+59mLzM/6XXGjpaet/yApgv+yc= github.com/ory/dockertest/v3 v3.6.5/go.mod h1:iYKQSRlYrt/2s5fJWYdB98kCQG6g/LjBMvzEYii63vg= @@ -932,8 +923,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac h1:jWKYCNlX4J5s8M0nHYkh7Y7c9gRVDEb3mq51j5J0F5M= -github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -1023,6 +1012,7 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.1-0.20201006035406-b97b5ead31f7/go.mod h1:yk5b0mALVusDL5fMM6Rd1wgnoO5jUPhwsQ6LQAJTidQ= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -1048,6 +1038,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tencentcloud/tencentcloud-sdk-go v1.0.162/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI= github.com/tencentcloud/tencentcloud-sdk-go v3.0.171+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= @@ -1118,8 +1109,10 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.8.0 h1:CUhrE4N1rqSE6FM9ecihEjRkLQu8cDfgDyoOs83mEY4= +go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -1155,7 +1148,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= @@ -1190,8 +1182,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1245,7 +1237,6 @@ golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= @@ -1331,27 +1322,24 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1386,6 +1374,7 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1426,9 +1415,8 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1541,10 +1529,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.35.0-dev.0.20201218190559-666aea1fb34c/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1 h1:ARnQJNWxGyYJpdf/JXscNlQr/uv607ZPU9Z7ogHi+iI= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/internal/provider/const.go b/internal/provider/const.go index 70110382..c2393d1c 100644 --- a/internal/provider/const.go +++ b/internal/provider/const.go @@ -1,18 +1,18 @@ package provider const ( - // Key used for common SDK ID resource attribute + // IDKey is used for common SDK ID resource attribute IDKey = "id" - // Key used for common "name" resource attribute + // NameKey is used for common "name" resource attribute NameKey = "name" - // Key used for common "description" resource attribute + // DescriptionKey is used for common "description" resource attribute DescriptionKey = "description" - // Key used for common "scope_id" resource attribute + // ScopeIdKey is used for common "scope_id" resource attribute ScopeIdKey = "scope_id" - // Key used for common "type" resource attribute + // TypeKey is used for common "type" resource attribute TypeKey = "type" - // Key used for common "host_catalog_id" resource attribute + // HostCatalogIdKey is used for common "host_catalog_id" resource attribute HostCatalogIdKey = "host_catalog_id" - // Key used for common "auth_method_id" resource attribute + // AuthMethodIdKey is used for common "auth_method_id" resource attribute AuthMethodIdKey = "auth_method_id" ) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index f97a096d..9b1cf27e 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -54,20 +54,22 @@ func New() *schema.Provider { }, }, ResourcesMap: map[string]*schema.Resource{ - "boundary_account": resourceAccount(), - "boundary_account_password": resourceAccountPassword(), - "boundary_account_oidc": resourceAccountOidc(), - "boundary_auth_method": resourceAuthMethod(), - "boundary_auth_method_password": resourceAuthMethodPassword(), - "boundary_auth_method_oidc": resourceAuthMethodOidc(), - "boundary_group": resourceGroup(), - "boundary_host": resourceHost(), - "boundary_host_catalog": resourceHostCatalog(), - "boundary_host_set": resourceHostset(), - "boundary_role": resourceRole(), - "boundary_scope": resourceScope(), - "boundary_target": resourceTarget(), - "boundary_user": resourceUser(), + "boundary_account": resourceAccount(), + "boundary_account_password": resourceAccountPassword(), + "boundary_account_oidc": resourceAccountOidc(), + "boundary_auth_method": resourceAuthMethod(), + "boundary_auth_method_password": resourceAuthMethodPassword(), + "boundary_auth_method_oidc": resourceAuthMethodOidc(), + "boundary_credential_library_vault": resourceCredentialLibraryVault(), + "boundary_credential_store_vault": resourceCredentialStoreVault(), + "boundary_group": resourceGroup(), + "boundary_host": resourceHost(), + "boundary_host_catalog": resourceHostCatalog(), + "boundary_host_set": resourceHostset(), + "boundary_role": resourceRole(), + "boundary_scope": resourceScope(), + "boundary_target": resourceTarget(), + "boundary_user": resourceUser(), }, } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index f7470b12..71693dd5 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -19,7 +19,7 @@ var ( tcPassword = "passpass" tcPAUM = "ampw_0000000000" tcConfig = []controller.Option{ - controller.WithDefaultAuthMethodId(tcPAUM), + controller.WithDefaultPasswordAuthMethodId(tcPAUM), controller.WithDefaultLoginName(tcLoginName), controller.WithDefaultPassword(tcPassword), } diff --git a/internal/provider/resource_credential_library_vault.go b/internal/provider/resource_credential_library_vault.go new file mode 100644 index 00000000..4b84a54a --- /dev/null +++ b/internal/provider/resource_credential_library_vault.go @@ -0,0 +1,237 @@ +package provider + +import ( + "context" + "net/http" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/credentiallibraries" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + credentialStoreIdKey = "credential_store_id" + credentialLibraryVaultHttpMethodKey = "http_method" + credentialLibraryVaultHttpRequestBodyKey = "http_request_body" + credentialLibraryVaultPathKey = "path" +) + +var libraryVaultAttrs = []string{ + credentialLibraryVaultHttpMethodKey, + credentialLibraryVaultHttpRequestBodyKey, + credentialLibraryVaultPathKey, +} + +func resourceCredentialLibraryVault() *schema.Resource { + return &schema.Resource{ + Description: "The credential library for Vault resource allows you to configure a Boundary credential library for Vault.", + + CreateContext: resourceCredentialLibraryCreateVault, + ReadContext: resourceCredentialLibraryReadVault, + UpdateContext: resourceCredentialLibraryUpdateVault, + DeleteContext: resourceCredentialLibraryDeleteVault, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + IDKey: { + Description: "The ID of the Vault credential library.", + Type: schema.TypeString, + Computed: true, + }, + NameKey: { + Description: "The Vault credential library name. Defaults to the resource name.", + Type: schema.TypeString, + Optional: true, + }, + DescriptionKey: { + Description: "The Vault credential library description.", + Type: schema.TypeString, + Optional: true, + }, + credentialStoreIdKey: { + Description: "The ID of the credential store that this library belongs to.", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + credentialLibraryVaultHttpMethodKey: { + Description: "The HTTP method to use when contacting Vault", + Type: schema.TypeString, + Optional: true, + }, + credentialLibraryVaultHttpRequestBodyKey: { + Description: "The raw string to use in HTTP request to Vault", + Type: schema.TypeString, + Optional: true, + }, + credentialLibraryVaultPathKey: { + Description: "The Vault path to query", + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func setFromVaultCredentialLibraryResponseMap(d *schema.ResourceData, raw map[string]interface{}) error { + if err := d.Set(NameKey, raw[NameKey]); err != nil { + return err + } + if err := d.Set(DescriptionKey, raw[DescriptionKey]); err != nil { + return err + } + if err := d.Set(credentialStoreIdKey, raw[credentialStoreIdKey]); err != nil { + return err + } + + if attrsVal, ok := raw["attributes"]; ok { + attrs := attrsVal.(map[string]interface{}) + for _, v := range libraryVaultAttrs { + if err := d.Set(v, attrs[v]); err != nil { + return err + } + } + } + + d.SetId(raw["id"].(string)) + + return nil +} + +func resourceCredentialLibraryCreateVault(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + md := meta.(*metaData) + + var opts []credentiallibraries.Option + if v, ok := d.GetOk(NameKey); ok { + opts = append(opts, credentiallibraries.WithName(v.(string))) + } + if v, ok := d.GetOk(DescriptionKey); ok { + opts = append(opts, credentiallibraries.WithDescription(v.(string))) + } + if v, ok := d.GetOk(credentialLibraryVaultHttpMethodKey); ok { + opts = append(opts, credentiallibraries.WithVaultCredentialLibraryHttpMethod(v.(string))) + } + if v, ok := d.GetOk(credentialLibraryVaultHttpRequestBodyKey); ok { + opts = append(opts, credentiallibraries.WithVaultCredentialLibraryHttpRequestBody(v.(string))) + } + if v, ok := d.GetOk(credentialLibraryVaultPathKey); ok { + opts = append(opts, credentiallibraries.WithVaultCredentialLibraryPath(v.(string))) + } + + var credentialStoreId string + cid, ok := d.GetOk(credentialStoreIdKey) + if ok { + credentialStoreId = cid.(string) + } else { + return diag.Errorf("no credential store ID is set") + } + + client := credentiallibraries.NewClient(md.client) + cr, err := client.Create(ctx, credentialStoreId, opts...) + if err != nil { + return diag.Errorf("error creating credential library: %v", err) + } + if cr == nil { + return diag.Errorf("nil credential library after create") + } + + if err := setFromVaultCredentialLibraryResponseMap(d, cr.GetResponse().Map); err != nil { + return diag.FromErr(err) + } + + return nil +} + +func resourceCredentialLibraryReadVault(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + md := meta.(*metaData) + client := credentiallibraries.NewClient(md.client) + + cr, err := client.Read(ctx, d.Id()) + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound { + d.SetId("") + return nil + } + return diag.Errorf("error reading credential library: %v", err) + } + if cr == nil { + return diag.Errorf("credential library nil after read") + } + + if err := setFromVaultCredentialLibraryResponseMap(d, cr.GetResponse().Map); err != nil { + return diag.FromErr(err) + } + + return nil +} + +func resourceCredentialLibraryUpdateVault(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + md := meta.(*metaData) + client := credentiallibraries.NewClient(md.client) + + var opts []credentiallibraries.Option + if d.HasChange(NameKey) { + opts = append(opts, credentiallibraries.DefaultName()) + nameVal, ok := d.GetOk(NameKey) + if ok { + opts = append(opts, credentiallibraries.WithName(nameVal.(string))) + } + } + if d.HasChange(DescriptionKey) { + opts = append(opts, credentiallibraries.DefaultDescription()) + descVal, ok := d.GetOk(DescriptionKey) + if ok { + opts = append(opts, credentiallibraries.WithDescription(descVal.(string))) + } + } + if d.HasChange(credentialLibraryVaultHttpMethodKey) { + opts = append(opts, credentiallibraries.DefaultVaultCredentialLibraryHttpMethod()) + v, ok := d.GetOk(credentialLibraryVaultHttpMethodKey) + if ok { + opts = append(opts, credentiallibraries.WithVaultCredentialLibraryHttpMethod(v.(string))) + } + } + if d.HasChange(credentialLibraryVaultHttpRequestBodyKey) { + opts = append(opts, credentiallibraries.DefaultVaultCredentialLibraryHttpRequestBody()) + v, ok := d.GetOk(credentialLibraryVaultHttpRequestBodyKey) + if ok { + opts = append(opts, credentiallibraries.WithVaultCredentialLibraryHttpRequestBody(v.(string))) + } + } + if d.HasChange(credentialLibraryVaultPathKey) { + v, ok := d.GetOk(credentialLibraryVaultPathKey) + if ok { + opts = append(opts, credentiallibraries.WithVaultCredentialLibraryPath(v.(string))) + } + } + + if len(opts) > 0 { + opts = append(opts, credentiallibraries.WithAutomaticVersioning(true)) + aur, err := client.Update(ctx, d.Id(), 0, opts...) + if err != nil { + return diag.Errorf("error updating credential library: %v", err) + } + + if err := setFromVaultCredentialLibraryResponseMap(d, aur.GetResponse().Map); err != nil { + return diag.Errorf("error setting credential library from response: %v", err) + } + } + + return nil +} + +func resourceCredentialLibraryDeleteVault(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + md := meta.(*metaData) + client := credentiallibraries.NewClient(md.client) + + _, err := client.Delete(ctx, d.Id()) + if err != nil { + return diag.Errorf("error deleting credential library: %v", err) + } + + return nil +} diff --git a/internal/provider/resource_credential_library_vault_test.go b/internal/provider/resource_credential_library_vault_test.go new file mode 100644 index 00000000..ff87d74c --- /dev/null +++ b/internal/provider/resource_credential_library_vault_test.go @@ -0,0 +1,154 @@ +package provider + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/credentiallibraries" + "github.com/hashicorp/boundary/testing/controller" + "github.com/hashicorp/boundary/testing/vault" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +const ( + vaultCredResc = "boundary_credential_library_vault.example" + vaultCredLibName = "foo" + vaultCredLibDesc = "the foo" + vaultCredLibPath = "/foo/bar" + vaultCredLibMethodGet = "GET" + vaultCredLibMethodPost = "POST" + vaultCredLibRequestBody = "foobar" + vaultCredLibStringUpdate = "_random" +) + +var vaultCredLibResource = fmt.Sprintf(` +resource "boundary_credential_library_vault" "example" { + name = "%s" + description = "%s" + credential_store_id = boundary_credential_store_vault.example.id + path = "%s" + http_method = "%s" +}`, vaultCredLibName, + vaultCredLibDesc, + vaultCredLibPath, + vaultCredLibMethodGet) + +var vaultCredLibResourceUpdate = fmt.Sprintf(` +resource "boundary_credential_library_vault" "example" { + name = "%s" + description = "%s" + credential_store_id = boundary_credential_store_vault.example.id + path = "%s" + http_method = "%s" + http_request_body = "%s" +}`, vaultCredLibName+vaultCredLibStringUpdate, + vaultCredLibDesc+vaultCredLibStringUpdate, + vaultCredLibPath+vaultCredLibStringUpdate, + vaultCredLibMethodPost, + vaultCredLibRequestBody) + +func TestAccCredentialLibraryVault(t *testing.T) { + tc := controller.NewTestController(t, tcConfig...) + defer tc.Shutdown() + url := tc.ApiAddrs()[0] + + vc := vault.NewTestVaultServer(t) + _, token := vc.CreateToken(t) + credStoreRes := vaultCredStoreResource(vc, + vaultCredStoreName, + vaultCredStoreDesc, + vaultCredStoreNamespace, + "www.original.com", + token, + true) + + var provider *schema.Provider + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories(&provider), + CheckDestroy: testAccCheckAuthMethodResourceDestroy(t, provider), + Steps: []resource.TestStep{ + { + // create + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResource), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(vaultCredResc, NameKey, vaultCredLibName), + resource.TestCheckResourceAttr(vaultCredResc, DescriptionKey, vaultCredLibDesc), + resource.TestCheckResourceAttr(vaultCredResc, credentialLibraryVaultPathKey, vaultCredLibPath), + resource.TestCheckResourceAttr(vaultCredResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet), + resource.TestCheckResourceAttr(vaultCredResc, credentialLibraryVaultHttpRequestBodyKey, ""), + + testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredResc), + ), + }, + importStep(vaultCredResc), + + { + // update + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(vaultCredResc, NameKey, vaultCredLibName+vaultCredLibStringUpdate), + resource.TestCheckResourceAttr(vaultCredResc, DescriptionKey, vaultCredLibDesc+vaultCredLibStringUpdate), + resource.TestCheckResourceAttr(vaultCredResc, credentialLibraryVaultPathKey, vaultCredLibPath+vaultCredLibStringUpdate), + resource.TestCheckResourceAttr(vaultCredResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodPost), + resource.TestCheckResourceAttr(vaultCredResc, credentialLibraryVaultHttpRequestBodyKey, vaultCredLibRequestBody), + + testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredResc), + ), + }, + importStep(vaultCredResc), + }, + }) +} + +func testAccCheckCredentialLibraryVaultResourceExists(testProvider *schema.Provider, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("not found: %s", name) + } + + id := rs.Primary.ID + if id == "" { + return fmt.Errorf("no ID is set") + } + + md := testProvider.Meta().(*metaData) + c := credentiallibraries.NewClient(md.client) + if _, err := c.Read(context.Background(), id); err != nil { + return fmt.Errorf("got an error reading %q: %w", id, err) + } + + return nil + } +} + +func testAccCheckCredentialLibraryVaultResourceDestroy(t *testing.T, testProvider *schema.Provider) resource.TestCheckFunc { + return func(s *terraform.State) error { + if testProvider.Meta() == nil { + t.Fatal("got nil provider metadata") + } + md := testProvider.Meta().(*metaData) + + for _, rs := range s.RootModule().Resources { + switch rs.Type { + case "boundary_credential_library_vault": + id := rs.Primary.ID + + c := credentiallibraries.NewClient(md.client) + _, err := c.Read(context.Background(), id) + if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound { + return fmt.Errorf("didn't get a 404 when reading destroyed vault credential library %q: %v", id, err) + } + + default: + continue + } + } + return nil + } +} diff --git a/internal/provider/resource_credential_store_vault.go b/internal/provider/resource_credential_store_vault.go new file mode 100644 index 00000000..ddbd0d4a --- /dev/null +++ b/internal/provider/resource_credential_store_vault.go @@ -0,0 +1,346 @@ +package provider + +import ( + "context" + "net/http" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/credentialstores" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + credentialStoreVaultAddressKey = "address" + credentialStoreVaultNamespaceKey = "namespace" + credentialStoreVaultCaCertKey = "ca_cert" + credentialStoreVaultTlsServerNameKey = "tls_server_name" + credentialStoreVaultTlsSkipVerifyKey = "tls_skip_verify" + credentialStoreVaultTokenKey = "token" + credentialStoreVaultTokenHmacKey = "token_hmac" + credentialStoreVaultClientCertificateKey = "client_certificate" + credentialStoreVaultClientCertificateKeyKey = "client_certificate_key" + credentialStoreVaultClientCertificateKeyHmacKey = "client_certificate_key_hmac" + credentialStoreType = "vault" +) + +var storeVaultAttrs = []string{ + credentialStoreVaultAddressKey, + credentialStoreVaultNamespaceKey, + credentialStoreVaultCaCertKey, + credentialStoreVaultTlsServerNameKey, + credentialStoreVaultTlsSkipVerifyKey, + credentialStoreVaultTokenHmacKey, + credentialStoreVaultClientCertificateKey, + credentialStoreVaultClientCertificateKeyHmacKey, +} + +func resourceCredentialStoreVault() *schema.Resource { + return &schema.Resource{ + Description: "The credential store for Vault resource allows you to configure a Boundary credential store for Vault.", + + CreateContext: resourceCredentialStoreVaultCreate, + ReadContext: resourceCredentialStoreVaultRead, + UpdateContext: resourceCredentialStoreVaultUpdate, + DeleteContext: resourceCredentialStoreVaultDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + IDKey: { + Description: "The ID of the Vault credential store.", + Type: schema.TypeString, + Computed: true, + }, + NameKey: { + Description: "The Vault credential store name. Defaults to the resource name.", + Type: schema.TypeString, + Optional: true, + }, + DescriptionKey: { + Description: "The Vault credential store description.", + Type: schema.TypeString, + Optional: true, + }, + ScopeIdKey: { + Description: "The scope for this credential store", + Type: schema.TypeString, + ForceNew: true, + Required: true, + }, + credentialStoreVaultAddressKey: { + Description: "The address to Vault server", + Type: schema.TypeString, + Required: true, + }, + credentialStoreVaultNamespaceKey: { + Description: "The namespace within Vault to use", + Type: schema.TypeString, + Optional: true, + }, + credentialStoreVaultCaCertKey: { + Description: "The Vault CA certificate to use", + Type: schema.TypeString, + Optional: true, + }, + credentialStoreVaultTlsServerNameKey: { + Description: "The Vault TLS server name", + Type: schema.TypeString, + Optional: true, + }, + credentialStoreVaultTlsSkipVerifyKey: { + Description: "Whether or not to skip TLS verification", + Type: schema.TypeBool, + Optional: true, + }, + credentialStoreVaultTokenKey: { + Description: "The Vault token", + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + credentialStoreVaultTokenHmacKey: { + Description: "The Vault token hmac", + Type: schema.TypeString, + Computed: true, + }, + credentialStoreVaultClientCertificateKey: { + Description: "The Vault client certificate", + Type: schema.TypeString, + Optional: true, + }, + credentialStoreVaultClientCertificateKeyKey: { + Description: "The Vault client certificate key", + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + credentialStoreVaultClientCertificateKeyHmacKey: { + Description: "The Vault client certificate key hmac", + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func setFromVaultCredentialStoreResponseMap(d *schema.ResourceData, raw map[string]interface{}) error { + if err := d.Set(NameKey, raw[NameKey]); err != nil { + return err + } + if err := d.Set(DescriptionKey, raw[DescriptionKey]); err != nil { + return err + } + if err := d.Set(ScopeIdKey, raw[ScopeIdKey]); err != nil { + return err + } + + if attrsVal, ok := raw["attributes"]; ok { + attrs := attrsVal.(map[string]interface{}) + for _, v := range storeVaultAttrs { + if err := d.Set(v, attrs[v]); err != nil { + return err + } + } + } + + d.SetId(raw["id"].(string)) + + return nil +} + +func resourceCredentialStoreVaultCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + md := meta.(*metaData) + + var opts []credentialstores.Option + if v, ok := d.GetOk(NameKey); ok { + opts = append(opts, credentialstores.WithName(v.(string))) + } + if v, ok := d.GetOk(DescriptionKey); ok { + opts = append(opts, credentialstores.WithDescription(v.(string))) + } + if v, ok := d.GetOk(credentialStoreVaultAddressKey); ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreAddress(v.(string))) + } + if v, ok := d.GetOk(credentialStoreVaultNamespaceKey); ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreNamespace(v.(string))) + } + if v, ok := d.GetOk(credentialStoreVaultCaCertKey); ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreCaCert(v.(string))) + } + if v, ok := d.GetOk(credentialStoreVaultTlsServerNameKey); ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreTlsServerName(v.(string))) + } + if v, ok := d.GetOk(credentialStoreVaultTlsSkipVerifyKey); ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreTlsSkipVerify(v.(bool))) + } + if v, ok := d.GetOk(credentialStoreVaultClientCertificateKey); ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreClientCertificate(v.(string))) + } + if v, ok := d.GetOk(credentialStoreVaultClientCertificateKeyKey); ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreClientCertificateKey(v.(string))) + } + if v, ok := d.GetOk(credentialStoreVaultTokenKey); ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreToken(v.(string))) + } + + var scope string + gotScope, ok := d.GetOk(ScopeIdKey) + if ok { + scope = gotScope.(string) + } else { + return diag.Errorf("no scope is set") + } + + client := credentialstores.NewClient(md.client) + cr, err := client.Create(ctx, credentialStoreType, scope, opts...) + if err != nil { + return diag.Errorf("error creating credential store: %v", err) + } + if cr == nil { + return diag.Errorf("nil credential store after create") + } + + if err := setFromVaultCredentialStoreResponseMap(d, cr.GetResponse().Map); err != nil { + return diag.Errorf("error generating credential store from response map: %v", err) + } + + return nil +} + +func resourceCredentialStoreVaultRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + md := meta.(*metaData) + client := credentialstores.NewClient(md.client) + + cr, err := client.Read(ctx, d.Id()) + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound { + d.SetId("") + return nil + } + return diag.Errorf("error reading credential store: %v", err) + } + if cr == nil { + return diag.Errorf("credential store nil after read") + } + + if err := setFromVaultCredentialStoreResponseMap(d, cr.GetResponse().Map); err != nil { + return diag.Errorf("error generating credential store from response map: %v", err) + } + + return nil +} + +func resourceCredentialStoreVaultUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + md := meta.(*metaData) + client := credentialstores.NewClient(md.client) + + var opts []credentialstores.Option + if d.HasChange(NameKey) { + opts = append(opts, credentialstores.DefaultName()) + nameVal, ok := d.GetOk(NameKey) + if ok { + opts = append(opts, credentialstores.WithName(nameVal.(string))) + } + } + + if d.HasChange(DescriptionKey) { + opts = append(opts, credentialstores.DefaultDescription()) + descVal, ok := d.GetOk(DescriptionKey) + if ok { + opts = append(opts, credentialstores.WithDescription(descVal.(string))) + } + } + + if d.HasChange(credentialStoreVaultAddressKey) { + v, ok := d.GetOk(credentialStoreVaultAddressKey) + if ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreAddress(v.(string))) + } + } + + if d.HasChange(credentialStoreVaultNamespaceKey) { + opts = append(opts, credentialstores.DefaultVaultCredentialStoreNamespace()) + v, ok := d.GetOk(credentialStoreVaultNamespaceKey) + if ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreNamespace(v.(string))) + } + } + + if d.HasChange(credentialStoreVaultCaCertKey) { + opts = append(opts, credentialstores.DefaultVaultCredentialStoreCaCert()) + v, ok := d.GetOk(credentialStoreVaultCaCertKey) + if ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreCaCert(v.(string))) + } + } + + if d.HasChange(credentialStoreVaultTlsServerNameKey) { + opts = append(opts, credentialstores.DefaultVaultCredentialStoreTlsServerName()) + v, ok := d.GetOk(credentialStoreVaultTlsServerNameKey) + if ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreTlsServerName(v.(string))) + } + } + + if d.HasChange(credentialStoreVaultTlsSkipVerifyKey) { + opts = append(opts, credentialstores.DefaultVaultCredentialStoreTlsSkipVerify()) + v, ok := d.GetOk(credentialStoreVaultTlsSkipVerifyKey) + if ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreTlsSkipVerify(v.(bool))) + } + } + + if d.HasChange(credentialStoreVaultTokenKey) { + v, ok := d.GetOk(credentialStoreVaultTokenKey) + if ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreToken(v.(string))) + } + } + + if d.HasChange(credentialStoreVaultClientCertificateKey) { + opts = append(opts, credentialstores.DefaultVaultCredentialStoreClientCertificate()) + v, ok := d.GetOk(credentialStoreVaultClientCertificateKey) + if ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreClientCertificate(v.(string))) + } + } + + if d.HasChange(credentialStoreVaultClientCertificateKeyKey) { + opts = append(opts, credentialstores.DefaultVaultCredentialStoreClientCertificateKey()) + v, ok := d.GetOk(credentialStoreVaultClientCertificateKeyKey) + if ok { + opts = append(opts, credentialstores.WithVaultCredentialStoreClientCertificateKey(v.(string))) + } + } + + if len(opts) > 0 { + opts = append(opts, credentialstores.WithAutomaticVersioning(true)) + crUpdate, err := client.Update(ctx, d.Id(), 0, opts...) + if err != nil { + return diag.Errorf("error updating credential store: %v", err) + } + if crUpdate == nil { + return diag.Errorf("credential store nil after update") + } + + if err = setFromVaultCredentialStoreResponseMap(d, crUpdate.GetResponse().Map); err != nil { + return diag.Errorf("error generating credential store from response map: %v", err) + } + } + + return nil +} + +func resourceCredentialStoreVaultDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + md := meta.(*metaData) + client := credentialstores.NewClient(md.client) + + _, err := client.Delete(ctx, d.Id()) + if err != nil { + return diag.Errorf("error deleting credential store: %v", err) + } + + return nil +} diff --git a/internal/provider/resource_credential_store_vault_test.go b/internal/provider/resource_credential_store_vault_test.go new file mode 100644 index 00000000..3225d1e2 --- /dev/null +++ b/internal/provider/resource_credential_store_vault_test.go @@ -0,0 +1,191 @@ +package provider + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "fmt" + "net/http" + "strings" + "testing" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/credentialstores" + "github.com/hashicorp/boundary/testing/controller" + "github.com/hashicorp/boundary/testing/vault" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "golang.org/x/crypto/blake2b" +) + +const ( + vaultCredStoreResc = "boundary_credential_store_vault.example" + vaultCredStoreName = "foo" + vaultCredStoreDesc = "the foo" + vaultCredStoreNamespace = "default" + vaultCredStoreUpdate = "_random" +) + +func vaultCredStoreResource(vc *vault.TestVaultServer, name, description, namespace, tlsServer, token string, skipVerify bool) string { + caCert := fmt.Sprintf("\"%s\"", strings.Replace(string(vc.CaCert), "\n", `\n`, -1)) + clientCert := fmt.Sprintf("\"%s\"", strings.Replace(string(vc.ClientCert), "\n", `\n`, -1)) + clientKey := fmt.Sprintf("\"%s\"", strings.Replace(string(vc.ClientKey), "\n", `\n`, -1)) + + return fmt.Sprintf(` +resource "boundary_credential_store_vault" "example" { + name = "%s" + description = "%s" + scope_id = boundary_scope.proj1.id + address = "%s" + namespace = "%s" + ca_cert = %s + tls_server_name = "%s" + tls_skip_verify = "%v" + token = "%s" + client_certificate = %s + client_certificate_key = %s + depends_on = [boundary_role.proj1_admin] +}`, name, + description, + vc.Addr, + namespace, + caCert, + tlsServer, + skipVerify, + token, + clientCert, + clientKey) +} + +func tokenHmac(token, accessor string) string { + key := blake2b.Sum256([]byte(accessor)) + mac := hmac.New(sha256.New, key[:]) + _, _ = mac.Write([]byte(token)) + hmac := mac.Sum(nil) + return base64.RawURLEncoding.EncodeToString(hmac) +} + +func TestAccCredentialStoreVault(t *testing.T) { + tc := controller.NewTestController(t, tcConfig...) + defer tc.Shutdown() + url := tc.ApiAddrs()[0] + + vc := vault.NewTestVaultServer(t) + secret, token := vc.CreateToken(t) + tHmac := tokenHmac(token, secret.Auth.Accessor) + res := vaultCredStoreResource(vc, + vaultCredStoreName, + vaultCredStoreDesc, + vaultCredStoreNamespace, + "www.original.com", + token, + true) + + vcUpdate := vault.NewTestVaultServer(t, vault.WithTestVaultTLS(vault.TestClientTLS)) + secret, tokenUpdate := vcUpdate.CreateToken(t) + tHmacUpdate := tokenHmac(tokenUpdate, secret.Auth.Accessor) + resUpdate := vaultCredStoreResource(vcUpdate, + vaultCredStoreName+vaultCredStoreUpdate, + vaultCredStoreDesc+vaultCredStoreUpdate, + vaultCredStoreNamespace+vaultCredStoreUpdate, + "www.updated.com", + tokenUpdate, + false) + + var provider *schema.Provider + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories(&provider), + CheckDestroy: testAccCheckCredentialStoreVaultResourceDestroy(t, provider), + Steps: []resource.TestStep{ + { + // create + Config: testConfig(url, fooOrg, firstProjectFoo, res), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(vaultCredStoreResc, NameKey, vaultCredStoreName), + resource.TestCheckResourceAttr(vaultCredStoreResc, DescriptionKey, vaultCredStoreDesc), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultAddressKey, vc.Addr), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultNamespaceKey, vaultCredStoreNamespace), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultCaCertKey, ""), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTlsServerNameKey, "www.original.com"), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTlsSkipVerifyKey, "true"), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTokenKey, token), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTokenHmacKey, tHmac), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultClientCertificateKey, ""), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultClientCertificateKeyKey, ""), + + testAccCheckCredentialStoreVaultResourceExists(provider, vaultCredStoreResc), + ), + }, + importStep(vaultCredStoreResc, credentialStoreVaultTokenKey, credentialStoreVaultClientCertificateKeyKey), + { + // update + Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(vaultCredStoreResc, NameKey, vaultCredStoreName+vaultCredStoreUpdate), + resource.TestCheckResourceAttr(vaultCredStoreResc, DescriptionKey, vaultCredStoreDesc+vaultCredStoreUpdate), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultAddressKey, vcUpdate.Addr), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultNamespaceKey, vaultCredStoreNamespace+vaultCredStoreUpdate), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultCaCertKey, string(vcUpdate.CaCert)), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTlsServerNameKey, "www.updated.com"), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTlsSkipVerifyKey, "false"), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTokenKey, tokenUpdate), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTokenHmacKey, tHmacUpdate), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultClientCertificateKey, string(vcUpdate.ClientCert)), + resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultClientCertificateKeyKey, string(vcUpdate.ClientKey)), + + testAccCheckCredentialStoreVaultResourceExists(provider, vaultCredStoreResc), + ), + }, + importStep(vaultCredStoreResc, credentialStoreVaultTokenKey, credentialStoreVaultClientCertificateKeyKey), + }, + }) +} + +func testAccCheckCredentialStoreVaultResourceExists(testProvider *schema.Provider, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("not found: %s", name) + } + + id := rs.Primary.ID + if id == "" { + return fmt.Errorf("no ID is set") + } + + md := testProvider.Meta().(*metaData) + c := credentialstores.NewClient(md.client) + if _, err := c.Read(context.Background(), id); err != nil { + return fmt.Errorf("got an error reading %q: %w", id, err) + } + + return nil + } +} + +func testAccCheckCredentialStoreVaultResourceDestroy(t *testing.T, testProvider *schema.Provider) resource.TestCheckFunc { + return func(s *terraform.State) error { + if testProvider.Meta() == nil { + t.Fatal("got nil provider metadata") + } + md := testProvider.Meta().(*metaData) + + for _, rs := range s.RootModule().Resources { + switch rs.Type { + case "boundary_credential_store_vault": + id := rs.Primary.ID + + c := credentialstores.NewClient(md.client) + _, err := c.Read(context.Background(), id) + if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound { + return fmt.Errorf("didn't get a 404 when reading destroyed Vault credential store %q: %v", id, err) + } + default: + continue + } + } + return nil + } +} diff --git a/internal/provider/resource_group.go b/internal/provider/resource_group.go index 3afded73..60aa4ee1 100644 --- a/internal/provider/resource_group.go +++ b/internal/provider/resource_group.go @@ -196,13 +196,11 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter if err := d.Set(NameKey, name); err != nil { return diag.FromErr(err) } - } if d.HasChange(DescriptionKey) { if err := d.Set(DescriptionKey, desc); err != nil { return diag.FromErr(err) } - } // The above call may not actually happen, so we use d.Id() and automatic diff --git a/internal/provider/resource_target.go b/internal/provider/resource_target.go index 56c82d04..2abb4a10 100644 --- a/internal/provider/resource_target.go +++ b/internal/provider/resource_target.go @@ -13,6 +13,7 @@ import ( const ( targetHostSetIdsKey = "host_set_ids" + targetCredentialLibraryIdsKey = "credential_library_ids" targetDefaultPortKey = "default_port" targetSessionMaxSecondsKey = "session_max_seconds" targetSessionConnectionLimitKey = "session_connection_limit" @@ -72,6 +73,12 @@ func resourceTarget() *schema.Resource { Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + targetCredentialLibraryIdsKey: { + Description: "A list of credential library ID's.", + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, targetSessionMaxSecondsKey: { Type: schema.TypeInt, Optional: true, @@ -107,6 +114,9 @@ func setFromTargetResponseMap(d *schema.ResourceData, raw map[string]interface{} if err := d.Set(targetHostSetIdsKey, raw["host_set_ids"]); err != nil { return err } + if err := d.Set(targetCredentialLibraryIdsKey, raw["credential_library_ids"]); err != nil { + return err + } if err := d.Set(targetSessionMaxSecondsKey, raw["session_max_seconds"]); err != nil { return err } @@ -206,6 +216,15 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte } } + var credentialLibraryIds []string + if credentialLibraryIdsVal, ok := d.GetOk(targetCredentialLibraryIdsKey); ok { + list := credentialLibraryIdsVal.(*schema.Set).List() + credentialLibraryIds = make([]string, 0, len(list)) + for _, i := range list { + credentialLibraryIds = append(credentialLibraryIds, i.(string)) + } + } + workerFilterVal, ok := d.GetOk(targetWorkerFilterKey) if ok { workerFilterStr := workerFilterVal.(string) @@ -223,12 +242,22 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte } raw := tcr.GetResponse().Map + version := tcr.Item.Version if hostSetIds != nil { - tur, err := tc.SetHostSets(ctx, tcr.Item.Id, tcr.Item.Version, hostSetIds) + tur, err := tc.SetHostSets(ctx, tcr.Item.Id, version, hostSetIds) if err != nil { return diag.Errorf("error setting host sets on target: %v", err) } raw = tur.GetResponse().Map + version = tur.Item.Version + } + + if credentialLibraryIds != nil { + tur, err := tc.SetCredentialLibraries(ctx, tcr.Item.Id, version, credentialLibraryIds) + if err != nil { + return diag.Errorf("error setting credential libraries on target: %v", err) + } + raw = tur.GetResponse().Map } if err := setFromTargetResponseMap(d, raw); err != nil { @@ -400,6 +429,23 @@ func resourceTargetUpdate(ctx context.Context, d *schema.ResourceData, meta inte } } + if d.HasChange(targetCredentialLibraryIdsKey) { + var credentialLibraryIds []string + if credentialLibraryIdsVal, ok := d.GetOk(targetCredentialLibraryIdsKey); ok { + credLibsIds := credentialLibraryIdsVal.(*schema.Set).List() + for _, credLibId := range credLibsIds { + credentialLibraryIds = append(credentialLibraryIds, credLibId.(string)) + } + } + _, err := tc.SetCredentialLibraries(ctx, d.Id(), 0, credentialLibraryIds, targets.WithAutomaticVersioning(true)) + if err != nil { + return diag.Errorf("error updating credential libraries in target: %v", err) + } + if err := d.Set(targetCredentialLibraryIdsKey, credentialLibraryIds); err != nil { + return diag.FromErr(err) + } + } + return nil } diff --git a/internal/provider/resource_target_test.go b/internal/provider/resource_target_test.go index c469f308..d7e5e3b9 100644 --- a/internal/provider/resource_target_test.go +++ b/internal/provider/resource_target_test.go @@ -6,6 +6,8 @@ import ( "net/http" "testing" + "github.com/hashicorp/boundary/testing/vault" + "github.com/hashicorp/boundary/api" "github.com/hashicorp/boundary/api/targets" "github.com/hashicorp/boundary/testing/controller" @@ -20,7 +22,7 @@ const ( ) var ( - fooHostSet = ` + fooBarHostSet = ` resource "boundary_host_catalog" "foo" { type = "static" name = "test" @@ -49,10 +51,36 @@ resource "boundary_host_set" "foo" { host_catalog_id = boundary_host_catalog.foo.id host_ids = [ boundary_host.foo.id, + ] +} + +resource "boundary_host_set" "bar" { + name = "bar" + type = "static" + host_catalog_id = boundary_host_catalog.foo.id + host_ids = [ boundary_host.bar.id, ] }` + fooBarCredLibs = ` +resource "boundary_credential_library_vault" "foo" { + name = "foo" + description = "foo library" + credential_store_id = boundary_credential_store_vault.example.id + path = "foo/bar" + http_method = "GET" +} + +resource "boundary_credential_library_vault" "bar" { + name = "bar" + description = "bar library" + credential_store_id = boundary_credential_store_vault.example.id + path = "bar/foo" + http_method = "GET" +} +` + fooTarget = fmt.Sprintf(` resource "boundary_target" "foo" { name = "test" @@ -62,6 +90,9 @@ resource "boundary_target" "foo" { host_set_ids = [ boundary_host_set.foo.id ] + credential_library_ids = [ + boundary_credential_library_vault.foo.id + ] default_port = 22 depends_on = [boundary_role.proj1_admin] session_max_seconds = 6000 @@ -76,7 +107,10 @@ resource "boundary_target" "foo" { type = "tcp" scope_id = boundary_scope.proj1.id host_set_ids = [ - boundary_host_set.foo.id + boundary_host_set.bar.id + ] + credential_library_ids = [ + boundary_credential_library_vault.bar.id ] default_port = 80 depends_on = [boundary_role.proj1_admin] @@ -92,6 +126,16 @@ func TestAccTarget(t *testing.T) { defer tc.Shutdown() url := tc.ApiAddrs()[0] + vc := vault.NewTestVaultServer(t) + _, token := vc.CreateToken(t) + credStoreRes := vaultCredStoreResource(vc, + vaultCredStoreName, + vaultCredStoreDesc, + vaultCredStoreNamespace, + "www.original.com", + token, + true) + var provider *schema.Provider resource.Test(t, resource.TestCase{ ProviderFactories: providerFactories(&provider), @@ -99,7 +143,7 @@ func TestAccTarget(t *testing.T) { Steps: []resource.TestStep{ { // test create - Config: testConfig(url, fooOrg, firstProjectFoo, fooHostSet, fooTarget), + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, fooBarCredLibs, fooBarHostSet, fooTarget), Check: resource.ComposeTestCheckFunc( testAccCheckTargetResourceExists(provider, "boundary_target.foo"), resource.TestCheckResourceAttr("boundary_target.foo", DescriptionKey, fooTargetDescription), @@ -108,12 +152,14 @@ func TestAccTarget(t *testing.T) { resource.TestCheckResourceAttr("boundary_target.foo", targetSessionMaxSecondsKey, "6000"), resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "6"), resource.TestCheckResourceAttr("boundary_target.foo", targetWorkerFilterKey, `type == "foo"`), + testAccCheckTargetResourceHostSet(provider, "boundary_target.foo", []string{"boundary_host_set.foo"}), + testAccCheckTargetResourceCredLibs(provider, "boundary_target.foo", []string{"boundary_credential_library_vault.foo"}), ), }, importStep("boundary_target.foo"), { // test update - Config: testConfig(url, fooOrg, firstProjectFoo, fooHostSet, fooTargetUpdate), + Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, fooBarCredLibs, fooBarHostSet, fooTargetUpdate), Check: resource.ComposeTestCheckFunc( testAccCheckTargetResourceExists(provider, "boundary_target.foo"), resource.TestCheckResourceAttr("boundary_target.foo", DescriptionKey, fooTargetDescriptionUpdate), @@ -121,6 +167,8 @@ func TestAccTarget(t *testing.T) { resource.TestCheckResourceAttr("boundary_target.foo", targetSessionMaxSecondsKey, "7000"), resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "7"), resource.TestCheckResourceAttr("boundary_target.foo", targetWorkerFilterKey, `type == "bar"`), + testAccCheckTargetResourceHostSet(provider, "boundary_target.foo", []string{"boundary_host_set.bar"}), + testAccCheckTargetResourceCredLibs(provider, "boundary_target.foo", []string{"boundary_credential_library_vault.bar"}), ), }, importStep("boundary_target.foo"), @@ -128,7 +176,7 @@ func TestAccTarget(t *testing.T) { }) } -func testAccCheckTargetResourceMembersSet(testProvider *schema.Provider, name string, hostSets []string) resource.TestCheckFunc { +func testAccCheckTargetResourceHostSet(testProvider *schema.Provider, name string, hostSets []string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { @@ -141,7 +189,7 @@ func testAccCheckTargetResourceMembersSet(testProvider *schema.Provider, name st } // ensure host sets are declared in state - hostSetIDs := []string{} + var hostSetIDs []string for _, hostSetResourceID := range hostSets { hs, ok := s.RootModule().Resources[hostSetResourceID] if !ok { @@ -169,15 +217,80 @@ func testAccCheckTargetResourceMembersSet(testProvider *schema.Provider, name st return fmt.Errorf("no hostSets found on target") } - for _, stateHostSet := range t.Item.HostSetIds { + if len(t.Item.HostSetIds) != len(hostSetIDs) { + return fmt.Errorf("tf state and boundary have different number of host sets") + } + + for _, stateHostSetId := range t.Item.HostSetIds { ok := false for _, gotHostSetID := range hostSetIDs { - if gotHostSetID == stateHostSet { + if gotHostSetID == stateHostSetId { ok = true } } if !ok { - return fmt.Errorf("host set in state not set in boundary: %s", stateHostSet) + return fmt.Errorf("host set id in state not set in boundary: %s", stateHostSetId) + } + } + + return nil + } +} + +func testAccCheckTargetResourceCredLibs(testProvider *schema.Provider, name string, credLibs []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("target resource not found: %s", name) + } + + id := rs.Primary.ID + if id == "" { + return fmt.Errorf("target resource ID is not set") + } + + // ensure host sets are declared in state + var credLibIDs []string + for _, credLibResourceID := range credLibs { + cl, ok := s.RootModule().Resources[credLibResourceID] + if !ok { + return fmt.Errorf("credential library resource not found: %s", credLibResourceID) + } + + credLibID := cl.Primary.ID + if id == "" { + return fmt.Errorf("credential library resource ID not set") + } + + credLibIDs = append(credLibIDs, credLibID) + } + + // check boundary to ensure it matches + md := testProvider.Meta().(*metaData) + tgtsClient := targets.NewClient(md.client) + + t, err := tgtsClient.Read(context.Background(), id) + if err != nil { + return fmt.Errorf("Got an error when reading target %q: %v", id, err) + } + + if len(t.Item.CredentialLibraries) == 0 { + return fmt.Errorf("no credential libraries found on target") + } + + if len(t.Item.CredentialLibraries) != len(credLibIDs) { + return fmt.Errorf("tf state and boundary have different number of credential libraries") + } + + for _, stateCredLibId := range t.Item.CredentialLibraryIds { + ok := false + for _, gotCredLibID := range credLibIDs { + if gotCredLibID == stateCredLibId { + ok = true + } + } + if !ok { + return fmt.Errorf("credential library id in state not set in boundary: %s", stateCredLibId) } }