Compare commits

...
65 Commits
Author SHA1 Message Date
Damian Debkowski f95954c83d fix: update resource state even if a subtask fails on resource creation (#289)
Release / go-version (push) Successful in 9s
Release / release-notes (push) Successful in 32s
Release / Terraform Provider Release (push) Failing after 0s
2022-10-17 22:00:23 -07:00
Damian Debkowski 1882beff3c fix: improve error message for invalid provider credentials (#290) 2022-10-17 11:13:45 -07:00
Damian Debkowski 807db1bef1 Support Vault Credential Library Mapping Overrides (#287) 2022-10-13 09:53:42 -07:00
Louis Ruch 24bd9521f4 fix: remove plugin cleanup
Release / go-version (push) Successful in 3s
Release / release-notes (push) Successful in 4s
Release / Terraform Provider Release (push) Failing after 0s
2022-10-12 11:07:10 -07:00
dependabot[bot] ef38f78c46 build(deps): bump github.com/kr/pretty from 0.3.0 to 0.3.1
Bumps [github.com/kr/pretty](https://github.com/kr/pretty) from 0.3.0 to 0.3.1.
- [Release notes](https://github.com/kr/pretty/releases)
- [Commits](https://github.com/kr/pretty/compare/v0.3.0...v0.3.1)

---
updated-dependencies:
- dependency-name: github.com/kr/pretty
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-12 10:31:01 -07:00
CalebAlbers 6255122fba Add or update MPL 2.0 license 2022-10-12 10:30:46 -07:00
Louis Ruch 622fca1c0e chore: run go generate
Release / go-version (push) Successful in 2s
Release / release-notes (push) Successful in 4s
Release / Terraform Provider Release (push) Failing after 0s
2022-10-04 10:22:37 -07:00
Louis Ruch 8aa93ccb69 chore: update changelog 2022-10-04 10:22:37 -07:00
Louis Ruch 1b96f89728 feat(plugin): support plugin exec dir 2022-10-03 18:22:14 -07:00
Louis Ruch e90db9895c chore: Update plugin deps 2022-10-03 18:22:14 -07:00
Damian Debkowski 7ae01cc6e0 feat: add support for json credentials (#271) 2022-09-29 10:31:11 -07:00
Damian Debkowski a134747539 fix: remove deprecated field application_credential_sources_ids (#273) 2022-09-28 12:05:50 -07:00
Louis Ruch 893b150ec9 chore: Update Boundary deps 2022-09-28 11:23:48 -07:00
Louis Ruch a61a13193c bug(vault): Fix panic after token expires 2022-09-28 11:23:17 -07:00
Damian Debkowski 292204caf0 chore: generate missing docs (#274) 2022-09-21 13:45:56 -07:00
Damian Debkowski a2e04949be fix: set secret variables to senitive (#272) 2022-09-21 10:24:34 -07:00
Damian Debkowski 8fad44cc94 fix: set target credentials on update (#270) 2022-09-21 09:30:49 -07:00
Louis Ruch e0fb0a44e0 Remove default_role from role 2022-09-21 09:18:22 -07:00
Louis Ruch 767a9ce1e6 chore: Update changelog
Release / go-version (push) Successful in 3s
Release / release-notes (push) Successful in 4s
Release / Terraform Provider Release (push) Failing after 0s
2022-09-13 11:09:39 -07:00
Louis Ruch f1d8cbf6e6 chore: go fmt 2022-09-13 11:09:39 -07:00
Louis Ruch 072c046b67 chore: Update to go 1.19.1 2022-09-13 11:09:39 -07:00
Louis Ruch 82ae1e3cd7 chore: update deps 2022-09-13 11:09:39 -07:00
Louis Ruch 14bc6a4453 feat(target): Support ssh target 2022-09-13 11:09:39 -07:00
Louis Ruch dc3aa09688 chore: deprecate application_credential_source_ids (#260)
* chore: deprecate application_credential_source_ids
2022-09-07 17:57:26 -07:00
Louis Ruch d6fb6d4c16 chore: run go generate
Release / go-version (push) Successful in 2s
Release / release-notes (push) Successful in 6s
Release / Terraform Provider Release (push) Failing after 0s
2022-08-26 10:43:27 -07:00
Louis Ruch 7164883b1b chore: update changelog 2022-08-26 10:40:52 -07:00
Louis Ruch 7f600f1cd5 fixup! feat: Add support for static ssh_private_key credential 2022-08-26 10:30:52 -07:00
Louis Ruch 145c91305c chore: Add changelog 2022-08-26 10:30:52 -07:00
Louis Ruch deb02086e3 feat(vault-library): Add support for credential type 2022-08-26 10:30:52 -07:00
Louis Ruch 445398286f feat: Add support for static ssh_private_key credential 2022-08-26 10:30:52 -07:00
Louis Ruch 66aad543f6 chore: update deps 2022-08-26 10:30:52 -07:00
Louis Ruch b491b4d123 chore: update changelog
Release / go-version (push) Successful in 3s
Release / release-notes (push) Successful in 4s
Release / Terraform Provider Release (push) Failing after 0s
2022-08-10 14:42:37 -07:00
Louis Ruch bf34f12c56 chore: update docs 2022-08-10 14:42:37 -07:00
Louis Ruch 0f4126d7d0 chore: Update deps 2022-08-10 14:42:37 -07:00
Danielle 41a58f4db5 Username Password Credential Provider (#242)
* add username password resource

* fix a few bugs in the tf code as well as complete tests with copied crypto code

* address PR comments

* clean up all of the hmac testing code

* remove plan only in test and fix some descriptions per PR
2022-08-10 13:38:30 -04:00
Louis Ruch 56f858006e Fix deprecation messages (#235) 2022-08-03 13:11:51 -07:00
Danielle 48672fd050 Static Credential Store (#236)
* added static store plus single test

* revert gomod and gosum changes

* upgrade boundary version from 0.8.1 to 0.9.1

* merged 0.9.1 update and cleaned up test file

* added example for static store, cleanup store and testfile for PR

* update scope test to ignore auto_create_admin_role needed for static credential store test

* update schema function and test comment

* remove auto_create_admin_role in favor of depends_on, and remove all references to the former
2022-07-27 16:08:17 -04:00
dependabot[bot] 5859c55f43 build(deps): bump github.com/stretchr/testify from 1.7.2 to 1.8.0 (#229) 2022-07-27 18:26:59 +00:00
dependabot[bot] fe704abbfc build(deps): bump github.com/hashicorp/terraform-plugin-sdk/v2 (#237) 2022-07-27 18:11:27 +00:00
Danielle e734090217 upgrade boundary version from 0.8.1 to 0.9.1 (#238) 2022-07-25 16:53:20 -04:00
dependabot[bot] f9b5452b84 build(deps): bump github.com/hashicorp/terraform-plugin-docs
Bumps [github.com/hashicorp/terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs) from 0.9.0 to 0.13.0.
- [Release notes](https://github.com/hashicorp/terraform-plugin-docs/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-docs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-docs/compare/v0.9.0...v0.13.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-docs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-25 09:30:27 -07:00
dependabot[bot] 531dfa486a build(deps): bump github.com/hashicorp/go-secure-stdlib/parseutil
Bumps [github.com/hashicorp/go-secure-stdlib/parseutil](https://github.com/hashicorp/go-secure-stdlib) from 0.1.5 to 0.1.6.
- [Release notes](https://github.com/hashicorp/go-secure-stdlib/releases)
- [Commits](https://github.com/hashicorp/go-secure-stdlib/compare/awsutil/v0.1.5...awsutil/v0.1.6)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-secure-stdlib/parseutil
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-06 23:53:15 +00:00
dependabot[bot] 0bbc96fc11 build(deps): bump github.com/stretchr/testify from 1.7.1 to 1.7.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.1 to 1.7.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.1...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-06 23:47:05 +00:00
Louis Ruch f88fe43c12 chore(release): Update changelog
Release / go-version (push) Successful in 2s
Release / release-notes (push) Successful in 4s
Release / Terraform Provider Release (push) Failing after 0s
2022-06-06 11:29:45 -07:00
Louis Ruch 48694a6bab bug(kms): Generate all kms plugin assets during goreleaser pre build (#216) 2022-06-06 11:28:24 -07:00
Louis Ruch d9d4941e0c chore(release): Update changelog
Release / go-version (push) Successful in 3s
Release / release-notes (push) Successful in 4s
Release / Terraform Provider Release (push) Failing after 0s
2022-06-01 13:24:31 -07:00
Louis Ruch 6a704b2505 chore(release): Update changelog 2022-06-01 13:22:31 -07:00
Louis Ruch 55089ad19b chore(go): Update to go 1.18.1 (#208)
* chore(go): Update to go 1.18.1

* chore(deps): Update dependencies
2022-06-01 13:18:01 -07:00
Louis Ruch d8ab0733e8 bug(kms): Add support for all Boundary supported KMSes (#213)
After moving to go-kms-wrapping V2, the Boundary Terraform Provider
did not load all KMS plugins resulting in an error when trying to
create a wrapper for any type other than aead:

    Error: error reading wrappers from "recovery_kms_hcl":
    Error configuring kms: plugin is nil
2022-06-01 13:17:49 -07:00
Jeanne Angeles Franco 9acb1b6f7f Add missing env: key (#207)
Release / go-version (push) Successful in 3s
Release / release-notes (push) Successful in 5s
Release / Terraform Provider Release (push) Failing after 0s
2022-05-16 15:36:54 -07:00
Louis Ruch e6c8309c17 chore(release): Update changelog 2022-05-16 14:46:50 -07:00
Louis Ruch 84bea9c333 chore(deps): Update deps 2022-05-16 14:45:48 -07:00
Louis Ruch 8422393315 chore(changelog): Update changelog 2022-05-16 14:08:27 -07:00
Louis Ruch b8c92ee913 chore(docs): Update docs 2022-05-16 14:08:27 -07:00
Louis Ruch 23c8315fcc fix(role): flaky test regex 2022-05-16 14:08:27 -07:00
Louis Ruch b17b46d3ab fix(kms): Update kms wrapper calls to v2 2022-05-16 14:08:27 -07:00
Louis Ruch 414cdff86e chore(deps): Update deps 2022-05-16 14:08:27 -07:00
Louis Ruch dfaea8e259 fix(account): Update account deprecations
resource_account has been depreacted in favor of
resource_account_password instead of deprecating the individual password
based fields.
2022-05-16 14:08:27 -07:00
dependabot[bot] 1d345d18ac build(deps): bump actions/upload-artifact from 2 to 3 (#195)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-13 15:20:36 -07:00
modrakeandJeanne Angeles Franco 577b9eb017 [DO NOT MERGE] changes for releases-api launch (#196)
* changes for releases-api launch

* Update to include terraform registry manifest

Co-authored-by: Jeanne Angeles Franco <jeanne.franco@hashicorp.com>
2022-05-10 13:57:48 -04:00
Brian Flad e92a34b624 Migrate Provider Releases from TeamCity to GitHub Actions (#193)
Internal RFC References:

* ENGSRV-035
* ENGSRV-064
* SEC-036
* SEC-061
* TF-279

_Please note: This process can be adjusted to suit your needs, but it will require changes to the workflow setup._

The initial release workflow submitted here is triggered by pushing a semantic version tag prepended with a `v` to the repository. For example:

```shell
git switch main # or your release branch
git pull
git tag v1.2.3
git push origin v1.2.3
```

The most important distinction from the TeamCity release process is that the repository must be fully prepared for the release, including the `CHANGELOG.md` file. Providers can decide the most appropriate process to manage the CHANGELOG or any release notes as part of this new process. This initial workflow will automatically grab contents from the top of the CHANGELOG through the previous release tag header line. [Example provider CHANGELOG](https://github.com/hashicorp/terraform-provider-tls/blob/156ae39c7e55ee8597f859a77ae2db739527376b/CHANGELOG.md) and its [GitHub Release description](https://github.com/hashicorp/terraform-provider-tls/releases/tag/v3.3.0).

Please reach out if you have questions.
2022-04-20 13:55:23 -07:00
KeisukeYamashita 93d925fcfd Add validate func for auth method type key (#178)
* Add validate func for auth key

Signed-off-by: KeisukeYamashita <19yamashita15@gmail.com>

* Add oidc

Signed-off-by: KeisukeYamashita <19yamashita15@gmail.com>
2022-04-02 12:19:43 -07:00
Louis Ruch 214bc5224f chore(deps): Update dependencies (#189) 2022-04-01 16:04:45 -07:00
lrolaz 98a2d617f2 Map Issuer and Subject from attributes elements (#177) 2022-04-01 12:33:10 -07:00
Louis Ruch cbcbdc9955 feat(role): Support partial success in role create/update (#171)
* feat(role): Support partial success in role create/update
2022-01-31 16:03:41 -08:00
144 changed files with 7431 additions and 944 deletions

No files matched your search

+51
View File
@@ -0,0 +1,51 @@
name: Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*'
permissions:
contents: write
jobs:
go-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.go-version.outputs.version }}
steps:
- uses: actions/checkout@v3
- id: go-version
run: echo "::set-output name=version::$(cat ./.go-version)"
release-notes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Generate Release Notes
run: sed -n -e "1{/# /d;}" -e "2{/^$/d;}" -e "/# $(git describe --abbrev=0 --exclude="$(git describe --abbrev=0 --match='v*.*.*' --tags)" --match='v*.*.*' --tags | tr -d v)/q;p" CHANGELOG.md > release-notes.txt
- uses: actions/upload-artifact@v3
with:
name: release-notes
path: release-notes.txt
retention-days: 1
terraform-provider-release:
name: 'Terraform Provider Release'
needs: [go-version, release-notes]
uses: hashicorp/ghaction-terraform-provider-release/.github/workflows/hashicorp.yml@v2
secrets:
hc-releases-github-token: '${{ secrets.HASHI_RELEASES_GITHUB_TOKEN }}'
hc-releases-host-staging: '${{ secrets.HC_RELEASES_HOST_STAGING }}'
hc-releases-host-prod: '${{ secrets.HC_RELEASES_HOST_PROD }}'
hc-releases-key-prod: '${{ secrets.HC_RELEASES_KEY_PROD }}'
hc-releases-key-staging: '${{ secrets.HC_RELEASES_KEY_STAGING }}'
hc-releases-terraform-registry-sync-token: '${{ secrets.TF_PROVIDER_RELEASE_TERRAFORM_REGISTRY_SYNC_TOKEN }}'
setup-signore-github-token: '${{ secrets.HASHI_SIGNORE_GITHUB_TOKEN }}'
signore-client-id: '${{ secrets.SIGNORE_CLIENT_ID }}'
signore-client-secret: '${{ secrets.SIGNORE_CLIENT_SECRET }}'
with:
release-notes: true
setup-go-version: '${{ needs.go-version.outputs.version }}'
# Product Version (e.g. v1.2.3 or github.ref_name)
product-version: '${{ github.ref_name }}'
+3
View File
@@ -33,3 +33,6 @@ website/vendor
# Keep windows files with windows line endings
*.winfile eol=crlf
# Compilation outputs
/plugins/kms/assets/*/*/boundary-plugin*
+1 -1
View File
@@ -1 +1 @@
1.17.5
1.19.1
+75
View File
@@ -0,0 +1,75 @@
archives:
- files:
# Ensure only built binary is archived
- 'none*'
format: zip
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
before:
hooks:
- 'go mod download'
- './scripts/plugins-all.sh'
builds:
- # Binary naming only required for Terraform CLI 0.12
binary: '{{ .ProjectName }}_v{{ .Version }}_x5'
env:
- CGO_ENABLED=0
flags:
- -trimpath
goos:
- darwin
- freebsd
- linux
- windows
goarch:
- '386'
- amd64
- arm
- arm64
ignore:
- goarch: arm
goos: windows
- goarch: arm64
goos: freebsd
- goarch: arm64
goos: windows
ldflags:
- -s -w -X main.Version={{.Version}}
mod_timestamp: '{{ .CommitTimestamp }}'
checksum:
algorithm: sha256
extra_files:
- glob: 'terraform-registry-manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
publishers:
- checksum: true
# Terraform CLI 0.10 - 0.11 perform discovery via HTTP headers on releases.hashicorp.com
# For providers which have existed since those CLI versions, exclude
# discovery by setting the protocol version headers to 5.
cmd: hc-releases upload -product {{ .ProjectName }} -version {{ .Version }} -file={{ .ArtifactPath }}={{ .ArtifactName }} -header=x-terraform-protocol-version=5 -header=x-terraform-protocol-versions=5.0
env:
- HC_RELEASES_HOST={{ .Env.HC_RELEASES_HOST }}
- HC_RELEASES_KEY={{ .Env.HC_RELEASES_KEY }}
extra_files:
- glob: 'terraform-registry-manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
name: upload
signature: true
release:
extra_files:
- glob: 'terraform-registry-manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
ids:
- none
signs:
- args: ["sign", "--dearmor", "--file", "${artifact}", "--out", "${signature}"]
artifacts: checksum
cmd: signore
signature: ${artifact}.sig
- args: ["sign", "--dearmor", "--file", "${artifact}", "--out", "${signature}"]
artifacts: checksum
cmd: signore
id: key-id
signature: ${artifact}.72D7468F.sig
snapshot:
name_template: "{{ .Tag }}-next"
+2
View File
@@ -0,0 +1,2 @@
url_source_repository = "https://github.com/hashicorp/terraform-provider-boundary"
url_license = "https://github.com/hashicorp/terraform-provider-boundary/blob/main/LICENSE"
-1
View File
@@ -1 +0,0 @@
Protocol 5.0 only
+112
View File
@@ -1,3 +1,115 @@
# Boundary Terraform Provider CHANGELOG
Canonical reference for changes, improvements, and bugfixes for the Boundary Terraform provider.
## Next
### New and Improved
* Add support for setting mapping overrides for vault credential libraries
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/287)).
### Bug Fixes
* Improve error message when authenticating to boundary
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/290)).
* Set state before returning an error when creating a resource
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/289))
## 1.1.1 (October 12, 2022)
### Bug Fixes
* The plugin cleanup function is being called before the entire Terraform workflow is complete.
([Issue](https://github.com/hashicorp/terraform-provider-boundary/issues/282)),
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/285)).
## 1.1.0 (October 4, 2022)
### New and Improved
* Add support for JSON credentials
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/271)).
* Add support for setting the plugin execution directory from the config
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/280)).
### Deprecations/Changes
* Fix panic resulting from expired Vault credential store tokens
([Issue](https://github.com/hashicorp/terraform-provider-boundary/issues/279),
[PR](https://github.com/hashicorp/terraform-provider-boundary/pull/277)).
* Remove `application_credential_source_ids` of the `target` resource which was deprecated
in 1.0.12 ([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/273)).
* Remove `default_role` from the `role` resource, this schema was never supported and was
included mistakenly ([Issue](https://github.com/hashicorp/terraform-provider-boundary/issues/130),
[PR](https://github.com/hashicorp/terraform-provider-boundary/pull/269)).
## 1.0.12 (September 13, 2022)
### New and Improved
* Add support for SSH targets
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/264)).
### Deprecations/Changes
* Deprecate `application_credential_source_ids` of the `target` resource
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/260)).
## 1.0.11 (August 26, 2022)
### New and Improved
* Add support for SSH private key credentials
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/257)).
* Add support for credential type in Vault libraries
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/257)).
## 1.0.10 (August 10, 2022)
### New and Improved
* Adds support for static credential stores
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/236)).
* Adds support for username password credentials
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/242)).
## 1.0.9 (June 6, 2022)
### Bug Fixes
* The bug fix released in 1.0.8 to resolve the `plugin is nil` error only worked for
Linux AMD64. This was due to a build issue where the plugin binaries were only built for
Linux AMD64. Other platforms would receive an error similar to:
Error: error reading wrappers from "recovery_kms_hcl":
Error configuring kms: error fetching kms plugin rpc client:
fork/exec boundary-plugin-kms-awskms.gz: exec format error
([Issue](https://github.com/hashicorp/terraform-provider-boundary/issues/209)),
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/216)).
## 1.0.8 (June 1, 2022)
### Bug Fixes
* After moving to go-kms-wrapping V2, the Boundary Terraform Provider
did not load all KMS plugins resulting in an error when trying to
create a wrapper for any type other than 'aead':
Error: error reading wrappers from "recovery_kms_hcl":
Error configuring kms: plugin is nil
([Issue](https://github.com/hashicorp/terraform-provider-boundary/issues/209)),
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/213)).
## 1.0.7 (May 16, 2022)
### Deprecations/Changes
* Undoes an erroneous deprecation of the `login_name` and `password` fields in `resource_account_password` and `resource_account`.
Deprecates `resource_account` that was replaced with `resource_account_password`
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/201)).
## 1.0.6 (January 21, 2022)
### New and Improved
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.17
FROM golang:1.19
WORKDIR /go/src/terraform-provider-boundary
COPY . .
+2
View File
@@ -1,3 +1,5 @@
Copyright (c) 2020 HashiCorp, Inc.
Mozilla Public License Version 2.0
==================================
+5
View File
@@ -44,6 +44,11 @@ install-go: ~/.go/bin/go
./ci/goinstall.sh
dev:
GOOS=${GOOS} GOARCH=${GOARCH} ./scripts/plugins.sh
mkdir -p $(INSTALL_PATH)
go build -o $(INSTALL_PATH)/terraform-provider-boundary main.go
dev-no-plugins:
mkdir -p $(INSTALL_PATH)
go build -o $(INSTALL_PATH)/terraform-provider-boundary main.go
+1 -1
View File
@@ -9,7 +9,7 @@ Requirements
------------
- [Terraform](https://www.terraform.io/downloads.html) >= 0.12.x
- [Go](https://golang.org/doc/install) >= 1.17
- [Go](https://golang.org/doc/install) >= 1.19
Building The Provider
---------------------
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
VERSION="1.17.5"
VERSION="1.19.1"
[ -z "$GOROOT" ] && GOROOT="$HOME/.go"
[ -z "$GOPATH" ] && GOPATH="$HOME/go"
+8 -7
View File
@@ -27,13 +27,14 @@ provider "boundary" {
### Required
- **addr** (String) The base url of the Boundary API, e.g. "http://127.0.0.1:9200". If not set, it will be read from the "BOUNDARY_ADDR" env var.
- `addr` (String) The base url of the Boundary API, e.g. "http://127.0.0.1:9200". If not set, it will be read from the "BOUNDARY_ADDR" env var.
### Optional
- **auth_method_id** (String) The auth method ID e.g. ampw_1234567890
- **password_auth_method_login_name** (String) The auth method login name for password-style auth methods
- **password_auth_method_password** (String) The auth method password for password-style auth methods
- **recovery_kms_hcl** (String) Can be a heredoc string or a path on disk. If set, the string/file will be parsed as HCL and used with the recovery KMS mechanism. While this is set, it will override any other authentication information; the KMS mechanism will always be used. See Boundary's KMS docs for examples: https://boundaryproject.io/docs/configuration/kms
- **tls_insecure** (Boolean) When set to true, does not validate the Boundary API endpoint certificate
- **token** (String) The Boundary token to use, as a string or path on disk containing just the string. If set, the token read here will be used in place of authenticating with the auth method specified in "auth_method_id", although the recovery KMS mechanism will still override this. Can also be set with the BOUNDARY_TOKEN environment variable.
- `auth_method_id` (String) The auth method ID e.g. ampw_1234567890
- `password_auth_method_login_name` (String) The auth method login name for password-style auth methods
- `password_auth_method_password` (String) The auth method password for password-style auth methods
- `plugin_execution_dir` (String) Specifies a directory that the Boundary provider can use to write and execute its built-in plugins.
- `recovery_kms_hcl` (String) Can be a heredoc string or a path on disk. If set, the string/file will be parsed as HCL and used with the recovery KMS mechanism. While this is set, it will override any other authentication information; the KMS mechanism will always be used. See Boundary's KMS docs for examples: https://boundaryproject.io/docs/configuration/kms
- `tls_insecure` (Boolean) When set to true, does not validate the Boundary API endpoint certificate
- `token` (String) The Boundary token to use, as a string or path on disk containing just the string. If set, the token read here will be used in place of authenticating with the auth method specified in "auth_method_id", although the recovery KMS mechanism will still override this. Can also be set with the BOUNDARY_TOKEN environment variable.
+9 -37
View File
@@ -3,60 +3,32 @@
page_title: "boundary_account Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The account resource allows you to configure a Boundary account.
Deprecated: use boundary_account_password instead.
---
# boundary_account (Resource)
The account resource allows you to configure a Boundary account.
Deprecated: use `boundary_account_password` instead.
## 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_auth_method" "password" {
scope_id = boundary_scope.org.id
type = "password"
}
resource "boundary_account" "jeff" {
auth_method_id = boundary_auth_method.password.id
type = "password"
login_name = "jeff"
password = "$uper$ecure"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **auth_method_id** (String) The resource ID for the auth method.
- **type** (String) The resource type.
- `auth_method_id` (String) The resource ID for the auth method.
- `type` (String) The resource type.
### Optional
- **description** (String) The account description.
- **login_name** (String, Deprecated) The login name for this account.
- **name** (String) The account name. Defaults to the resource name.
- **password** (String, Deprecated) The account password. Only set on create, changes will not be reflected when updating account.
- `description` (String) The account description.
- `login_name` (String) The login name for this account.
- `name` (String) The account name. Defaults to the resource name.
- `password` (String, Sensitive) The account password. Only set on create, changes will not be reflected when updating account.
### Read-Only
- **id** (String) The ID of the account.
- `id` (String) The ID of the account.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_account.foo <my-id>
```
+6 -6
View File
@@ -17,17 +17,17 @@ The account resource allows you to configure a Boundary account.
### Required
- **auth_method_id** (String) The resource ID for the auth method.
- `auth_method_id` (String) The resource ID for the auth method.
### Optional
- **description** (String) The account description.
- **issuer** (String) The OIDC issuer.
- **name** (String) The account name. Defaults to the resource name.
- **subject** (String) The OIDC subject.
- `description` (String) The account description.
- `issuer` (String) The OIDC issuer.
- `name` (String) The account name. Defaults to the resource name.
- `subject` (String) The OIDC subject.
### Read-Only
- **id** (String) The ID of the account.
- `id` (String) The ID of the account.
+35 -7
View File
@@ -10,25 +10,53 @@ description: |-
The account resource allows you to configure a Boundary account.
## 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_auth_method" "password" {
scope_id = boundary_scope.org.id
type = "password"
}
resource "boundary_account_password" "jeff" {
auth_method_id = boundary_auth_method.password.id
type = "password"
login_name = "jeff"
password = "$uper$ecure"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **auth_method_id** (String) The resource ID for the auth method.
- **type** (String) The resource type.
- `auth_method_id` (String) The resource ID for the auth method.
- `type` (String) The resource type.
### Optional
- **description** (String) The account description.
- **login_name** (String, Deprecated) The login name for this account.
- **name** (String) The account name. Defaults to the resource name.
- **password** (String, Deprecated) The account password. Only set on create, changes will not be reflected when updating account.
- `description` (String) The account description.
- `login_name` (String) The login name for this account.
- `name` (String) The account name. Defaults to the resource name.
- `password` (String, Sensitive) The account password. Only set on create, changes will not be reflected when updating account.
### Read-Only
- **id** (String) The ID of the account.
- `id` (String) The ID of the account.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_account_password.foo <my-id>
```
+7 -7
View File
@@ -32,19 +32,19 @@ resource "boundary_auth_method" "password" {
### Required
- **scope_id** (String) The scope ID.
- **type** (String) The resource type.
- `scope_id` (String) The scope ID.
- `type` (String) The resource type.
### Optional
- **description** (String) The auth method description.
- **min_login_name_length** (Number, Deprecated) The minimum login name length.
- **min_password_length** (Number, Deprecated) The minimum password length.
- **name** (String) The auth method name. Defaults to the resource name.
- `description` (String) The auth method description.
- `min_login_name_length` (Number, Deprecated) The minimum login name length.
- `min_password_length` (Number, Deprecated) The minimum password length.
- `name` (String) The auth method name. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the account.
- `id` (String) The ID of the account.
## Import
+20 -20
View File
@@ -17,31 +17,31 @@ The OIDC auth method resource allows you to configure a Boundary auth_method_oid
### Required
- **scope_id** (String) The scope ID.
- `scope_id` (String) The scope ID.
### Optional
- **account_claim_maps** (List of String) Account claim maps for the to_claim of sub.
- **allowed_audiences** (List of String) Audiences for which the provider responses will be allowed
- **api_url_prefix** (String) The API prefix to use when generating callback URLs for the provider. Should be set to an address at which the provider can reach back to the controller.
- **callback_url** (String) The URL that should be provided to the IdP for callbacks.
- **claims_scopes** (List of String) Claims scopes.
- **client_id** (String) The client ID assigned to this auth method from the provider.
- **client_secret** (String) The secret key assigned to this auth method from the provider. Once set, only the hash will be kept and the original value can be removed from configuration.
- **client_secret_hmac** (String) The HMAC of the client secret returned by the Boundary controller, which is used for comparison after initial setting of the value.
- **description** (String) The auth method description.
- **disable_discovered_config_validation** (Boolean) Disables validation logic ensuring that the OIDC provider's information from its discovery endpoint matches the information here. The validation is only performed at create or update time.
- **idp_ca_certs** (List of String) A list of CA certificates to trust when validating the IdP's token signatures.
- **is_primary_for_scope** (Boolean) When true, makes this auth method the primary auth method for the scope in which it resides. The primary auth method for a scope means the the user will be automatically created when they login using an OIDC account.
- **issuer** (String) The issuer corresponding to the provider, which must match the issuer field in generated tokens.
- **max_age** (Number) The max age to provide to the provider, indicating how much time is allowed to have passed since the last authentication before the user is challenged again.
- **name** (String) The auth method name. Defaults to the resource name.
- **signing_algorithms** (List of String) Allowed signing algorithms for the provider's issued tokens.
- **state** (String) Can be one of 'inactive', 'active-private', or 'active-public'. Currently automatically set to active-public.
- **type** (String) The type of auth method; hardcoded.
- `account_claim_maps` (List of String) Account claim maps for the to_claim of sub.
- `allowed_audiences` (List of String) Audiences for which the provider responses will be allowed
- `api_url_prefix` (String) The API prefix to use when generating callback URLs for the provider. Should be set to an address at which the provider can reach back to the controller.
- `callback_url` (String) The URL that should be provided to the IdP for callbacks.
- `claims_scopes` (List of String) Claims scopes.
- `client_id` (String) The client ID assigned to this auth method from the provider.
- `client_secret` (String, Sensitive) The secret key assigned to this auth method from the provider. Once set, only the hash will be kept and the original value can be removed from configuration.
- `client_secret_hmac` (String) The HMAC of the client secret returned by the Boundary controller, which is used for comparison after initial setting of the value.
- `description` (String) The auth method description.
- `disable_discovered_config_validation` (Boolean) Disables validation logic ensuring that the OIDC provider's information from its discovery endpoint matches the information here. The validation is only performed at create or update time.
- `idp_ca_certs` (List of String) A list of CA certificates to trust when validating the IdP's token signatures.
- `is_primary_for_scope` (Boolean) When true, makes this auth method the primary auth method for the scope in which it resides. The primary auth method for a scope means the the user will be automatically created when they login using an OIDC account.
- `issuer` (String) The issuer corresponding to the provider, which must match the issuer field in generated tokens.
- `max_age` (Number) The max age to provide to the provider, indicating how much time is allowed to have passed since the last authentication before the user is challenged again.
- `name` (String) The auth method name. Defaults to the resource name.
- `signing_algorithms` (List of String) Allowed signing algorithms for the provider's issued tokens.
- `state` (String) Can be one of 'inactive', 'active-private', or 'active-public'. Currently automatically set to active-public.
- `type` (String) The type of auth method; hardcoded.
### Read-Only
- **id** (String) The ID of the auth method.
- `id` (String) The ID of the auth method.
+7 -7
View File
@@ -17,18 +17,18 @@ The auth method resource allows you to configure a Boundary auth_method_password
### Required
- **scope_id** (String) The scope ID.
- `scope_id` (String) The scope ID.
### Optional
- **description** (String) The auth method description.
- **min_login_name_length** (Number) The minimum login name length.
- **min_password_length** (Number) The minimum password length.
- **name** (String) The auth method name. Defaults to the resource name.
- **type** (String) The resource type, hardcoded per resource
- `description` (String) The auth method description.
- `min_login_name_length` (Number) The minimum login name length.
- `min_password_length` (Number) The minimum password length.
- `name` (String) The auth method name. Defaults to the resource name.
- `type` (String) The resource type, hardcoded per resource
### Read-Only
- **id** (String) The ID of the account.
- `id` (String) The ID of the account.
+69
View File
@@ -0,0 +1,69 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_credential_json Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The json credential resource allows you to congiure a credential using a json object.
---
# boundary_credential_json (Resource)
The json credential resource allows you to congiure a credential using a json object.
## Example Usage
```terraform
resource "boundary_scope" "org" {
name = "organization_one"
description = "global 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_static" "example" {
name = "example_static_credential_store"
description = "My first static credential store!"
scope_id = boundary_scope.project.id
}
resource "boundary_credential_json" "example" {
name = "example_json"
description = "My first json credential!"
credential_store_id = boundary_credential_store_static.example.id
object = file("~/object.json") # change to valid json file
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `credential_store_id` (String) The credential store in which to save this json credential.
- `object` (String, Sensitive) The object for the this json credential. Either values encoded with the "jsonencode" function, pre-escaped JSON string, or a file
### Optional
- `description` (String) The description of this json credential.
- `name` (String) The name of this json credential. Defaults to the resource name.
### Read-Only
- `id` (String) The ID of this json credential.
- `object_hmac` (String) The object hmac.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_credential_json.example_json <my-id>
```
+36 -7
View File
@@ -56,6 +56,33 @@ resource "boundary_credential_library_vault" "bar" {
}
EOT
}
resource "boundary_credential_library_vault" "baz" {
name = "baz"
description = "vault username password credential with mapping overrides"
credential_store_id = boundary_credential_store_vault.foo.id
path = "my/secret/baz" # change to Vault backend path
http_method = "GET"
credential_type = "username_password"
credential_mapping_overrides = {
password_attribute = "alternative_password_label"
username_attribute = "alternative_username_label"
}
}
resource "boundary_credential_library_vault" "quz" {
name = "quz"
description = "vault ssh private key credential with mapping overrides"
credential_store_id = boundary_credential_store_vault.foo.id
path = "my/secret/quz" # change to Vault backend path
http_method = "GET"
credential_type = "ssh_private_key"
credential_mapping_overrides = {
private_key_attribute = "alternative_key_label"
private_key_passphrase_attribute = "alternative_passphrase_label"
username_attribute = "alternative_username_label"
}
}
```
<!-- schema generated by tfplugindocs -->
@@ -63,19 +90,21 @@ EOT
### Required
- **credential_store_id** (String) The ID of the credential store that this library belongs to.
- **path** (String) The path in Vault to request credentials from.
- `credential_store_id` (String) The ID of the credential store that this library belongs to.
- `path` (String) The path in Vault to request credentials from.
### Optional
- **description** (String) The Vault credential library description.
- **http_method** (String) The HTTP method the library uses when requesting credentials from Vault. Defaults to 'GET'
- **http_request_body** (String) The body of the HTTP request the library sends to Vault when requesting credentials. Only valid if `http_method` is set to `POST`.
- **name** (String) The Vault credential library name. Defaults to the resource name.
- `credential_mapping_overrides` (Map of String) The credential mapping override.
- `credential_type` (String) The type of credential the library generates.
- `description` (String) The Vault credential library description.
- `http_method` (String) The HTTP method the library uses when requesting credentials from Vault. Defaults to 'GET'
- `http_request_body` (String) The body of the HTTP request the library sends to Vault when requesting credentials. Only valid if `http_method` is set to `POST`.
- `name` (String) The Vault credential library name. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the Vault credential library.
- `id` (String) The ID of the Vault credential library.
## Import
@@ -0,0 +1,74 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_credential_ssh_private_key Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The SSH private key credential resource allows you to configure a credential using a username, private key and optional passphrase.
---
# boundary_credential_ssh_private_key (Resource)
The SSH private key credential resource allows you to configure a credential using a username, private key and optional passphrase.
## Example Usage
```terraform
resource "boundary_scope" "org" {
name = "organization_one"
description = "global 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_static" "example" {
name = "example_static_credential_store"
description = "My first static credential store!"
scope_id = boundary_scope.project.id
}
resource "boundary_credential_ssh_private_key" "example" {
name = "example_ssh_private_key"
description = "My first ssh private key credential!"
credential_store_id = boundary_credential_store_static.example.id
username = "my-username"
private_key = file("~/.ssh/id_rsa") # change to valid SSH Private Key
private_key_passphrase = "optional-passphrase" # change to the passphrase of the Private Key if required
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `credential_store_id` (String) ID of the credential store this credential belongs to.
- `private_key` (String, Sensitive) The private key associated with the credential.
- `username` (String) The username associated with the credential.
### Optional
- `description` (String) The description of the credential.
- `name` (String) The name of the credential. Defaults to the resource name.
- `private_key_passphrase` (String, Sensitive) The passphrase of the private key associated with the credential.
### Read-Only
- `id` (String) The ID of the credential.
- `private_key_hmac` (String) The private key hmac.
- `private_key_passphrase_hmac` (String) The private key passphrase hmac.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_credential_ssh_private_key.example_ssh_private_key <my-id>
```
+60
View File
@@ -0,0 +1,60 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_credential_store_static Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The static credential store resource allows you to configure a Boundary static credential store.
---
# boundary_credential_store_static (Resource)
The static credential store resource allows you to configure a Boundary static credential store.
## Example Usage
```terraform
resource "boundary_scope" "org" {
name = "organization_one"
description = "global 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_static" "example" {
name = "example_static_credential_store"
description = "My first static credential store!"
scope_id = boundary_scope.project.id
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `scope_id` (String) The scope for this credential store.
### Optional
- `description` (String) The static credential store description.
- `name` (String) The static credential store name. Defaults to the resource name.
### Read-Only
- `id` (String) The ID of the static credential store.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_credential_store_static.example_static_credential_store <my-id>
```
+14 -14
View File
@@ -42,26 +42,26 @@ resource "boundary_credential_store_vault" "example" {
### Required
- **address** (String) The address to Vault server. This should be a complete URL such as 'https://127.0.0.1:8200'
- **scope_id** (String) The scope for this credential store.
- **token** (String, Sensitive) A token used for accessing Vault.
- `address` (String) The address to Vault server. This should be a complete URL such as 'https://127.0.0.1:8200'
- `scope_id` (String) The scope for this credential store.
- `token` (String, Sensitive) A token used for accessing Vault.
### Optional
- **ca_cert** (String) A PEM-encoded CA certificate to verify the Vault server's TLS certificate.
- **client_certificate** (String) A PEM-encoded client certificate to use for TLS authentication to the Vault server.
- **client_certificate_key** (String, Sensitive) A PEM-encoded private key matching the client certificate from 'client_certificate'.
- **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) Name to use as the SNI host when connecting to Vault via TLS.
- **tls_skip_verify** (Boolean) Whether or not to skip TLS verification.
- `ca_cert` (String) A PEM-encoded CA certificate to verify the Vault server's TLS certificate.
- `client_certificate` (String) A PEM-encoded client certificate to use for TLS authentication to the Vault server.
- `client_certificate_key` (String, Sensitive) A PEM-encoded private key matching the client certificate from 'client_certificate'.
- `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) Name to use as the SNI host when connecting to Vault via TLS.
- `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.
- `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
@@ -0,0 +1,71 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_credential_username_password Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The username/password credential resource allows you to configure a credential using a username and password pair.
---
# boundary_credential_username_password (Resource)
The username/password credential resource allows you to configure a credential using a username and password pair.
## Example Usage
```terraform
resource "boundary_scope" "org" {
name = "organization_one"
description = "global 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_static" "example" {
name = "example_static_credential_store"
description = "My first static credential store!"
scope_id = boundary_scope.project.id
}
resource "boundary_credential_username_password" "example" {
name = "example_username_password"
description = "My first username password credential!"
credential_store_id = boundary_credential_store_static.example.id
username = "my-username"
password = "my-password"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `credential_store_id` (String) The credential store in which to save this username/password credential.
- `password` (String, Sensitive) The password of this username/password credential.
- `username` (String) The username of this username/password credential.
### Optional
- `description` (String) The description of this username/password credential.
- `name` (String) The name of this username/password credential. Defaults to the resource name.
### Read-Only
- `id` (String) The ID of this username/password credential.
- `password_hmac` (String) The password hmac.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_credential_username_password.example_username_password <my-id>
```
+5 -5
View File
@@ -69,17 +69,17 @@ resource "boundary_group" "example" {
### Required
- **scope_id** (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- `scope_id` (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
### Optional
- **description** (String) The group description.
- **member_ids** (Set of String) Resource IDs for group members, these are most likely boundary users.
- **name** (String) The group name. Defaults to the resource name.
- `description` (String) The group description.
- `member_ids` (Set of String) Resource IDs for group members, these are most likely boundary users.
- `name` (String) The group name. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the group.
- `id` (String) The ID of the group.
## Import
+9 -8
View File
@@ -3,12 +3,12 @@
page_title: "boundary_host Resource - terraform-provider-boundary"
subcategory: ""
description: |-
Deprecated: use resource_host_static instead.
Deprecated: use boundary_host_static instead.
---
# boundary_host (Resource)
Deprecated: use `resource_host_static` instead.
Deprecated: use `boundary_host_static` instead.
## Example Usage
@@ -36,6 +36,7 @@ resource "boundary_host_catalog" "static" {
}
resource "boundary_host" "example" {
type = "static"
name = "example_host"
description = "My first host!"
address = "10.0.0.1"
@@ -48,18 +49,18 @@ resource "boundary_host" "example" {
### Required
- **host_catalog_id** (String)
- **type** (String) The type of host
- `host_catalog_id` (String)
- `type` (String) The type of host
### Optional
- **address** (String) The static address of the host resource as `<IP>` (note: port assignment occurs in the target resource definition, do not add :port here) or a domain name.
- **description** (String) The host description.
- **name** (String) The host name. Defaults to the resource name.
- `address` (String) The static address of the host resource as `<IP>` (note: port assignment occurs in the target resource definition, do not add :port here) or a domain name.
- `description` (String) The host description.
- `name` (String) The host name. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the host.
- `id` (String) The ID of the host.
## Import
+7 -7
View File
@@ -3,12 +3,12 @@
page_title: "boundary_host_catalog Resource - terraform-provider-boundary"
subcategory: ""
description: |-
Deprecated: use resource_host_catalog_static instead.
Deprecated: use boundary_host_catalog_static instead.
---
# boundary_host_catalog (Resource)
Deprecated: use `resource_host_catalog_static` instead.
Deprecated: use `boundary_host_catalog_static` instead.
## Example Usage
@@ -41,17 +41,17 @@ resource "boundary_host_catalog" "example" {
### Required
- **scope_id** (String) The scope ID in which the resource is created.
- **type** (String) The host catalog type. Only `static` is supported.
- `scope_id` (String) The scope ID in which the resource is created.
- `type` (String) The host catalog type. Only `static` is supported.
### Optional
- **description** (String) The host catalog description.
- **name** (String) The host catalog name. Defaults to the resource name.
- `description` (String) The host catalog description.
- `name` (String) The host catalog name. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the host catalog.
- `id` (String) The ID of the host catalog.
## Import
+81 -12
View File
@@ -10,30 +10,99 @@ description: |-
The host catalog resource allows you to configure a Boundary plugin-type host catalog. Host catalogs are always part of a project, so a project resource should be used inline or you should have the project ID in hand to successfully configure a host catalog.
## Example Usage
```terraform
resource "boundary_scope" "org" {
name = "organization_one"
description = "My first scope!"
scope_id = boundary_scope.global.id
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
}
# For more information about the aws plugin, please visit here:
# https://github.com/hashicorp/boundary-plugin-host-aws
#
# For more information about aws users, please visit here:
# https://learn.hashicorp.com/tutorials/boundary/aws-host-catalogs?in=boundary/oss-access-management#configure-terraform-and-iam-user-privileges
resource "boundary_host_catalog_plugin" "aws_example" {
name = "My aws catalog"
description = "My first host catalog!"
scope_id = boundary_scope.project.id
plugin_name = "aws"
attributes_json = jsonencode({ "region" = "us-east-1" })
# recommended to pass in aws secrets using a file() or using environment variables
# the secrets below must be generated in aws by creating a aws iam user with programmatic access
secrets_json = jsonencode({
"access_key_id" = "aws_access_key_id_value",
"secret_access_key" = "aws_secret_access_key_value"
})
}
# For more information about the azure plugin, please visit here:
# https://github.com/hashicorp/boundary-plugin-host-azure
#
# For more information about azure ad applications, please visit here:
# https://learn.hashicorp.com/tutorials/boundary/azure-host-catalogs#register-a-new-azure-ad-application-1
resource "boundary_host_catalog_plugin" "azure_example" {
name = "My azure catalog"
description = "My second host catalog!"
scope_id = boundary_scope.project.id
plugin_name = "azure"
# the attributes below must be generated in azure by creating an ad application
attributes_json = jsonencode({
"disable_credential_rotation" = "true",
"tenant_id" = "ARM_TENANT_ID",
"subscription_id" = "ARM_SUBSCRIPTION_ID",
"client_id" = "ARM_CLIENT_ID"
})
# recommended to pass in aws secrets using a file() or using environment variables
# the secrets below must be generated in azure by creating an ad application
secrets_json = jsonencode({
"secret_value" = "ARM_CLIENT_SECRET"
})
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String) The scope ID in which the resource is created.
- `scope_id` (String) The scope ID in which the resource is created.
### Optional
- **attributes_json** (String) The attributes for the host catalog. Either values encoded with the "jsonencode" function, pre-escaped JSON string, or a file:// or env:// path. Set to a string "null" or remove the block to clear all attributes in the host catalog.
- **description** (String) The host catalog description.
- **internal_force_update** (String) Internal only. Used to force update so that we can always check the value of secrets.
- **internal_hmac_used_for_secrets_config_hmac** (String) Internal only. The Boundary-provided HMAC used to calculate the current value of the HMAC'd config. Used for drift detection.
- **internal_secrets_config_hmac** (String) Internal only. HMAC of (serverSecretsHmac + config secrets). Used for proper secrets handling.
- **name** (String) The host catalog name. Defaults to the resource name.
- **plugin_id** (String) The ID of the plugin that should back the resource. This or plugin_name must be defined.
- **plugin_name** (String) The name of the plugin that should back the resource. This or plugin_id must be defined.
- **secrets_hmac** (String) The HMAC'd secrets value returned from the server.
- **secrets_json** (String, Sensitive) The secrets for the host catalog. Either values encoded with the "jsonencode" function, pre-escaped JSON string, or a file:// or env:// path. Set to a string "null" to clear any existing values. NOTE: Unlike "attributes_json", removing this block will NOT clear secrets from the host catalog; this allows injecting secrets for one call, then removing them for storage.
- `attributes_json` (String) The attributes for the host catalog. Either values encoded with the "jsonencode" function, pre-escaped JSON string, or a file:// or env:// path. Set to a string "null" or remove the block to clear all attributes in the host catalog.
- `description` (String) The host catalog description.
- `internal_force_update` (String) Internal only. Used to force update so that we can always check the value of secrets.
- `internal_hmac_used_for_secrets_config_hmac` (String) Internal only. The Boundary-provided HMAC used to calculate the current value of the HMAC'd config. Used for drift detection.
- `internal_secrets_config_hmac` (String) Internal only. HMAC of (serverSecretsHmac + config secrets). Used for proper secrets handling.
- `name` (String) The host catalog name. Defaults to the resource name.
- `plugin_id` (String) The ID of the plugin that should back the resource. This or plugin_name must be defined.
- `plugin_name` (String) The name of the plugin that should back the resource. This or plugin_id must be defined.
- `secrets_hmac` (String) The HMAC'd secrets value returned from the server.
- `secrets_json` (String, Sensitive) The secrets for the host catalog. Either values encoded with the "jsonencode" function, pre-escaped JSON string, or a file:// or env:// path. Set to a string "null" to clear any existing values. NOTE: Unlike "attributes_json", removing this block will NOT clear secrets from the host catalog; this allows injecting secrets for one call, then removing them for storage.
### Read-Only
- **id** (String) The ID of the host catalog.
- `id` (String) The ID of the host catalog.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_host_catalog_plugin.foo <my-id>
```
+33 -4
View File
@@ -10,22 +10,51 @@ description: |-
The static host catalog resource allows you to configure a Boundary static-type host catalog. Host catalogs are always part of a project, so a project resource should be used inline or you should have the project ID in hand to successfully configure a host catalog.
## Example Usage
```terraform
resource "boundary_scope" "org" {
name = "organization_one"
description = "My first scope!"
scope_id = boundary_scope.global.id
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_host_catalog_static" "example" {
name = "My catalog"
description = "My first host catalog!"
scope_id = boundary_scope.project.id
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String) The scope ID in which the resource is created.
- `scope_id` (String) The scope ID in which the resource is created.
### Optional
- **description** (String) The host catalog description.
- **name** (String) The host catalog name. Defaults to the resource name.
- `description` (String) The host catalog description.
- `name` (String) The host catalog name. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the host catalog.
- `id` (String) The ID of the host catalog.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_host_catalog_static.foo <my-id>
```
+15 -12
View File
@@ -3,12 +3,12 @@
page_title: "boundary_host_set Resource - terraform-provider-boundary"
subcategory: ""
description: |-
Deprecated: use resource_host_set_static instead.
Deprecated: use boundary_host_set_static instead.
---
# boundary_host_set (Resource)
Deprecated: use `resource_host_set_static` instead.
Deprecated: use `boundary_host_set_static` instead.
## Example Usage
@@ -29,10 +29,12 @@ resource "boundary_scope" "project" {
}
resource "boundary_host_catalog" "static" {
type = "static"
scope_id = boundary_scope.project.id
}
resource "boundary_host" "1" {
resource "boundary_host" "first" {
type = "static"
name = "host_1"
description = "My first host!"
address = "10.0.0.1"
@@ -40,7 +42,8 @@ resource "boundary_host" "1" {
scope_id = boundary_scope.project.id
}
resource "boundary_host" "2" {
resource "boundary_host" "second" {
type = "static"
name = "host_2"
description = "My second host!"
address = "10.0.0.2"
@@ -52,8 +55,8 @@ resource "boundary_host_set" "web" {
host_catalog_id = boundary_host_catalog.static.id
type = "static"
host_ids = [
boundary_host.1.id,
boundary_host.2.id,
boundary_host.first.id,
boundary_host.second.id,
]
}
```
@@ -63,18 +66,18 @@ resource "boundary_host_set" "web" {
### Required
- **host_catalog_id** (String) The catalog for the host set.
- **type** (String) The type of host set
- `host_catalog_id` (String) The catalog for the host set.
- `type` (String) The type of host set
### Optional
- **description** (String) The host set description.
- **host_ids** (Set of String) The list of host IDs contained in this set.
- **name** (String) The host set name. Defaults to the resource name.
- `description` (String) The host set description.
- `host_ids` (Set of String) The list of host IDs contained in this set.
- `name` (String) The host set name. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the host set.
- `id` (String) The ID of the host set.
## Import
+120 -8
View File
@@ -10,26 +10,138 @@ description: |-
The host_set_plugin resource allows you to configure a Boundary host set. Host sets are always part of a host catalog, so a host catalog resource should be used inline or you should have the host catalog ID in hand to successfully configure a host set.
## 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
}
# For more information about the aws plugin, please visit here:
# https://github.com/hashicorp/boundary-plugin-host-aws
#
# For more information about aws users, please visit here:
# https://learn.hashicorp.com/tutorials/boundary/aws-host-catalogs?in=boundary/oss-access-management#configure-terraform-and-iam-user-privileges
resource "boundary_host_catalog_plugin" "aws_example" {
name = "My aws catalog"
description = "My first host catalog!"
scope_id = boundary_scope.project.id
plugin_name = "aws"
attributes_json = jsonencode({ "region" = "us-east-1" })
# recommended to pass in aws secrets using a file() or using environment variables
# the secrets below must be generated in aws by creating a aws iam user with programmatic access
secrets_json = jsonencode({
"access_key_id" = "aws_access_key_id_value",
"secret_access_key" = "aws_secret_access_key_value"
})
}
resource "boundary_host_set_plugin" "web" {
name = "My web host set plugin"
host_catalog_id = boundary_host_catalog_plugin.aws_exmaple.id
attributes_json = jsonencode({ "filters" = "tag:service-type=web" })
}
resource "boundary_host_set_plugin" "foobar" {
name = "My foobar host set plugin"
host_catalog_id = boundary_host_catalog_plugin.aws_exmaple.id
preferred_endpoints = ["cidr:54.0.0.0/8"]
attributes_json = jsonencode({
"filters" = "tag-key=foo",
"filters" = "tag-key=bar"
})
}
resource "boundary_host_set_plugin" "launch" {
name = "My launch host set plugin"
host_catalog_id = boundary_host_catalog_plugin.aws_exmaple.id
sync_interval_seconds = 60
attributes_json = jsonencode({
"filters" = "tag:development=prod,dev",
"filters" = "launch-time=2022-01-04T*"
})
}
# For more information about the azure plugin, please visit here:
# https://github.com/hashicorp/boundary-plugin-host-azure
#
# For more information about azure ad applications, please visit here:
# https://learn.hashicorp.com/tutorials/boundary/azure-host-catalogs#register-a-new-azure-ad-application-1
resource "boundary_host_catalog_plugin" "azure_example" {
name = "My azure catalog"
description = "My second host catalog!"
scope_id = boundary_scope.project.id
plugin_name = "azure"
# the attributes below must be generated in azure by creating an ad application
attributes_json = jsonencode({
"disable_credential_rotation" = "true",
"tenant_id" = "ARM_TENANT_ID",
"subscription_id" = "ARM_SUBSCRIPTION_ID",
"client_id" = "ARM_CLIENT_ID"
})
# recommended to pass in aws secrets using a file() or using environment variables
# the secrets below must be generated in azure by creating an ad application
secrets_json = jsonencode({
"secret_value" = "ARM_CLIENT_SECRET"
})
}
resource "boundary_host_set_plugin" "database" {
name = "My database host set plugin"
host_catalog_id = boundary_host_catalog_plugin.azure_exmaple.id
attributes_json = jsonencode({ "filter" = "tagName eq 'service-type' and tagValue eq 'database'" })
}
resource "boundary_host_set_plugin" "foodev" {
name = "My foodev host set plugin"
host_catalog_id = boundary_host_catalog_plugin.azure_exmaple.id
preferred_endpoints = ["cidr:54.0.0.0/8"]
sync_interval_seconds = 60
attributes_json = jsonencode({
"filter" = "tagName eq 'tag-key' and tagValue eq 'foo'",
"filter" = "tagName eq 'application' and tagValue eq 'dev'",
})
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **host_catalog_id** (String) The catalog for the host set.
- `host_catalog_id` (String) The catalog for the host set.
### Optional
- **attributes_json** (String) The attributes for the host set. Either values encoded with the "jsonencode" function, pre-escaped JSON string, or a file:// or env:// path. Set to a string "null" or remove the block to clear all attributes in the host set.
- **description** (String) The host set description.
- **name** (String) The host set name. Defaults to the resource name.
- **preferred_endpoints** (List of String) The ordered list of preferred endpoints.
- **sync_interval_seconds** (Number) The value to set for the sync interval seconds.
- **type** (String) The type of host set
- `attributes_json` (String) The attributes for the host set. Either values encoded with the "jsonencode" function, pre-escaped JSON string, or a file:// or env:// path. Set to a string "null" or remove the block to clear all attributes in the host set.
- `description` (String) The host set description.
- `name` (String) The host set name. Defaults to the resource name.
- `preferred_endpoints` (List of String) The ordered list of preferred endpoints.
- `sync_interval_seconds` (Number) The value to set for the sync interval seconds.
- `type` (String) The type of host set
### Read-Only
- **id** (String) The ID of the host set.
- `id` (String) The ID of the host set.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_host_set_plugin.foo <my-id>
```
+55 -6
View File
@@ -10,24 +10,73 @@ description: |-
The host_set_static resource allows you to configure a Boundary host set. Host sets are always part of a host catalog, so a host catalog resource should be used inline or you should have the host catalog ID in hand to successfully configure a host set.
## 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_host_catalog_static" "example" {
scope_id = boundary_scope.project.id
}
resource "boundary_host_static" "first" {
name = "host_1"
description = "My first host!"
address = "10.0.0.1"
host_catalog_id = boundary_host_catalog_static.example.id
}
resource "boundary_host_static" "second" {
name = "host_2"
description = "My second host!"
address = "10.0.0.2"
host_catalog_id = boundary_host_catalog_static.example.id
}
resource "boundary_host_set_static" "web" {
host_catalog_id = boundary_host_catalog_static.example.id
host_ids = [
boundary_host_static.first.id,
boundary_host_static.second.id,
]
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **host_catalog_id** (String) The catalog for the host set.
- `host_catalog_id` (String) The catalog for the host set.
### Optional
- **description** (String) The host set description.
- **host_ids** (Set of String) The list of host IDs contained in this set.
- **name** (String) The host set name. Defaults to the resource name.
- **type** (String) The type of host set
- `description` (String) The host set description.
- `host_ids` (Set of String) The list of host IDs contained in this set.
- `name` (String) The host set name. Defaults to the resource name.
- `type` (String) The type of host set
### Read-Only
- **id** (String) The ID of the host set.
- `id` (String) The ID of the host set.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_host_set_static.foo <my-id>
```
+42 -6
View File
@@ -10,24 +10,60 @@ description: |-
The static host resource allows you to configure a Boundary static host. Hosts are always part of a project, so a project resource should be used inline or you should have the project ID in hand to successfully configure a host.
## 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_host_catalog_static" "example" {
name = "My catalog"
description = "My first host catalog!"
scope_id = boundary_scope.project.id
}
resource "boundary_host_static" "example" {
name = "example_host"
description = "My first host!"
address = "10.0.0.1"
host_catalog_id = boundary_host_catalog_static.example.id
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **host_catalog_id** (String)
- `host_catalog_id` (String)
### Optional
- **address** (String) The static address of the host resource as `<IP>` (note: port assignment occurs in the target resource definition, do not add :port here) or a domain name.
- **description** (String) The host description.
- **name** (String) The host name. Defaults to the resource name.
- **type** (String) The type of host
- `address` (String) The static address of the host resource as `<IP>` (note: port assignment occurs in the target resource definition, do not add :port here) or a domain name.
- `description` (String) The host description.
- `name` (String) The host name. Defaults to the resource name.
- `type` (String) The type of host
### Read-Only
- **id** (String) The ID of the host.
- `id` (String) The ID of the host.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_host_static.foo <my-id>
```
+5 -5
View File
@@ -17,16 +17,16 @@ The managed group resource allows you to configure a Boundary group.
### Required
- **auth_method_id** (String) The resource ID for the auth method.
- **filter** (String) Boolean expression to filter the workers for this managed group.
- `auth_method_id` (String) The resource ID for the auth method.
- `filter` (String) Boolean expression to filter the workers for this managed group.
### Optional
- **description** (String) The managed group description.
- **name** (String) The managed group name. Defaults to the resource name.
- `description` (String) The managed group description.
- `name` (String) The managed group name. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the group.
- `id` (String) The ID of the group.
+7 -8
View File
@@ -122,20 +122,19 @@ resource "boundary_role" "readonly" {
### Required
- **scope_id** (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- `scope_id` (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
### Optional
- **default_role** (Boolean) Indicates that the role containing this value is the default role (that is, has the id 'r_default'), which triggers some specialized behavior to allow it to be imported and managed.
- **description** (String) The role description.
- **grant_scope_id** (String)
- **grant_strings** (Set of String) A list of stringified grants for the role.
- **name** (String) The role name. Defaults to the resource name.
- **principal_ids** (Set of String) A list of principal (user or group) IDs to add as principals on the role.
- `description` (String) The role description.
- `grant_scope_id` (String)
- `grant_strings` (Set of String) A list of stringified grants for the role.
- `name` (String) The role name. Defaults to the resource name.
- `principal_ids` (Set of String) A list of principal (user or group) IDs to add as principals on the role.
### Read-Only
- **id** (String) The ID of the role.
- `id` (String) The ID of the role.
## Import
+7 -7
View File
@@ -65,19 +65,19 @@ resource "boundary_role" "org_admin" {
### Required
- **scope_id** (String) The scope ID containing the sub scope resource.
- `scope_id` (String) The scope ID containing the sub scope resource.
### Optional
- **auto_create_admin_role** (Boolean) If set, when a new scope is created, the provider will not disable the functionality that automatically creates a role in the new scope and gives permissions to manage the scope to the provider's user. Marking this true makes for simpler HCL but results in role resources that are unmanaged by Terraform.
- **auto_create_default_role** (Boolean) Only relevant when creating an org scope. If set, when a new scope is created, the provider will not disable the functionality that automatically creates a role in the new scope and gives listing of scopes and auth methods and the ability to authenticate to the anonymous user. Marking this true makes for simpler HCL but results in role resources that are unmanaged by Terraform.
- **description** (String) The scope description.
- **global_scope** (Boolean) Indicates that the scope containing this value is the global scope, which triggers some specialized behavior to allow it to be imported and managed.
- **name** (String) The scope name. Defaults to the resource name.
- `auto_create_admin_role` (Boolean) If set, when a new scope is created, the provider will not disable the functionality that automatically creates a role in the new scope and gives permissions to manage the scope to the provider's user. Marking this true makes for simpler HCL but results in role resources that are unmanaged by Terraform.
- `auto_create_default_role` (Boolean) Only relevant when creating an org scope. If set, when a new scope is created, the provider will not disable the functionality that automatically creates a role in the new scope and gives listing of scopes and auth methods and the ability to authenticate to the anonymous user. Marking this true makes for simpler HCL but results in role resources that are unmanaged by Terraform.
- `description` (String) The scope description.
- `global_scope` (Boolean) Indicates that the scope containing this value is the global scope, which triggers some specialized behavior to allow it to be imported and managed.
- `name` (String) The scope name. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the scope.
- `id` (String) The ID of the scope.
## Import
+31 -12
View File
@@ -47,6 +47,7 @@ resource "boundary_credential_library_vault" "foo" {
credential_store_id = boundary_credential_store_vault.foo.id
path = "my/secret/foo" # change to Vault backend path
http_method = "GET"
credential_type = "username_password"
}
resource "boundary_host_catalog" "foo" {
@@ -57,6 +58,7 @@ resource "boundary_host_catalog" "foo" {
}
resource "boundary_host" "foo" {
type = "static"
name = "foo"
host_catalog_id = boundary_host_catalog.foo.id
scope_id = boundary_scope.project.id
@@ -64,6 +66,7 @@ resource "boundary_host" "foo" {
}
resource "boundary_host" "bar" {
type = "static"
name = "bar"
host_catalog_id = boundary_host_catalog.foo.id
scope_id = boundary_scope.project.id
@@ -71,6 +74,7 @@ resource "boundary_host" "bar" {
}
resource "boundary_host_set" "foo" {
type = "static"
name = "foo"
host_catalog_id = boundary_host_catalog.foo.id
@@ -89,7 +93,21 @@ resource "boundary_target" "foo" {
host_source_ids = [
boundary_host_set.foo.id
]
application_credential_source_ids = [
brokered_credential_source_ids = [
boundary_credential_library_vault.foo.id
]
}
resource "boundary_target" "ssh_foo" {
name = "ssh_foo"
description = "Ssh target"
type = "ssh"
default_port = "22"
scope_id = boundary_scope.project.id
host_source_ids = [
boundary_host_set.foo.id
]
injected_application_credential_source_ids = [
boundary_credential_library_vault.foo.id
]
}
@@ -100,23 +118,24 @@ resource "boundary_target" "foo" {
### Required
- **scope_id** (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- **type** (String) The target resource type.
- `scope_id` (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- `type` (String) The target resource type.
### Optional
- **application_credential_source_ids** (Set of String) A list of application credential source ID's.
- **default_port** (Number) The default port for this target.
- **description** (String) The target description.
- **host_source_ids** (Set of String) A list of host source ID's.
- **name** (String) The target name. Defaults to the resource name.
- **session_connection_limit** (Number)
- **session_max_seconds** (Number)
- **worker_filter** (String) Boolean expression to filter the workers for this target
- `brokered_credential_source_ids` (Set of String) A list of brokered credential source ID's.
- `default_port` (Number) The default port for this target.
- `description` (String) The target description.
- `host_source_ids` (Set of String) A list of host source ID's.
- `injected_application_credential_source_ids` (Set of String) A list of injected application credential source ID's.
- `name` (String) The target name. Defaults to the resource name.
- `session_connection_limit` (Number)
- `session_max_seconds` (Number)
- `worker_filter` (String) Boolean expression to filter the workers for this target
### Read-Only
- **id** (String) The ID of the target.
- `id` (String) The ID of the target.
## Import
+7 -7
View File
@@ -26,7 +26,7 @@ resource "boundary_auth_method" "password" {
type = "password"
}
resource "boundary_account" "jeff" {
resource "boundary_account_password" "jeff" {
auth_method_id = boundary_auth_method.password.id
type = "password"
login_name = "jeff"
@@ -36,7 +36,7 @@ resource "boundary_account" "jeff" {
resource "boundary_user" "jeff" {
name = "jeff"
description = "Jeff's user resource"
account_ids = [boundary_account.jeff.id]
account_ids = [boundary_account_password.jeff.id]
scope_id = boundary_scope.org.id
}
```
@@ -46,17 +46,17 @@ resource "boundary_user" "jeff" {
### Required
- **scope_id** (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- `scope_id` (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
### Optional
- **account_ids** (Set of String) Account ID's to associate with this user resource.
- **description** (String) The user description.
- **name** (String) The username. Defaults to the resource name.
- `account_ids` (Set of String) Account ID's to associate with this user resource.
- `description` (String) The user description.
- `name` (String) The username. Defaults to the resource name.
### Read-Only
- **id** (String) The ID of the user.
- `id` (String) The ID of the user.
## Import
@@ -1 +0,0 @@
terraform import boundary_account.foo <my-id>
@@ -0,0 +1 @@
terraform import boundary_account_password.foo <my-id>
@@ -11,7 +11,7 @@ resource "boundary_auth_method" "password" {
type = "password"
}
resource "boundary_account" "jeff" {
resource "boundary_account_password" "jeff" {
auth_method_id = boundary_auth_method.password.id
type = "password"
login_name = "jeff"
@@ -0,0 +1 @@
terraform import boundary_credential_json.example_json <my-id>
@@ -0,0 +1,27 @@
resource "boundary_scope" "org" {
name = "organization_one"
description = "global 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_static" "example" {
name = "example_static_credential_store"
description = "My first static credential store!"
scope_id = boundary_scope.project.id
}
resource "boundary_credential_json" "example" {
name = "example_json"
description = "My first json credential!"
credential_store_id = boundary_credential_store_static.example.id
object = file("~/object.json") # change to valid json file
}
@@ -41,3 +41,30 @@ resource "boundary_credential_library_vault" "bar" {
}
EOT
}
resource "boundary_credential_library_vault" "baz" {
name = "baz"
description = "vault username password credential with mapping overrides"
credential_store_id = boundary_credential_store_vault.foo.id
path = "my/secret/baz" # change to Vault backend path
http_method = "GET"
credential_type = "username_password"
credential_mapping_overrides = {
password_attribute = "alternative_password_label"
username_attribute = "alternative_username_label"
}
}
resource "boundary_credential_library_vault" "quz" {
name = "quz"
description = "vault ssh private key credential with mapping overrides"
credential_store_id = boundary_credential_store_vault.foo.id
path = "my/secret/quz" # change to Vault backend path
http_method = "GET"
credential_type = "ssh_private_key"
credential_mapping_overrides = {
private_key_attribute = "alternative_key_label"
private_key_passphrase_attribute = "alternative_passphrase_label"
username_attribute = "alternative_username_label"
}
}
@@ -0,0 +1 @@
terraform import boundary_credential_ssh_private_key.example_ssh_private_key <my-id>
@@ -0,0 +1,29 @@
resource "boundary_scope" "org" {
name = "organization_one"
description = "global 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_static" "example" {
name = "example_static_credential_store"
description = "My first static credential store!"
scope_id = boundary_scope.project.id
}
resource "boundary_credential_ssh_private_key" "example" {
name = "example_ssh_private_key"
description = "My first ssh private key credential!"
credential_store_id = boundary_credential_store_static.example.id
username = "my-username"
private_key = file("~/.ssh/id_rsa") # change to valid SSH Private Key
private_key_passphrase = "optional-passphrase" # change to the passphrase of the Private Key if required
}
@@ -0,0 +1 @@
terraform import boundary_credential_store_static.example_static_credential_store <my-id>
@@ -0,0 +1,20 @@
resource "boundary_scope" "org" {
name = "organization_one"
description = "global 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_static" "example" {
name = "example_static_credential_store"
description = "My first static credential store!"
scope_id = boundary_scope.project.id
}
@@ -0,0 +1 @@
terraform import boundary_credential_username_password.example_username_password <my-id>
@@ -0,0 +1,28 @@
resource "boundary_scope" "org" {
name = "organization_one"
description = "global 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_static" "example" {
name = "example_static_credential_store"
description = "My first static credential store!"
scope_id = boundary_scope.project.id
}
resource "boundary_credential_username_password" "example" {
name = "example_username_password"
description = "My first username password credential!"
credential_store_id = boundary_credential_store_static.example.id
username = "my-username"
password = "my-password"
}
@@ -21,6 +21,7 @@ resource "boundary_host_catalog" "static" {
}
resource "boundary_host" "example" {
type = "static"
name = "example_host"
description = "My first host!"
address = "10.0.0.1"
@@ -0,0 +1 @@
terraform import boundary_host_catalog_plugin.foo <my-id>
@@ -0,0 +1,60 @@
resource "boundary_scope" "org" {
name = "organization_one"
description = "My first scope!"
scope_id = boundary_scope.global.id
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
}
# For more information about the aws plugin, please visit here:
# https://github.com/hashicorp/boundary-plugin-host-aws
#
# For more information about aws users, please visit here:
# https://learn.hashicorp.com/tutorials/boundary/aws-host-catalogs?in=boundary/oss-access-management#configure-terraform-and-iam-user-privileges
resource "boundary_host_catalog_plugin" "aws_example" {
name = "My aws catalog"
description = "My first host catalog!"
scope_id = boundary_scope.project.id
plugin_name = "aws"
attributes_json = jsonencode({ "region" = "us-east-1" })
# recommended to pass in aws secrets using a file() or using environment variables
# the secrets below must be generated in aws by creating a aws iam user with programmatic access
secrets_json = jsonencode({
"access_key_id" = "aws_access_key_id_value",
"secret_access_key" = "aws_secret_access_key_value"
})
}
# For more information about the azure plugin, please visit here:
# https://github.com/hashicorp/boundary-plugin-host-azure
#
# For more information about azure ad applications, please visit here:
# https://learn.hashicorp.com/tutorials/boundary/azure-host-catalogs#register-a-new-azure-ad-application-1
resource "boundary_host_catalog_plugin" "azure_example" {
name = "My azure catalog"
description = "My second host catalog!"
scope_id = boundary_scope.project.id
plugin_name = "azure"
# the attributes below must be generated in azure by creating an ad application
attributes_json = jsonencode({
"disable_credential_rotation" = "true",
"tenant_id" = "ARM_TENANT_ID",
"subscription_id" = "ARM_SUBSCRIPTION_ID",
"client_id" = "ARM_CLIENT_ID"
})
# recommended to pass in aws secrets using a file() or using environment variables
# the secrets below must be generated in azure by creating an ad application
secrets_json = jsonencode({
"secret_value" = "ARM_CLIENT_SECRET"
})
}
@@ -0,0 +1 @@
terraform import boundary_host_catalog_static.foo <my-id>
@@ -0,0 +1,20 @@
resource "boundary_scope" "org" {
name = "organization_one"
description = "My first scope!"
scope_id = boundary_scope.global.id
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_host_catalog_static" "example" {
name = "My catalog"
description = "My first host catalog!"
scope_id = boundary_scope.project.id
}
@@ -14,10 +14,12 @@ resource "boundary_scope" "project" {
}
resource "boundary_host_catalog" "static" {
type = "static"
scope_id = boundary_scope.project.id
}
resource "boundary_host" "1" {
resource "boundary_host" "first" {
type = "static"
name = "host_1"
description = "My first host!"
address = "10.0.0.1"
@@ -25,7 +27,8 @@ resource "boundary_host" "1" {
scope_id = boundary_scope.project.id
}
resource "boundary_host" "2" {
resource "boundary_host" "second" {
type = "static"
name = "host_2"
description = "My second host!"
address = "10.0.0.2"
@@ -37,7 +40,7 @@ resource "boundary_host_set" "web" {
host_catalog_id = boundary_host_catalog.static.id
type = "static"
host_ids = [
boundary_host.1.id,
boundary_host.2.id,
boundary_host.first.id,
boundary_host.second.id,
]
}
@@ -0,0 +1 @@
terraform import boundary_host_set_plugin.foo <my-id>
@@ -0,0 +1,103 @@
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
}
# For more information about the aws plugin, please visit here:
# https://github.com/hashicorp/boundary-plugin-host-aws
#
# For more information about aws users, please visit here:
# https://learn.hashicorp.com/tutorials/boundary/aws-host-catalogs?in=boundary/oss-access-management#configure-terraform-and-iam-user-privileges
resource "boundary_host_catalog_plugin" "aws_example" {
name = "My aws catalog"
description = "My first host catalog!"
scope_id = boundary_scope.project.id
plugin_name = "aws"
attributes_json = jsonencode({ "region" = "us-east-1" })
# recommended to pass in aws secrets using a file() or using environment variables
# the secrets below must be generated in aws by creating a aws iam user with programmatic access
secrets_json = jsonencode({
"access_key_id" = "aws_access_key_id_value",
"secret_access_key" = "aws_secret_access_key_value"
})
}
resource "boundary_host_set_plugin" "web" {
name = "My web host set plugin"
host_catalog_id = boundary_host_catalog_plugin.aws_exmaple.id
attributes_json = jsonencode({ "filters" = "tag:service-type=web" })
}
resource "boundary_host_set_plugin" "foobar" {
name = "My foobar host set plugin"
host_catalog_id = boundary_host_catalog_plugin.aws_exmaple.id
preferred_endpoints = ["cidr:54.0.0.0/8"]
attributes_json = jsonencode({
"filters" = "tag-key=foo",
"filters" = "tag-key=bar"
})
}
resource "boundary_host_set_plugin" "launch" {
name = "My launch host set plugin"
host_catalog_id = boundary_host_catalog_plugin.aws_exmaple.id
sync_interval_seconds = 60
attributes_json = jsonencode({
"filters" = "tag:development=prod,dev",
"filters" = "launch-time=2022-01-04T*"
})
}
# For more information about the azure plugin, please visit here:
# https://github.com/hashicorp/boundary-plugin-host-azure
#
# For more information about azure ad applications, please visit here:
# https://learn.hashicorp.com/tutorials/boundary/azure-host-catalogs#register-a-new-azure-ad-application-1
resource "boundary_host_catalog_plugin" "azure_example" {
name = "My azure catalog"
description = "My second host catalog!"
scope_id = boundary_scope.project.id
plugin_name = "azure"
# the attributes below must be generated in azure by creating an ad application
attributes_json = jsonencode({
"disable_credential_rotation" = "true",
"tenant_id" = "ARM_TENANT_ID",
"subscription_id" = "ARM_SUBSCRIPTION_ID",
"client_id" = "ARM_CLIENT_ID"
})
# recommended to pass in aws secrets using a file() or using environment variables
# the secrets below must be generated in azure by creating an ad application
secrets_json = jsonencode({
"secret_value" = "ARM_CLIENT_SECRET"
})
}
resource "boundary_host_set_plugin" "database" {
name = "My database host set plugin"
host_catalog_id = boundary_host_catalog_plugin.azure_exmaple.id
attributes_json = jsonencode({ "filter" = "tagName eq 'service-type' and tagValue eq 'database'" })
}
resource "boundary_host_set_plugin" "foodev" {
name = "My foodev host set plugin"
host_catalog_id = boundary_host_catalog_plugin.azure_exmaple.id
preferred_endpoints = ["cidr:54.0.0.0/8"]
sync_interval_seconds = 60
attributes_json = jsonencode({
"filter" = "tagName eq 'tag-key' and tagValue eq 'foo'",
"filter" = "tagName eq 'application' and tagValue eq 'dev'",
})
}
@@ -0,0 +1 @@
terraform import boundary_host_set_static.foo <my-id>
@@ -0,0 +1,40 @@
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_host_catalog_static" "example" {
scope_id = boundary_scope.project.id
}
resource "boundary_host_static" "first" {
name = "host_1"
description = "My first host!"
address = "10.0.0.1"
host_catalog_id = boundary_host_catalog_static.example.id
}
resource "boundary_host_static" "second" {
name = "host_2"
description = "My second host!"
address = "10.0.0.2"
host_catalog_id = boundary_host_catalog_static.example.id
}
resource "boundary_host_set_static" "web" {
host_catalog_id = boundary_host_catalog_static.example.id
host_ids = [
boundary_host_static.first.id,
boundary_host_static.second.id,
]
}
@@ -0,0 +1 @@
terraform import boundary_host_static.foo <my-id>
@@ -0,0 +1,27 @@
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_host_catalog_static" "example" {
name = "My catalog"
description = "My first host catalog!"
scope_id = boundary_scope.project.id
}
resource "boundary_host_static" "example" {
name = "example_host"
description = "My first host!"
address = "10.0.0.1"
host_catalog_id = boundary_host_catalog_static.example.id
}
+19 -1
View File
@@ -32,6 +32,7 @@ resource "boundary_credential_library_vault" "foo" {
credential_store_id = boundary_credential_store_vault.foo.id
path = "my/secret/foo" # change to Vault backend path
http_method = "GET"
credential_type = "username_password"
}
resource "boundary_host_catalog" "foo" {
@@ -42,6 +43,7 @@ resource "boundary_host_catalog" "foo" {
}
resource "boundary_host" "foo" {
type = "static"
name = "foo"
host_catalog_id = boundary_host_catalog.foo.id
scope_id = boundary_scope.project.id
@@ -49,6 +51,7 @@ resource "boundary_host" "foo" {
}
resource "boundary_host" "bar" {
type = "static"
name = "bar"
host_catalog_id = boundary_host_catalog.foo.id
scope_id = boundary_scope.project.id
@@ -56,6 +59,7 @@ resource "boundary_host" "bar" {
}
resource "boundary_host_set" "foo" {
type = "static"
name = "foo"
host_catalog_id = boundary_host_catalog.foo.id
@@ -74,7 +78,21 @@ resource "boundary_target" "foo" {
host_source_ids = [
boundary_host_set.foo.id
]
application_credential_source_ids = [
brokered_credential_source_ids = [
boundary_credential_library_vault.foo.id
]
}
resource "boundary_target" "ssh_foo" {
name = "ssh_foo"
description = "Ssh target"
type = "ssh"
default_port = "22"
scope_id = boundary_scope.project.id
host_source_ids = [
boundary_host_set.foo.id
]
injected_application_credential_source_ids = [
boundary_credential_library_vault.foo.id
]
}
+2 -2
View File
@@ -11,7 +11,7 @@ resource "boundary_auth_method" "password" {
type = "password"
}
resource "boundary_account" "jeff" {
resource "boundary_account_password" "jeff" {
auth_method_id = boundary_auth_method.password.id
type = "password"
login_name = "jeff"
@@ -21,6 +21,6 @@ resource "boundary_account" "jeff" {
resource "boundary_user" "jeff" {
name = "jeff"
description = "Jeff's user resource"
account_ids = [boundary_account.jeff.id]
account_ids = [boundary_account_password.jeff.id]
scope_id = boundary_scope.org.id
}
+94 -116
View File
@@ -1,206 +1,184 @@
module github.com/hashicorp/terraform-provider-boundary
go 1.17
go 1.19
require (
github.com/hashicorp/boundary v0.7.4
github.com/hashicorp/boundary/api v0.0.21
github.com/hashicorp/boundary/sdk v0.0.13
github.com/hashicorp/cap v0.1.1
github.com/hashicorp/boundary v0.11.0
github.com/hashicorp/boundary/api v0.0.30
github.com/hashicorp/boundary/sdk v0.0.24
github.com/hashicorp/cap v0.2.0
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
github.com/hashicorp/go-kms-wrapping v0.6.8
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2
github.com/hashicorp/terraform-plugin-docs v0.5.1
github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1
github.com/kr/pretty v0.3.0
github.com/hashicorp/go-kms-wrapping/v2 v2.0.6-0.20220722192355-a843f53fa48d
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.5
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.3
github.com/hashicorp/terraform-plugin-docs v0.13.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.23.0
github.com/kr/pretty v0.3.1
github.com/mitchellh/go-homedir v1.1.0
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
github.com/stretchr/testify v1.8.0
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
)
require (
cloud.google.com/go v0.81.0 // indirect
cloud.google.com/go/storage v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go v58.0.0+incompatible // indirect
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.21 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.14 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/agext/levenshtein v1.2.2 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f // indirect
github.com/apparentlymart/go-textseg v1.0.0 // indirect
github.com/alessio/shellescape v1.4.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-metrics v0.3.9 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/aws/aws-sdk-go v1.40.55 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/cenkalti/backoff/v3 v3.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
github.com/containerd/containerd v1.4.3 // indirect
github.com/containerd/continuity v0.0.0-20200709052629-daa8e1ccc0bc // indirect
github.com/coreos/go-oidc/v3 v3.0.0 // indirect
github.com/danieljoos/wincred v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/continuity v0.1.0 // indirect
github.com/coreos/go-oidc/v3 v3.1.0 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/docker/cli v20.10.7+incompatible // indirect
github.com/docker/docker v20.10.7+incompatible // indirect
github.com/docker/docker v20.10.9+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b // indirect
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
github.com/godbus/dbus/v5 v5.0.4 // indirect
github.com/godbus/dbus/v5 v5.0.6 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-migrate/migrate/v4 v4.14.1 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang-migrate/migrate/v4 v4.15.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.2 // indirect
github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c // indirect
github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 // indirect
github.com/hashicorp/eventlogger/filters/encrypt v0.1.7 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-dbw v0.0.0-20211215222256-2ff0d37184ff // indirect
github.com/hashicorp/go-getter v1.5.3 // indirect
github.com/hashicorp/go-hclog v1.0.0 // indirect
github.com/hashicorp/go-immutable-radix v1.1.0 // indirect
github.com/hashicorp/go-dbw v0.0.0-20220725170111-b7cb3aa3d628 // indirect
github.com/hashicorp/go-hclog v1.2.2 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220711120347-32232bae6803 // indirect
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.4.1 // indirect
github.com/hashicorp/go-plugin v1.4.4 // indirect
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 // indirect
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 // indirect
github.com/hashicorp/go-secure-stdlib/configutil v0.1.2 // indirect
github.com/hashicorp/go-secure-stdlib/gatedwriter v0.1.1 // indirect
github.com/hashicorp/go-secure-stdlib/kv-builder v0.1.1 // indirect
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.3-0.20211129134915-8f7e6fb52622 // indirect
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.4 // indirect
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 // indirect
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/go-version v1.3.0 // indirect
github.com/hashicorp/golang-lru v0.5.3 // indirect
github.com/hashicorp/hc-install v0.3.1 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hc-install v0.4.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/hcl/v2 v2.3.0 // indirect
github.com/hashicorp/hcl/v2 v2.14.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.15.0 // indirect
github.com/hashicorp/terraform-json v0.13.0 // indirect
github.com/hashicorp/terraform-plugin-go v0.5.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.2.0 // indirect
github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 // indirect
github.com/hashicorp/nodeenrollment v0.1.17 // indirect
github.com/hashicorp/terraform-exec v0.17.3 // indirect
github.com/hashicorp/terraform-json v0.14.0 // indirect
github.com/hashicorp/terraform-plugin-go v0.14.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.7.0 // indirect
github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c // indirect
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect
github.com/hashicorp/vault/api v1.1.1 // indirect
github.com/hashicorp/vault/sdk v0.2.1 // indirect
github.com/hashicorp/vault/api v1.3.1 // indirect
github.com/hashicorp/vault/sdk v0.3.0 // 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.12 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.10.1 // indirect
github.com/jackc/pgconn v1.12.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.9.0 // indirect
github.com/jackc/pgx/v4 v4.14.0 // indirect
github.com/jefferai/go-libsecret v0.0.0-20210105015933-d08a58b018bc // indirect
github.com/jackc/pgtype v1.11.0 // indirect
github.com/jackc/pgx/v4 v4.16.1 // indirect
github.com/jefferai/go-libsecret v0.0.0-20210525195240-b53481abef97 // indirect
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f // indirect
github.com/jefferai/keyring v1.1.7-0.20210105022822-8749b3d9ce79 // indirect
github.com/jefferai/keyring v1.1.7-0.20220316160357-58a74bb55891 // indirect
github.com/jinzhu/gorm v1.9.12 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.3 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.11 // indirect
github.com/jstemmer/go-junit-report v0.9.1 // indirect
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect
github.com/klauspost/compress v1.13.5 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.10.2 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v2.0.1+incompatible // indirect
github.com/mitchellh/cli v1.1.2 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mitchellh/cli v1.1.4 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v1.0.0-rc9 // indirect
github.com/oracle/oci-go-sdk v13.1.0+incompatible // indirect
github.com/opencontainers/runc v1.0.2 // indirect
github.com/ory/dockertest/v3 v3.7.0 // indirect
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
github.com/pires/go-proxyproto v0.6.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/russross/blackfriday v1.6.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/ulikunitz/xz v0.5.8 // indirect
github.com/sethvargo/go-diceware v0.3.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
github.com/vmihailenco/tagparser v0.1.1 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xo/dburl v0.9.0 // indirect
github.com/zalando/go-keyring v0.1.1 // indirect
github.com/zclconf/go-cty v1.10.0 // indirect
go.opencensus.io v0.23.0 // indirect
github.com/xo/dburl v0.11.0 // indirect
github.com/zalando/go-keyring v0.2.1 // indirect
github.com/zclconf/go-cty v1.11.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf // indirect
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c // indirect
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
golang.org/x/tools v0.1.8-0.20211102182255-bb4add04ddef // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/api v0.44.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 // indirect
google.golang.org/grpc v1.41.0-dev.0.20210907181116-2f3355d2244e // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
google.golang.org/genproto v0.0.0-20220805133916-01dd62135a58 // indirect
google.golang.org/grpc v1.48.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gorm.io/driver/postgres v1.2.2 // indirect
gorm.io/driver/sqlite v1.2.4 // indirect
gorm.io/gorm v1.22.3 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/postgres v1.3.8 // indirect
gorm.io/driver/sqlite v1.3.6 // indirect
gorm.io/gorm v1.23.8 // indirect
)
+878 -347
View File
File diff suppressed because it is too large. Load diff
+61 -27
View File
@@ -4,14 +4,18 @@ import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/sdk/wrapper"
wrapping "github.com/hashicorp/go-kms-wrapping"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/hashicorp/go-secure-stdlib/configutil/v2"
"github.com/hashicorp/go-secure-stdlib/pluginutil/v2"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
kms_plugin_assets "github.com/hashicorp/terraform-provider-boundary/plugins/kms"
)
func init() {
@@ -57,30 +61,39 @@ func New() *schema.Provider {
Optional: true,
Description: "When set to true, does not validate the Boundary API endpoint certificate",
},
"plugin_execution_dir": {
Type: schema.TypeString,
Optional: true,
Description: `Specifies a directory that the Boundary provider can use to write and execute its built-in plugins.`,
},
},
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_credential_library_vault": resourceCredentialLibraryVault(),
"boundary_credential_store_vault": resourceCredentialStoreVault(),
"boundary_managed_group": resourceManagedGroup(),
"boundary_group": resourceGroup(),
"boundary_host": resourceHost(),
"boundary_host_static": resourceHostStatic(),
"boundary_host_catalog": resourceHostCatalog(),
"boundary_host_catalog_static": resourceHostCatalogStatic(),
"boundary_host_catalog_plugin": resourceHostCatalogPlugin(),
"boundary_host_set": resourceHostSet(),
"boundary_host_set_static": resourceHostSetStatic(),
"boundary_host_set_plugin": resourceHostSetPlugin(),
"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_credential_store_static": resourceCredentialStoreStatic(),
"boundary_credential_username_password": resourceCredentialUsernamePassword(),
"boundary_credential_ssh_private_key": resourceCredentialSshPrivateKey(),
"boundary_credential_json": resourceCredentialJson(),
"boundary_managed_group": resourceManagedGroup(),
"boundary_group": resourceGroup(),
"boundary_host": resourceHost(),
"boundary_host_static": resourceHostStatic(),
"boundary_host_catalog": resourceHostCatalog(),
"boundary_host_catalog_static": resourceHostCatalogStatic(),
"boundary_host_catalog_plugin": resourceHostCatalogPlugin(),
"boundary_host_set": resourceHostSet(),
"boundary_host_set_static": resourceHostSetStatic(),
"boundary_host_set_plugin": resourceHostSetPlugin(),
"boundary_role": resourceRole(),
"boundary_scope": resourceScope(),
"boundary_target": resourceTarget(),
"boundary_user": resourceUser(),
},
}
@@ -109,16 +122,28 @@ func providerAuthenticate(ctx context.Context, d *schema.ResourceData, md *metaD
if err != nil {
return fmt.Errorf(`error reading data from "recovery_kms_hcl": %v`, err)
}
wrapper, err := wrapper.GetWrapperFromHcl(recoveryHclStr, "recovery")
opts := []pluginutil.Option{
pluginutil.WithPluginsMap(kms_plugin_assets.BuiltinKmsPlugins()),
pluginutil.WithPluginsFilesystem(kms_plugin_assets.KmsPluginPrefix, kms_plugin_assets.FileSystem()),
}
if execDir, ok := d.GetOk("plugin_execution_dir"); ok {
opts = append(opts, pluginutil.WithPluginExecutionDirectory(execDir.(string)))
}
// TODO: cleanup plugin when finished
wrapper, _, err := wrapper.GetWrapperFromHcl(
ctx,
recoveryHclStr,
"recovery",
configutil.WithPluginOptions(opts...))
if err != nil {
return fmt.Errorf(`error reading wrappers from "recovery_kms_hcl": %v`, err)
}
if wrapper == nil {
return errors.New(`No "kms" block with purpose "recovery" found in "recovery_kms_hcl"`)
}
if err := wrapper.Init(ctx); err != nil {
return fmt.Errorf("error initializing recovery kms: %v", err)
}
md.recoveryKmsWrapper = wrapper
md.client.SetRecoveryKmsWrapper(wrapper)
@@ -152,6 +177,15 @@ func providerAuthenticate(ctx context.Context, d *schema.ResourceData, md *metaD
at, err := am.Authenticate(ctx, authMethodId.(string), "login", credentials)
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil {
statusCode := apiErr.Response().StatusCode()
if statusCode == http.StatusNotFound {
return fmt.Errorf("unknown auth_method_id: %s", err.Error())
}
if statusCode == http.StatusUnauthorized {
return fmt.Errorf("invalid login name or password: %s", err.Error())
}
}
return err
}
md.client.SetToken(at.Attributes["token"].(string))
+7 -8
View File
@@ -1,6 +1,7 @@
package provider
import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
@@ -8,8 +9,8 @@ import (
"testing"
"github.com/hashicorp/boundary/testing/controller"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/hashicorp/go-kms-wrapping/v2/aead"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
@@ -36,7 +37,7 @@ func providerFactories(p **schema.Provider) map[string]func() (*schema.Provider,
}
}
func testWrapper(t *testing.T, key string) wrapping.Wrapper {
func testWrapper(ctx context.Context, t *testing.T, key string) wrapping.Wrapper {
var keyBytes []byte
switch key {
case "":
@@ -56,15 +57,13 @@ func testWrapper(t *testing.T, key string) wrapping.Wrapper {
t.Fatal(err)
}
}
wrapper := aead.NewWrapper(nil)
wrapper := aead.NewWrapper()
_, err := wrapper.SetConfig(map[string]string{
"key_id": key,
})
_, err := wrapper.SetConfig(ctx, wrapping.WithKeyId(key))
if err != nil {
t.Fatal(err)
}
if err := wrapper.SetAESGCMKeyBytes(keyBytes); err != nil {
if err := wrapper.SetAesGcmKeyBytes(keyBytes); err != nil {
t.Fatal(err)
}
return wrapper
+3 -8
View File
@@ -12,7 +12,8 @@ import (
func resourceAccount() *schema.Resource {
return &schema.Resource{
Description: "The account resource allows you to configure a Boundary account.",
DeprecationMessage: "Deprecated: use `boundary_account_password` instead.",
Description: "Deprecated: use `boundary_account_password` instead.",
CreateContext: resourceAccountCreate,
ReadContext: resourceAccountRead,
@@ -54,14 +55,12 @@ func resourceAccount() *schema.Resource {
Description: "The login name for this account.",
Type: schema.TypeString,
Optional: true,
Deprecated: "Will be removed in favor of using attributes parameter",
},
accountPasswordKey: {
Description: "The account password. Only set on create, changes will not be reflected when updating account.",
Type: schema.TypeString,
Optional: true,
Deprecated: "Will be removed in favor of using attributes parameter",
Sensitive: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if d.Id() == "" {
// This is a new resource do not suppress password diff
@@ -88,7 +87,6 @@ func setFromAccountResponseMap(d *schema.ResourceData, raw map[string]interface{
return err
}
// TODO(malnick) - remove after deprecation cycle in favor of attributes
switch raw["type"].(string) {
case accountTypePassword:
if attrsVal, ok := raw["attributes"]; ok {
@@ -123,12 +121,10 @@ func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, meta int
opts = append(opts, accounts.WithDescription(descVal.(string)))
}
// TODO(malnick) - remove after deprecation cycle
if name, ok := d.GetOk(accountLoginNameKey); ok {
opts = append(opts, accounts.WithPasswordAccountLoginName(name.(string)))
}
// TODO(malnick) - remove after deprecation cycle
if pass, ok := d.GetOk(accountPasswordKey); ok {
opts = append(opts, accounts.WithPasswordAccountPassword(pass.(string)))
}
@@ -195,7 +191,6 @@ func resourceAccountUpdate(ctx context.Context, d *schema.ResourceData, meta int
}
}
// TODO(malnick) - remove after deprecation cycle
if d.HasChange(accountLoginNameKey) {
opts = append(opts, accounts.DefaultPasswordAccountLoginName())
if keyVal, ok := d.GetOk(accountLoginNameKey); ok {
+5 -2
View File
@@ -70,8 +70,11 @@ func setFromAccountOidcResponseMap(d *schema.ResourceData, raw map[string]interf
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(AuthMethodIdKey, raw["auth_method_id"])
d.Set(accountOidcIssuerKey, raw["issuer"])
d.Set(accountOidcSubjectKey, raw["subject"])
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
d.Set(accountOidcIssuerKey, attrs["issuer"])
d.Set(accountOidcSubjectKey, attrs["subject"])
}
d.SetId(raw["id"].(string))
}
@@ -60,14 +60,12 @@ func resourceAccountPassword() *schema.Resource {
Description: "The login name for this account.",
Type: schema.TypeString,
Optional: true,
Deprecated: "Will be removed in favor of using attributes parameter",
},
accountPasswordKey: {
Description: "The account password. Only set on create, changes will not be reflected when updating account.",
Type: schema.TypeString,
Optional: true,
Deprecated: "Will be removed in favor of using attributes parameter",
Sensitive: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if d.Id() == "" {
// This is a new resource do not suppress password diff
@@ -9,6 +9,7 @@ import (
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
func resourceAuthMethod() *schema.Resource {
@@ -50,6 +51,10 @@ func resourceAuthMethod() *schema.Resource {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
authmethodTypeOidc,
authmethodTypePassword,
}, false),
},
authmethodMinLoginNameLengthKey: {
Description: "The minimum login name length.",
@@ -99,6 +99,7 @@ func resourceAuthMethodOidc() *schema.Resource {
Description: "The secret key assigned to this auth method from the provider. Once set, only the hash will be kept and the original value can be removed from configuration.",
Type: schema.TypeString,
Optional: true,
Sensitive: true,
},
authmethodOidcIssuerKey: {
Description: "The issuer corresponding to the provider, which must match the issuer field in generated tokens.",
@@ -0,0 +1,222 @@
package provider
import (
"context"
"encoding/json"
"net/http"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
credentialJsonCredentialType = "json"
credentialJsonObjectKey = "object"
credentialJsonObjectHmacKey = "object_hmac"
)
func resourceCredentialJson() *schema.Resource {
return &schema.Resource{
Description: "The json credential resource allows you to congiure a credential using a json object.",
CreateContext: resourceCredentialJsonCreate,
ReadContext: resourceCredentialJsonRead,
UpdateContext: resourceCredentialJsonUpdate,
DeleteContext: resourceCredentialJsonDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
IDKey: {
Description: "The ID of this json credential.",
Type: schema.TypeString,
Computed: true,
},
NameKey: {
Description: "The name of this json credential. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The description of this json credential.",
Type: schema.TypeString,
Optional: true,
},
credentialStoreIdKey: {
Description: "The credential store in which to save this json credential.",
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
credentialJsonObjectKey: {
Description: `The object for the this json credential. Either values encoded with the "jsonencode" function, pre-escaped JSON string, or a file`,
Type: schema.TypeString,
Required: true,
Sensitive: true,
},
credentialJsonObjectHmacKey: {
Description: "The object hmac.",
Type: schema.TypeString,
Computed: true,
},
},
}
}
func setFromCredentialJsonResponseMap(d *schema.ResourceData, raw map[string]interface{}, fromRead bool) 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{})
stateObjectHmac := d.Get(credentialJsonObjectHmacKey)
boundaryObjectHmac := attrs[credentialJsonObjectHmacKey].(string)
if stateObjectHmac.(string) != boundaryObjectHmac && fromRead {
if err := d.Set(credentialJsonObjectKey, "(changed in Boundary)"); err != nil {
return err
}
}
if err := d.Set(credentialJsonObjectHmacKey, boundaryObjectHmac); err != nil {
return nil
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceCredentialJsonCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
var opts []credentials.Option
if v, ok := d.GetOk(NameKey); ok {
opts = append(opts, credentials.WithName(v.(string)))
}
if v, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, credentials.WithDescription(v.(string)))
}
if v, ok := d.GetOk(credentialJsonObjectKey); ok {
var jsonObject map[string]interface{}
if err := json.Unmarshal([]byte(v.(string)), &jsonObject); err != nil {
return diag.Errorf("error unmarshaling json: %v", err)
}
opts = append(opts, credentials.WithJsonCredentialObject(jsonObject))
}
var credentialStoreId string
retrievedStoreId, ok := d.GetOk(credentialStoreIdKey)
if ok {
credentialStoreId = retrievedStoreId.(string)
} else {
return diag.Errorf("credential store id is unset")
}
client := credentials.NewClient(md.client)
cred, err := client.Create(ctx, credentialJsonCredentialType, credentialStoreId, opts...)
if err != nil {
return diag.Errorf("error creating credential: %v", err)
}
if cred == nil {
return diag.Errorf("nil credential after create")
}
if err := setFromCredentialJsonResponseMap(d, cred.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential from response map: %v", err)
}
return nil
}
func resourceCredentialJsonRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentials.NewClient(md.client)
cred, 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: %v", err)
}
if cred == nil {
return diag.Errorf("credential nil after read")
}
if err := setFromCredentialJsonResponseMap(d, cred.GetResponse().Map, true); err != nil {
return diag.Errorf("error generating credential from response map: %v", err)
}
return nil
}
func resourceCredentialJsonUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentials.NewClient(md.client)
var opts []credentials.Option
if d.HasChange(NameKey) {
opts = append(opts, credentials.DefaultName())
if v, ok := d.GetOk(NameKey); ok {
opts = append(opts, credentials.WithName(v.(string)))
}
}
if d.HasChange(DescriptionKey) {
opts = append(opts, credentials.DefaultDescription())
if v, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, credentials.WithDescription(v.(string)))
}
}
if d.HasChange(credentialJsonObjectKey) {
if v, ok := d.GetOk(credentialJsonObjectKey); ok {
var jsonObject map[string]interface{}
if err := json.Unmarshal([]byte(v.(string)), &jsonObject); err != nil {
return diag.Errorf("error unmarshaling json: %v", err)
}
opts = append(opts, credentials.WithJsonCredentialObject(jsonObject))
}
}
if len(opts) > 0 {
opts = append(opts, credentials.WithAutomaticVersioning(true))
credUpdate, err := client.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating credential: %v", err)
}
if credUpdate == nil {
return diag.Errorf("credential nil after update")
}
if err = setFromCredentialJsonResponseMap(d, credUpdate.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential from response map: %v", err)
}
}
return nil
}
func resourceCredentialJsonDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentials.NewClient(md.client)
_, err := client.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting credential: %v", err)
}
return nil
}
@@ -0,0 +1,198 @@
package provider
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/boundary/testing/controller"
"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 (
jsonCredResc = "boundary_credential_json.example"
jsonCredName = "bar"
jsonCredNameUpdate = "foo"
jsonCredDesc = "the bar"
jsonCredDescUpdate = "the foo"
jsonCredObj = `jsonencode({
password = "password",
username = "admin"
})`
jsonCredObjUpdate = `jsonencode({
password = "password",
username = "db-admin"
})`
)
func jsonCredResource(name, description, object string) string {
return fmt.Sprintf(`
resource "boundary_credential_store_static" "example" {
name = "static store name"
description = "static store description"
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}
resource "boundary_credential_json" "example" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_static.example.id
object = %s
}`, name, description, object)
}
func TestAccCredentialJson(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
res := jsonCredResource(
jsonCredName,
jsonCredDesc,
jsonCredObj,
)
resUpdate := jsonCredResource(
jsonCredNameUpdate,
jsonCredDescUpdate,
jsonCredObjUpdate,
)
var provider *schema.Provider
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckCredentialJsonResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// create
Config: testConfig(url, fooOrg, firstProjectFoo, res),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(jsonCredResc, NameKey, jsonCredName),
resource.TestCheckResourceAttr(jsonCredResc, DescriptionKey, jsonCredDesc),
resource.TestCheckResourceAttr(jsonCredResc, credentialJsonObjectKey, `{"password":"password","username":"admin"}`),
testAccCheckCredentialJsonObjectHmac(provider),
testAccCheckCredentialJsonResourceExists(provider, jsonCredResc),
),
},
importStep(jsonCredResc, credentialJsonObjectKey),
{
// update
Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(jsonCredResc, NameKey, jsonCredNameUpdate),
resource.TestCheckResourceAttr(jsonCredResc, DescriptionKey, jsonCredDescUpdate),
resource.TestCheckResourceAttr(jsonCredResc, credentialJsonObjectKey, `{"password":"password","username":"db-admin"}`),
testAccCheckCredentialJsonObjectHmac(provider),
testAccCheckCredentialJsonResourceExists(provider, jsonCredResc),
),
},
importStep(jsonCredResc, credentialJsonObjectKey),
{
// Run a plan only update and verify no changes
PlanOnly: true,
Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate),
},
importStep(jsonCredResc, credentialJsonObjectKey),
{
// update again but apply a preConfig to externally update resource
PreConfig: func() { jsonCredExternalUpdate(t, provider) },
Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate),
},
importStep(jsonCredResc, credentialJsonObjectKey),
},
})
}
func testAccCheckCredentialJsonResourceDestroy(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_json":
id := rs.Primary.ID
c := credentials.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 json credential %q: %v", id, err)
}
default:
continue
}
}
return nil
}
}
func testAccCheckCredentialJsonResourceExists(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")
}
storeId = id
md := testProvider.Meta().(*metaData)
c := credentials.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 testAccCheckCredentialJsonObjectHmac(testProvider *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[jsonCredResc]
if !ok {
return fmt.Errorf("not found: %s", jsonCredResc)
}
computed := rs.Primary.Attributes["object_hmac"]
if len(computed) != 43 {
return fmt.Errorf("Computed password hmac not the expected length of 43 characters. hmac: %q", computed)
}
return nil
}
}
func jsonCredExternalUpdate(t *testing.T, testProvider *schema.Provider) {
if storeId == "" {
t.Fatal("storeId must be set before testing an external update")
}
md := testProvider.Meta().(*metaData)
c := credentials.NewClient(md.client)
cr, err := c.Read(context.Background(), storeId)
if err != nil {
t.Fatal(fmt.Errorf("got an error reading %q: %w", storeId, err))
}
// update credential options
var opts []credentials.Option
opts = append(opts, credentials.WithDescription("this is an updated description"))
_, err = c.Update(context.Background(), cr.Item.Id, cr.Item.Version, opts...)
if err != nil {
t.Fatal(fmt.Errorf("got an error updating %q: %w", cr.Item.Id, err))
}
}
@@ -6,16 +6,17 @@ import (
"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"
credentialStoreIdKey = "credential_store_id"
credentialLibraryVaultHttpMethodKey = "http_method"
credentialLibraryVaultHttpRequestBodyKey = "http_request_body"
credentialLibraryVaultPathKey = "path"
credentialLibraryCredentialTypeKey = "credential_type"
credentialLibraryCredentialMappingOverridesKey = "credential_mapping_overrides"
)
var libraryVaultAttrs = []string{
@@ -73,6 +74,16 @@ func resourceCredentialLibraryVault() *schema.Resource {
Type: schema.TypeString,
Required: true,
},
credentialLibraryCredentialTypeKey: {
Description: "The type of credential the library generates.",
Type: schema.TypeString,
Optional: true,
},
credentialLibraryCredentialMappingOverridesKey: {
Description: "The credential mapping override.",
Type: schema.TypeMap,
Optional: true,
},
},
}
}
@@ -88,6 +99,10 @@ func setFromVaultCredentialLibraryResponseMap(d *schema.ResourceData, raw map[st
return err
}
if err := d.Set(credentialLibraryCredentialMappingOverridesKey, raw[credentialLibraryCredentialMappingOverridesKey]); err != nil {
return err
}
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
for _, v := range libraryVaultAttrs {
@@ -121,6 +136,13 @@ func resourceCredentialLibraryCreateVault(ctx context.Context, d *schema.Resourc
if v, ok := d.GetOk(credentialLibraryVaultPathKey); ok {
opts = append(opts, credentiallibraries.WithVaultCredentialLibraryPath(v.(string)))
}
if v, ok := d.GetOk(credentialLibraryCredentialTypeKey); ok {
opts = append(opts, credentiallibraries.WithCredentialType(v.(string)))
}
if v, ok := d.GetOk(credentialLibraryCredentialMappingOverridesKey); ok {
mappingOverrides := v.(map[string]interface{})
opts = append(opts, credentiallibraries.WithCredentialMappingOverrides(mappingOverrides))
}
var credentialStoreId string
cid, ok := d.GetOk(credentialStoreIdKey)
@@ -208,6 +230,40 @@ func resourceCredentialLibraryUpdateVault(ctx context.Context, d *schema.Resourc
opts = append(opts, credentiallibraries.WithVaultCredentialLibraryPath(v.(string)))
}
}
if d.HasChange(credentialLibraryCredentialMappingOverridesKey) {
var oldMapping, newMapping map[string]interface{}
old, new := d.GetChange(credentialLibraryCredentialMappingOverridesKey)
if old == nil {
old = map[string]interface{}{}
}
oldMapping = old.(map[string]interface{})
if new == nil {
new = map[string]interface{}{}
}
newMapping = new.(map[string]interface{})
newAttrs := []string{}
for k := range newMapping {
newAttrs = append(newAttrs, k)
}
for oldAttr := range oldMapping {
isRemoved := true
for _, newAttr := range newAttrs {
if oldAttr == newAttr {
isRemoved = false
break
}
}
if isRemoved {
newMapping[oldAttr] = nil
}
}
opts = append(opts, credentiallibraries.WithCredentialMappingOverrides(newMapping))
}
if len(opts) > 0 {
opts = append(opts, credentiallibraries.WithAutomaticVersioning(true))
@@ -16,23 +16,26 @@ import (
)
const (
vaultCredResc = "boundary_credential_library_vault.example"
vaultCredLibName = "foo"
vaultCredLibDesc = "the foo"
vaultCredLibPath = "/foo/bar"
vaultCredLibMethodGet = "GET"
vaultCredLibMethodPost = "POST"
vaultCredLibRequestBody = "foobar"
vaultCredLibStringUpdate = "_random"
vaultCredResc = "boundary_credential_library_vault.example"
vaultCredTypedResc = "boundary_credential_library_vault.typed_example"
vaultCredUsernamePasswordResc = "boundary_credential_library_vault.username_password_mapping_override"
vaultCredSshPrivateKeyResc = "boundary_credential_library_vault.ssh_private_key_mapping_override"
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"
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
http_method = "%s"
path = "%s"
http_method = "%s"
}`, vaultCredLibName,
vaultCredLibDesc,
vaultCredLibPath,
@@ -40,18 +43,96 @@ resource "boundary_credential_library_vault" "example" {
var vaultCredLibResourceUpdate = fmt.Sprintf(`
resource "boundary_credential_library_vault" "example" {
name = "%s"
description = "%s"
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
http_method = "%s"
http_request_body = "%s"
path = "%s"
http_method = "%s"
http_request_body = "%s"
}`, vaultCredLibName+vaultCredLibStringUpdate,
vaultCredLibDesc+vaultCredLibStringUpdate,
vaultCredLibPath+vaultCredLibStringUpdate,
vaultCredLibMethodPost,
vaultCredLibRequestBody)
var vaultTypedCredLibResource = fmt.Sprintf(`
resource "boundary_credential_library_vault" "typed_example" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
http_method = "%s"
credential_type = "ssh_private_key"
}`, vaultCredLibName,
vaultCredLibDesc,
vaultCredLibPath,
vaultCredLibMethodGet)
var vaultUsernamePasswordMappingOverrideCredLibResource = fmt.Sprintf(`
resource "boundary_credential_library_vault" "username_password_mapping_override" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
http_method = "%s"
credential_type = "username_password"
credential_mapping_overrides = {
password_attribute = "alternative_password_label"
username_attribute = "alternative_username_label"
}
}`, vaultCredLibName,
vaultCredLibDesc,
vaultCredLibPath,
vaultCredLibMethodGet)
var vaultUsernamePasswordMappingOverrideCredLibResourceUpdate = fmt.Sprintf(`
resource "boundary_credential_library_vault" "username_password_mapping_override" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
http_method = "%s"
credential_type = "username_password"
credential_mapping_overrides = {
password_attribute = "updated_password_label"
username_attribute = "updated_username_label"
}
}`, vaultCredLibName,
vaultCredLibDesc,
vaultCredLibPath,
vaultCredLibMethodGet)
var vaultUsernamePasswordMappingOverrideCredLibResourceRemove = fmt.Sprintf(`
resource "boundary_credential_library_vault" "username_password_mapping_override" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
http_method = "%s"
credential_type = "username_password"
}`, vaultCredLibName,
vaultCredLibDesc,
vaultCredLibPath,
vaultCredLibMethodGet)
var vaultSshPrivateKeyMappingOverrideCredLibResource = fmt.Sprintf(`
resource "boundary_credential_library_vault" "ssh_private_key_mapping_override" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
http_method = "%s"
credential_type = "ssh_private_key"
credential_mapping_overrides = {
private_key_attribute = "alternative_key_label"
private_key_passphrase_attribute = "alternative_passphrase_label"
username_attribute = "alternative_username_label"
}
}`, vaultCredLibName,
vaultCredLibDesc,
vaultCredLibPath,
vaultCredLibMethodGet)
func TestAccCredentialLibraryVault(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
@@ -69,8 +150,9 @@ func TestAccCredentialLibraryVault(t *testing.T) {
var provider *schema.Provider
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckAuthMethodResourceDestroy(t, provider),
CheckDestroy: testAccCheckCredentialLibraryVaultResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// create
@@ -101,6 +183,90 @@ func TestAccCredentialLibraryVault(t *testing.T) {
),
},
importStep(vaultCredResc),
{
// create typed credential library, note credential type is immutable so no need for update test
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate, vaultTypedCredLibResource),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultCredTypedResc, NameKey, vaultCredLibName),
resource.TestCheckResourceAttr(vaultCredTypedResc, DescriptionKey, vaultCredLibDesc),
resource.TestCheckResourceAttr(vaultCredTypedResc, credentialLibraryVaultPathKey, vaultCredLibPath),
resource.TestCheckResourceAttr(vaultCredTypedResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet),
resource.TestCheckResourceAttr(vaultCredTypedResc, credentialLibraryVaultHttpRequestBodyKey, ""),
resource.TestCheckResourceAttr(vaultCredTypedResc, credentialLibraryCredentialTypeKey, "ssh_private_key"),
testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredTypedResc),
),
},
importStep(vaultCredResc),
{
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate, vaultUsernamePasswordMappingOverrideCredLibResource),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, NameKey, vaultCredLibName),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, DescriptionKey, vaultCredLibDesc),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultPathKey, vaultCredLibPath),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpRequestBodyKey, ""),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryCredentialTypeKey, "username_password"),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, "credential_mapping_overrides.password_attribute", "alternative_password_label"),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, "credential_mapping_overrides.username_attribute", "alternative_username_label"),
testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredUsernamePasswordResc),
),
},
importStep(vaultCredResc),
{
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate, vaultUsernamePasswordMappingOverrideCredLibResourceUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, NameKey, vaultCredLibName),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, DescriptionKey, vaultCredLibDesc),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultPathKey, vaultCredLibPath),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpRequestBodyKey, ""),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryCredentialTypeKey, "username_password"),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, "credential_mapping_overrides.password_attribute", "updated_password_label"),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, "credential_mapping_overrides.username_attribute", "updated_username_label"),
testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredUsernamePasswordResc),
),
},
importStep(vaultCredResc),
{
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate, vaultUsernamePasswordMappingOverrideCredLibResourceRemove),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, NameKey, vaultCredLibName),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, DescriptionKey, vaultCredLibDesc),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultPathKey, vaultCredLibPath),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryVaultHttpRequestBodyKey, ""),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryCredentialTypeKey, "username_password"),
resource.TestCheckResourceAttr(vaultCredUsernamePasswordResc, credentialLibraryCredentialMappingOverridesKey+".%", "0"),
testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredUsernamePasswordResc),
),
},
importStep(vaultCredResc),
{
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultCredLibResourceUpdate, vaultSshPrivateKeyMappingOverrideCredLibResource),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, NameKey, vaultCredLibName),
resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, DescriptionKey, vaultCredLibDesc),
resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, credentialLibraryVaultPathKey, vaultCredLibPath),
resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, credentialLibraryVaultHttpMethodKey, vaultCredLibMethodGet),
resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, credentialLibraryVaultHttpRequestBodyKey, ""),
resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, credentialLibraryCredentialTypeKey, "ssh_private_key"),
resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, "credential_mapping_overrides.private_key_attribute", "alternative_key_label"),
resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, "credential_mapping_overrides.private_key_passphrase_attribute", "alternative_passphrase_label"),
resource.TestCheckResourceAttr(vaultCredSshPrivateKeyResc, "credential_mapping_overrides.username_attribute", "alternative_username_label"),
testAccCheckCredentialLibraryVaultResourceExists(provider, vaultCredSshPrivateKeyResc),
),
},
importStep(vaultCredResc),
},
})
}
@@ -0,0 +1,278 @@
package provider
import (
"context"
"net/http"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
credentialSshPrivateKeyUsernameKey = "username"
credentialSshPrivateKeyPrivateKeyKey = "private_key"
credentialSshPrivateKeyPrivateKeyHmacKey = "private_key_hmac"
credentialSshPrivateKeyPassphraseKey = "private_key_passphrase"
credentialSshPrivateKeyPassphraseHmacKey = "private_key_passphrase_hmac"
credentialSshPrivateKeyCredentialType = "ssh_private_key"
)
func resourceCredentialSshPrivateKey() *schema.Resource {
return &schema.Resource{
Description: "The SSH private key credential resource allows you to configure a credential using a username, private key and optional passphrase.",
CreateContext: resourceCredentialSshPrivateKeyCreate,
ReadContext: resourceCredentialSshPrivateKeyRead,
UpdateContext: resourceCredentialSshPrivateKeyUpdate,
DeleteContext: resourceCredentialSshPrivateKeyDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
IDKey: {
Description: "The ID of the credential.",
Type: schema.TypeString,
Computed: true,
},
NameKey: {
Description: "The name of the credential. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The description of the credential.",
Type: schema.TypeString,
Optional: true,
},
credentialStoreIdKey: {
Description: "ID of the credential store this credential belongs to.",
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
credentialSshPrivateKeyUsernameKey: {
Description: "The username associated with the credential.",
Type: schema.TypeString,
Required: true,
},
credentialSshPrivateKeyPrivateKeyKey: {
Description: "The private key associated with the credential.",
Type: schema.TypeString,
Required: true,
Sensitive: true,
},
credentialSshPrivateKeyPrivateKeyHmacKey: {
Description: "The private key hmac.",
Type: schema.TypeString,
Computed: true,
},
credentialSshPrivateKeyPassphraseKey: {
Description: "The passphrase of the private key associated with the credential.",
Type: schema.TypeString,
Optional: true,
Sensitive: true,
},
credentialSshPrivateKeyPassphraseHmacKey: {
Description: "The private key passphrase hmac.",
Type: schema.TypeString,
Computed: true,
},
},
}
}
func setFromCredentialSshPrivateKeyResponseMap(d *schema.ResourceData, raw map[string]interface{}, fromRead bool) 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{})
if err := d.Set(credentialSshPrivateKeyUsernameKey, attrs[credentialSshPrivateKeyUsernameKey]); err != nil {
return err
}
statePrivKeyHmac := d.Get(credentialSshPrivateKeyPrivateKeyHmacKey)
boundaryPrivKeyHmac := attrs[credentialSshPrivateKeyPrivateKeyHmacKey].(string)
if statePrivKeyHmac.(string) != boundaryPrivKeyHmac && fromRead {
// PrivateKeyHmac has changed in Boundary, therefore the private key has changed.
// Update private key value to force tf to attempt update.
if err := d.Set(credentialSshPrivateKeyPrivateKeyKey, "(changed in Boundary)"); err != nil {
return err
}
}
if err := d.Set(credentialSshPrivateKeyPrivateKeyHmacKey, boundaryPrivKeyHmac); err != nil {
return err
}
var statePassphraseHmac, boundaryPassphraseHmac string
if v := d.Get(credentialSshPrivateKeyPassphraseHmacKey); v != nil {
statePassphraseHmac = v.(string)
}
if v := attrs[credentialSshPrivateKeyPassphraseHmacKey]; v != nil {
boundaryPassphraseHmac = v.(string)
}
if statePassphraseHmac != boundaryPassphraseHmac && fromRead {
// PassphraseHmac has changed in Boundary, therefore the private key passphrase has changed.
// Update private key passphrase value to force tf to attempt update.
if err := d.Set(credentialSshPrivateKeyPassphraseKey, "(changed in Boundary)"); err != nil {
return err
}
}
if err := d.Set(credentialSshPrivateKeyPassphraseHmacKey, boundaryPassphraseHmac); err != nil {
return err
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceCredentialSshPrivateKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
var opts []credentials.Option
if v, ok := d.GetOk(NameKey); ok {
opts = append(opts, credentials.WithName(v.(string)))
}
if v, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, credentials.WithDescription(v.(string)))
}
if v, ok := d.GetOk(credentialSshPrivateKeyUsernameKey); ok {
opts = append(opts, credentials.WithSshPrivateKeyCredentialUsername(v.(string)))
}
if v, ok := d.GetOk(credentialSshPrivateKeyPrivateKeyKey); ok {
opts = append(opts, credentials.WithSshPrivateKeyCredentialPrivateKey(v.(string)))
}
if v, ok := d.GetOk(credentialSshPrivateKeyPassphraseKey); ok {
opts = append(opts, credentials.WithSshPrivateKeyCredentialPrivateKeyPassphrase(v.(string)))
}
var credentialStoreId string
retrievedStoreId, ok := d.GetOk(credentialStoreIdKey)
if ok {
credentialStoreId = retrievedStoreId.(string)
} else {
return diag.Errorf("credential store id is unset")
}
client := credentials.NewClient(md.client)
cr, err := client.Create(ctx, credentialSshPrivateKeyCredentialType, credentialStoreId, opts...)
if err != nil {
return diag.Errorf("error creating credential: %v", err)
}
if cr == nil {
return diag.Errorf("nil credential after create")
}
if err := setFromCredentialSshPrivateKeyResponseMap(d, cr.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential from response map: %v", err)
}
return nil
}
func resourceCredentialSshPrivateKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentials.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: %v", err)
}
if cr == nil {
return diag.Errorf("credential nil after read")
}
if err := setFromCredentialSshPrivateKeyResponseMap(d, cr.GetResponse().Map, true); err != nil {
return diag.Errorf("error generating credential from response map: %v", err)
}
return nil
}
func resourceCredentialSshPrivateKeyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentials.NewClient(md.client)
var opts []credentials.Option
if d.HasChange(NameKey) {
opts = append(opts, credentials.DefaultName())
nameVal, ok := d.GetOk(NameKey)
if ok {
opts = append(opts, credentials.WithName(nameVal.(string)))
}
}
if d.HasChange(DescriptionKey) {
opts = append(opts, credentials.DefaultDescription())
descVal, ok := d.GetOk(DescriptionKey)
if ok {
opts = append(opts, credentials.WithDescription(descVal.(string)))
}
}
if d.HasChange(credentialSshPrivateKeyUsernameKey) {
usernameVal, ok := d.GetOk(credentialSshPrivateKeyUsernameKey)
if ok {
opts = append(opts, credentials.WithSshPrivateKeyCredentialUsername(usernameVal.(string)))
}
}
if d.HasChange(credentialSshPrivateKeyPrivateKeyKey) {
privKeyVal, ok := d.GetOk(credentialSshPrivateKeyPrivateKeyKey)
if ok {
opts = append(opts, credentials.WithSshPrivateKeyCredentialPrivateKey(privKeyVal.(string)))
}
}
if d.HasChange(credentialSshPrivateKeyPassphraseKey) {
passVal, ok := d.GetOk(credentialSshPrivateKeyPassphraseKey)
if ok {
opts = append(opts, credentials.WithSshPrivateKeyCredentialPrivateKeyPassphrase(passVal.(string)))
}
}
if len(opts) > 0 {
opts = append(opts, credentials.WithAutomaticVersioning(true))
crUpdate, err := client.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating credential: %v", err)
}
if crUpdate == nil {
return diag.Errorf("credential nil after update")
}
if err = setFromCredentialSshPrivateKeyResponseMap(d, crUpdate.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential from response map: %v", err)
}
}
return nil
}
func resourceCredentialSshPrivateKeyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentials.NewClient(md.client)
_, err := client.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting credential: %v", err)
}
return nil
}
@@ -0,0 +1,180 @@
package provider
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/boundary/testing/controller"
"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/ssh/testdata"
)
const (
sshPrivateKeyCredResc = "boundary_credential_ssh_private_key.example"
sshPrivateKeyCredName = "Dr Jekyll"
sshPrivateKeyCredDesc = "my best description"
sshPrivateKeyUpdate = "some magic update string"
sshPrivateKeyUsername = "my-user"
)
var staticStore = fmt.Sprintf(`
resource "boundary_credential_store_static" "ssh_store" {
name = "static store name"
description = "static store description"
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}`)
func sshPrivateKeyResource(name, description, username, privateKey, passphrase string) string {
return fmt.Sprintf(`
resource "boundary_credential_ssh_private_key" "example" {
name = %q
description = %q
credential_store_id = boundary_credential_store_static.ssh_store.id
username = %q
private_key = %q
private_key_passphrase = %q
}`, name, description, username, privateKey, passphrase)
}
func TestAccCredentialSshPrivateKey(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
privKey := string(testdata.PEMBytes["rsa"])
privKeyUpdate := string(testdata.PEMEncryptedKeys[0].PEMBytes)
privKeyUpdatePassphrase := testdata.PEMEncryptedKeys[0].EncryptionKey
res := sshPrivateKeyResource(
sshPrivateKeyCredName,
sshPrivateKeyCredDesc,
sshPrivateKeyUsername,
privKey,
"",
)
resUpdate := sshPrivateKeyResource(
sshPrivateKeyCredName+sshPrivateKeyUpdate,
sshPrivateKeyCredDesc+sshPrivateKeyUpdate,
sshPrivateKeyUsername+sshPrivateKeyUpdate,
privKeyUpdate,
privKeyUpdatePassphrase,
)
var provider *schema.Provider
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckCredentialSshPrivateKeyResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// create
Config: testConfig(url, fooOrg, firstProjectFoo, staticStore, res),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, NameKey, sshPrivateKeyCredName),
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, DescriptionKey, sshPrivateKeyCredDesc),
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, credentialSshPrivateKeyUsernameKey, sshPrivateKeyUsername),
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, credentialSshPrivateKeyPrivateKeyKey, privKey),
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, credentialSshPrivateKeyPassphraseKey, ""),
testAccCheckCredentialStoreSshPrivateKeyHmac(provider),
testAccCheckCredentialSshPrivateKeyResourceExists(provider, sshPrivateKeyCredResc),
),
},
importStep(sshPrivateKeyCredResc, credentialSshPrivateKeyPrivateKeyKey, credentialSshPrivateKeyPassphraseKey),
{
// update
Config: testConfig(url, fooOrg, firstProjectFoo, staticStore, resUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, NameKey, sshPrivateKeyCredName+sshPrivateKeyUpdate),
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, DescriptionKey, sshPrivateKeyCredDesc+sshPrivateKeyUpdate),
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, credentialSshPrivateKeyUsernameKey, sshPrivateKeyUsername+sshPrivateKeyUpdate),
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, credentialSshPrivateKeyPrivateKeyKey, privKeyUpdate),
resource.TestCheckResourceAttr(sshPrivateKeyCredResc, credentialSshPrivateKeyPassphraseKey, privKeyUpdatePassphrase),
testAccCheckCredentialStoreSshPrivateKeyHmac(provider),
testAccCheckCredentialSshPrivateKeyResourceExists(provider, sshPrivateKeyCredResc),
),
},
},
})
}
func testAccCheckCredentialSshPrivateKeyResourceExists(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")
}
storeId = id
md := testProvider.Meta().(*metaData)
c := credentials.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 testAccCheckCredentialSshPrivateKeyResourceDestroy(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_ssh_private_key":
id := rs.Primary.ID
c := credentials.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 credential %q: %v", id, err)
}
default:
continue
}
}
return nil
}
}
func testAccCheckCredentialStoreSshPrivateKeyHmac(testProvider *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[sshPrivateKeyCredResc]
if !ok {
return fmt.Errorf("not found: %s", sshPrivateKeyCredResc)
}
computed := rs.Primary.Attributes["private_key_hmac"]
if len(computed) != 43 {
return fmt.Errorf("computed private key hmac not the expected length of 43 characters, got: %q", computed)
}
if rs.Primary.Attributes["private_key_passphrase"] != "" {
// We set a passphrase, validate the computed hmac is expected length
computed := rs.Primary.Attributes["private_key_passphrase_hmac"]
if len(computed) != 43 {
return fmt.Errorf("computed private key passphrase hmac not the expected length of 43 characters, got: %q", computed)
}
}
return nil
}
}
@@ -0,0 +1,176 @@
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 staticCredentialStoreType = "static"
func resourceCredentialStoreStatic() *schema.Resource {
return &schema.Resource{
Description: "The static credential store resource allows you to configure a Boundary static credential store.",
CreateContext: resourceStaticCredentialStoreCreate,
ReadContext: resourceStaticCredentialStoreRead,
UpdateContext: resourceStaticCredentialStoreUpdate,
DeleteContext: resourceStaticCredentialStoreDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
IDKey: {
Description: "The ID of the static credential store.",
Type: schema.TypeString,
Computed: true,
},
NameKey: {
Description: "The static credential store name. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The static credential store description.",
Type: schema.TypeString,
Optional: true,
},
ScopeIdKey: {
Description: "The scope for this credential store.",
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
},
}
}
func setFromStaticCredentialStoreResponseMap(d *schema.ResourceData, raw map[string]interface{}, fromRead bool) 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
}
d.SetId(raw["id"].(string))
return nil
}
func resourceStaticCredentialStoreCreate(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)))
}
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, staticCredentialStoreType, 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 := setFromStaticCredentialStoreResponseMap(d, cr.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential store from response map: %v", err)
}
return nil
}
func resourceStaticCredentialStoreRead(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 := setFromStaticCredentialStoreResponseMap(d, cr.GetResponse().Map, true); err != nil {
return diag.Errorf("error generating credential store from response map: %v", err)
}
return nil
}
func resourceStaticCredentialStoreUpdate(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 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 = setFromStaticCredentialStoreResponseMap(d, crUpdate.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential store from response map: %v", err)
}
}
return nil
}
func resourceStaticCredentialStoreDelete(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
}
@@ -0,0 +1,139 @@
package provider
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentialstores"
"github.com/hashicorp/boundary/testing/controller"
"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 (
staticCredStoreResc = "boundary_credential_store_static.example"
staticCredStoreName = "foo"
staticCredStoreDesc = "the foo"
staticCredStoreNamespace = "static"
staticCredStoreUpdate = "_random"
)
func staticCredStoreResource(name string, description string) string {
return fmt.Sprintf(`
resource "boundary_credential_store_static" "example" {
name = "%s"
description = "%s"
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}`, name,
description)
}
func TestAccCredentialStoreStatic(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
res := staticCredStoreResource(staticCredStoreName,
staticCredStoreDesc)
resUpdate := staticCredStoreResource(staticCredStoreName+staticCredStoreUpdate,
staticCredStoreDesc+staticCredStoreUpdate)
var provider *schema.Provider
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckCredentialStoreStaticResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// create
Config: testConfig(url, fooOrg, firstProjectFoo, res),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(staticCredStoreResc, NameKey, staticCredStoreName),
resource.TestCheckResourceAttr(staticCredStoreResc, DescriptionKey, staticCredStoreDesc),
testAccCheckCredentialStoreVaultResourceExists(provider, staticCredStoreResc),
),
},
importStep(staticCredStoreResc),
{
// update
Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(staticCredStoreResc, NameKey, staticCredStoreName+staticCredStoreUpdate),
resource.TestCheckResourceAttr(staticCredStoreResc, DescriptionKey, staticCredStoreDesc+staticCredStoreUpdate),
testAccCheckCredentialStoreVaultResourceExists(provider, staticCredStoreResc),
),
},
importStep(staticCredStoreResc),
{
// Run a plan only update and verify no changes
PlanOnly: true,
Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate),
},
importStep(staticCredStoreResc),
{
// update again but apply a preConfig to externally update resource
// TODO: Boundary currently causes an error on moving back to a previously
// used token, for now verify that a plan only step had changes
PreConfig: func() { staticCredentialStoreExternalUpdate(t, provider) },
PlanOnly: true,
ExpectNonEmptyPlan: true,
Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate),
},
importStep(staticCredStoreResc),
},
})
}
func staticCredentialStoreExternalUpdate(t *testing.T, testProvider *schema.Provider) {
if storeId == "" {
t.Fatal("storeId must be set before testing an external update")
}
md := testProvider.Meta().(*metaData)
c := credentialstores.NewClient(md.client)
cr, err := c.Read(context.Background(), storeId)
if err != nil {
t.Fatal(fmt.Errorf("got an error reading %q: %w", storeId, err))
}
// update credential store options
var opts []credentialstores.Option
opts = append(opts, credentialstores.WithDescription("this is an updated description, my guy"))
_, err = c.Update(context.Background(), cr.Item.Id, cr.Item.Version, opts...)
if err != nil {
t.Fatal(fmt.Errorf("got an error updating %q: %w", cr.Item.Id, err))
}
}
func testAccCheckCredentialStoreStaticResourceDestroy(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_static":
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 static credential store %q: %v", id, err)
}
default:
continue
}
}
return nil
}
}
@@ -2,6 +2,7 @@ package provider
import (
"context"
"fmt"
"net/http"
"github.com/hashicorp/boundary/api"
@@ -123,36 +124,49 @@ func resourceCredentialStoreVault() *schema.Resource {
}
}
func setFromVaultCredentialStoreResponseMap(d *schema.ResourceData, raw map[string]interface{}, fromRead bool) error {
func setFromVaultCredentialStoreResponseMap(d *schema.ResourceData, raw map[string]interface{}, fromRead bool) diag.Diagnostics {
if err := d.Set(NameKey, raw[NameKey]); err != nil {
return err
return diag.FromErr(err)
}
if err := d.Set(DescriptionKey, raw[DescriptionKey]); err != nil {
return err
return diag.FromErr(err)
}
if err := d.Set(ScopeIdKey, raw[ScopeIdKey]); err != nil {
return err
return diag.FromErr(err)
}
var diags diag.Diagnostics
csId := raw["id"]
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
return diag.FromErr(err)
}
}
stateTokenHmac := d.Get(credentialStoreVaultTokenHmacKey)
boundaryTokenHmac := attrs[credentialStoreVaultTokenHmacKey].(string)
if stateTokenHmac.(string) != boundaryTokenHmac && fromRead {
// TokenHmac has changed in Boundary, therefore the token has changed.
// Update token value to force tf to attempt update.
if err := d.Set(credentialStoreVaultTokenKey, "(changed in Boundary)"); err != nil {
return err
boundaryTokenHmac, ok := attrs[credentialStoreVaultTokenHmacKey]
tokenStatus := attrs["token_status"]
switch {
case ok:
boundaryTokenHmacStr := boundaryTokenHmac.(string)
stateTokenHmac := d.Get(credentialStoreVaultTokenHmacKey)
if stateTokenHmac.(string) != boundaryTokenHmacStr && fromRead {
// TokenHmac has changed in Boundary, therefore the token has changed.
// Update token value to force tf to attempt update.
if err := d.Set(credentialStoreVaultTokenKey, "(changed in Boundary)"); err != nil {
return diag.FromErr(err)
}
}
}
if err := d.Set(credentialStoreVaultTokenHmacKey, boundaryTokenHmac); err != nil {
return err
if err := d.Set(credentialStoreVaultTokenHmacKey, boundaryTokenHmacStr); err != nil {
return diag.FromErr(err)
}
case tokenStatus == "expired":
diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: fmt.Sprintf("Vault token has expired for credential store %q, please update.", csId),
})
}
stateClientKeyHmac := d.Get(credentialStoreVaultClientCertificateKeyHmacKey)
@@ -164,17 +178,17 @@ func setFromVaultCredentialStoreResponseMap(d *schema.ResourceData, raw map[stri
// ClientKeyHmac has changed in Boundary, therefore the ClientKey has changed.
// Update ClientKey value to force tf to attempt update.
if err := d.Set(credentialStoreVaultClientCertificateKeyKey, "(changed in Boundary)"); err != nil {
return err
return diag.FromErr(err)
}
}
if err := d.Set(credentialStoreVaultClientCertificateKeyHmacKey, boundaryClientKeyHmac); err != nil {
return err
return diag.FromErr(err)
}
}
d.SetId(raw["id"].(string))
d.SetId(csId.(string))
return nil
return diags
}
func resourceCredentialStoreVaultCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -229,8 +243,8 @@ func resourceCredentialStoreVaultCreate(ctx context.Context, d *schema.ResourceD
return diag.Errorf("nil credential store after create")
}
if err := setFromVaultCredentialStoreResponseMap(d, cr.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential store from response map: %v", err)
if diagnostics := setFromVaultCredentialStoreResponseMap(d, cr.GetResponse().Map, false); diagnostics != nil {
return diagnostics
}
return nil
@@ -252,8 +266,8 @@ func resourceCredentialStoreVaultRead(ctx context.Context, d *schema.ResourceDat
return diag.Errorf("credential store nil after read")
}
if err := setFromVaultCredentialStoreResponseMap(d, cr.GetResponse().Map, true); err != nil {
return diag.Errorf("error generating credential store from response map: %v", err)
if diagnostics := setFromVaultCredentialStoreResponseMap(d, cr.GetResponse().Map, true); diagnostics != nil {
return diagnostics
}
return nil
@@ -352,8 +366,8 @@ func resourceCredentialStoreVaultUpdate(ctx context.Context, d *schema.ResourceD
return diag.Errorf("credential store nil after update")
}
if err = setFromVaultCredentialStoreResponseMap(d, crUpdate.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential store from response map: %v", err)
if diagnostics := setFromVaultCredentialStoreResponseMap(d, crUpdate.GetResponse().Map, false); diagnostics != nil {
return diagnostics
}
}
@@ -0,0 +1,237 @@
package provider
import (
"context"
"net/http"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
credentialUsernamePasswordUsernameKey = "username"
credentialUsernamePasswordPasswordKey = "password"
credentialUsernamePasswordPasswordHmacKey = "password_hmac"
credentialUsernamePasswordCredentialType = "username_password"
)
func resourceCredentialUsernamePassword() *schema.Resource {
return &schema.Resource{
Description: "The username/password credential resource allows you to configure a credential using a username and password pair.",
CreateContext: resourceCredentialUsernamePasswordCreate,
ReadContext: resourceCredentialUsernamePasswordRead,
UpdateContext: resourceCredentialUsernamePasswordUpdate,
DeleteContext: resourceCredentialUsernamePasswordDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
IDKey: {
Description: "The ID of this username/password credential.",
Type: schema.TypeString,
Computed: true,
},
NameKey: {
Description: "The name of this username/password credential. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The description of this username/password credential.",
Type: schema.TypeString,
Optional: true,
},
credentialStoreIdKey: {
Description: "The credential store in which to save this username/password credential.",
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
credentialUsernamePasswordUsernameKey: {
Description: "The username of this username/password credential.",
Type: schema.TypeString,
Required: true,
},
credentialUsernamePasswordPasswordKey: {
Description: "The password of this username/password credential.",
Type: schema.TypeString,
Required: true,
Sensitive: true,
},
credentialUsernamePasswordPasswordHmacKey: {
Description: "The password hmac.",
Type: schema.TypeString,
Computed: true,
},
},
}
}
func setFromCredentialUsernamePasswordResponseMap(d *schema.ResourceData, raw map[string]interface{}, fromRead bool) 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{})
if err := d.Set(credentialUsernamePasswordUsernameKey, attrs[credentialUsernamePasswordUsernameKey]); err != nil {
return err
}
statePasswordHmac := d.Get(credentialUsernamePasswordPasswordHmacKey)
boundaryPasswordHmac := attrs[credentialUsernamePasswordPasswordHmacKey].(string)
if statePasswordHmac.(string) != boundaryPasswordHmac && fromRead {
// PasswordHmac has changed in Boundary, therefore the password has changed.
// Update password value to force tf to attempt update.
if err := d.Set(credentialUsernamePasswordPasswordKey, "(changed in Boundary)"); err != nil {
return err
}
}
if err := d.Set(credentialUsernamePasswordPasswordHmacKey, boundaryPasswordHmac); err != nil {
return err
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceCredentialUsernamePasswordCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
var opts []credentials.Option
if v, ok := d.GetOk(NameKey); ok {
opts = append(opts, credentials.WithName(v.(string)))
}
if v, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, credentials.WithDescription(v.(string)))
}
if v, ok := d.GetOk(credentialUsernamePasswordUsernameKey); ok {
opts = append(opts, credentials.WithUsernamePasswordCredentialUsername(v.(string)))
}
if v, ok := d.GetOk(credentialUsernamePasswordPasswordKey); ok {
opts = append(opts, credentials.WithUsernamePasswordCredentialPassword(v.(string)))
}
var credentialStoreId string
retrievedStoreId, ok := d.GetOk(credentialStoreIdKey)
if ok {
credentialStoreId = retrievedStoreId.(string)
} else {
return diag.Errorf("credential store id is unset")
}
client := credentials.NewClient(md.client)
cr, err := client.Create(ctx, credentialUsernamePasswordCredentialType, credentialStoreId, opts...)
if err != nil {
return diag.Errorf("error creating credential: %v", err)
}
if cr == nil {
return diag.Errorf("nil credential after create")
}
if err := setFromCredentialUsernamePasswordResponseMap(d, cr.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential from response map: %v", err)
}
return nil
}
func resourceCredentialUsernamePasswordRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentials.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: %v", err)
}
if cr == nil {
return diag.Errorf("credential nil after read")
}
if err := setFromCredentialUsernamePasswordResponseMap(d, cr.GetResponse().Map, true); err != nil {
return diag.Errorf("error generating credential from response map: %v", err)
}
return nil
}
func resourceCredentialUsernamePasswordUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentials.NewClient(md.client)
var opts []credentials.Option
if d.HasChange(NameKey) {
opts = append(opts, credentials.DefaultName())
nameVal, ok := d.GetOk(NameKey)
if ok {
opts = append(opts, credentials.WithName(nameVal.(string)))
}
}
if d.HasChange(DescriptionKey) {
opts = append(opts, credentials.DefaultDescription())
descVal, ok := d.GetOk(DescriptionKey)
if ok {
opts = append(opts, credentials.WithDescription(descVal.(string)))
}
}
if d.HasChange(credentialUsernamePasswordUsernameKey) {
usernameVal, ok := d.GetOk(credentialUsernamePasswordUsernameKey)
if ok {
opts = append(opts, credentials.WithUsernamePasswordCredentialUsername(usernameVal.(string)))
}
}
if d.HasChange(credentialUsernamePasswordPasswordKey) {
passwordVal, ok := d.GetOk(credentialUsernamePasswordPasswordKey)
if ok {
opts = append(opts, credentials.WithUsernamePasswordCredentialPassword(passwordVal.(string)))
}
}
if len(opts) > 0 {
opts = append(opts, credentials.WithAutomaticVersioning(true))
crUpdate, err := client.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating credential: %v", err)
}
if crUpdate == nil {
return diag.Errorf("credential nil after update")
}
if err = setFromCredentialUsernamePasswordResponseMap(d, crUpdate.GetResponse().Map, false); err != nil {
return diag.Errorf("error generating credential from response map: %v", err)
}
}
return nil
}
func resourceCredentialUsernamePasswordDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentials.NewClient(md.client)
_, err := client.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting credential: %v", err)
}
return nil
}
@@ -0,0 +1,199 @@
package provider
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/boundary/testing/controller"
"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 (
usernamePasswordCredResc = "boundary_credential_username_password.example"
usernamePasswordCredName = "foo"
usernamePasswordCredDesc = "the foo"
usernamePasswordCredUsername = "default_username"
usernamePasswordCredPassword = "default_password"
usernamePasswordCredUpdate = "_random"
)
func usernamePasswordCredResource(name, description, username, password string) string {
return fmt.Sprintf(`
resource "boundary_credential_store_static" "example" {
name = "static store name"
description = "static store description"
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}
resource "boundary_credential_username_password" "example" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_static.example.id
username = "%s"
password = "%s"
}`, name,
description,
username,
password)
}
func TestAccCredentialUsernamePassword(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
res := usernamePasswordCredResource(
usernamePasswordCredName,
usernamePasswordCredDesc,
usernamePasswordCredUsername,
usernamePasswordCredPassword,
)
resUpdate := usernamePasswordCredResource(
usernamePasswordCredName,
usernamePasswordCredDesc,
usernamePasswordCredUsername+usernamePasswordCredUpdate,
usernamePasswordCredPassword+usernamePasswordCredUpdate,
)
var provider *schema.Provider
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckCredentialUsernamePasswordResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// create
Config: testConfig(url, fooOrg, firstProjectFoo, res),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(usernamePasswordCredResc, NameKey, usernamePasswordCredName),
resource.TestCheckResourceAttr(usernamePasswordCredResc, DescriptionKey, usernamePasswordCredDesc),
resource.TestCheckResourceAttr(usernamePasswordCredResc, credentialUsernamePasswordUsernameKey, usernamePasswordCredUsername),
resource.TestCheckResourceAttr(usernamePasswordCredResc, credentialUsernamePasswordPasswordKey, usernamePasswordCredPassword),
testAccCheckCredentialStoreUsernamePasswordHmac(provider),
testAccCheckCredentialUsernamePasswordResourceExists(provider, usernamePasswordCredResc),
),
},
importStep(usernamePasswordCredResc, credentialUsernamePasswordPasswordKey),
{
// update
Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(usernamePasswordCredResc, NameKey, usernamePasswordCredName),
resource.TestCheckResourceAttr(usernamePasswordCredResc, DescriptionKey, usernamePasswordCredDesc),
resource.TestCheckResourceAttr(usernamePasswordCredResc, credentialUsernamePasswordUsernameKey, usernamePasswordCredUsername+usernamePasswordCredUpdate),
resource.TestCheckResourceAttr(usernamePasswordCredResc, credentialUsernamePasswordPasswordKey, usernamePasswordCredPassword+usernamePasswordCredUpdate),
testAccCheckCredentialStoreUsernamePasswordHmac(provider),
testAccCheckCredentialUsernamePasswordResourceExists(provider, usernamePasswordCredResc),
),
},
importStep(usernamePasswordCredResc, credentialUsernamePasswordPasswordKey),
{
// Run a plan only update and verify no changes
PlanOnly: true,
Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate),
},
importStep(usernamePasswordCredResc, credentialUsernamePasswordPasswordKey),
{
// update again but apply a preConfig to externally update resource
PreConfig: func() { usernamePasswordCredExternalUpdate(t, provider) },
Config: testConfig(url, fooOrg, firstProjectFoo, resUpdate),
},
importStep(usernamePasswordCredResc, credentialUsernamePasswordPasswordKey),
},
})
}
func usernamePasswordCredExternalUpdate(t *testing.T, testProvider *schema.Provider) {
if storeId == "" {
t.Fatal("storeId must be set before testing an external update")
}
md := testProvider.Meta().(*metaData)
c := credentials.NewClient(md.client)
cr, err := c.Read(context.Background(), storeId)
if err != nil {
t.Fatal(fmt.Errorf("got an error reading %q: %w", storeId, err))
}
// update credential options
var opts []credentials.Option
opts = append(opts, credentials.WithDescription("this is an updated description, my guy"))
_, err = c.Update(context.Background(), cr.Item.Id, cr.Item.Version, opts...)
if err != nil {
t.Fatal(fmt.Errorf("got an error updating %q: %w", cr.Item.Id, err))
}
}
func testAccCheckCredentialUsernamePasswordResourceExists(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")
}
storeId = id
md := testProvider.Meta().(*metaData)
c := credentials.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 testAccCheckCredentialUsernamePasswordResourceDestroy(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_username_password":
id := rs.Primary.ID
c := credentials.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 username password credential %q: %v", id, err)
}
default:
continue
}
}
return nil
}
}
func testAccCheckCredentialStoreUsernamePasswordHmac(testProvider *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[usernamePasswordCredResc]
if !ok {
return fmt.Errorf("not found: %s", usernamePasswordCredResc)
}
computed := rs.Primary.Attributes["password_hmac"]
if len(computed) != 43 {
return fmt.Errorf("Computed password hmac not the expected length of 43 characters. hmac: %q", computed)
}
return nil
}
}
+9 -8
View File
@@ -75,7 +75,7 @@ func setFromGroupResponseMap(d *schema.ResourceData, raw map[string]interface{})
return nil
}
func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) {
md := meta.(*metaData)
var scopeId string
@@ -108,7 +108,12 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter
if gcr == nil {
return diag.Errorf("group nil after create")
}
raw := gcr.GetResponse().Map
apiResponse := gcr.GetResponse().Map
defer func() {
if err := setFromGroupResponseMap(d, apiResponse); err != nil {
errs = append(errs, diag.FromErr(err)...)
}
}()
if val, ok := d.GetOk(groupMemberIdsKey); ok {
list := val.(*schema.Set).List()
@@ -118,16 +123,12 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter
}
gcsmr, err := grps.SetMembers(ctx, gcr.Item.Id, gcr.Item.Version, memberIds)
if err != nil {
return diag.Errorf("error setting principals on role: %v", err)
return diag.Errorf("error setting members on group: %v", err)
}
if gcsmr == nil {
return diag.Errorf("group nil after setting members")
}
raw = gcsmr.GetResponse().Map
}
if err := setFromGroupResponseMap(d, raw); err != nil {
return diag.FromErr(err)
apiResponse = gcsmr.GetResponse().Map
}
return nil
+2 -1
View File
@@ -114,7 +114,7 @@ resource "boundary_group" "proj1" {
// NOTE: this test also tests out the recovery KMS mechanism.
func TestAccGroup(t *testing.T) {
wrapper := testWrapper(t, tcRecoveryKey)
wrapper := testWrapper(context.Background(), t, tcRecoveryKey)
tc := controller.NewTestController(t, append(tcConfig, controller.WithRecoveryKms(wrapper))...)
defer tc.Shutdown()
@@ -122,6 +122,7 @@ func TestAccGroup(t *testing.T) {
var provider *schema.Provider
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckGroupResourceDestroy(t, provider),
Steps: []resource.TestStep{
@@ -16,8 +16,8 @@ const (
func resourceHostCatalog() *schema.Resource {
return &schema.Resource{
DeprecationMessage: "Deprecated: use `resource_host_catalog_static` instead.",
Description: "Deprecated: use `resource_host_catalog_static` instead.",
DeprecationMessage: "Deprecated: use `boundary_host_catalog_static` instead.",
Description: "Deprecated: use `boundary_host_catalog_static` instead.",
CreateContext: resourceHostCatalogStaticCreate(true),
ReadContext: resourceHostCatalogStaticRead(true),
+10 -9
View File
@@ -17,8 +17,8 @@ const (
func resourceHostSet() *schema.Resource {
return &schema.Resource{
DeprecationMessage: "Deprecated: use `resource_host_set_static` instead.",
Description: "Deprecated: use `resource_host_set_static` instead.",
DeprecationMessage: "Deprecated: use `boundary_host_set_static` instead.",
Description: "Deprecated: use `boundary_host_set_static` instead.",
CreateContext: resourceHostSetStaticCreate,
ReadContext: resourceHostSetStaticRead,
@@ -139,7 +139,7 @@ func setFromHostSetStaticResponseMap(d *schema.ResourceData, raw map[string]inte
return nil
}
func resourceHostSetStaticCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
func resourceHostSetStaticCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) {
md := meta.(*metaData)
var hostsetHostCatalogId string
@@ -195,7 +195,12 @@ func resourceHostSetStaticCreate(ctx context.Context, d *schema.ResourceData, me
if hscr == nil {
return diag.Errorf("nil host set after create")
}
raw := hscr.GetResponse().Map
apiResponse := hscr.GetResponse().Map
defer func() {
if err := setFromHostSetStaticResponseMap(d, apiResponse); err != nil {
errs = append(errs, diag.FromErr(err)...)
}
}()
if hostIds != nil {
hsshr, err := hsClient.SetHosts(ctx, hscr.Item.Id, hscr.Item.Version, hostIds)
@@ -205,11 +210,7 @@ func resourceHostSetStaticCreate(ctx context.Context, d *schema.ResourceData, me
if hsshr == nil {
return diag.Errorf("nil host set after setting hosts")
}
raw = hsshr.GetResponse().Map
}
if err := setFromHostSetStaticResponseMap(d, raw); err != nil {
return diag.FromErr(err)
apiResponse = hsshr.GetResponse().Map
}
return nil
+2 -2
View File
@@ -17,8 +17,8 @@ const (
func resourceHost() *schema.Resource {
return &schema.Resource{
DeprecationMessage: "Deprecated: use `resource_host_static` instead.",
Description: "Deprecated: use `resource_host_static` instead.",
DeprecationMessage: "Deprecated: use `boundary_host_static` instead.",
Description: "Deprecated: use `boundary_host_static` instead.",
CreateContext: resourceHostStaticCreate,
ReadContext: resourceHostStaticRead,
@@ -41,7 +41,7 @@ resource "boundary_managed_group" "foo" {
)
func TestAccManagedGroup(t *testing.T) {
wrapper := testWrapper(t, tcRecoveryKey)
wrapper := testWrapper(context.Background(), t, tcRecoveryKey)
tp := oidc.StartTestProvider(t)
tc := controller.NewTestController(t, append(tcConfig, controller.WithRecoveryKms(wrapper))...)
+34 -43
View File
@@ -14,7 +14,6 @@ const (
roleGrantScopeIdKey = "grant_scope_id"
rolePrincipalIdsKey = "principal_ids"
roleGrantStringsKey = "grant_strings"
roleDefaultRoleKey = "default_role"
)
func resourceRole() *schema.Resource {
@@ -68,11 +67,6 @@ func resourceRole() *schema.Resource {
Optional: true,
Computed: true,
},
roleDefaultRoleKey: {
Type: schema.TypeBool,
Optional: true,
Description: "Indicates that the role containing this value is the default role (that is, has the id 'r_default'), which triggers some specialized behavior to allow it to be imported and managed.",
},
},
}
}
@@ -100,12 +94,7 @@ func setFromRoleResponseMap(d *schema.ResourceData, raw map[string]interface{})
return nil
}
func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
if d.Get(roleDefaultRoleKey).(bool) {
d.SetId("r_default")
return resourceRoleRead(ctx, d, meta)
}
func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) {
md := meta.(*metaData)
var scopeId string
@@ -162,35 +151,38 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
if tcr == nil {
return diag.Errorf("nil role after create")
}
raw := tcr.GetResponse().Map
apiResponse := tcr.GetResponse().Map
defer func() {
if err := setFromRoleResponseMap(d, apiResponse); err != nil {
errs = append(errs, diag.FromErr(err)...)
}
}()
if principalIds != nil {
tspr, err := rc.SetPrincipals(ctx, tcr.Item.Id, 0, principalIds, roles.WithAutomaticVersioning(true))
if err != nil {
return diag.Errorf("error setting principal IDs on role: %v", err)
switch {
case err != nil:
errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "error setting principals", Detail: err.Error()})
case tspr == nil:
errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting principals"})
default:
apiResponse = tspr.GetResponse().Map
}
if tspr == nil {
return diag.Errorf("nil role after setting principal IDs")
}
raw = tspr.GetResponse().Map
}
if grantStrings != nil {
tsgr, err := rc.SetGrants(ctx, tcr.Item.Id, 0, grantStrings, roles.WithAutomaticVersioning(true))
if err != nil {
return diag.Errorf("error setting grant strings on role: %v", err)
switch {
case err != nil:
errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grants", Detail: err.Error()})
case tsgr == nil:
errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting grants"})
default:
apiResponse = tsgr.GetResponse().Map
}
if tsgr == nil {
return diag.Errorf("nil role after setting grant strings")
}
raw = tsgr.GetResponse().Map
}
if err := setFromRoleResponseMap(d, raw); err != nil {
return diag.FromErr(err)
}
return nil
return errs
}
func resourceRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -279,6 +271,7 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
}
}
var diags diag.Diagnostics
if d.HasChange(roleGrantStringsKey) {
var grantStrings []string
if grantStringsVal, ok := d.GetOk(roleGrantStringsKey); ok {
@@ -289,10 +282,11 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
}
_, err := rc.SetGrants(ctx, d.Id(), 0, grantStrings, roles.WithAutomaticVersioning(true))
if err != nil {
return diag.Errorf("error updating grant strings on role: %v", err)
}
if err := d.Set(roleGrantStringsKey, grantStrings); err != nil {
return diag.FromErr(err)
diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grants", Detail: err.Error()})
} else {
if err := d.Set(roleGrantStringsKey, grantStrings); err != nil {
return diag.FromErr(err)
}
}
}
@@ -306,21 +300,18 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
}
_, err := rc.SetPrincipals(ctx, d.Id(), 0, principalIds, roles.WithAutomaticVersioning(true))
if err != nil {
return diag.Errorf("error updating grant strings on role: %v", err)
}
if err := d.Set(rolePrincipalIdsKey, principalIds); err != nil {
return diag.FromErr(err)
diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting principals", Detail: err.Error()})
} else {
if err := d.Set(rolePrincipalIdsKey, principalIds); err != nil {
return diag.FromErr(err)
}
}
}
return nil
return diags
}
func resourceRoleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
if d.Get(roleDefaultRoleKey).(bool) {
return nil
}
md := meta.(*metaData)
rc := roles.NewClient(md.client)
+107 -23
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net/http"
"regexp"
"strings"
"testing"
@@ -54,13 +55,23 @@ resource "boundary_role" "foo" {
depends_on = [boundary_role.proj1_admin]
}`, fooRoleDescriptionUpdate)
projRoleWithPrincipal = `
fooUser = `
resource "boundary_user" "foo" {
name = "foo"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}
`
barUser = `
resource "boundary_user" "bar" {
name = "bar"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}
`
projRoleWithPrincipal = `
resource "boundary_role" "with_principal" {
name = "with_principal"
description = "with principal"
@@ -69,27 +80,33 @@ resource "boundary_role" "with_principal" {
depends_on = [boundary_role.proj1_admin]
}`
projRoleWithPrincipalUpdate = `
resource "boundary_user" "foo" {
name = "foo"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}
resource "boundary_user" "bar" {
name = "bar"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}
projRoleWithInvalidPrincipal = `
resource "boundary_role" "with_principal" {
name = "with_principal"
description = "with principal"
principal_ids = ["u_fakeuser"]
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}`
projRoleWithPrincipalUpdate = `
resource "boundary_role" "with_principal" {
name = "with_principal_update"
description = "with principal update"
principal_ids = [boundary_user.foo.id, boundary_user.bar.id]
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}`
projRoleWithInvalidPrincipalUpdate = `
resource "boundary_role" "with_principal" {
name = "with_principal_update"
description = "with principal update"
principal_ids = [boundary_user.foo.id, "u_fakeuser"]
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}`
projRoleWithGroups = `
resource "boundary_group" "foo" {
name = "foo"
@@ -128,6 +145,7 @@ resource "boundary_role" "with_groups" {
readonlyGrant = "id=*;type=*;actions=read"
readonlyGrantUpdate = "id=*;type=*;actions=read,create"
invalidGrant = "id=*;type=*;actions=badaction"
projRoleWithGrants = fmt.Sprintf(`
resource "boundary_role" "with_grants" {
@@ -138,10 +156,28 @@ resource "boundary_role" "with_grants" {
depends_on = [boundary_role.proj1_admin]
}`, readonlyGrant)
projRoleWithGrantsUpdate = fmt.Sprintf(`
projRoleWithInvalidGrants = fmt.Sprintf(`
resource "boundary_role" "with_grants" {
name = "with_grants"
description = "with grants"
grant_strings = ["%s"]
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}`, invalidGrant)
projRoleWithInvalidGrantsUpdate = fmt.Sprintf(`
resource "boundary_role" "with_grants" {
name = "with_grants_update"
description = "with grants update"
grant_strings = ["%s", "%s"]
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}`, readonlyGrant, invalidGrant)
projRoleWithGrantsUpdate = fmt.Sprintf(`
resource "boundary_role" "with_grants" {
name = "with_grants_update"
description = "with grants update"
grant_strings = ["%s", "%s"]
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
@@ -209,11 +245,23 @@ func TestAccRoleWithGrants(t *testing.T) {
var provider *schema.Provider
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckRoleResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// test project role create with grants
// Create should return error due to invalid grant, however the role will still
// be created and should be set in state.
Config: testConfig(url, fooOrg, firstProjectFoo, projRoleWithInvalidGrants),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_grants"),
resource.TestCheckResourceAttr("boundary_role.with_grants", "name", "with_grants"),
resource.TestCheckResourceAttr("boundary_role.with_grants", "description", "with grants"),
),
ExpectError: regexp.MustCompile(`Improperly`),
},
{
// Create again with valid grants should succeed
Config: testConfig(url, fooOrg, firstProjectFoo, projRoleWithGrants),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_grants"),
@@ -224,13 +272,24 @@ func TestAccRoleWithGrants(t *testing.T) {
},
importStep("boundary_role.with_grants"),
{
// test project role update with grants
// Update should return error due to invalid grant, however the role will still
// be updated and should be set in state.
Config: testConfig(url, fooOrg, firstProjectFoo, projRoleWithInvalidGrantsUpdate),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_grants"),
resource.TestCheckResourceAttr("boundary_role.with_grants", "name", "with_grants_update"),
resource.TestCheckResourceAttr("boundary_role.with_grants", "description", "with grants update"),
),
ExpectError: regexp.MustCompile(`Improperly`),
},
{
// Update should now succeed
Config: testConfig(url, fooOrg, firstProjectFoo, projRoleWithGrantsUpdate),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_grants"),
testAccCheckRoleResourceGrantsSet(provider, "boundary_role.with_grants", []string{readonlyGrant, readonlyGrantUpdate}),
resource.TestCheckResourceAttr("boundary_role.with_grants", "name", "with_grants"),
resource.TestCheckResourceAttr("boundary_role.with_grants", "description", "with grants"),
resource.TestCheckResourceAttr("boundary_role.with_grants", "name", "with_grants_update"),
resource.TestCheckResourceAttr("boundary_role.with_grants", "description", "with grants update"),
),
},
importStep("boundary_role.with_grants"),
@@ -245,12 +304,24 @@ func TestAccRoleWithPrincipals(t *testing.T) {
var provider *schema.Provider
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckRoleResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// test create
Config: testConfig(url, fooOrg, firstProjectFoo, projRoleWithPrincipal),
// Create with invalid principal should create role but return empty plan
// since principal was not set correctly.
Config: testConfig(url, fooOrg, firstProjectFoo, projRoleWithInvalidPrincipal),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_principal"),
resource.TestCheckResourceAttr("boundary_role.with_principal", DescriptionKey, "with principal"),
resource.TestCheckResourceAttr("boundary_role.with_principal", NameKey, "with_principal"),
),
ExpectError: regexp.MustCompile(`Unable to set principals on role`),
},
{
// Create again without invalid principal should produce empty plan
Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, projRoleWithPrincipal),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_principal"),
testAccCheckRoleResourcePrincipalsSet(provider, "boundary_role.with_principal", []string{"boundary_user.foo"}),
@@ -262,13 +333,26 @@ func TestAccRoleWithPrincipals(t *testing.T) {
},
importStep("boundary_role.with_principal"),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, projRoleWithPrincipalUpdate),
// Update with invalid principal should update role but return empty plan
// since principal was not set correctly.
Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, projRoleWithInvalidPrincipalUpdate),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_principal"),
resource.TestCheckResourceAttr("boundary_role.with_principal", DescriptionKey, "with principal update"),
resource.TestCheckResourceAttr("boundary_role.with_principal", NameKey, "with_principal_update"),
),
ExpectError: regexp.MustCompile(`Unable to set principals on role`),
},
{
// Update again without invalid principal should produce empty plan
Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, barUser, projRoleWithPrincipalUpdate),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_principal"),
testAccCheckUserResourceExists(provider, "boundary_user.foo"),
testAccCheckUserResourceExists(provider, "boundary_user.bar"),
testAccCheckRoleResourcePrincipalsSet(provider, "boundary_role.with_principal", []string{"boundary_user.foo", "boundary_user.bar"}),
resource.TestCheckResourceAttr("boundary_role.with_principal", DescriptionKey, "with principal update"),
resource.TestCheckResourceAttr("boundary_role.with_principal", NameKey, "with_principal_update"),
),
},
importStep("boundary_role.with_principal"),
+124 -46
View File
@@ -13,13 +13,15 @@ import (
const (
targetHostSourceIdsKey = "host_source_ids"
targetApplicationCredentialSourceIdsKey = "application_credential_source_ids"
targetBrokeredCredentialSourceIdsKey = "brokered_credential_source_ids"
targetInjectedAppCredentialSourceIdsKey = "injected_application_credential_source_ids"
targetDefaultPortKey = "default_port"
targetSessionMaxSecondsKey = "session_max_seconds"
targetSessionConnectionLimitKey = "session_connection_limit"
targetWorkerFilterKey = "worker_filter"
targetTypeTcp = "tcp"
targetTypeSsh = "ssh"
)
func resourceTarget() *schema.Resource {
@@ -73,8 +75,14 @@ func resourceTarget() *schema.Resource {
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
targetApplicationCredentialSourceIdsKey: {
Description: "A list of application credential source ID's.",
targetBrokeredCredentialSourceIdsKey: {
Description: "A list of brokered credential source ID's.",
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
targetInjectedAppCredentialSourceIdsKey: {
Description: "A list of injected application credential source ID's.",
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
@@ -114,7 +122,10 @@ func setFromTargetResponseMap(d *schema.ResourceData, raw map[string]interface{}
if err := d.Set(targetHostSourceIdsKey, raw["host_source_ids"]); err != nil {
return err
}
if err := d.Set(targetApplicationCredentialSourceIdsKey, raw["application_credential_source_ids"]); err != nil {
if err := d.Set(targetBrokeredCredentialSourceIdsKey, raw["brokered_credential_source_ids"]); err != nil {
return err
}
if err := d.Set(targetInjectedAppCredentialSourceIdsKey, raw["injected_application_credential_source_ids"]); err != nil {
return err
}
if err := d.Set(targetSessionMaxSecondsKey, raw["session_max_seconds"]); err != nil {
@@ -128,7 +139,7 @@ func setFromTargetResponseMap(d *schema.ResourceData, raw map[string]interface{}
}
switch raw["type"].(string) {
case targetTypeTcp:
case targetTypeTcp, targetTypeSsh:
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
if defPort, ok := attrs["default_port"].(json.Number); ok {
@@ -144,7 +155,7 @@ func setFromTargetResponseMap(d *schema.ResourceData, raw map[string]interface{}
return nil
}
func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) {
md := meta.(*metaData)
var scopeId string
@@ -161,7 +172,7 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte
return diag.Errorf("no type provided")
}
switch typeStr {
case targetTypeTcp:
case targetTypeTcp, targetTypeSsh:
default:
return diag.Errorf("invalid type provided")
}
@@ -182,10 +193,15 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte
defaultPortVal, ok := d.GetOk(targetDefaultPortKey)
if ok {
defaultPortInt := defaultPortVal.(int)
if defaultPortInt < 0 {
return diag.Errorf(`"default_port" cannot be less than zero`)
if defaultPortInt < 0 || defaultPortInt > 65535 {
return diag.Errorf(`"default_port" must be a valid tcp port`)
}
switch typeStr {
case targetTypeTcp:
opts = append(opts, targets.WithTcpTargetDefaultPort(uint32(defaultPortInt)))
case targetTypeSsh:
opts = append(opts, targets.WithSshTargetDefaultPort(uint32(defaultPortInt)))
}
opts = append(opts, targets.WithTcpTargetDefaultPort(uint32(defaultPortInt)))
}
sessionMaxSecondsVal, ok := d.GetOk(targetSessionMaxSecondsKey)
@@ -215,12 +231,21 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte
}
}
var credentialSourceIds []string
if credentialSourceIdsVal, ok := d.GetOk(targetApplicationCredentialSourceIdsKey); ok {
var brokeredCreds []string
if credentialSourceIdsVal, ok := d.GetOk(targetBrokeredCredentialSourceIdsKey); ok {
list := credentialSourceIdsVal.(*schema.Set).List()
credentialSourceIds = make([]string, 0, len(list))
brokeredCreds = make([]string, 0, len(list))
for _, i := range list {
credentialSourceIds = append(credentialSourceIds, i.(string))
brokeredCreds = append(brokeredCreds, i.(string))
}
}
var injectedCreds []string
if credentialSourceIdsVal, ok := d.GetOk(targetInjectedAppCredentialSourceIdsKey); ok {
list := credentialSourceIdsVal.(*schema.Set).List()
injectedCreds = make([]string, 0, len(list))
for _, i := range list {
injectedCreds = append(injectedCreds, i.(string))
}
}
@@ -231,7 +256,6 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte
}
tc := targets.NewClient(md.client)
tcr, err := tc.Create(ctx, typeStr, scopeId, opts...)
if err != nil {
return diag.Errorf("error creating target: %v", err)
@@ -239,7 +263,12 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte
if tcr == nil {
return diag.Errorf("target nil after create")
}
raw := tcr.GetResponse().Map
apiResponse := tcr.GetResponse().Map
defer func() {
if err := setFromTargetResponseMap(d, apiResponse); err != nil {
errs = append(errs, diag.FromErr(err)...)
}
}()
version := tcr.Item.Version
if hostSourceIds != nil {
@@ -247,20 +276,29 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte
if err != nil {
return diag.Errorf("error setting host sources on target: %v", err)
}
raw = tur.GetResponse().Map
if tur == nil {
return diag.Errorf("nil target after setting host sources")
}
apiResponse = tur.GetResponse().Map
version = tur.Item.Version
}
if credentialSourceIds != nil {
tur, err := tc.SetCredentialSources(ctx, tcr.Item.Id, version, targets.WithApplicationCredentialSourceIds(credentialSourceIds))
var credOpts []targets.Option
if brokeredCreds != nil {
credOpts = append(credOpts, targets.WithBrokeredCredentialSourceIds(brokeredCreds))
}
if injectedCreds != nil {
credOpts = append(credOpts, targets.WithInjectedApplicationCredentialSourceIds(injectedCreds))
}
if len(credOpts) > 0 {
tur, err := tc.SetCredentialSources(ctx, tcr.Item.Id, version, credOpts...)
if err != nil {
return diag.Errorf("error setting credential sources on target: %v", err)
}
raw = tur.GetResponse().Map
}
if err := setFromTargetResponseMap(d, raw); err != nil {
return diag.FromErr(err)
if tur == nil {
return diag.Errorf("nil target after setting credential sources")
}
apiResponse = tur.GetResponse().Map
}
return nil
@@ -295,6 +333,16 @@ func resourceTargetUpdate(ctx context.Context, d *schema.ResourceData, meta inte
var opts []targets.Option
var typeStr string
if typeVal, ok := d.GetOk(TypeKey); ok {
typeStr = typeVal.(string)
}
switch typeStr {
case targetTypeTcp, targetTypeSsh:
default:
return diag.Errorf("invalid type provided")
}
var name *string
if d.HasChange(NameKey) {
opts = append(opts, targets.DefaultName())
@@ -319,15 +367,30 @@ func resourceTargetUpdate(ctx context.Context, d *schema.ResourceData, meta inte
var defaultPort *int
if d.HasChange(targetDefaultPortKey) {
opts = append(opts, targets.DefaultTcpTargetDefaultPort())
defaultPortVal, ok := d.GetOk(targetDefaultPortKey)
if ok {
defaultPortInt := defaultPortVal.(int)
if defaultPortInt < 0 {
return diag.Errorf(`"default_port" cannot be less than zero`)
switch typeStr {
case targetTypeTcp:
opts = append(opts, targets.DefaultTcpTargetDefaultPort())
defaultPortVal, ok := d.GetOk(targetDefaultPortKey)
if ok {
defaultPortInt := defaultPortVal.(int)
if defaultPortInt < 0 || defaultPortInt > 65535 {
return diag.Errorf(`"default_port" must be a valid tcp port`)
}
defaultPort = &defaultPortInt
opts = append(opts, targets.WithTcpTargetDefaultPort(uint32(defaultPortInt)))
}
case targetTypeSsh:
opts = append(opts, targets.DefaultSshTargetDefaultPort())
defaultPortVal, ok := d.GetOk(targetDefaultPortKey)
if ok {
defaultPortInt := defaultPortVal.(int)
if defaultPortInt < 0 || defaultPortInt > 65535 {
return diag.Errorf(`"default_port" must be a valid tcp port`)
}
defaultPort = &defaultPortInt
opts = append(opts, targets.WithSshTargetDefaultPort(uint32(defaultPortInt)))
}
defaultPort = &defaultPortInt
opts = append(opts, targets.WithTcpTargetDefaultPort(uint32(defaultPortInt)))
}
}
@@ -428,31 +491,46 @@ func resourceTargetUpdate(ctx context.Context, d *schema.ResourceData, meta inte
}
}
// The above calls may not actually happen, so we use d.Id() and automatic
// versioning here
if d.HasChange(targetApplicationCredentialSourceIdsKey) {
var credentialSourceIds []string
if credentialSourceIdsVal, ok := d.GetOk(targetApplicationCredentialSourceIdsKey); ok {
// if any of the credential types are changed, then all credential ids must be gathered
// because the SetCredentialSources function will remove ids that are not present.
if d.HasChange(targetBrokeredCredentialSourceIdsKey) || d.HasChange(targetInjectedAppCredentialSourceIdsKey) {
credOpts := []targets.Option{
targets.WithAutomaticVersioning(true),
}
if credentialSourceIdsVal, ok := d.GetOk(targetBrokeredCredentialSourceIdsKey); ok {
var credentialSourceIds []string
credSourceIds := credentialSourceIdsVal.(*schema.Set).List()
for _, credSourceId := range credSourceIds {
credentialSourceIds = append(credentialSourceIds, credSourceId.(string))
}
credOpts = append(credOpts, targets.WithBrokeredCredentialSourceIds(credentialSourceIds))
}
opts := []targets.Option{
targets.WithAutomaticVersioning(true),
targets.DefaultApplicationCredentialSourceIds(),
}
if len(credentialSourceIds) > 0 {
opts = append(opts, targets.WithApplicationCredentialSourceIds(credentialSourceIds))
if credentialSourceIdsVal, ok := d.GetOk(targetInjectedAppCredentialSourceIdsKey); ok {
var credentialSourceIds []string
credSourceIds := credentialSourceIdsVal.(*schema.Set).List()
for _, credSourceId := range credSourceIds {
credentialSourceIds = append(credentialSourceIds, credSourceId.(string))
}
credOpts = append(credOpts, targets.WithInjectedApplicationCredentialSourceIds(credentialSourceIds))
}
_, err := tc.SetCredentialSources(ctx, d.Id(), 0, opts...)
result, err := tc.SetCredentialSources(ctx, d.Id(), 0, credOpts...)
if err != nil {
return diag.Errorf("error updating credential sources in target: %v", err)
}
if err := d.Set(targetApplicationCredentialSourceIdsKey, credentialSourceIds); err != nil {
return diag.FromErr(err)
if d.HasChange(targetBrokeredCredentialSourceIdsKey) {
if err := d.Set(targetBrokeredCredentialSourceIdsKey, result.Item.BrokeredCredentialSourceIds); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(targetInjectedAppCredentialSourceIdsKey) {
if err := d.Set(targetInjectedAppCredentialSourceIdsKey, result.Item.InjectedApplicationCredentialSourceIds); err != nil {
return diag.FromErr(err)
}
}
}
+57 -10
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"regexp"
"testing"
"github.com/hashicorp/boundary/api"
@@ -89,7 +90,7 @@ resource "boundary_target" "foo" {
host_source_ids = [
boundary_host_set.foo.id
]
application_credential_source_ids = [
brokered_credential_source_ids = [
boundary_credential_library_vault.foo.id
]
default_port = 22
@@ -108,7 +109,7 @@ resource "boundary_target" "foo" {
host_source_ids = [
boundary_host_set.bar.id
]
application_credential_source_ids = [
brokered_credential_source_ids = [
boundary_credential_library_vault.bar.id
]
default_port = 80
@@ -130,6 +131,21 @@ resource "boundary_target" "foo" {
session_connection_limit = 7
worker_filter = "type == \"bar\""
}`, fooTargetDescriptionUpdate)
fooTargetPartialSuccess = fmt.Sprintf(`
resource "boundary_target" "foo" {
name = "expected_to_fail"
description = "%s"
type = "tcp"
scope_id = boundary_scope.proj1.id
default_port = 80
depends_on = [boundary_role.proj1_admin]
session_max_seconds = 7000
session_connection_limit = 7
injected_application_credential_source_ids = [
boundary_credential_library_vault.bar.id
]
}`, fooTargetDescription)
)
func TestAccTarget(t *testing.T) {
@@ -166,7 +182,7 @@ func TestAccTarget(t *testing.T) {
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "6"),
resource.TestCheckResourceAttr("boundary_target.foo", targetWorkerFilterKey, `type == "foo"`),
testAccCheckTargetResourceHostSource(provider, "boundary_target.foo", []string{"boundary_host_set.foo"}),
testAccCheckTargetResourceAppCredSources(provider, "boundary_target.foo", []string{"boundary_credential_library_vault.foo"}),
testAccCheckTargetResourceBrokeredCredSources(provider, "boundary_target.foo", []string{"boundary_credential_library_vault.foo"}),
),
},
importStep("boundary_target.foo"),
@@ -181,7 +197,7 @@ func TestAccTarget(t *testing.T) {
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "7"),
resource.TestCheckResourceAttr("boundary_target.foo", targetWorkerFilterKey, `type == "bar"`),
testAccCheckTargetResourceHostSource(provider, "boundary_target.foo", []string{"boundary_host_set.bar"}),
testAccCheckTargetResourceAppCredSources(provider, "boundary_target.foo", []string{"boundary_credential_library_vault.bar"}),
testAccCheckTargetResourceBrokeredCredSources(provider, "boundary_target.foo", []string{"boundary_credential_library_vault.bar"}),
),
},
importStep("boundary_target.foo"),
@@ -196,7 +212,38 @@ func TestAccTarget(t *testing.T) {
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "7"),
resource.TestCheckResourceAttr("boundary_target.foo", targetWorkerFilterKey, `type == "bar"`),
testAccCheckTargetResourceHostSource(provider, "boundary_target.foo", nil),
testAccCheckTargetResourceAppCredSources(provider, "boundary_target.foo", nil),
testAccCheckTargetResourceBrokeredCredSources(provider, "boundary_target.foo", nil),
),
},
importStep("boundary_target.foo"),
{
// test updating state file when the target is created, but fails on associating an invalid injected credential source to a tcp target type
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, fooBarCredLibs, fooTargetPartialSuccess),
Check: resource.ComposeTestCheckFunc(
testAccCheckTargetResourceExists(provider, "boundary_target.foo"),
resource.TestCheckResourceAttr("boundary_target.foo", DescriptionKey, ""),
resource.TestCheckResourceAttr("boundary_target.foo", targetDefaultPortKey, "80"),
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionMaxSecondsKey, "7000"),
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "7"),
resource.TestCheckResourceAttr("boundary_target.foo", targetHostSourceIdsKey+".%", "0"),
resource.TestCheckResourceAttr("boundary_target.foo", targetInjectedAppCredentialSourceIdsKey+".%", "0"),
),
ExpectError: regexp.MustCompile("Unable to set credential sources in target: tcp.VetCredentialSources: tcp.Target only supports credential purpose"),
},
importStep("boundary_target.foo", targetInjectedAppCredentialSourceIdsKey),
{
// test resolving invalid injected credential source error without raising duplicate name error, due to state file not being in sync.
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, fooBarCredLibs, fooTargetUpdateUnsetHostAndCredSources),
Check: resource.ComposeTestCheckFunc(
testAccCheckTargetResourceExists(provider, "boundary_target.foo"),
resource.TestCheckResourceAttr("boundary_target.foo", DescriptionKey, fooTargetDescriptionUpdate),
resource.TestCheckResourceAttr("boundary_target.foo", targetDefaultPortKey, "80"),
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionMaxSecondsKey, "7000"),
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "7"),
resource.TestCheckResourceAttr("boundary_target.foo", targetWorkerFilterKey, `type == "bar"`),
resource.TestCheckResourceAttr("boundary_target.foo", targetHostSourceIdsKey+".%", "0"),
resource.TestCheckResourceAttr("boundary_target.foo", targetInjectedAppCredentialSourceIdsKey+".%", "0"),
resource.TestCheckResourceAttr("boundary_target.foo", targetBrokeredCredentialSourceIdsKey+".%", "0"),
),
},
importStep("boundary_target.foo"),
@@ -261,7 +308,7 @@ func testAccCheckTargetResourceHostSource(testProvider *schema.Provider, name st
}
}
func testAccCheckTargetResourceAppCredSources(testProvider *schema.Provider, name string, credSources []string) resource.TestCheckFunc {
func testAccCheckTargetResourceBrokeredCredSources(testProvider *schema.Provider, name string, credSources []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
@@ -298,11 +345,11 @@ func testAccCheckTargetResourceAppCredSources(testProvider *schema.Provider, nam
return fmt.Errorf("got an error when reading target %q: %w", id, err)
}
if len(t.Item.ApplicationCredentialSourceIds) != len(credSourceIDs) {
return fmt.Errorf("tf state and boundary have different number of application credential sources")
if len(t.Item.BrokeredCredentialSourceIds) != len(credSourceIDs) {
return fmt.Errorf("tf state and boundary have different number of brokered credential sources")
}
for _, stateCredSourceId := range t.Item.ApplicationCredentialSourceIds {
for _, stateCredSourceId := range t.Item.BrokeredCredentialSourceIds {
ok := false
for _, gotCredSourceID := range credSourceIDs {
if gotCredSourceID == stateCredSourceId {
@@ -310,7 +357,7 @@ func testAccCheckTargetResourceAppCredSources(testProvider *schema.Provider, nam
}
}
if !ok {
return fmt.Errorf("application credential source id in state not set in boundary: %s", stateCredSourceId)
return fmt.Errorf("brokered credential source id in state not set in boundary: %s", stateCredSourceId)
}
}
+8 -7
View File
@@ -73,7 +73,7 @@ func setFromUserResponseMap(d *schema.ResourceData, raw map[string]interface{})
return nil
}
func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) {
md := meta.(*metaData)
var scopeId string
@@ -106,7 +106,12 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf
if ucr == nil {
return diag.Errorf("user nil after create")
}
raw := ucr.GetResponse().Map
apiResponse := ucr.GetResponse().Map
defer func() {
if err := setFromUserResponseMap(d, apiResponse); err != nil {
errs = append(errs, diag.FromErr(err)...)
}
}()
if val, ok := d.GetOk(userAccountIDsKey); ok {
list := val.(*schema.Set).List()
@@ -121,11 +126,7 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf
if usrac == nil {
return diag.Errorf("user nil after setting accounts")
}
raw = usrac.GetResponse().Map
}
if err := setFromUserResponseMap(d, raw); err != nil {
return diag.FromErr(err)
apiResponse = usrac.GetResponse().Map
}
return nil
+6
View File
@@ -0,0 +1,6 @@
# Boundary External Plugins Modules
The Go modules under `plugins` are not intended to be used directly. They are
here purely to allow compilation of the various plugins Boundary supports
without pulling the dependencies (and any clashes, and any init behavior)
directly into Boundary.
+19
View File
@@ -0,0 +1,19 @@
package kms_plugin_assets
import (
"io/fs"
"path"
"runtime"
)
const contentDir = "assets"
func FileSystem() fs.FS {
// Remove the root
dir := path.Join(contentDir, runtime.GOOS, runtime.GOARCH)
f, err := fs.Sub(content, dir)
if err != nil {
panic(err)
}
return f
}
@@ -0,0 +1,3 @@
This directory contains assets for darwin amd64 kms plugins. This file
in particular exists so that the Go embed package does not throw errors
when importing this module outside of the main tree.
@@ -0,0 +1,3 @@
This directory contains assets for darwin arm64 kms plugins. This file
in particular exists so that the Go embed package does not throw errors
when importing this module outside of the main tree.
Loaded 100 of 144 files, more files were not shown because too many files have changed in this diff. Show more