Compare commits

...
27 Commits
Author SHA1 Message Date
Hugo 1c1c095a66 docs: Update changelog for new version
Lint GitHub Actions workflows / actionlint (push) Successful in 15s
make-gen-delta / Check for uncommitted changes from make gen (push) Failing after 1m9s
Release / go-version (push) Successful in 3s
Release / release-notes (push) Failing after 7s
Release / Terraform Provider Release (push) Skipped
test / test (push) Failing after 52m54s
2024-10-21 17:15:10 +01:00
Hugo f9fd811205 feat(provider): Support worker filter on dynamic host catalogs 2024-10-21 17:15:10 +01:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] ad43c0bec0 Result of tsccr-helper -log-level=info gha update -latest . (#625)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-10-09 12:29:58 -04:00
Hugo 5d6d277186 chore(provider): Remove support for GrantScopeId (#627) 2024-10-08 18:37:12 +01:00
Michael Li 4590e7f8c8 chore: Update use of deprecated utility (#624) 2024-09-23 14:26:01 -04:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] 47c3f92f5c Result of tsccr-helper -log-level=info gha update -latest . (#622)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-09-03 10:28:25 -04:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] 490476a5e5 Result of tsccr-helper -log-level=info gha update -latest . (#620)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-08-01 09:38:25 -04:00
Bruno Schaatsbergen a256b763ae docs: separate worker and controller led resources (#619)
* docs: separate worker and controller led resources

* docs: change title for consistency

* docs: make docs

* chore: use unique names for workers and add an 'Example Usage' section
2024-07-22 09:24:22 -04:00
Bruno Schaatsbergen f6539b969c chore: replace deprecated arguments in provider example (#617)
* chore: replace deprecated arguments in provider example

* chore: fix extra newline

* Revert "chore: fix extra newline"

This reverts commit cf2de60acfcf6c9499d0306243041f950a0851f6.

* Revert "chore: replace deprecated arguments in provider example"

This reverts commit 49a336f7684707edb36021eef7d11bb92e4fa1b4.

* chore: change example

* chore: update example in README

* chore: create a tests for the deprecated auth method
2024-07-15 10:02:58 -04:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] 695e28a38b Result of tsccr-helper -log-level=info gha update -latest . (#616)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-07-10 09:38:56 -04:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] b7b11dd817 Result of tsccr-helper -log-level=info gha update -latest . (#615)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-06-03 09:55:35 -04:00
Michael Li b9bc73d4cf chore: Update to use Node20 version of Actions (#610) 2024-05-08 15:11:15 -04:00
Damian Debkowski e847f5fde7 chore: update changelog for 1.1.15 release
Lint GitHub Actions workflows / actionlint (push) Failing after 16s
make-gen-delta / Check for uncommitted changes from make gen (push) Failing after 1m48s
Release / go-version (push) Successful in 8s
Release / release-notes (push) Failing after 39s
Release / Terraform Provider Release (push) Skipped
test / test (push) Failing after 4m50s
2024-05-01 09:57:27 -07:00
Todd 53c2a5769c Add target alias resource (#571)
* Add target alias resource

* increment the number of authorize_acctions returned on an admin user
2024-05-01 09:37:45 -07:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] ed1bf21505 Result of tsccr-helper -log-level=info gha update -latest . (#606)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-05-01 10:19:55 -04:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] 6a23fc20f9 Result of tsccr-helper -log-level=info gha update -latest . (#591)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-04-01 09:55:08 -04:00
Louis Ruch 41653b2ccd chore: Update deps 2024-03-03 22:02:25 -08:00
dependabot[bot] 398eaf8406 build(deps): bump github.com/jimlambrt/gldap from 0.1.9 to 0.1.11
Bumps [github.com/jimlambrt/gldap](https://github.com/jimlambrt/gldap) from 0.1.9 to 0.1.11.
- [Release notes](https://github.com/jimlambrt/gldap/releases)
- [Commits](https://github.com/jimlambrt/gldap/compare/v0.1.9...v0.1.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-03 21:30:12 -08:00
dependabot[bot] c71ba43a3d build(deps): bump golang.org/x/crypto from 0.19.0 to 0.20.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/crypto/compare/v0.19.0...v0.20.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-03 21:29:40 -08:00
hashicorp-tsccr[bot]andhashicorp-tsccr[bot] 1153ea44ee Result of tsccr-helper -log-level=info gha update -latest . (#569)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2024-03-01 09:24:07 -05:00
Danielle Miu 2020819648 add new section to changelog for next 2024-02-14 13:32:21 -05:00
Danielle Miu b7eadd565f update changelog to include policies feat
Lint GitHub Actions workflows / actionlint (push) Failing after 13s
make-gen-delta / Check for uncommitted changes from make gen (push) Failing after 1m5s
Release / go-version (push) Successful in 7s
Release / release-notes (push) Failing after 24s
Release / Terraform Provider Release (push) Skipped
test / test (push) Failing after 4m19s
2024-02-14 13:17:58 -05:00
daniandDanielle Miu 7e7707d3af feat(policies): support storage policies within terraform (#558)
Co-authored-by: Danielle Miu <29378233+DanielleMiu@users.noreply.github.com>
2024-02-13 16:22:18 -08:00
Jeff Mitchell 8c12b27f8f Add support for grant_scope_ids (#562) 2024-02-13 15:01:48 -05:00
Jeff Mitchell 93e41ae83d Add id warning (#563)
* Add id warning

This adds a warning about using id in grants, which is no longer
supported when running against 0.15+.

* Add to Create as well
2024-02-09 15:30:13 -05:00
Jeff Mitchell bbc5889404 Fix space at beginning of description 2024-02-08 09:15:26 -05:00
Elim Tsiagbey 933192280b docs: Add required field for storage bucket example (#557)
Update example for dynamic credential for storage bucket to include `disable_credential_rotation` attribute. `disable_credential_rotation` needs to be set to true when configuring dynamic credentials
2024-02-06 11:00:46 -05:00
44 changed files with 2220 additions and 659 deletions

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
actionlint: actionlint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Check workflow files - name: Check workflow files
uses: docker://docker.mirror.hashicorp.services/rhysd/actionlint@sha256:02ccb6d91e4cb4a7b21eb99d5274d257e81ae667688d730e89d7ea0d6d35db91 uses: docker://docker.mirror.hashicorp.services/rhysd/actionlint@sha256:02ccb6d91e4cb4a7b21eb99d5274d257e81ae667688d730e89d7ea0d6d35db91
with: with:
+39 -20
View File
@@ -22,7 +22,7 @@ jobs:
id: boundary-team-role id: boundary-team-role
run: | run: |
TEAM="boundary" TEAM="boundary"
ROLE="$(hub api orgs/hashicorp/teams/${TEAM}/memberships/${{ github.actor }} | jq -r '.role | select(.!=null)')" ROLE="$(gh api orgs/hashicorp/teams/${TEAM}/memberships/${{ github.actor }} | jq -r '.role | select(.!=null)')"
if [[ -n "${ROLE}" ]]; then if [[ -n "${ROLE}" ]]; then
echo "Actor ${{ github.actor }} is a ${TEAM} team member, skipping ticket creation" echo "Actor ${{ github.actor }} is a ${TEAM} team member, skipping ticket creation"
else else
@@ -32,13 +32,6 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.JIRA_SYNC_GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.JIRA_SYNC_GITHUB_TOKEN }}
- name: Login
uses: atlassian/gajira-login@45fd029b9f1d6d8926c6f04175aa80c0e42c9026 # v3.0.1
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
- name: Set ticket type - name: Set ticket type
if: github.event.action == 'opened' && !steps.boundary-team-role.outputs.role if: github.event.action == 'opened' && !steps.boundary-team-role.outputs.role
id: set-ticket-type id: set-ticket-type
@@ -52,6 +45,10 @@ jobs:
- name: Create ticket - name: Create ticket
if: github.event.action == 'opened' && !steps.boundary-team-role.outputs.role if: github.event.action == 'opened' && !steps.boundary-team-role.outputs.role
uses: tomhjp/gh-action-jira-create@3ed1789cad3521292e591a7cfa703215ec1348bf # v0.2.1 uses: tomhjp/gh-action-jira-create@3ed1789cad3521292e591a7cfa703215ec1348bf # v0.2.1
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
with: with:
project: ICU project: ICU
issuetype: "GH Issue" issuetype: "GH Issue"
@@ -64,6 +61,10 @@ jobs:
if: github.event.action != 'opened' if: github.event.action != 'opened'
id: search id: search
uses: tomhjp/gh-action-jira-search@04700b457f317c3e341ce90da5a3ff4ce058f2fa # v0.2.2 uses: tomhjp/gh-action-jira-search@04700b457f317c3e341ce90da5a3ff4ce058f2fa # v0.2.2
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
with: with:
# cf[10089] is Issue Link custom field # cf[10089] is Issue Link custom field
jql: 'issuetype = "GH Issue" and cf[10089]="${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' jql: 'issuetype = "GH Issue" and cf[10089]="${{ github.event.issue.html_url || github.event.pull_request.html_url }}"'
@@ -71,20 +72,38 @@ jobs:
- name: Sync comment - name: Sync comment
if: github.event.action == 'created' && steps.search.outputs.issue if: github.event.action == 'created' && steps.search.outputs.issue
uses: tomhjp/gh-action-jira-comment@6eb6b9ead70221916b6badd118c24535ed220bd9 # v0.2.0 uses: tomhjp/gh-action-jira-comment@6eb6b9ead70221916b6badd118c24535ed220bd9 # v0.2.0
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
with: with:
issue: ${{ steps.search.outputs.issue }} issue: ${{ steps.search.outputs.issue }}
comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}"
- name: Close ticket - name: Transitions
if: (github.event.action == 'closed' || github.event.action == 'deleted') && steps.search.outputs.issue id: transitions
uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 if: steps.search.outputs.issue
with: run: |
issue: ${{ steps.search.outputs.issue }} if [[ "${{ github.event.action }}" == "closed" || "${{ github.event.action }}" == "deleted" ]]; then
transition: Done echo "Closing ticket"
echo "name=Done" >> "$GITHUB_OUTPUT"
elif [[ "${{ github.event.action }}" == "reopened" ]]; then
echo "Reopening ticket"
echo "name='Reopen'" >> "$GITHUB_OUTPUT"
fi
- name: Reopen ticket # Transition issue API reference: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-transitions-post
if: github.event.action == 'reopened' && steps.search.outputs.issue - name: Transition ticket
uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 if: steps.transitions.outputs.name
with: run: |
issue: ${{ steps.search.outputs.issue }} transitions="$(curl --silent \
transition: "To Do" --url "${{ secrets.JIRA_BASE_URL }}rest/api/3/issue/${{ steps.search.outputs.issue }}/transitions" \
--user "${{ secrets.JIRA_USER_EMAIL }}:${{ secrets.JIRA_API_TOKEN }}" \
--header "Accept: application/json")"
id="$(echo "${transitions}" | jq -r '.transitions[] | select(.name == "${{ steps.transitions.outputs.name }}") | .id')"
curl --silent \
--url "${{ secrets.JIRA_BASE_URL }}rest/api/3/issue/${{ steps.search.outputs.issue }}/transitions" \
--user "${{ secrets.JIRA_USER_EMAIL }}:${{ secrets.JIRA_API_TOKEN }}" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data "$(printf '{"transition": {"id": "%s"}}' "${id}")"
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
name: "Check for uncommitted changes from make gen" name: "Check for uncommitted changes from make gen"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with: with:
fetch-depth: '0' fetch-depth: '0'
- name: Determine Go version - name: Determine Go version
@@ -24,7 +24,7 @@ jobs:
echo "Building with Go $(cat .go-version)" echo "Building with Go $(cat .go-version)"
echo "go-version=$(cat .go-version)" >> "$GITHUB_OUTPUT" echo "go-version=$(cat .go-version)" >> "$GITHUB_OUTPUT"
- name: Set up Go - name: Set up Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
with: with:
go-version: "${{ steps.get-go-version.outputs.go-version }}" go-version: "${{ steps.get-go-version.outputs.go-version }}"
- name: Running go mod tidy - name: Running go mod tidy
+4 -4
View File
@@ -14,7 +14,7 @@ jobs:
outputs: outputs:
version: ${{ steps.go-version.outputs.go-version }} version: ${{ steps.go-version.outputs.go-version }}
steps: steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- id: go-version - id: go-version
# We use .go-version as our source of truth for current Go # We use .go-version as our source of truth for current Go
# version, because "goenv" can react to it automatically. # version, because "goenv" can react to it automatically.
@@ -24,12 +24,12 @@ jobs:
release-notes: release-notes:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Generate Release Notes - 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 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@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with: with:
name: release-notes name: release-notes
path: release-notes.txt path: release-notes.txt
@@ -37,7 +37,7 @@ jobs:
terraform-provider-release: terraform-provider-release:
name: 'Terraform Provider Release' name: 'Terraform Provider Release'
needs: [go-version, release-notes] needs: [go-version, release-notes]
uses: hashicorp/ghaction-terraform-provider-release/.github/workflows/hashicorp.yml@v3 uses: hashicorp/ghaction-terraform-provider-release/.github/workflows/hashicorp.yml@v4
secrets: secrets:
hc-releases-github-token: '${{ secrets.HASHI_RELEASES_GITHUB_TOKEN }}' hc-releases-github-token: '${{ secrets.HASHI_RELEASES_GITHUB_TOKEN }}'
hc-releases-host-staging: '${{ secrets.HC_RELEASES_HOST_STAGING }}' hc-releases-host-staging: '${{ secrets.HC_RELEASES_HOST_STAGING }}'
+3 -3
View File
@@ -13,7 +13,7 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Determine go version - name: Determine go version
id: get-go-version id: get-go-version
# We use .go-version as our source of truth for current Go # We use .go-version as our source of truth for current Go
@@ -22,7 +22,7 @@ jobs:
echo "Building with Go $(cat .go-version)" echo "Building with Go $(cat .go-version)"
echo "go-version=$(cat .go-version)" >> "$GITHUB_OUTPUT" echo "go-version=$(cat .go-version)" >> "$GITHUB_OUTPUT"
- name: Set up Go - name: Set up Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
with: with:
go-version: "${{ steps.get-go-version.outputs.go-version }}" go-version: "${{ steps.get-go-version.outputs.go-version }}"
cache: false cache: false
@@ -32,7 +32,7 @@ jobs:
echo "go-build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT" echo "go-build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
echo "go-mod=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT" echo "go-mod=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"
- name: Set up Go modules cache - name: Set up Go modules cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with: with:
path: | path: |
${{ steps.go-cache-paths.outputs.go-build }} ${{ steps.go-cache-paths.outputs.go-build }}
+34 -2
View File
@@ -2,11 +2,43 @@
Canonical reference for changes, improvements, and bugfixes for the Boundary Terraform provider. Canonical reference for changes, improvements, and bugfixes for the Boundary Terraform provider.
## 1.2.0 (October 21, 2024)
### New and Improved
* Introduces support for specifying a worker filter in dynamic host catalogs
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/626))
### Deprecations/Changes
* With Boundary 0.15, a deprecation notice was put under the `grant_scope_id`
field, and a new `grant_scope_ids` field was introduced to replace it. With
Boundary v0.17.1 and Boundary API v0.0.52, `grant_scope_id` support was
entirely removed. `grant_scope_id` support has now been removed from this TF
provider.
## 1.1.15 (May 1, 2024)
### New and Improved
* Add support for a target alias as a resource
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/571))
## 1.1.14 (February 14, 2024)
### New and Improved
* Support the multi-value `grant_scope_ids` field in the role provider
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/562))
* Support Boundary [Storage Policies](https://developer.hashicorp.com/boundary/docs/configuration/session-recording/configure-storage-policy)
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/558))
## 1.1.13 (February 1, 2024) ## 1.1.13 (February 1, 2024)
### New and Improved ### New and Improved
* Allow dynamic credentials when configuring storage buckets. * Allow dynamic credentials when configuring storage buckets
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/549)) ([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/549))
## 1.1.12 (January 8, 2024) ## 1.1.12 (January 8, 2024)
@@ -318,4 +350,4 @@ Update provider to handle new domain errors ([PR](https://github.com/hashicorp/t
## 0.1.0 (October 14, 2020) ## 0.1.0 (October 14, 2020)
Initial release! Initial release!
+4 -4
View File
@@ -97,10 +97,10 @@ Please see our detailed docs for individual resource usage. Below is a complex e
```hcl ```hcl
provider "boundary" { provider "boundary" {
addr = "http://127.0.0.1:9200" addr = "http://127.0.0.1:9200"
auth_method_id = "ampw_1234567890" # changeme auth_method_id = "ampw_1234567890" # changeme
password_auth_method_login_name = "myuser" # changeme auth_method_login_name = "myuser" # changeme
password_auth_method_password = "passpass" # changeme auth_method_password = "passpass" # changeme
} }
variable "users" { variable "users" {
+11 -11
View File
@@ -15,23 +15,23 @@ Do not keep your authentication password in HCL for production environments, use
```terraform ```terraform
provider "boundary" { provider "boundary" {
addr = "http://127.0.0.1:9200" addr = "http://127.0.0.1:9200"
auth_method_id = "ampw_1234567890" # changeme auth_method_id = "ampw_1234567890" # changeme
password_auth_method_login_name = "myuser" # changeme auth_method_login_name = "myuser" # changeme
password_auth_method_password = "passpass" # changeme auth_method_password = "passpass" # changeme
} }
provider "boundary" { provider "boundary" {
addr = "http://127.0.0.1:9200" addr = "http://127.0.0.1:9200"
password_auth_method_login_name = "myuser" auth_method_login_name = "myuser"
password_auth_method_password = "passpass" auth_method_password = "passpass"
} }
provider "boundary" { provider "boundary" {
addr = "http://127.0.0.1:9200" addr = "http://127.0.0.1:9200"
password_auth_method_login_name = "myuser" auth_method_login_name = "myuser"
password_auth_method_password = "passpass" auth_method_password = "passpass"
scope_id = "s_1234567890" scope_id = "s_1234567890"
} }
``` ```
+106
View File
@@ -0,0 +1,106 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_alias_target Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The target alias resource allows you to configure a Boundary target alias.
---
# boundary_alias_target (Resource)
The target alias resource allows you to configure a Boundary target alias.
## 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_host_catalog_static" "foo" {
name = "test"
description = "test catalog"
scope_id = boundary_scope.project.id
}
resource "boundary_host_static" "foo" {
name = "foo"
host_catalog_id = boundary_host_catalog_static.foo.id
address = "10.0.0.1"
}
resource "boundary_host_static" "bar" {
name = "bar"
host_catalog_id = boundary_host_catalog_static.foo.id
address = "127.0.0.1"
}
resource "boundary_host_set_static" "foo" {
name = "foo"
host_catalog_id = boundary_host_catalog_static.foo.id
host_ids = [
boundary_host_static.foo.id,
boundary_host_static.bar.id,
]
}
resource "boundary_target" "foo" {
name = "foo"
description = "Foo target"
type = "tcp"
default_port = "22"
scope_id = boundary_scope.project.id
host_source_ids = [
boundary_host_set_static.foo.id,
]
}
resource "boundary_alias_target" "example_alias_target" {
name = "example_alias_target"
description = "Example alias to target foo using host boundary_host_static.bar"
scope_id = "global"
value = "example.bar.foo.boundary"
destination_id = boundary_target.foo.id
authorize_session_host_id = boundary_host_static.bar.id
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `scope_id` (String) The scope ID.
- `value` (String) The value of the alias.
### Optional
- `authorize_session_host_id` (String) The host id to pass to Boundary when performing an authorize session action.
- `description` (String) The alias description.
- `destination_id` (String) The destination of the alias.
- `name` (String) The alias name. Defaults to the resource name.
- `type` (String) The type of alias; hardcoded.
### Read-Only
- `id` (String) The ID of the account.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_alias_target.example_alias_target <my-id>
```
+1
View File
@@ -94,6 +94,7 @@ resource "boundary_host_catalog_plugin" "azure_example" {
- `plugin_name` (String) The name of the plugin that should back the resource. This or plugin_id 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_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. - `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.
- `worker_filter` (String) HCP Only. A filter used to control which PKI workers can handle dynamic host catalog requests.
### Read-Only ### Read-Only
+33
View File
@@ -0,0 +1,33 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_policy_storage Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The storage policy resource allows you to configure a Boundary storage policy. Storage policies allow an admin to configure how long session recordings must be stored and when to delete them. Storage policies must be applied to the global scope or an org scope in order to take effect.
---
# boundary_policy_storage (Resource)
The storage policy resource allows you to configure a Boundary storage policy. Storage policies allow an admin to configure how long session recordings must be stored and when to delete them. Storage policies must be applied to the global scope or an org scope in order to take effect.
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `scope_id` (String) The scope for this policy.
### Optional
- `delete_after_days` (Number) The number of days after which a session recording will be automatically deleted. Defaults to 0: never automatically delete. However, delete_after_days and retain_for_days cannot both be 0.
- `delete_after_overridable` (Boolean) Whether or not the associated delete_after_days value can be overridden by org scopes. Note: if the associated delete_after_days value is 0, overridable is ignored
- `description` (String) The policy description.
- `name` (String) The policy name. Defaults to the resource name.
- `retain_for_days` (Number) The number of days a session recording is required to be stored. Defaults to 0: allow deletions at any time. However, retain_for_days and delete_after_days cannot both be 0.
- `retain_for_overridable` (Boolean) Whether or not the associated retain_for_days value can be overridden by org scopes. Note: if the associated retain_for_days value is 0, overridable is ignored.
### Read-Only
- `id` (String) The ID of the policy.
+1 -1
View File
@@ -127,7 +127,7 @@ resource "boundary_role" "readonly" {
### Optional ### Optional
- `description` (String) The role description. - `description` (String) The role description.
- `grant_scope_id` (String) - `grant_scope_ids` (Set of String) A list of scopes for which the grants in this role should apply, which can include the special values "this", "children", or "descendants"
- `grant_strings` (Set of String) A list of stringified grants for the role. - `grant_strings` (Set of String) A list of stringified grants for the role.
- `name` (String) The role name. Defaults to the resource name. - `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. - `principal_ids` (Set of String) A list of principal (user or group) IDs to add as principals on the role.
+4 -4
View File
@@ -53,10 +53,10 @@ resource "boundary_scope" "org" {
} }
resource "boundary_role" "org_admin" { resource "boundary_role" "org_admin" {
scope_id = boundary_scope.global.id scope_id = boundary_scope.global.id
grant_scope_id = boundary_scope.org.id grant_scope_ids = [boundary_scope.org.id]
grant_strings = ["ids=*;type=*;actions=*"] grant_strings = ["ids=*;type=*;actions=*"]
principal_ids = ["u_auth"] principal_ids = ["u_auth"]
} }
``` ```
+25
View File
@@ -0,0 +1,25 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_scope_policy_attachment Resource - terraform-provider-boundary"
subcategory: ""
description: |-
---
# boundary_scope_policy_attachment (Resource)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `policy_id` (String)
- `scope_id` (String)
### Read-Only
- `id` (String) The ID of this resource.
+3 -2
View File
@@ -48,8 +48,9 @@ resource "boundary_storage_bucket" "aws_dynamic_credentials_example" {
# the role_arn value should be the same arn used as the instance profile that is attached to the ec2 instance # the role_arn value should be the same arn used as the instance profile that is attached to the ec2 instance
# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html # https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html
attributes_json = jsonencode({ attributes_json = jsonencode({
"region" = "us-east-1" "region" = "us-east-1"
"role_arn" = "arn:aws:iam::123456789012:role/S3Access" "role_arn" = "arn:aws:iam::123456789012:role/S3Access"
"disable_credential_rotation" = "true"
}) })
worker_filter = "\"pki\" in \"/tags/type\"" worker_filter = "\"pki\" in \"/tags/type\""
} }
+8 -3
View File
@@ -1,5 +1,4 @@
--- ---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_worker Resource - terraform-provider-boundary" page_title: "boundary_worker Resource - terraform-provider-boundary"
subcategory: "" subcategory: ""
description: |- description: |-
@@ -12,16 +11,22 @@ The resource allows you to create a self-managed worker object.
## Example Usage ## Example Usage
### Controller-led worker
```terraform ```terraform
resource "boundary_worker" "controller_led" { resource "boundary_worker" "controller_led" {
scope_id = "global" scope_id = "global"
name = "worker 1" name = "controller-led-worker-1"
description = "self managed worker with controller led auth" description = "self managed worker with controller led auth"
} }
```
### Worker-led worker
```terraform
resource "boundary_worker" "worker_led" { resource "boundary_worker" "worker_led" {
scope_id = "global" scope_id = "global"
name = "worker 2" name = "worker-led-worker-1"
description = "self managed worker with worker led auth" description = "self managed worker with worker led auth"
worker_generated_auth_token = var.worker_generated_auth_token worker_generated_auth_token = var.worker_generated_auth_token
} }
+11 -11
View File
@@ -1,19 +1,19 @@
provider "boundary" { provider "boundary" {
addr = "http://127.0.0.1:9200" addr = "http://127.0.0.1:9200"
auth_method_id = "ampw_1234567890" # changeme auth_method_id = "ampw_1234567890" # changeme
password_auth_method_login_name = "myuser" # changeme auth_method_login_name = "myuser" # changeme
password_auth_method_password = "passpass" # changeme auth_method_password = "passpass" # changeme
} }
provider "boundary" { provider "boundary" {
addr = "http://127.0.0.1:9200" addr = "http://127.0.0.1:9200"
password_auth_method_login_name = "myuser" auth_method_login_name = "myuser"
password_auth_method_password = "passpass" auth_method_password = "passpass"
} }
provider "boundary" { provider "boundary" {
addr = "http://127.0.0.1:9200" addr = "http://127.0.0.1:9200"
password_auth_method_login_name = "myuser" auth_method_login_name = "myuser"
password_auth_method_password = "passpass" auth_method_password = "passpass"
scope_id = "s_1234567890" scope_id = "s_1234567890"
} }
@@ -0,0 +1 @@
terraform import boundary_alias_target.example_alias_target <my-id>
@@ -0,0 +1,62 @@
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_host_catalog_static" "foo" {
name = "test"
description = "test catalog"
scope_id = boundary_scope.project.id
}
resource "boundary_host_static" "foo" {
name = "foo"
host_catalog_id = boundary_host_catalog_static.foo.id
address = "10.0.0.1"
}
resource "boundary_host_static" "bar" {
name = "bar"
host_catalog_id = boundary_host_catalog_static.foo.id
address = "127.0.0.1"
}
resource "boundary_host_set_static" "foo" {
name = "foo"
host_catalog_id = boundary_host_catalog_static.foo.id
host_ids = [
boundary_host_static.foo.id,
boundary_host_static.bar.id,
]
}
resource "boundary_target" "foo" {
name = "foo"
description = "Foo target"
type = "tcp"
default_port = "22"
scope_id = boundary_scope.project.id
host_source_ids = [
boundary_host_set_static.foo.id,
]
}
resource "boundary_alias_target" "example_alias_target" {
name = "example_alias_target"
description = "Example alias to target foo using host boundary_host_static.bar"
scope_id = "global"
value = "example.bar.foo.boundary"
destination_id = boundary_target.foo.id
authorize_session_host_id = boundary_host_static.bar.id
}
@@ -0,0 +1,27 @@
resource "boundary_scope" "global" {
global_scope = true
scope_id = "global"
}
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_policy_storage" "foo" {
name = "foo"
description = "Foo policy"
scope_id = boundary_scope.org.id
retain_for_days = 10
retain_for_overridable = false
delete_after_days = 10
delete_after_overridable = true
}
resource "boundary_scope_policy_attachment" "foo_attachment" {
scope_id = boundary_scope.org.id
policy_id = boundary_policy_storage.foo.id
}
+4 -4
View File
@@ -5,8 +5,8 @@ resource "boundary_scope" "org" {
} }
resource "boundary_role" "org_admin" { resource "boundary_role" "org_admin" {
scope_id = boundary_scope.global.id scope_id = boundary_scope.global.id
grant_scope_id = boundary_scope.org.id grant_scope_ids = [boundary_scope.org.id]
grant_strings = ["ids=*;type=*;actions=*"] grant_strings = ["ids=*;type=*;actions=*"]
principal_ids = ["u_auth"] principal_ids = ["u_auth"]
} }
@@ -33,8 +33,9 @@ resource "boundary_storage_bucket" "aws_dynamic_credentials_example" {
# the role_arn value should be the same arn used as the instance profile that is attached to the ec2 instance # the role_arn value should be the same arn used as the instance profile that is attached to the ec2 instance
# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html # https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html
attributes_json = jsonencode({ attributes_json = jsonencode({
"region" = "us-east-1" "region" = "us-east-1"
"role_arn" = "arn:aws:iam::123456789012:role/S3Access" "role_arn" = "arn:aws:iam::123456789012:role/S3Access"
"disable_credential_rotation" = "true"
}) })
worker_filter = "\"pki\" in \"/tags/type\"" worker_filter = "\"pki\" in \"/tags/type\""
} }
@@ -0,0 +1,5 @@
resource "boundary_worker" "controller_led" {
scope_id = "global"
name = "controller-led-worker-1"
description = "self managed worker with controller led auth"
}
@@ -1,13 +0,0 @@
resource "boundary_worker" "controller_led" {
scope_id = "global"
name = "worker 1"
description = "self managed worker with controller-led auth"
worker_generated_auth_token = var.worker_generated_auth_token
}
resource "boundary_worker" "worker_led" {
scope_id = "global"
name = "worker 2"
description = "self managed worker with worker-led auth"
worker_generated_auth_token = var.worker_generated_auth_token
}
@@ -1,12 +1,6 @@
resource "boundary_worker" "controller_led" {
scope_id = "global"
name = "worker 1"
description = "self managed worker with controller led auth"
}
resource "boundary_worker" "worker_led" { resource "boundary_worker" "worker_led" {
scope_id = "global" scope_id = "global"
name = "worker 2" name = "worker-led-worker-1"
description = "self managed worker with worker led auth" description = "self managed worker with worker led auth"
worker_generated_auth_token = var.worker_generated_auth_token worker_generated_auth_token = var.worker_generated_auth_token
} }
+90 -88
View File
@@ -1,94 +1,95 @@
module github.com/hashicorp/terraform-provider-boundary module github.com/hashicorp/terraform-provider-boundary
go 1.21.5 go 1.23.1
require ( require (
github.com/YakDriver/regexache v0.23.0 github.com/YakDriver/regexache v0.23.0
github.com/hashicorp/boundary v0.15.0 github.com/hashicorp/boundary v0.18.0
github.com/hashicorp/boundary/api v0.0.45 github.com/hashicorp/boundary/api v0.0.53
github.com/hashicorp/boundary/sdk v0.0.42 github.com/hashicorp/boundary/sdk v0.0.49
github.com/hashicorp/cap v0.4.1 github.com/hashicorp/cap v0.5.1-0.20240315182732-faa330bfb8df
github.com/hashicorp/cap/ldap v0.0.0-20231012003312-273118a6e3b8 github.com/hashicorp/cap/ldap v0.0.0-20240206183135-ed8f24513744
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637 github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
github.com/hashicorp/go-kms-wrapping/v2 v2.0.15 github.com/hashicorp/go-kms-wrapping/v2 v2.0.17-0.20240313190905-91d44aa8e360
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11 github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.6 github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.7
github.com/hashicorp/terraform-plugin-docs v0.18.0 github.com/hashicorp/terraform-plugin-docs v0.18.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0
github.com/jimlambrt/gldap v0.1.9 github.com/jimlambrt/gldap v0.1.11
github.com/kr/pretty v0.3.1 github.com/kr/pretty v0.3.1
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.18.0 golang.org/x/crypto v0.25.0
mvdan.cc/gofumpt v0.5.0 mvdan.cc/gofumpt v0.6.0
) )
require ( require (
dario.cat/mergo v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect
github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.0 // indirect
github.com/agext/levenshtein v1.2.2 // indirect github.com/agext/levenshtein v1.2.2 // indirect
github.com/alessio/shellescape v1.4.1 // indirect github.com/alessio/shellescape v1.4.2 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/armon/go-radix v1.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect github.com/cloudflare/circl v1.3.7 // indirect
github.com/containerd/continuity v0.3.0 // indirect github.com/containerd/continuity v0.4.3 // indirect
github.com/coreos/go-oidc/v3 v3.8.0 // indirect github.com/coreos/go-oidc/v3 v3.9.0 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect github.com/danieljoos/wincred v1.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v23.0.1+incompatible // indirect github.com/docker/cli v25.0.3+incompatible // indirect
github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.5.1-0.20231206184617-48ba0b76bc88 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
github.com/fatih/color v1.16.0 // indirect github.com/fatih/color v1.17.0 // indirect
github.com/fatih/structs v1.1.0 // indirect github.com/fatih/structs v1.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/glebarez/sqlite v1.9.0 // indirect github.com/glebarez/sqlite v1.10.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/go-ldap/ldap/v3 v3.4.6 // indirect github.com/go-ldap/ldap/v3 v3.4.6 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-migrate/migrate/v4 v4.16.2 // indirect github.com/golang-migrate/migrate/v4 v4.17.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.4.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/hashicorp/cli v1.1.6 // indirect github.com/hashicorp/cli v1.1.6 // indirect
github.com/hashicorp/dbassert v0.0.0-20231012105025-1bc1bd88e22b // indirect github.com/hashicorp/dbassert v0.0.0-20231012105025-1bc1bd88e22b // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0 // indirect github.com/hashicorp/eventlogger v0.2.9 // indirect
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0 // indirect github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231208142215-efdb51ec090d // indirect
github.com/hashicorp/go-bexpr v0.1.13 // indirect github.com/hashicorp/go-bexpr v0.1.13 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-dbw v0.1.1 // indirect github.com/hashicorp/go-dbw v0.1.5-0.20240909162114-6cee92b3da36 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20231124110655-4315424d22d0 // indirect github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20231219183231-6bac757bb482 // indirect
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5 // indirect github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.7 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect
github.com/hashicorp/go-rate v0.0.0-20231204194614-cc8d401f70ab // indirect github.com/hashicorp/go-rate v0.0.0-20231204194614-cc8d401f70ab // indirect
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 // indirect github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 // indirect
github.com/hashicorp/go-secure-stdlib/gatedwriter v0.1.1 // indirect github.com/hashicorp/go-secure-stdlib/gatedwriter v0.1.1 // indirect
@@ -98,34 +99,33 @@ require (
github.com/hashicorp/go-secure-stdlib/reloadutil 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/strutil v0.1.2 // indirect
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3 // indirect github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3 // indirect
github.com/hashicorp/go-sockaddr v1.0.5 // indirect github.com/hashicorp/go-sockaddr v1.0.6 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hc-install v0.6.2 // indirect github.com/hashicorp/hc-install v0.6.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/hcl/v2 v2.19.1 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/nodeenrollment v0.2.9 // indirect github.com/hashicorp/nodeenrollment v0.2.13 // indirect
github.com/hashicorp/terraform-exec v0.20.0 // indirect github.com/hashicorp/terraform-exec v0.20.0 // indirect
github.com/hashicorp/terraform-json v0.21.0 // indirect github.com/hashicorp/terraform-json v0.21.0 // indirect
github.com/hashicorp/terraform-plugin-go v0.20.0 // indirect github.com/hashicorp/terraform-plugin-go v0.22.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/vault/api v1.10.0 // indirect github.com/hashicorp/vault/api v1.12.0 // indirect
github.com/hashicorp/vault/sdk v0.3.0 // indirect github.com/hashicorp/vault/sdk v0.11.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect
github.com/huandu/xstrings v1.4.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect github.com/imdario/mergo v0.3.16 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.1 // indirect github.com/jackc/pgconn v1.14.3 // indirect
github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.2 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/jackc/pgx/v4 v4.18.1 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jackc/pgx/v5 v5.4.3 // indirect
github.com/jefferai/go-libsecret v0.0.0-20210525195240-b53481abef97 // 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/isbadcipher v0.0.0-20190226160619-51d2077c035f // indirect
github.com/jefferai/keyring v1.1.7-0.20220316160357-58a74bb55891 // indirect github.com/jefferai/keyring v1.1.7-0.20220316160357-58a74bb55891 // indirect
@@ -138,7 +138,6 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a // indirect github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a // indirect
github.com/mitchellh/cli v1.1.5 // indirect github.com/mitchellh/cli v1.1.5 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -147,65 +146,68 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/pointerstructure v1.2.1 // indirect github.com/mitchellh/pointerstructure v1.2.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect
github.com/mtibben/percent v0.2.1 // indirect github.com/mtibben/percent v0.2.1 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/oklog/run v1.1.0 // indirect github.com/oklog/run v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/image-spec v1.1.0-rc6 // indirect
github.com/opencontainers/runc v1.1.12 // indirect github.com/opencontainers/runc v1.2.0-rc.3 // indirect
github.com/ory/dockertest/v3 v3.10.0 // indirect github.com/ory/dockertest/v3 v3.10.0 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/posener/complete v1.2.3 // indirect github.com/posener/complete v1.2.3 // indirect
github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_golang v1.18.0 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/common v0.46.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect github.com/prometheus/procfs v0.12.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/russross/blackfriday v1.6.0 // indirect github.com/russross/blackfriday v1.6.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sethvargo/go-diceware v0.3.0 // indirect github.com/sethvargo/go-diceware v0.3.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.2 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cast v1.6.0 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xo/dburl v0.16.0 // indirect github.com/xo/dburl v0.23.1 // indirect
github.com/yuin/goldmark v1.6.0 // indirect github.com/yuin/goldmark v1.6.0 // indirect
github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/yuin/goldmark-meta v1.1.0 // indirect
github.com/zalando/go-keyring v0.2.3 // indirect github.com/zalando/go-keyring v0.2.3 // indirect
github.com/zclconf/go-cty v1.14.1 // indirect github.com/zclconf/go-cty v1.14.2 // indirect
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect
golang.org/x/mod v0.14.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.18.0 // indirect golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect
golang.org/x/sync v0.4.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.16.0 // indirect golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.16.0 // indirect golang.org/x/term v0.22.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.14.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/appengine v1.6.8 // indirect google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect
google.golang.org/grpc v1.60.0 // indirect google.golang.org/grpc v1.61.1 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/postgres v1.5.4 // indirect gorm.io/driver/postgres v1.5.9 // indirect
gorm.io/driver/sqlite v1.5.4 // indirect gorm.io/driver/sqlite v1.5.6 // indirect
gorm.io/gorm v1.25.5 // indirect gorm.io/gorm v1.25.11 // indirect
modernc.org/libc v1.22.5 // indirect modernc.org/libc v1.41.0 // indirect
modernc.org/mathutil v1.5.0 // indirect modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.5.0 // indirect modernc.org/memory v1.7.2 // indirect
modernc.org/sqlite v1.23.1 // indirect modernc.org/sqlite v1.28.0 // indirect
) )
+211 -429
View File
File diff suppressed because it is too large. Load diff
+4
View File
@@ -56,4 +56,8 @@ const (
ParentScopeIdKey = "parent_scope_id" ParentScopeIdKey = "parent_scope_id"
// GroupMemberIdsKey is used for common "member_ids" resource attribute // GroupMemberIdsKey is used for common "member_ids" resource attribute
GroupMemberIdsKey = "member_ids" GroupMemberIdsKey = "member_ids"
// ValueKey is used for common "value" resource attribute
ValueKey = "value"
// DestinationIdKey is used for common "destination_id" resource attribute
DestinationIdKey = "destination_id"
) )
+1 -1
View File
@@ -42,7 +42,7 @@ resource "boundary_scope" "project" {
resource "boundary_role" "org_admin" { resource "boundary_role" "org_admin" {
scope_id = "global" scope_id = "global"
grant_scope_id = boundary_scope.org.id grant_scope_ids = [boundary_scope.org.id]
grant_strings = ["ids=*;type=*;actions=*"] grant_strings = ["ids=*;type=*;actions=*"]
principal_ids = ["u_auth"] principal_ids = ["u_auth"]
} }
+1 -1
View File
@@ -83,7 +83,7 @@ func TestAccUserDataSource_globalAdminUser(t *testing.T) {
resource.TestCheckResourceAttr(dataSourceName, LoginNameKey, "testuser"), resource.TestCheckResourceAttr(dataSourceName, LoginNameKey, "testuser"),
resource.TestMatchResourceAttr(dataSourceName, IDKey, regexache.MustCompile(`^u_.+`)), resource.TestMatchResourceAttr(dataSourceName, IDKey, regexache.MustCompile(`^u_.+`)),
resource.TestMatchResourceAttr(dataSourceName, PrimaryAccountIdKey, regexache.MustCompile(`^acctpw_.+`)), resource.TestMatchResourceAttr(dataSourceName, PrimaryAccountIdKey, regexache.MustCompile(`^acctpw_.+`)),
resource.TestCheckResourceAttr(dataSourceName, "authorized_actions.#", "7"), resource.TestCheckResourceAttr(dataSourceName, "authorized_actions.#", "8"),
resource.TestCheckResourceAttr(dataSourceName, "scope.0.name", "global"), resource.TestCheckResourceAttr(dataSourceName, "scope.0.name", "global"),
resource.TestCheckResourceAttr(dataSourceName, "scope.0.id", "global"), resource.TestCheckResourceAttr(dataSourceName, "scope.0.id", "global"),
resource.TestCheckResourceAttr(dataSourceName, "scope.0.type", "global"), resource.TestCheckResourceAttr(dataSourceName, "scope.0.type", "global"),
+3
View File
@@ -98,6 +98,7 @@ func New() *schema.Provider {
"boundary_account_password": resourceAccountPassword(), "boundary_account_password": resourceAccountPassword(),
"boundary_account_oidc": resourceAccountOidc(), "boundary_account_oidc": resourceAccountOidc(),
"boundary_account_ldap": resourceAccountLdap(), "boundary_account_ldap": resourceAccountLdap(),
"boundary_alias_target": resourceAliasTarget(),
"boundary_auth_method": resourceAuthMethod(), "boundary_auth_method": resourceAuthMethod(),
"boundary_auth_method_password": resourceAuthMethodPassword(), "boundary_auth_method_password": resourceAuthMethodPassword(),
"boundary_auth_method_oidc": resourceAuthMethodOidc(), "boundary_auth_method_oidc": resourceAuthMethodOidc(),
@@ -120,6 +121,8 @@ func New() *schema.Provider {
"boundary_host_set": resourceHostSet(), "boundary_host_set": resourceHostSet(),
"boundary_host_set_static": resourceHostSetStatic(), "boundary_host_set_static": resourceHostSetStatic(),
"boundary_host_set_plugin": resourceHostSetPlugin(), "boundary_host_set_plugin": resourceHostSetPlugin(),
"boundary_policy_storage": resourcePolicyStorage(),
"boundary_scope_policy_attachment": resourceScopePolicyAttachment(),
"boundary_role": resourceRole(), "boundary_role": resourceRole(),
"boundary_scope": resourceScope(), "boundary_scope": resourceScope(),
"boundary_storage_bucket": resourceStorageBucket(), "boundary_storage_bucket": resourceStorageBucket(),
+35
View File
@@ -106,6 +106,19 @@ provider "boundary" {
func testConfigWithDefaultAuthMethod(url string, res ...string) string { func testConfigWithDefaultAuthMethod(url string, res ...string) string {
provider := fmt.Sprintf(` provider := fmt.Sprintf(`
provider "boundary" {
addr = "%s"
auth_method_login_name = "%s"
auth_method_password = "%s"
}`, url, tcLoginName, tcPassword)
c := []string{provider}
c = append(c, res...)
return strings.Join(c, "\n")
}
func testConfigWithDeprecatedAuthMethod(url string, res ...string) string {
provider := fmt.Sprintf(`
provider "boundary" { provider "boundary" {
addr = "%s" addr = "%s"
password_auth_method_login_name = "%s" password_auth_method_login_name = "%s"
@@ -256,6 +269,28 @@ func TestConfigWithDefaultAuthMethod(t *testing.T) {
}) })
} }
func TestConfigWithDeprecatedAuthMethod(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
var provider *schema.Provider
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckScopeResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
Config: testConfigWithDeprecatedAuthMethod(url, fooOrg, firstProjectFoo, secondProject),
Check: resource.ComposeTestCheckFunc(
testAccCheckScopeResourceExists(provider, "boundary_scope.org1"),
testProviderTokenExists(provider),
),
},
},
})
}
func TestConfigWithoutAMPWCredentials(t *testing.T) { func TestConfigWithoutAMPWCredentials(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...) tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown() defer tc.Shutdown()
@@ -92,7 +92,7 @@ resource "boundary_user" "alice" {
resource "boundary_role" "ldap_principal" { resource "boundary_role" "ldap_principal" {
scope_id = boundary_scope.global.id scope_id = boundary_scope.global.id
grant_scope_id = boundary_scope.global.id grant_scope_ids = [boundary_scope.global.id]
grant_strings = ["ids=*;type=*;actions=*"] grant_strings = ["ids=*;type=*;actions=*"]
principal_ids = [boundary_user.alice.id] principal_ids = [boundary_user.alice.id]
}` }`
+269
View File
@@ -0,0 +1,269 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"net/http"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/aliases"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
aliasTypeTarget = "target"
aliasTargetAuthorizeSessionHostIdKey = "authorize_session_host_id"
)
func resourceAliasTarget() *schema.Resource {
return &schema.Resource{
Description: "The target alias resource allows you to configure a Boundary target alias.",
CreateContext: resourceTargetAliasCreate,
ReadContext: resourceTargetAliasRead,
UpdateContext: resourceTargetAliasUpdate,
DeleteContext: resourceTargetAliasDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
IDKey: {
Description: "The ID of the account.",
Type: schema.TypeString,
Computed: true,
},
NameKey: {
Description: "The alias name. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The alias description.",
Type: schema.TypeString,
Optional: true,
},
ScopeIdKey: {
Description: "The scope ID.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
ValueKey: {
Description: "The value of the alias.",
Type: schema.TypeString,
Required: true,
},
DestinationIdKey: {
Description: "The destination of the alias.",
Type: schema.TypeString,
Optional: true,
},
// Target specific configurable parameters
aliasTargetAuthorizeSessionHostIdKey: {
Description: "The host id to pass to Boundary when performing an authorize session action.",
Type: schema.TypeString,
Optional: true,
},
TypeKey: {
Description: "The type of alias; hardcoded.",
Type: schema.TypeString,
Default: aliasTypeTarget,
Optional: true,
},
},
}
}
func setFromTargetAliasResponseMap(d *schema.ResourceData, raw map[string]interface{}) error {
if err := d.Set(NameKey, raw["name"]); err != nil {
return err
}
if err := d.Set(DescriptionKey, raw["description"]); err != nil {
return err
}
if err := d.Set(ScopeIdKey, raw["scope_id"]); err != nil {
return err
}
if err := d.Set(ValueKey, raw["value"]); err != nil {
return err
}
if err := d.Set(DestinationIdKey, raw["destination_id"]); err != nil {
return err
}
if err := d.Set(TypeKey, raw["type"]); err != nil {
return err
}
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
tarAttrs, err := aliases.AttributesMapToTargetAliasAttributes(attrs)
if err != nil {
return err
}
if tarAttrs.AuthorizeSessionArguments != nil && tarAttrs.AuthorizeSessionArguments.HostId != "" {
if err := d.Set(aliasTargetAuthorizeSessionHostIdKey, tarAttrs.AuthorizeSessionArguments.HostId); err != nil {
return err
}
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceTargetAliasCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}
var typeStr string
if typeVal, ok := d.GetOk(TypeKey); ok {
typeStr = typeVal.(string)
} else {
return diag.Errorf("no type provided")
}
opts := []aliases.Option{}
if typeVal, ok := d.GetOk(ValueKey); ok {
opts = append(opts, aliases.WithValue(typeVal.(string)))
} else {
return diag.Errorf("no alias value provided")
}
if nameVal, ok := d.GetOk(NameKey); ok {
opts = append(opts, aliases.WithName(nameVal.(string)))
}
if descVal, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, aliases.WithDescription(descVal.(string)))
}
if destVal, ok := d.GetOk(DestinationIdKey); ok {
opts = append(opts, aliases.WithDestinationId(destVal.(string)))
}
if hostIdVal, ok := d.GetOk(aliasTargetAuthorizeSessionHostIdKey); ok {
opts = append(opts, aliases.WithTargetAliasAuthorizeSessionArgumentsHostId(hostIdVal.(string)))
}
aliasClient := aliases.NewClient(md.client)
alcr, err := aliasClient.Create(ctx, typeStr, scopeId, opts...)
if err != nil {
return diag.Errorf("error creating alias: %v", err)
}
if alcr == nil {
return diag.Errorf("nil alias after create")
}
if err := setFromTargetAliasResponseMap(d, alcr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceTargetAliasRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
alClient := aliases.NewClient(md.client)
alrr, err := alClient.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
return diag.Errorf("error reading auth method: %v", err)
}
if alrr == nil {
return diag.Errorf("auth method nil after read")
}
if err := setFromTargetAliasResponseMap(d, alrr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceTargetAliasUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
alClient := aliases.NewClient(md.client)
opts := []aliases.Option{}
if d.HasChange(NameKey) {
opts = append(opts, aliases.DefaultName())
if nameVal, ok := d.GetOk(NameKey); ok {
opts = append(opts, aliases.WithName(nameVal.(string)))
}
}
if d.HasChange(DescriptionKey) {
opts = append(opts, aliases.DefaultDescription())
if descVal, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, aliases.WithDescription(descVal.(string)))
}
}
if d.HasChange(ValueKey) {
if valVal, ok := d.GetOk(ValueKey); ok {
opts = append(opts, aliases.WithValue(valVal.(string)))
} else {
return diag.Errorf("no value provided")
}
}
if d.HasChange(DestinationIdKey) {
opts = append(opts, aliases.DefaultDestinationId())
if destId, ok := d.GetOk(DestinationIdKey); ok {
opts = append(opts, aliases.WithDestinationId(destId.(string)))
}
}
if d.HasChange(aliasTargetAuthorizeSessionHostIdKey) {
opts = append(opts, aliases.DefaultTargetAliasAuthorizeSessionArgumentsHostId())
if authSessHostId, ok := d.GetOk(aliasTargetAuthorizeSessionHostIdKey); ok {
opts = append(opts, aliases.WithTargetAliasAuthorizeSessionArgumentsHostId(authSessHostId.(string)))
}
}
if len(opts) > 0 {
opts = append(opts, aliases.WithAutomaticVersioning(true))
alur, err := alClient.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating auth method: %v", err)
}
if err := setFromTargetAliasResponseMap(d, alur.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
return nil
}
func resourceTargetAliasDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := aliases.NewClient(md.client)
_, err := client.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting alias: %v", err)
}
return nil
}
@@ -0,0 +1,187 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/aliases"
"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"
)
type aliasDestroyType string
const (
targetAliasType aliasDestroyType = "boundary_alias_target"
targetAliasResc = "boundary_alias_target.example"
targetAliasName = "foo"
targetAliasDesc = "the foo"
targetAliasValue = "value.example"
targetAliasValueUpdated = "value.example.updated"
targetAliasNamespace = "target"
targetAliasUpdate = "_random"
)
var fooBarTarget = `
resource "boundary_target" "foo" {
type = "tcp"
name = "test"
description = "test target"
default_port = 22
address = "127.0.0.1"
scope_id = boundary_scope.proj1.id
depends_on = [boundary_role.proj1_admin]
}`
var aliasId string
func targetAliasResource(name string, description string, value string) string {
return fmt.Sprintf(`
resource "boundary_alias_target" "example" {
name = "%s"
description = "%s"
value = "%s"
scope_id = "global"
destination_id = boundary_target.foo.id
authorize_session_host_id = "hst_1234567890"
depends_on = [boundary_target.foo]
}`, name, description, value)
}
func TestAccAliasTarget(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
res := targetAliasResource(targetAliasName, targetAliasDesc, targetAliasValue)
resUpdate := targetAliasResource(targetAliasName+targetAliasUpdate,
targetAliasDesc+targetAliasUpdate, targetAliasValueUpdated)
var provider *schema.Provider
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckAliasResourceDestroy(t, provider, targetAliasType),
Steps: []resource.TestStep{
{
// create
Config: testConfig(url, fooOrg, firstProjectFoo, fooBarTarget, res),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(targetAliasResc, NameKey, targetAliasName),
resource.TestCheckResourceAttr(targetAliasResc, DescriptionKey, targetAliasDesc),
resource.TestCheckResourceAttr(targetAliasResc, ValueKey, targetAliasValue),
testAccCheckAliasResourceExists(provider, targetAliasResc),
),
},
importStep(targetAliasResc),
{
// update
Config: testConfig(url, fooOrg, firstProjectFoo, fooBarTarget, resUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(targetAliasResc, NameKey, targetAliasName+targetAliasUpdate),
resource.TestCheckResourceAttr(targetAliasResc, DescriptionKey, targetAliasDesc+targetAliasUpdate),
resource.TestCheckResourceAttr(targetAliasResc, ValueKey, targetAliasValueUpdated),
testAccCheckAliasResourceExists(provider, targetAliasResc),
),
},
importStep(targetAliasResc),
{
// Run a plan only update and verify no changes
PlanOnly: true,
Config: testConfig(url, fooOrg, firstProjectFoo, fooBarTarget, resUpdate),
},
importStep(targetAliasResc),
{
// 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() { targetAliasExternalUpdate(t, provider) },
PlanOnly: true,
ExpectNonEmptyPlan: true,
Config: testConfig(url, fooOrg, firstProjectFoo, fooBarTarget, resUpdate),
},
importStep(targetAliasResc),
},
})
}
func targetAliasExternalUpdate(t *testing.T, testProvider *schema.Provider) {
if aliasId == "" {
t.Fatal("aliasId must be set before testing an external update")
}
md := testProvider.Meta().(*metaData)
ac := aliases.NewClient(md.client)
ar, err := ac.Read(context.Background(), aliasId)
if err != nil {
t.Fatal(fmt.Errorf("got an error reading %q: %w", storeId, err))
}
// update alias options
var opts []aliases.Option
opts = append(opts, aliases.WithDescription("this is an updated description, my guy"))
_, err = ac.Update(context.Background(), ar.Item.Id, ar.Item.Version, opts...)
if err != nil {
t.Fatal(fmt.Errorf("got an error updating %q: %w", ar.Item.Id, err))
}
}
func testAccCheckAliasResourceExists(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")
}
aliasId = id
md := testProvider.Meta().(*metaData)
c := aliases.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 testAccCheckAliasResourceDestroy(t *testing.T, testProvider *schema.Provider, typ aliasDestroyType) 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 string(typ):
id := rs.Primary.ID
c := aliases.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 target alias %q: %v", id, err)
}
default:
continue
}
}
return nil
}
}
@@ -118,6 +118,11 @@ func resourceHostCatalogPlugin() *schema.Resource {
Optional: true, Optional: true,
Computed: true, Computed: true,
}, },
WorkerFilterKey: {
Description: "HCP Only. A filter used to control which PKI workers can handle dynamic host catalog requests.",
Type: schema.TypeString,
Optional: true,
},
internalSecretsConfigHmacKey: { internalSecretsConfigHmacKey: {
Description: "Internal only. HMAC of (serverSecretsHmac + config secrets). Used for proper secrets handling.", Description: "Internal only. HMAC of (serverSecretsHmac + config secrets). Used for proper secrets handling.",
Type: schema.TypeString, Type: schema.TypeString,
@@ -294,6 +299,9 @@ func setFromHostCatalogPluginResponseMap(d *schema.ResourceData, raw map[string]
if err := d.Set(ScopeIdKey, raw[ScopeIdKey]); err != nil { if err := d.Set(ScopeIdKey, raw[ScopeIdKey]); err != nil {
return err return err
} }
if err := d.Set(WorkerFilterKey, raw[WorkerFilterKey]); err != nil {
return err
}
// Plugin stuff // Plugin stuff
{ {
if err := d.Set(PluginIdKey, raw[PluginIdKey]); err != nil { if err := d.Set(PluginIdKey, raw[PluginIdKey]); err != nil {
@@ -399,6 +407,12 @@ func resourceHostCatalogPluginCreate(ctx context.Context, d *schema.ResourceData
opts = append(opts, hostcatalogs.WithDescription(descStr)) opts = append(opts, hostcatalogs.WithDescription(descStr))
} }
workerFilterVal, ok := d.GetOk(WorkerFilterKey)
if ok {
workerFilterStr := workerFilterVal.(string)
opts = append(opts, hostcatalogs.WithWorkerFilter(workerFilterStr))
}
attrsVal, ok := d.GetOk(AttributesJsonKey) attrsVal, ok := d.GetOk(AttributesJsonKey)
if ok { if ok {
attrsStr, err := parseutil.ParsePath(attrsVal.(string)) attrsStr, err := parseutil.ParsePath(attrsVal.(string))
@@ -556,6 +570,15 @@ func resourceHostCatalogPluginUpdate(ctx context.Context, d *schema.ResourceData
} }
} }
if d.HasChange(WorkerFilterKey) {
opts = append(opts, hostcatalogs.DefaultWorkerFilter())
workerFilterVal, ok := d.GetOk(WorkerFilterKey)
if ok {
workerFilterStr := workerFilterVal.(string)
opts = append(opts, hostcatalogs.WithWorkerFilter(workerFilterStr))
}
}
if d.HasChange(AttributesJsonKey) { if d.HasChange(AttributesJsonKey) {
attrsVal, ok := d.GetOk(AttributesJsonKey) attrsVal, ok := d.GetOk(AttributesJsonKey)
if ok { if ok {
@@ -22,6 +22,9 @@ const (
testPluginHostCatalogDescription = "bar" testPluginHostCatalogDescription = "bar"
testPluginHostCatalogDescriptionUpdate = "bar foo" testPluginHostCatalogDescriptionUpdate = "bar foo"
testPluginHostCatalogDescriptionUpdate2 = "bar foo foo" testPluginHostCatalogDescriptionUpdate2 = "bar foo foo"
testPluginHostCatalogWorkerFilterUpdate = "\"dev\" in \"/tags/type\""
testPluginHostCatalogWorkerFilterUpdate2 = "\"pki\" in \"/tags/type\""
) )
// expectedAttributesState is used here and in plugin host sets to control how // expectedAttributesState is used here and in plugin host sets to control how
@@ -244,6 +247,212 @@ func TestAccPluginHostCatalog(t *testing.T) {
}) })
} }
func TestAccPluginHostCatalogWithWorkerFilter(t *testing.T) {
t.Skip("Skipping test until Boundary Terraform Provider can unit tests for Boundary Enterprise only features")
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
resName := "boundary_host_catalog_plugin.foo"
initialValuesStr := fmt.Sprintf(`
description = "%s"
attributes_json = jsonencode({
foo = "bar"
zip = "zap"
})
secrets_json = jsonencode({
hush = "puppies"
})
`,
testPluginHostCatalogDescription,
)
// Changed description, secrets and added worker filter.
valuesStrUpdate1 := fmt.Sprintf(`
description = "%s"
worker_filter = %q
attributes_json = jsonencode({
foo = "bar"
zip = "zoop"
})
secrets_json = jsonencode({
flush = "fluppies"
})
`,
testPluginHostCatalogDescriptionUpdate,
testPluginHostCatalogWorkerFilterUpdate,
)
// Changed description, no secrets update, nullify worker filter.
valuesStrUpdate2 := fmt.Sprintf(`
description = "%s"
worker_filter = null
attributes_json = jsonencode({
foo = "bar"
zip = "zoop"
})
secrets_json = jsonencode({
flush = "fluppies"
})
`,
testPluginHostCatalogDescriptionUpdate2,
)
// Same description and set a worker filter again, now empty secrets.
valuesStrUpdate3 := fmt.Sprintf(`
description = "%s"
worker_filter = %q
attributes_json = jsonencode({
foo = "bar"
zip = "zoop"
})
`,
testPluginHostCatalogDescriptionUpdate2,
testPluginHostCatalogWorkerFilterUpdate2,
)
// Same description, now explicitly unset secrets and blankify attrs and
// worker filter.
valuesStrUpdate4 := fmt.Sprintf(`
description = "%s"
secrets_json = "null"
`,
testPluginHostCatalogDescriptionUpdate2,
)
// Set values again
valuesStrUpdate5 := fmt.Sprintf(`
description = "%s"
worker_filter = %q
attributes_json = jsonencode({
foo = "bar"
zip = "zoop"
})
secrets_json = jsonencode({
flush = "fluppies"
})
`,
testPluginHostCatalogDescriptionUpdate2,
testPluginHostCatalogWorkerFilterUpdate2,
)
// Explicitly set both secrets and attributes to null
valuesStrUpdate6 := fmt.Sprintf(`
description = "%s"
worker_filter = %q
attributes_json = "null"
secrets_json = "null"
`,
testPluginHostCatalogDescriptionUpdate2,
testPluginHostCatalogWorkerFilterUpdate2,
)
initialHcl := fmt.Sprintf(projPluginHostCatalogBase, initialValuesStr)
update1Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate1)
update2Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate2)
update3Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate3)
update4Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate4)
update5Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate5)
update6Hcl := fmt.Sprintf(projPluginHostCatalogBase, valuesStrUpdate6)
var provider *schema.Provider
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckHostCatalogResourceDestroy(t, provider, baseHostCatalogType),
Steps: []resource.TestStep{
{
// test create
Config: testConfig(url, fooOrg, firstProjectFoo, initialHcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckScopeResourceExists(provider, "boundary_scope.org1"),
testAccCheckScopeResourceExists(provider, "boundary_scope.proj1"),
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslyEmptyNowSet),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescription),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update1Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetButChanged),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate),
resource.TestCheckResourceAttr(resName, WorkerFilterKey, testPluginHostCatalogWorkerFilterUpdate),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update2Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNoChange),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// this runs the same HCL; mostly used in some manual checking
// to ensure update is still called even when nothing has
// changed (as we need for secrets)
Config: testConfig(url, fooOrg, firstProjectFoo, update2Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNoChange),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update3Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNoChange),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
resource.TestCheckResourceAttr(resName, WorkerFilterKey, testPluginHostCatalogWorkerFilterUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update4Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNowEmpty),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update5Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslyEmptyNowSet),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
resource.TestCheckResourceAttr(resName, WorkerFilterKey, testPluginHostCatalogWorkerFilterUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
{
// test update
Config: testConfig(url, fooOrg, firstProjectFoo, update6Hcl),
Check: resource.ComposeTestCheckFunc(
testAccCheckPluginHostCatalogResourceExists(provider, resName, expectedAttributesStatePreviouslySetNowEmpty),
resource.TestCheckResourceAttr(resName, DescriptionKey, testPluginHostCatalogDescriptionUpdate2),
resource.TestCheckResourceAttr(resName, WorkerFilterKey, testPluginHostCatalogWorkerFilterUpdate2),
),
ExpectNonEmptyPlan: true,
},
importStep(resName, SecretsJsonKey, internalHmacUsedForSecretsConfigHmacKey, internalForceUpdateKey, internalSecretsConfigHmacKey),
},
})
}
func testAccCheckPluginHostCatalogResourceExists(testProvider *schema.Provider, name string, expAttrState expectedAttributesState) resource.TestCheckFunc { func testAccCheckPluginHostCatalogResourceExists(testProvider *schema.Provider, name string, expAttrState expectedAttributesState) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name] rs, ok := s.RootModule().Resources[name]
@@ -0,0 +1,303 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"net/http"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/policies"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
policyStorageRetainForDaysKey = "retain_for_days"
policyStorageRetainForOverridableKey = "retain_for_overridable"
policyStorageDeleteAfterDaysKey = "delete_after_days"
policyStorageDeleteAfterOverridableKey = "delete_after_overridable"
policyTypeStorage = "storage"
daysField = "days"
overridableField = "overridable"
)
func resourcePolicyStorage() *schema.Resource {
return &schema.Resource{
Description: "The storage policy resource allows you to configure a Boundary storage policy. " +
"Storage policies allow an admin to configure how long session recordings must be stored and when " +
"to delete them. Storage policies must be applied to the global scope or an org scope in order to take effect.",
CreateContext: resourcePolicyStorageCreate,
ReadContext: resourcePolicyStorageRead,
UpdateContext: resourcePolicyStorageUpdate,
DeleteContext: resourcePolicyStorageDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
IDKey: {
Description: "The ID of the policy.",
Type: schema.TypeString,
Computed: true,
},
NameKey: {
Description: "The policy name. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The policy description.",
Type: schema.TypeString,
Optional: true,
},
ScopeIdKey: {
Description: "The scope for this policy.",
Type: schema.TypeString,
Required: true,
},
policyStorageRetainForDaysKey: {
Description: "The number of days a session recording is required to be stored. Defaults to 0: allow deletions at any time. However, " + policyStorageRetainForDaysKey + " and " + policyStorageDeleteAfterDaysKey + " cannot both be 0.",
Type: schema.TypeInt,
Optional: true,
AtLeastOneOf: []string{policyStorageDeleteAfterDaysKey},
},
policyStorageRetainForOverridableKey: {
Description: "Whether or not the associated " + policyStorageRetainForDaysKey + " value can be overridden by org scopes. Note: if the associated " + policyStorageRetainForDaysKey + " value is 0, overridable is ignored.",
Type: schema.TypeBool,
Optional: true,
},
policyStorageDeleteAfterDaysKey: {
Description: "The number of days after which a session recording will be automatically deleted. Defaults to 0: never automatically delete. However, " + policyStorageDeleteAfterDaysKey + " and " + policyStorageRetainForDaysKey + " cannot both be 0.",
Type: schema.TypeInt,
Optional: true,
AtLeastOneOf: []string{policyStorageRetainForDaysKey},
},
policyStorageDeleteAfterOverridableKey: {
Description: "Whether or not the associated " + policyStorageDeleteAfterDaysKey + " value can be overridden by org scopes. Note: if the associated " + policyStorageDeleteAfterDaysKey + " value is 0, overridable is ignored",
Type: schema.TypeBool,
Optional: true,
},
},
}
}
func setFromPolicyItem(d *schema.ResourceData, p *policies.Policy) error {
d.SetId(p.Id)
if err := d.Set(NameKey, p.Name); err != nil {
return err
}
if err := d.Set(DescriptionKey, p.Description); err != nil {
return err
}
if err := d.Set(ScopeIdKey, p.ScopeId); err != nil {
return err
}
// parse attributes to the retain/delete values so that the tf syntax is simpler
attributes, err := p.GetStoragePolicyAttributes()
if err != nil {
return err
}
// retain_for attribute
if attributes.RetainFor != nil {
if err := d.Set(policyStorageRetainForDaysKey, attributes.RetainFor.Days); err != nil {
return err
}
if err := d.Set(policyStorageRetainForOverridableKey, attributes.RetainFor.Overridable); err != nil {
return err
}
} else {
// no RetainFor, set to nil
if err := d.Set(policyStorageRetainForDaysKey, nil); err != nil {
return err
}
if err := d.Set(policyStorageRetainForOverridableKey, nil); err != nil {
return err
}
}
// delete_after attribute
if attributes.DeleteAfter != nil {
if err := d.Set(policyStorageDeleteAfterDaysKey, attributes.DeleteAfter.Days); err != nil {
return err
}
if err := d.Set(policyStorageDeleteAfterOverridableKey, attributes.DeleteAfter.Overridable); err != nil {
return err
}
} else {
// no DeleteAfter, set to nil
if err := d.Set(policyStorageDeleteAfterDaysKey, nil); err != nil {
return err
}
if err := d.Set(policyStorageDeleteAfterOverridableKey, nil); err != nil {
return err
}
}
return nil
}
func resourcePolicyStorageCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
pClient := policies.NewClient(md.client)
opts := []policies.Option{}
var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}
if nameVal, ok := d.GetOk(NameKey); ok {
opts = append(opts, policies.WithName(nameVal.(string)))
}
if descVal, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, policies.WithDescription(descVal.(string)))
}
if retainDaysVal, ok := d.GetOk(policyStorageRetainForDaysKey); ok {
opts = append(opts, policies.WithStoragePolicyRetainForDays(int32(retainDaysVal.(int))))
} else {
opts = append(opts, policies.DefaultStoragePolicyRetainForDays())
}
if retainOverrideVal, ok := d.GetOk(policyStorageRetainForOverridableKey); ok {
opts = append(opts, policies.WithStoragePolicyRetainForOverridable(retainOverrideVal.(bool)))
} else {
opts = append(opts, policies.DefaultStoragePolicyRetainForOverridable())
}
if deleteDaysVal, ok := d.GetOk(policyStorageDeleteAfterDaysKey); ok {
opts = append(opts, policies.WithStoragePolicyDeleteAfterDays(int32(deleteDaysVal.(int))))
} else {
opts = append(opts, policies.DefaultStoragePolicyDeleteAfterDays())
}
if deleteOverrideVal, ok := d.GetOk(policyStorageDeleteAfterOverridableKey); ok {
opts = append(opts, policies.WithStoragePolicyDeleteAfterOverridable(deleteOverrideVal.(bool)))
} else {
opts = append(opts, policies.DefaultStoragePolicyDeleteAfterOverridable())
}
p, err := pClient.Create(ctx, policyTypeStorage, scopeId, opts...)
if err != nil {
return diag.Errorf("error creating storage policy: %v", err)
}
if p == nil {
return diag.Errorf("nil storage policy after create")
}
if err := setFromPolicyItem(d, p.GetItem()); err != nil {
return diag.FromErr(err)
}
return nil
}
func resourcePolicyStorageRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
pClient := policies.NewClient(md.client)
p, err := pClient.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 policy: %v", err)
}
if p == nil {
return diag.Errorf("policy nil after read")
}
if err := setFromPolicyItem(d, p.GetItem()); err != nil {
return diag.FromErr(err)
}
return nil
}
func resourcePolicyStorageUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
pClient := policies.NewClient(md.client)
opts := []policies.Option{}
if d.HasChange(NameKey) {
if nameVal, ok := d.GetOk(NameKey); ok {
name := nameVal.(string)
opts = append(opts, policies.WithName(name))
} else {
opts = append(opts, policies.DefaultName())
}
}
if d.HasChange(DescriptionKey) {
if descriptionVal, ok := d.GetOk(DescriptionKey); ok {
description := descriptionVal.(string)
opts = append(opts, policies.WithDescription(description))
} else {
opts = append(opts, policies.DefaultDescription())
}
}
if d.HasChanges(policyStorageRetainForDaysKey, policyStorageRetainForOverridableKey) {
if retainDaysVal, ok := d.GetOk(policyStorageRetainForDaysKey); ok {
opts = append(opts, policies.WithStoragePolicyRetainForDays(int32(retainDaysVal.(int))))
} else {
opts = append(opts, policies.DefaultStoragePolicyRetainForDays())
}
if retainOverrideVal, ok := d.GetOk(policyStorageRetainForOverridableKey); ok {
opts = append(opts, policies.WithStoragePolicyRetainForOverridable(retainOverrideVal.(bool)))
} else {
opts = append(opts, policies.DefaultStoragePolicyRetainForOverridable())
}
}
if d.HasChanges(policyStorageDeleteAfterDaysKey, policyStorageDeleteAfterOverridableKey) {
if deleteDaysVal, ok := d.GetOk(policyStorageDeleteAfterDaysKey); ok {
opts = append(opts, policies.WithStoragePolicyDeleteAfterDays(int32(deleteDaysVal.(int))))
} else {
opts = append(opts, policies.DefaultStoragePolicyDeleteAfterDays())
}
if deleteOverrideVal, ok := d.GetOk(policyStorageDeleteAfterOverridableKey); ok {
opts = append(opts, policies.WithStoragePolicyDeleteAfterOverridable(deleteOverrideVal.(bool)))
} else {
opts = append(opts, policies.DefaultStoragePolicyDeleteAfterOverridable())
}
}
if len(opts) > 0 {
opts = append(opts, policies.WithAutomaticVersioning(true))
p, err := pClient.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error creating storage policy: %v", err)
}
if p == nil {
return diag.Errorf("nil storage policy after create")
}
if err := setFromPolicyItem(d, p.GetItem()); err != nil {
return diag.FromErr(err)
}
}
return nil
}
func resourcePolicyStorageDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
pClient := policies.NewClient(md.client)
_, err := pClient.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting policy: %v", err)
}
return nil
}
+110 -36
View File
@@ -5,7 +5,12 @@ package provider
import ( import (
"context" "context"
"encoding/json"
"errors"
"fmt"
"net/http" "net/http"
"strings"
"unicode"
"github.com/hashicorp/boundary/api" "github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/roles" "github.com/hashicorp/boundary/api/roles"
@@ -14,9 +19,9 @@ import (
) )
const ( const (
roleGrantScopeIdKey = "grant_scope_id" roleGrantScopeIdsKey = "grant_scope_ids"
rolePrincipalIdsKey = "principal_ids" rolePrincipalIdsKey = "principal_ids"
roleGrantStringsKey = "grant_strings" roleGrantStringsKey = "grant_strings"
) )
func resourceRole() *schema.Resource { func resourceRole() *schema.Resource {
@@ -60,15 +65,17 @@ func resourceRole() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString}, Elem: &schema.Schema{Type: schema.TypeString},
}, },
roleGrantStringsKey: { roleGrantStringsKey: {
Description: " A list of stringified grants for the role.", Description: "A list of stringified grants for the role.",
Type: schema.TypeSet, Type: schema.TypeSet,
Optional: true, Optional: true,
Elem: &schema.Schema{Type: schema.TypeString}, Elem: &schema.Schema{Type: schema.TypeString},
}, },
roleGrantScopeIdKey: { roleGrantScopeIdsKey: {
Type: schema.TypeString, Description: `A list of scopes for which the grants in this role should apply, which can include the special values "this", "children", or "descendants"`,
Optional: true, Type: schema.TypeSet,
Computed: true, Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
}, },
}, },
} }
@@ -90,14 +97,15 @@ func setFromRoleResponseMap(d *schema.ResourceData, raw map[string]interface{})
if err := d.Set(roleGrantStringsKey, raw["grant_strings"]); err != nil { if err := d.Set(roleGrantStringsKey, raw["grant_strings"]); err != nil {
return err return err
} }
if err := d.Set(roleGrantScopeIdKey, raw["grant_scope_id"]); err != nil { if err := d.Set(roleGrantScopeIdsKey, raw["grant_scope_ids"]); err != nil {
return err return err
} }
d.SetId(raw["id"].(string)) d.SetId(raw["id"].(string))
return nil return nil
} }
func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (errs diag.Diagnostics) { func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) {
md := meta.(*metaData) md := meta.(*metaData)
var scopeId string var scopeId string
@@ -121,10 +129,13 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
opts = append(opts, roles.WithDescription(descStr)) opts = append(opts, roles.WithDescription(descStr))
} }
grantScopeIdVal, ok := d.GetOk(roleGrantScopeIdKey) var grantScopeIds []string
if ok { if grantScopeIdsVal, ok := d.GetOk(roleGrantScopeIdsKey); ok {
grantScopeIdStr := grantScopeIdVal.(string) list := grantScopeIdsVal.(*schema.Set).List()
opts = append(opts, roles.WithGrantScopeId(grantScopeIdStr)) grantScopeIds = make([]string, 0, len(list))
for _, i := range list {
grantScopeIds = append(grantScopeIds, i.(string))
}
} }
var principalIds []string var principalIds []string
@@ -141,6 +152,16 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
list := grantStringsVal.(*schema.Set).List() list := grantStringsVal.(*schema.Set).List()
grantStrings = make([]string, 0, len(list)) grantStrings = make([]string, 0, len(list))
for _, i := range list { for _, i := range list {
for _, grant := range list {
deprecationNotice, err := checkGrantForDeprecation(grant.(string))
if err != nil {
return diag.FromErr(err)
}
if deprecationNotice != "" {
diags = append(diags, diag.Diagnostic{Severity: diag.Warning, Summary: "deprecated field found in grant", Detail: deprecationNotice})
}
grantStrings = append(grantStrings, grant.(string))
}
grantStrings = append(grantStrings, i.(string)) grantStrings = append(grantStrings, i.(string))
} }
} }
@@ -157,7 +178,7 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
apiResponse := tcr.GetResponse().Map apiResponse := tcr.GetResponse().Map
defer func() { defer func() {
if err := setFromRoleResponseMap(d, apiResponse); err != nil { if err := setFromRoleResponseMap(d, apiResponse); err != nil {
errs = append(errs, diag.FromErr(err)...) diags = append(diags, diag.FromErr(err)...)
} }
}() }()
@@ -165,9 +186,9 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
tspr, err := rc.SetPrincipals(ctx, tcr.Item.Id, 0, principalIds, roles.WithAutomaticVersioning(true)) tspr, err := rc.SetPrincipals(ctx, tcr.Item.Id, 0, principalIds, roles.WithAutomaticVersioning(true))
switch { switch {
case err != nil: case err != nil:
errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "error setting principals", Detail: err.Error()}) diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting principals", Detail: err.Error()})
case tspr == nil: case tspr == nil:
errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting principals"}) diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting principals"})
default: default:
apiResponse = tspr.GetResponse().Map apiResponse = tspr.GetResponse().Map
} }
@@ -177,15 +198,27 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
tsgr, err := rc.SetGrants(ctx, tcr.Item.Id, 0, grantStrings, roles.WithAutomaticVersioning(true)) tsgr, err := rc.SetGrants(ctx, tcr.Item.Id, 0, grantStrings, roles.WithAutomaticVersioning(true))
switch { switch {
case err != nil: case err != nil:
errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grants", Detail: err.Error()}) diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grants", Detail: err.Error()})
case tsgr == nil: case tsgr == nil:
errs = append(errs, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting grants"}) diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting grants"})
default: default:
apiResponse = tsgr.GetResponse().Map apiResponse = tsgr.GetResponse().Map
} }
} }
return errs if grantScopeIds != nil {
tsgr, err := rc.SetGrantScopes(ctx, tcr.Item.Id, 0, grantScopeIds, roles.WithAutomaticVersioning(true))
switch {
case err != nil:
diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grant scopes", Detail: err.Error()})
case tsgr == nil:
diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "nil role after setting grant scope ids"})
default:
apiResponse = tsgr.GetResponse().Map
}
}
return diags
} }
func resourceRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -239,17 +272,6 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
} }
} }
var grantScopeId *string
if d.HasChange(roleGrantScopeIdKey) {
opts = append(opts, roles.DefaultGrantScopeId())
grantScopeIdVal, ok := d.GetOk(roleGrantScopeIdKey)
if ok {
grantScopeIdStr := grantScopeIdVal.(string)
grantScopeId = &grantScopeIdStr
opts = append(opts, roles.WithGrantScopeId(grantScopeIdStr))
}
}
if len(opts) > 0 { if len(opts) > 0 {
opts = append(opts, roles.WithAutomaticVersioning(true)) opts = append(opts, roles.WithAutomaticVersioning(true))
_, err := rc.Update(ctx, d.Id(), 0, opts...) _, err := rc.Update(ctx, d.Id(), 0, opts...)
@@ -268,11 +290,6 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
return diag.FromErr(err) return diag.FromErr(err)
} }
} }
if d.HasChange(roleGrantScopeIdKey) {
if err := d.Set(roleGrantScopeIdKey, grantScopeId); err != nil {
return diag.FromErr(err)
}
}
var diags diag.Diagnostics var diags diag.Diagnostics
if d.HasChange(roleGrantStringsKey) { if d.HasChange(roleGrantStringsKey) {
@@ -280,6 +297,13 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
if grantStringsVal, ok := d.GetOk(roleGrantStringsKey); ok { if grantStringsVal, ok := d.GetOk(roleGrantStringsKey); ok {
grants := grantStringsVal.(*schema.Set).List() grants := grantStringsVal.(*schema.Set).List()
for _, grant := range grants { for _, grant := range grants {
deprecationNotice, err := checkGrantForDeprecation(grant.(string))
if err != nil {
return diag.FromErr(err)
}
if deprecationNotice != "" {
diags = append(diags, diag.Diagnostic{Severity: diag.Warning, Summary: "deprecated field found in grant", Detail: deprecationNotice})
}
grantStrings = append(grantStrings, grant.(string)) grantStrings = append(grantStrings, grant.(string))
} }
} }
@@ -311,6 +335,24 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
} }
} }
if d.HasChange(roleGrantScopeIdsKey) {
var grantScopeIds []string
if grantScopeIdsVal, ok := d.GetOk(roleGrantScopeIdsKey); ok {
grantScopes := grantScopeIdsVal.(*schema.Set).List()
for _, grantScope := range grantScopes {
grantScopeIds = append(grantScopeIds, grantScope.(string))
}
}
_, err := rc.SetGrantScopes(ctx, d.Id(), 0, grantScopeIds, roles.WithAutomaticVersioning(true))
if err != nil {
diags = append(diags, diag.Diagnostic{Severity: diag.Error, Summary: "error setting grant scopes", Detail: err.Error()})
} else {
if err := d.Set(roleGrantScopeIdsKey, grantScopeIds); err != nil {
return diag.FromErr(err)
}
}
}
return diags return diags
} }
@@ -325,3 +367,35 @@ func resourceRoleDelete(ctx context.Context, d *schema.ResourceData, meta interf
return nil return nil
} }
func checkGrantForDeprecation(grantString string) (string, error) {
if len(grantString) == 0 {
return "", errors.New("missing grant string")
}
grantString = strings.ToValidUTF8(grantString, string(unicode.ReplacementChar))
switch {
case grantString[0] == '{':
raw := make(map[string]any, 4)
if err := json.Unmarshal([]byte(grantString), &raw); err != nil {
return "", fmt.Errorf("error json unmarshalling grant string: %w", err)
}
if raw["id"] != nil {
return `Grant contains an "id" field which is deprecated and will not be accepted from Boundary 0.15+. Please use "ids" instead.`, nil
}
default:
parts := strings.Split(grantString, ";")
for _, part := range parts {
splitPart := strings.Split(part, "=")
if len(splitPart) < 2 {
return "", fmt.Errorf("invalid grant part: %s", part)
}
if splitPart[0] == "id" {
return `Grant contains an "id" field which is deprecated and will not be accepted from Boundary 0.15+. Please use "ids" instead.`, nil
}
}
}
return "", nil
}
@@ -0,0 +1,179 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"fmt"
"regexp"
"testing"
"github.com/hashicorp/boundary/api/roles"
"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"
)
var (
orgRoleWithGrantScopes = `
resource "boundary_role" "with_grant_scopes" {
name = "with_grant_scopes"
description = "with grant scopes"
principal_ids = [boundary_user.foo.id]
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
grant_scope_ids = ["this", "children", boundary_scope.proj1.id]
}`
orgRoleWithGrantScopesUpdate = `
resource "boundary_role" "with_grant_scopes" {
name = "with_grant_scopes_update"
description = "with grant scopes update"
principal_ids = [boundary_user.foo.id]
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
grant_scope_ids = ["this"]
}`
orgRoleWithInvalidGrantScopesUpdate = `
resource "boundary_role" "with_grant_scopes" {
name = "with_grant_scopes_update"
description = "with grant scopes update"
principal_ids = [boundary_user.foo.id]
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
grant_scope_ids = ["this", "children", "p_foobar1234"]
}`
conversionOrgRoleWithGrantScopeIdsConversion = `
resource "boundary_role" "with_grant_scope_id" {
name = "grant scope id role"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
grant_scope_ids = ["this", boundary_scope.proj1.id]
}`
conversionOrgRoleWithGrantScopeIdsUpdate = `
resource "boundary_role" "with_grant_scope_id" {
name = "grant scope id role"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
grant_scope_ids = ["this", "children"]
}`
)
// TestAccRoleWithGrantScopes exercises creation and update with valid and
// invalid grant scopes
func TestAccRoleWithGrantScopes(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
t.Cleanup(tc.Shutdown)
url := tc.ApiAddrs()[0]
var provider *schema.Provider
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckRoleResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// Create with valid grant scopes should create role
Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, orgRoleWithGrantScopes),
Check: resource.ComposeTestCheckFunc(
testAccCheckScopeResourceExists(provider, "boundary_scope.proj1"),
testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scopes"),
testAccCheckUserResourceExists(provider, "boundary_user.foo"),
testAccCheckRoleResourceGrantScopesSet(provider, "boundary_role.with_grant_scopes", []string{"this", "children", "boundary_scope.proj1"}),
resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", DescriptionKey, "with grant scopes"),
resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", NameKey, "with_grant_scopes"),
),
},
importStep("boundary_role.with_grant_scopes"),
{
// Update with invalid grant scopes should update role but return empty plan
// since grant scopes were not set correctly.
Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, orgRoleWithInvalidGrantScopesUpdate),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scopes"),
resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", DescriptionKey, "with grant scopes update"),
resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", NameKey, "with_grant_scopes_update"),
),
ExpectError: regexp.MustCompile(`Unable to set grant scopes on role`),
},
{
// Update again without invalid principal should produce empty plan
Config: testConfig(url, fooOrg, firstProjectFoo, fooUser, orgRoleWithGrantScopesUpdate),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoleResourceExists(provider, "boundary_role.with_grant_scopes"),
testAccCheckRoleResourceGrantScopesSet(provider, "boundary_role.with_grant_scopes", []string{"this"}),
resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", DescriptionKey, "with grant scopes update"),
resource.TestCheckResourceAttr("boundary_role.with_grant_scopes", NameKey, "with_grant_scopes_update"),
),
},
importStep("boundary_role.with_grant_scopes"),
},
})
}
func testAccCheckRoleResourceGrantScopesSet(testProvider *schema.Provider, name string, grantScopeIds []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("role resource not found: %s", name)
}
id := rs.Primary.ID
if id == "" {
return fmt.Errorf("role resource ID is not set")
}
scopeIds := []string{}
for _, grantScope := range grantScopeIds {
if grantScope == "children" || grantScope == "this" || grantScope == "descendants" {
scopeIds = append(scopeIds, grantScope)
continue
}
scope, ok := s.RootModule().Resources[grantScope]
if !ok {
return fmt.Errorf("scope resource not found: %s", grantScope)
}
scopeId := scope.Primary.ID
if scopeId == "" {
return fmt.Errorf("scope resource ID not set")
}
scopeIds = append(scopeIds, scopeId)
}
md := testProvider.Meta().(*metaData)
rolesClient := roles.NewClient(md.client)
rr, err := rolesClient.Read(context.Background(), id)
if err != nil {
return fmt.Errorf("Got an error when reading role %q: %v", id, err)
}
// for every grant scope set on the role in the state, ensure each role
// in boundary has the same setings
if len(rr.Item.GrantScopeIds) == 0 {
return fmt.Errorf("no grant scope ids found in boundary")
}
for _, stateGrantScopeId := range rr.Item.GrantScopeIds {
ok := false
for _, gotScopeId := range scopeIds {
if gotScopeId == stateGrantScopeId {
ok = true
}
}
if !ok {
return fmt.Errorf("grant scope id in state not set in boundary: %s", stateGrantScopeId)
}
}
return nil
}
}
+4 -4
View File
@@ -189,7 +189,7 @@ resource "boundary_role" "with_grants" {
func TestAccRoleToOrgToProject(t *testing.T) { func TestAccRoleToOrgToProject(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...) tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown() t.Cleanup(tc.Shutdown)
url := tc.ApiAddrs()[0] url := tc.ApiAddrs()[0]
var provider *schema.Provider var provider *schema.Provider
@@ -243,7 +243,7 @@ func TestAccRoleToOrgToProject(t *testing.T) {
func TestAccRoleWithGrants(t *testing.T) { func TestAccRoleWithGrants(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...) tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown() t.Cleanup(tc.Shutdown)
url := tc.ApiAddrs()[0] url := tc.ApiAddrs()[0]
var provider *schema.Provider var provider *schema.Provider
@@ -302,7 +302,7 @@ func TestAccRoleWithGrants(t *testing.T) {
func TestAccRoleWithPrincipals(t *testing.T) { func TestAccRoleWithPrincipals(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...) tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown() t.Cleanup(tc.Shutdown)
url := tc.ApiAddrs()[0] url := tc.ApiAddrs()[0]
var provider *schema.Provider var provider *schema.Provider
@@ -365,7 +365,7 @@ func TestAccRoleWithPrincipals(t *testing.T) {
func TestAccRoleWithGroups(t *testing.T) { func TestAccRoleWithGroups(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...) tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown() t.Cleanup(tc.Shutdown)
url := tc.ApiAddrs()[0] url := tc.ApiAddrs()[0]
var provider *schema.Provider var provider *schema.Provider
@@ -0,0 +1,164 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/scopes"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
policyIdKey = "policy_id"
storagePolicyPrefix = "pst_"
)
func resourceScopePolicyAttachment() *schema.Resource {
return &schema.Resource{
CreateContext: resourceScopePolicyAttachmentCreate,
ReadWithoutTimeout: resourceScopePolicyAttachmentRead,
DeleteWithoutTimeout: resourceScopePolicyAttachmentDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
ScopeIdKey: {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
policyIdKey: {
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
},
}
}
func resourceScopePolicyAttachmentCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
scp := scopes.NewClient(md.client)
opts := []scopes.Option{scopes.WithAutomaticVersioning(true)}
var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}
if err := d.Set(ScopeIdKey, scopeId); err != nil {
return diag.FromErr(err)
}
var policyId string
if policyIdVal, ok := d.GetOk(policyIdKey); ok {
policyId = policyIdVal.(string)
} else {
return diag.Errorf("no policy ID provided")
}
// other policy types may be added here and in other switches with this file
switch {
case strings.HasPrefix(policyId, storagePolicyPrefix):
if _, err := scp.AttachStoragePolicy(ctx, scopeId, 0, policyId, opts...); err != nil {
return diag.FromErr(err)
}
default:
return diag.Errorf("unknown policy type provided.")
}
if err := d.Set(policyIdKey, policyId); err != nil {
return diag.FromErr(err)
}
d.SetId(fmt.Sprintf("%s:%s", policyId, scopeId))
return nil
}
func resourceScopePolicyAttachmentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
scp := scopes.NewClient(md.client)
var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}
var policyId string
if policyIdVal, ok := d.GetOk(policyIdKey); ok {
policyId = policyIdVal.(string)
} else {
return diag.Errorf("no policy ID provided")
}
s, err := scp.Read(ctx, scopeId)
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
// the scope is gone, destroy this resource
d.SetId("")
return nil
}
return diag.Errorf("error reading scope: %v", err)
}
if s == nil {
return diag.Errorf("scope nil after read")
}
switch {
case strings.HasPrefix(policyId, storagePolicyPrefix):
if policyId != s.GetItem().StoragePolicyId {
// this policy is no longer attached to the scope, destroy this resource
d.SetId("")
return nil
}
default:
return diag.Errorf("unknown policy type provided.")
}
return nil
}
func resourceScopePolicyAttachmentDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
scp := scopes.NewClient(md.client)
opts := []scopes.Option{scopes.WithAutomaticVersioning(true)}
var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}
var policyId string
if policyIdVal, ok := d.GetOk(policyIdKey); ok {
policyId = policyIdVal.(string)
} else {
return diag.Errorf("no policy ID provided")
}
switch {
case strings.HasPrefix(policyId, storagePolicyPrefix):
if _, err := scp.DetachStoragePolicy(ctx, scopeId, 0, opts...); err != nil {
return diag.FromErr(err)
}
default:
return diag.Errorf("unknown policy type provided.")
}
return nil
}
+4 -4
View File
@@ -33,7 +33,7 @@ resource "boundary_scope" "org1" {
resource "boundary_role" "org1_admin" { resource "boundary_role" "org1_admin" {
scope_id = boundary_scope.global.id scope_id = boundary_scope.global.id
grant_scope_id = boundary_scope.org1.id grant_scope_ids = [boundary_scope.org1.id]
grant_strings = ["ids=*;type=*;actions=*"] grant_strings = ["ids=*;type=*;actions=*"]
principal_ids = ["u_auth"] principal_ids = ["u_auth"]
} }
@@ -49,7 +49,7 @@ resource "boundary_scope" "proj1" {
resource "boundary_role" "proj1_admin" { resource "boundary_role" "proj1_admin" {
scope_id = boundary_scope.org1.id scope_id = boundary_scope.org1.id
grant_scope_id = boundary_scope.proj1.id grant_scope_ids = [boundary_scope.proj1.id]
grant_strings = ["ids=*;type=*;actions=*"] grant_strings = ["ids=*;type=*;actions=*"]
principal_ids = ["u_auth"] principal_ids = ["u_auth"]
} }
@@ -65,7 +65,7 @@ resource "boundary_scope" "proj1" {
resource "boundary_role" "proj1_admin" { resource "boundary_role" "proj1_admin" {
scope_id = boundary_scope.org1.id scope_id = boundary_scope.org1.id
grant_scope_id = boundary_scope.proj1.id grant_scope_ids = [boundary_scope.proj1.id]
grant_strings = ["ids=*;type=*;actions=*"] grant_strings = ["ids=*;type=*;actions=*"]
principal_ids = ["u_auth"] principal_ids = ["u_auth"]
} }
@@ -80,7 +80,7 @@ resource "boundary_scope" "proj2" {
resource "boundary_role" "proj2_admin" { resource "boundary_role" "proj2_admin" {
scope_id = boundary_scope.org1.id scope_id = boundary_scope.org1.id
grant_scope_id = boundary_scope.proj2.id grant_scope_ids = [boundary_scope.proj2.id]
grant_strings = ["ids=*;type=*;actions=*"] grant_strings = ["ids=*;type=*;actions=*"]
principal_ids = ["u_auth"] principal_ids = ["u_auth"]
} }
+28
View File
@@ -0,0 +1,28 @@
---
page_title: "boundary_worker Resource - terraform-provider-boundary"
subcategory: ""
description: |-
{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
# boundary_worker (Resource)
{{ .Description | trimspace }}
## Example Usage
### Controller-led worker
{{tffile "examples/resources/boundary_worker/controller_led.tf"}}
### Worker-led worker
{{tffile "examples/resources/boundary_worker/worker_led.tf"}}
{{ .SchemaMarkdown | trimspace }}
## Import
Import is supported using the following syntax:
{{codefile "shell" "examples/resources/boundary_worker/import.sh" }}