Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c1c095a66 | ||
|
|
f9fd811205 | ||
|
|
ad43c0bec0 | ||
|
|
5d6d277186 | ||
|
|
4590e7f8c8 | ||
|
|
47c3f92f5c | ||
|
|
490476a5e5 | ||
|
|
a256b763ae | ||
|
|
f6539b969c | ||
|
|
695e28a38b | ||
|
|
b7b11dd817 | ||
|
|
b9bc73d4cf | ||
|
|
e847f5fde7 | ||
|
|
53c2a5769c | ||
|
|
ed1bf21505 | ||
|
|
6a23fc20f9 | ||
|
|
41653b2ccd | ||
|
|
398eaf8406 | ||
|
|
c71ba43a3d | ||
|
|
1153ea44ee | ||
|
|
2020819648 | ||
|
|
b7eadd565f | ||
|
|
7e7707d3af | ||
|
|
8c12b27f8f | ||
|
|
93e41ae83d | ||
|
|
bbc5889404 | ||
|
|
933192280b | ||
|
|
8383852445 | ||
|
|
06e9bf65c9 | ||
|
|
9070fdfd6c | ||
|
|
692793b6a2 | ||
|
|
6a5aef4467 | ||
|
|
af4f61199a | ||
|
|
747f962295 | ||
|
|
417bad5d36 | ||
|
|
069032103e | ||
|
|
292b1057e8 | ||
|
|
bd0754188a | ||
|
|
f09ab2dda7 | ||
|
|
288fa8e9b0 | ||
|
|
a65623e36a | ||
|
|
e4b7ff1490 | ||
|
|
db838bdaed | ||
|
|
c1bc59c36e | ||
|
|
8e5cefb668 | ||
|
|
f002574978 | ||
|
|
44b2ffb53f | ||
|
|
7d3191a2ae | ||
|
|
d270a75143 | ||
|
|
f0e5ed6760 | ||
|
|
e57d6b2060 | ||
|
|
212d64aaf8 | ||
|
|
6f9a6a84cb | ||
|
|
9e30a68a61 | ||
|
|
99c7ae65e3 | ||
|
|
f8b69875c3 | ||
|
|
0c26e251cd | ||
|
|
9042a61ff4 | ||
|
|
76a6686340 | ||
|
|
61c0ac92b3 | ||
|
|
b1936800b3 | ||
|
|
375fd1f5a2 | ||
|
|
de6ae59fb0 | ||
|
|
6ded668eff | ||
|
|
47bbdb2254 | ||
|
|
1aa73ab586 | ||
|
|
b91b346aec | ||
|
|
67e5191b8c | ||
|
|
e78c21a13f | ||
|
|
dd5e5ea2da | ||
|
|
e44d658615 | ||
|
|
608d95e620 | ||
|
|
dc3adfa96a | ||
|
|
13f6a2497d | ||
|
|
b181e37a36 | ||
|
|
bb5ed8609c | ||
|
|
8bb1823ab4 | ||
|
|
34dbe456de | ||
|
|
63a1d9a5e9 | ||
|
|
54fd626ae5 | ||
|
|
fdcc5a6dab | ||
|
|
0f2473a1d7 | ||
|
|
b06da56d75 | ||
|
|
9d351f8112 | ||
|
|
7f1fa78833 | ||
|
|
2ff5736a18 | ||
|
|
1d3eb8c971 | ||
|
|
e396447acc | ||
|
|
e27d8eb65d | ||
|
|
46f47aa7da | ||
|
|
58bc673ba9 | ||
|
|
cdf16323f9 | ||
|
|
d48724aa09 | ||
|
|
3a1d5f842b | ||
|
|
2f697d5df0 | ||
|
|
fcb13d9c76 | ||
|
|
f1fbaa1bd9 | ||
|
|
0a7d7ad555 | ||
|
|
508580a46c | ||
|
|
c3b08a1b4f | ||
|
|
afbc61a0ca | ||
|
|
260abbb113 | ||
|
|
4ccc1962dc | ||
|
|
99da3ab62a | ||
|
|
33a0152bfe | ||
|
|
7559ab51ba | ||
|
|
56c10e45a3 | ||
|
|
866d323f4a | ||
|
|
4e8e885666 | ||
|
|
d660ad3093 | ||
|
|
105bc0c1f9 | ||
|
|
9062327923 | ||
|
|
023f3d5ca1 | ||
|
|
658c360ab7 | ||
|
|
b5e05ae7f8 | ||
|
|
d786b75f0e | ||
|
|
850589998d | ||
|
|
8b232119b3 | ||
|
|
da549d50ac | ||
|
|
f566289dcc | ||
|
|
4b0a161ce2 | ||
|
|
6d3f89038c | ||
|
|
008a6ff618 | ||
|
|
8983decd06 | ||
|
|
605d124d51 | ||
|
|
9186304d5d | ||
|
|
1c05eab410 | ||
|
|
749cce8ec1 |
No files matched your search
@@ -6,9 +6,9 @@ permissions:
|
||||
|
||||
jobs:
|
||||
actionlint:
|
||||
runs-on: ${{ fromJSON(vars.RUNNER) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- name: Check workflow files
|
||||
uses: docker://docker.mirror.hashicorp.services/rhysd/actionlint@sha256:02ccb6d91e4cb4a7b21eb99d5274d257e81ae667688d730e89d7ea0d6d35db91
|
||||
with:
|
||||
|
||||
+39
-20
@@ -22,7 +22,7 @@ jobs:
|
||||
id: boundary-team-role
|
||||
run: |
|
||||
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
|
||||
echo "Actor ${{ github.actor }} is a ${TEAM} team member, skipping ticket creation"
|
||||
else
|
||||
@@ -32,13 +32,6 @@ jobs:
|
||||
env:
|
||||
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
|
||||
if: github.event.action == 'opened' && !steps.boundary-team-role.outputs.role
|
||||
id: set-ticket-type
|
||||
@@ -52,6 +45,10 @@ jobs:
|
||||
- name: Create ticket
|
||||
if: github.event.action == 'opened' && !steps.boundary-team-role.outputs.role
|
||||
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:
|
||||
project: ICU
|
||||
issuetype: "GH Issue"
|
||||
@@ -64,6 +61,10 @@ jobs:
|
||||
if: github.event.action != 'opened'
|
||||
id: search
|
||||
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:
|
||||
# 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 }}"'
|
||||
@@ -71,20 +72,38 @@ jobs:
|
||||
- name: Sync comment
|
||||
if: github.event.action == 'created' && steps.search.outputs.issue
|
||||
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:
|
||||
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 }}"
|
||||
|
||||
- name: Close ticket
|
||||
if: (github.event.action == 'closed' || github.event.action == 'deleted') && steps.search.outputs.issue
|
||||
uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3
|
||||
with:
|
||||
issue: ${{ steps.search.outputs.issue }}
|
||||
transition: Done
|
||||
- name: Transitions
|
||||
id: transitions
|
||||
if: steps.search.outputs.issue
|
||||
run: |
|
||||
if [[ "${{ github.event.action }}" == "closed" || "${{ github.event.action }}" == "deleted" ]]; then
|
||||
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
|
||||
if: github.event.action == 'reopened' && steps.search.outputs.issue
|
||||
uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3
|
||||
with:
|
||||
issue: ${{ steps.search.outputs.issue }}
|
||||
transition: "To Do"
|
||||
# Transition issue API reference: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-transitions-post
|
||||
- name: Transition ticket
|
||||
if: steps.transitions.outputs.name
|
||||
run: |
|
||||
transitions="$(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")"
|
||||
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}")"
|
||||
@@ -0,0 +1,43 @@
|
||||
name: "make-gen-delta"
|
||||
on:
|
||||
- workflow_dispatch
|
||||
- push
|
||||
- pull_request
|
||||
- workflow_call
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
make-gen-delta:
|
||||
name: "Check for uncommitted changes from make gen"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
- name: Determine Go version
|
||||
id: get-go-version
|
||||
# We use .go-version as our source of truth for current Go
|
||||
# version, because "goenv" can react to it automatically.
|
||||
run: |
|
||||
echo "Building with Go $(cat .go-version)"
|
||||
echo "go-version=$(cat .go-version)" >> "$GITHUB_OUTPUT"
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: "${{ steps.get-go-version.outputs.go-version }}"
|
||||
- name: Running go mod tidy
|
||||
run: |
|
||||
go mod tidy
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
make tools
|
||||
- name: Running make gen
|
||||
run: |
|
||||
make gen
|
||||
- name: Check for changes
|
||||
run: |
|
||||
git diff --exit-code
|
||||
git status --porcelain
|
||||
test -z "$(git status --porcelain)"
|
||||
@@ -12,20 +12,24 @@ jobs:
|
||||
go-version:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.go-version.outputs.version }}
|
||||
version: ${{ steps.go-version.outputs.go-version }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- id: go-version
|
||||
run: echo "name=version::$(cat ./.go-version)" >> "$GITHUB_OUTPUT"
|
||||
# We use .go-version as our source of truth for current Go
|
||||
# version, because "goenv" can react to it automatically.
|
||||
run: |
|
||||
echo "Building with Go $(cat .go-version)"
|
||||
echo "go-version=$(cat .go-version)" >> "$GITHUB_OUTPUT"
|
||||
release-notes:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Generate Release Notes
|
||||
run: sed -n -e "1{/# /d;}" -e "2{/^$/d;}" -e "/# $(git describe --abbrev=0 --exclude="$(git describe --abbrev=0 --match='v*.*.*' --tags)" --match='v*.*.*' --tags | tr -d v)/q;p" CHANGELOG.md > release-notes.txt
|
||||
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: release-notes
|
||||
path: release-notes.txt
|
||||
@@ -33,7 +37,7 @@ jobs:
|
||||
terraform-provider-release:
|
||||
name: 'Terraform Provider Release'
|
||||
needs: [go-version, release-notes]
|
||||
uses: hashicorp/ghaction-terraform-provider-release/.github/workflows/hashicorp.yml@v2
|
||||
uses: hashicorp/ghaction-terraform-provider-release/.github/workflows/hashicorp.yml@v4
|
||||
secrets:
|
||||
hc-releases-github-token: '${{ secrets.HASHI_RELEASES_GITHUB_TOKEN }}'
|
||||
hc-releases-host-staging: '${{ secrets.HC_RELEASES_HOST_STAGING }}'
|
||||
|
||||
@@ -2,6 +2,7 @@ name: test
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
- workflow_dispatch
|
||||
- workflow_call
|
||||
|
||||
@@ -10,9 +11,9 @@ permissions:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ fromJSON(vars.RUNNER) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- name: Determine go version
|
||||
id: get-go-version
|
||||
# We use .go-version as our source of truth for current Go
|
||||
@@ -21,16 +22,17 @@ jobs:
|
||||
echo "Building with Go $(cat .go-version)"
|
||||
echo "go-version=$(cat .go-version)" >> "$GITHUB_OUTPUT"
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version: "${{ steps.get-go-version.outputs.go-version }}"
|
||||
cache: false
|
||||
- name: Determine Go cache paths
|
||||
id: go-cache-paths
|
||||
run: |
|
||||
echo "go-build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
|
||||
echo "go-mod=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"
|
||||
- name: Set up Go modules cache
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: |
|
||||
${{ steps.go-cache-paths.outputs.go-build }}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.20.5
|
||||
1.21.5
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch a test function",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${fileDirname}",
|
||||
"args": [
|
||||
"-test.v",
|
||||
"-test.run",
|
||||
"^${selectedText}$"
|
||||
],
|
||||
"showLog": true,
|
||||
"envFile": "${workspaceFolder}/.vscode/private.env"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
TF_ACC=1
|
||||
TF_LOG=INFO
|
||||
GOFLAGS='-mod=readonly'
|
||||
+87
-1
@@ -2,6 +2,92 @@
|
||||
|
||||
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)
|
||||
|
||||
### New and Improved
|
||||
|
||||
* Allow dynamic credentials when configuring storage buckets
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/549))
|
||||
|
||||
## 1.1.12 (January 8, 2024)
|
||||
|
||||
### New and Improved
|
||||
|
||||
* Add support to configure valid_principals with Vault SSH Certificate Credential Library
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/512))
|
||||
|
||||
## 1.1.11 (December 13, 2023)
|
||||
|
||||
### New and Improved
|
||||
|
||||
* Add support for OIDC prompts. Using prompts, the Relying Party (RP) can customize the authentication and authorization flow to suit their specific needs and improve the user experience. [OIDC Authentication request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) server.
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/519))
|
||||
* Add boundary_auth_method data source
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/505))
|
||||
* Add boundary_group data source
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/503))
|
||||
* Add boundary_account data source
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/493))
|
||||
* Add boundary_user data source
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/468))
|
||||
|
||||
### Bug Fix
|
||||
* Fix boundary_worker overwriting worker generated auth token during
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/461))
|
||||
|
||||
## 1.1.10 (October 11, 2023)
|
||||
|
||||
### New and Improved
|
||||
|
||||
* Add support for Scope datasource
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/474))
|
||||
* LDAP: Add support for maximum_page_size and dereference_aliases
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/453))
|
||||
|
||||
|
||||
## 1.1.9 (July 19, 2023)
|
||||
|
||||
### New and Improved
|
||||
|
||||
* Add support for a storage bucket as a resource
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/417))
|
||||
* Add option to enable session recording on a target resource
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/421))
|
||||
* Update docs for host set plugin filters examples
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/420))
|
||||
|
||||
## 1.1.8 (June 13, 2023)
|
||||
|
||||
### New and Improved
|
||||
@@ -264,4 +350,4 @@ Update provider to handle new domain errors ([PR](https://github.com/hashicorp/t
|
||||
|
||||
## 0.1.0 (October 14, 2020)
|
||||
|
||||
Initial release!
|
||||
Initial release!
|
||||
@@ -18,14 +18,12 @@ DOCKER_ARGS ?= -d
|
||||
PG_OPTS ?=
|
||||
TEST_DB_PORT ?= 5432
|
||||
BOUNDARY_VERSION = $(shell go mod edit -json | jq -r '.["Require"][] | select(.Path=="github.com/hashicorp/boundary") | .["Version"]')
|
||||
GOPATH ?= ~/go
|
||||
GOPATH ?= $(abspath ~/go)
|
||||
GOMODCACHE ?= $(GOPATH)/pkg/mod
|
||||
|
||||
tools:
|
||||
go generate -tags tools tools/tools.go
|
||||
|
||||
fmtcheck:
|
||||
echo "Placeholder"
|
||||
go install github.com/hashicorp/copywrite@v0.15.0
|
||||
|
||||
test:
|
||||
echo "Placeholder"
|
||||
@@ -81,3 +79,14 @@ test-database-down:
|
||||
docker rm -v boundary-sql-tests || true
|
||||
|
||||
.PHONY: testacc tools docs test-database-up test-database-down
|
||||
|
||||
.PHONY: copywrite
|
||||
copywrite:
|
||||
copywrite headers
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
gofumpt -w $$(find . -name '*.go')
|
||||
|
||||
.PHONY: gen
|
||||
gen: docs copywrite fmt
|
||||
@@ -1,18 +1,15 @@
|
||||

|
||||
|
||||
Terraform Provider Boundary
|
||||
==================
|
||||
# Terraform Provider Boundary
|
||||
|
||||
Available in the [Terraform Registry](https://registry.terraform.io/providers/hashicorp/boundary/latest).
|
||||
|
||||
Requirements
|
||||
------------
|
||||
## Requirements
|
||||
|
||||
- [Terraform](https://www.terraform.io/downloads.html) >= 0.12.x
|
||||
- [Go](https://golang.org/doc/install) >= 1.20
|
||||
- [Terraform](https://www.terraform.io/downloads.html) >= 0.13.x
|
||||
- [Go](https://golang.org/doc/install) >= 1.21
|
||||
|
||||
Building The Provider
|
||||
---------------------
|
||||
## Building The Provider
|
||||
|
||||
1. Clone the repository
|
||||
1. Enter the repository directory
|
||||
@@ -22,32 +19,30 @@ You'll need to ensure that your Terraform file contains the information necessar
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_providers {
|
||||
boundary = {
|
||||
source = "localhost/providers/boundary"
|
||||
version = "0.0.1"
|
||||
}
|
||||
}
|
||||
required_providers {
|
||||
boundary = {
|
||||
source = "localhost/providers/boundary"
|
||||
version = "0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Adding Dependencies
|
||||
---------------------
|
||||
## Adding Dependencies
|
||||
|
||||
This provider uses [Go modules](https://github.com/golang/go/wiki/Modules).
|
||||
Please see the Go documentation for the most up to date information about using Go modules.
|
||||
|
||||
To add a new dependency `github.com/author/dependency` to your Terraform provider:
|
||||
|
||||
```
|
||||
```shell
|
||||
go get github.com/author/dependency
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
Then commit the changes to `go.mod` and `go.sum`.
|
||||
|
||||
Developing the Provider
|
||||
---------------------------
|
||||
## Developing the Provider
|
||||
|
||||
If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above).
|
||||
|
||||
@@ -72,25 +67,40 @@ $ make testacc
|
||||
For more details on the docker image and troubleshooting see the
|
||||
[boundary testing doc](https://github.com/hashicorp/boundary/blob/main/CONTRIBUTING.md#testing).
|
||||
|
||||
Generating Docs
|
||||
----------------------
|
||||
## Debugging the provider
|
||||
|
||||
If you're using vscode, this provider has delve debugging baked into the [.vscode](https://github.com/hashicorp/terraform-provider-boundary/tree/main/.vscode) directory. To debug you can find instructions [here.](https://dev.to/drewmullen/vscode-terraform-provider-development-setup-debugging-6bn#usage)
|
||||
|
||||
## Generating Docs
|
||||
|
||||
This provider uses the [terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs/) tool to generate documentation. Each resource and data source requires a set of examples [examples](https://github.com/hashicorp/terraform-provider-boundary/tree/main/examples) then you execute a binary to generate the docs which are placed in [./docs](https://github.com/hashicorp/terraform-provider-boundary/tree/main/docs). Optionally, you can specify your own [templates](https://github.com/hashicorp/terraform-provider-boundary/tree/main/templates) for a resource or data source if you want to add custom information (example: [data "boundary_scope"](https://github.com/hashicorp/terraform-provider-boundary/blob/main/templates/data-sources/scope.md.tmpl#L11))
|
||||
|
||||
### New Resources & Data Sources (documentation templates)
|
||||
|
||||
If you're adding a new resource or a data source, you must include a documentation template for your new R/DS in [./templates](https://github.com/hashicorp/terraform-provider-boundary/tree/main/templates) directory. This provider uses the [terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs/) tool to generate documentation. For full details see the link.
|
||||
|
||||
### Documentation Examples for Resources & Data Sources
|
||||
|
||||
Examples are required for all resources and data sources. This provider uses the [terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs/) tool to generate documentation. To add examples, add a new file to the [./examples](https://github.com/hashicorp/terraform-provider-boundary/tree/main/examples) directory or update the existing files. Next make sure the corresponding template in [./templates](https://github.com/hashicorp/terraform-provider-boundary/tree/main/templates) directory references this example.
|
||||
|
||||
### Generating
|
||||
|
||||
From the root of the repo run:
|
||||
|
||||
```
|
||||
```shell
|
||||
go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
|
||||
```
|
||||
|
||||
Using the provider
|
||||
----------------------
|
||||
## Using the provider
|
||||
|
||||
Please see our detailed docs for individual resource usage. Below is a complex example using the Boundary provider to configure all resource types available:
|
||||
|
||||
```hcl
|
||||
provider "boundary" {
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_id = "ampw_1234567890" # changeme
|
||||
password_auth_method_login_name = "myuser" # changeme
|
||||
password_auth_method_password = "passpass" # changeme
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_id = "ampw_1234567890" # changeme
|
||||
auth_method_login_name = "myuser" # changeme
|
||||
auth_method_password = "passpass" # changeme
|
||||
}
|
||||
|
||||
variable "users" {
|
||||
@@ -140,7 +150,7 @@ resource "boundary_user" "users" {
|
||||
scope_id = boundary_scope.corp.id
|
||||
}
|
||||
|
||||
// organization level group for readonly users
|
||||
// organization level group for readonly users
|
||||
resource "boundary_group" "readonly" {
|
||||
name = "readonly"
|
||||
description = "Organization group for readonly users"
|
||||
@@ -153,7 +163,7 @@ resource "boundary_role" "organization_readonly" {
|
||||
name = "readonly"
|
||||
description = "Read-only role"
|
||||
principal_ids = [boundary_group.readonly_users.id]
|
||||
grant_strings = ["id=*;type=*;actions=read"]
|
||||
grant_strings = ["ids=*;type=*;actions=read"]
|
||||
scope_id = boundary_scope.corp.id
|
||||
}
|
||||
|
||||
@@ -164,7 +174,7 @@ resource "boundary_role" "organization_admin" {
|
||||
principal_ids = concat(
|
||||
[for user in boundary_user.user: user.id]
|
||||
)
|
||||
grant_strings = ["id=*;type=*;actions=create,read,update,delete"]
|
||||
grant_strings = ["ids=*;type=*;actions=create,read,update,delete"]
|
||||
scope_id = boundary_scope.corp.id
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "boundary_account Data Source - terraform-provider-boundary"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
The boundary_account data source allows you to find a Boundary account.
|
||||
---
|
||||
|
||||
# boundary_account (Data Source)
|
||||
|
||||
The boundary_account data source allows you to find a Boundary account.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
# Retrieve the ID of a Boundary account
|
||||
data "boundary_account" "admin" {
|
||||
name = "admin"
|
||||
auth_method_id = "ampw_1234567890"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `auth_method_id` (String) The auth method ID that will be queried for the account.
|
||||
- `name` (String) The name of the account to retrieve.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `description` (String) The description of the retrieved account.
|
||||
- `id` (String) The ID of the retrieved account.
|
||||
- `scope` (List of Object) (see [below for nested schema](#nestedatt--scope))
|
||||
- `type` (String) The type of the account
|
||||
|
||||
<a id="nestedatt--scope"></a>
|
||||
### Nested Schema for `scope`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String)
|
||||
- `id` (String)
|
||||
- `name` (String)
|
||||
- `parent_scope_id` (String)
|
||||
- `type` (String)
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "boundary_auth_method Data Source - terraform-provider-boundary"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
The boundaryauthmethod data source allows you to find a Boundary auth method.
|
||||
---
|
||||
|
||||
# boundary_auth_method (Data Source)
|
||||
|
||||
The boundary_auth_method data source allows you to find a Boundary auth method.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
# Retrieve an auth method from the global scope
|
||||
data "boundary_auth_method" "auth_method" {
|
||||
name = "password_auth_method"
|
||||
}
|
||||
|
||||
# Auth method from a org scope
|
||||
data "boundary_scope" "org" {
|
||||
name = "my-org"
|
||||
scope_id = "global"
|
||||
}
|
||||
|
||||
data "boundary_auth_method" "auth_method" {
|
||||
name = "password_auth_method"
|
||||
scope_id = data.boundary_scope.org.id
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) The name of the auth method to retrieve.
|
||||
|
||||
### Optional
|
||||
|
||||
- `scope_id` (String) The scope ID in which the resource is created. Defaults `global` if unset.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `description` (String) The description of the retrieved auth method.
|
||||
- `id` (String) The ID of the retrieved auth method.
|
||||
- `scope` (List of Object) (see [below for nested schema](#nestedatt--scope))
|
||||
- `type` (String) The type of the auth method
|
||||
|
||||
<a id="nestedatt--scope"></a>
|
||||
### Nested Schema for `scope`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String)
|
||||
- `id` (String)
|
||||
- `name` (String)
|
||||
- `parent_scope_id` (String)
|
||||
- `type` (String)
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "boundary_group Data Source - terraform-provider-boundary"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
The boundary_group data source allows you to find a Boundary group.
|
||||
---
|
||||
|
||||
# boundary_group (Data Source)
|
||||
|
||||
The boundary_group data source allows you to find a Boundary group.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
# Retrieve a user from the global scope
|
||||
data "boundary_group" "global_group" {
|
||||
name = "admin"
|
||||
}
|
||||
|
||||
# User from an org scope
|
||||
data "boundary_scope" "org" {
|
||||
name = "org"
|
||||
scope_id = "global"
|
||||
}
|
||||
|
||||
data "boundary_group" "org_group" {
|
||||
name = "username"
|
||||
scope_id = data.boundary_scope.org.id
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) The name of the group to retrieve.
|
||||
|
||||
### Optional
|
||||
|
||||
- `scope_id` (String) The scope ID in which the resource is created. Defaults `global` if unset.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `description` (String) The description of the retrieved group.
|
||||
- `id` (String) The ID of the retrieved group.
|
||||
- `member_ids` (Set of String) Resource IDs for group members, these are most likely boundary users.
|
||||
- `scope` (List of Object) (see [below for nested schema](#nestedatt--scope))
|
||||
|
||||
<a id="nestedatt--scope"></a>
|
||||
### Nested Schema for `scope`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String)
|
||||
- `id` (String)
|
||||
- `name` (String)
|
||||
- `parent_scope_id` (String)
|
||||
- `type` (String)
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
page_title: "boundary_scope Data Source - terraform-provider-boundary"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
The scope data source allows you to discover an existing Boundary scope by name.
|
||||
---
|
||||
|
||||
# boundary_scope (Data Source)
|
||||
|
||||
The scope data source allows you to discover an existing Boundary scope by name.
|
||||
Please note that the Global scope will always have an id of "global", and does not need to be discovered with this data source.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
# Retrieve the ID of a Boundary project
|
||||
data "boundary_scope" "org" {
|
||||
name = "SecOps"
|
||||
scope_id = "global"
|
||||
}
|
||||
|
||||
data "boundary_scope" "project" {
|
||||
name = "2111"
|
||||
scope_id = data.boundary_scope.id
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) The name of the scope to retrieve.
|
||||
- `scope_id` (String) The parent scope ID that will be queried for the scope.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `description` (String) The description of the retrieved scope.
|
||||
- `id` (String) The ID of the retrieved scope.
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "boundary_user Data Source - terraform-provider-boundary"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
The user data source allows you to find a Boundary user.
|
||||
---
|
||||
|
||||
# boundary_user (Data Source)
|
||||
|
||||
The user data source allows you to find a Boundary user.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
# Retrieve a user from the global scope
|
||||
data "boundary_user" "global_scope_admin" {
|
||||
name = "admin"
|
||||
}
|
||||
|
||||
# User from a org scope
|
||||
data "boundary_user" "org_user" {
|
||||
name = "username"
|
||||
scope_id = data.boundary_scope.org.id
|
||||
}
|
||||
|
||||
data "boundary_scope" "org" {
|
||||
name = "my-org"
|
||||
scope_id = data.boundary_scope.org.id
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) The username to search for.
|
||||
|
||||
### Optional
|
||||
|
||||
- `scope_id` (String) The scope ID in which the resource is created. Defaults `global` if unset.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `account_ids` (Set of String) Account ID's to associate with this user resource.
|
||||
- `authorized_actions` (List of String) A list of actions that the worker is entitled to perform.
|
||||
- `description` (String) The user description.
|
||||
- `id` (String) The ID of the user.
|
||||
- `login_name` (String) Login name for user.
|
||||
- `primary_account_id` (String) Primary account ID.
|
||||
- `scope` (List of Object) (see [below for nested schema](#nestedatt--scope))
|
||||
|
||||
<a id="nestedatt--scope"></a>
|
||||
### Nested Schema for `scope`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String)
|
||||
- `id` (String)
|
||||
- `name` (String)
|
||||
- `parent_scope_id` (String)
|
||||
- `type` (String)
|
||||
+11
-11
@@ -15,23 +15,23 @@ Do not keep your authentication password in HCL for production environments, use
|
||||
|
||||
```terraform
|
||||
provider "boundary" {
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_id = "ampw_1234567890" # changeme
|
||||
password_auth_method_login_name = "myuser" # changeme
|
||||
password_auth_method_password = "passpass" # changeme
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_id = "ampw_1234567890" # changeme
|
||||
auth_method_login_name = "myuser" # changeme
|
||||
auth_method_password = "passpass" # changeme
|
||||
}
|
||||
|
||||
provider "boundary" {
|
||||
addr = "http://127.0.0.1:9200"
|
||||
password_auth_method_login_name = "myuser"
|
||||
password_auth_method_password = "passpass"
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_login_name = "myuser"
|
||||
auth_method_password = "passpass"
|
||||
}
|
||||
|
||||
provider "boundary" {
|
||||
addr = "http://127.0.0.1:9200"
|
||||
password_auth_method_login_name = "myuser"
|
||||
password_auth_method_password = "passpass"
|
||||
scope_id = "s_1234567890"
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_login_name = "myuser"
|
||||
auth_method_password = "passpass"
|
||||
scope_id = "s_1234567890"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -30,5 +30,3 @@ Deprecated: use `boundary_account_password` instead.
|
||||
### Read-Only
|
||||
|
||||
- `id` (String) The ID of the account.
|
||||
|
||||
|
||||
@@ -10,7 +10,44 @@ description: |-
|
||||
|
||||
The account resource allows you to configure a Boundary account.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = "global"
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_ldap" "forumsys_ldap" {
|
||||
name = "forumsys public LDAP"
|
||||
scope_id = "global" # add the new auth method to the global scope
|
||||
urls = ["ldap://ldap.forumsys.com"] # the addr of the LDAP server
|
||||
user_dn = "dc=example,dc=com" # the basedn for users
|
||||
user_attr = "uid" # the user attribute
|
||||
group_dn = "dc=example,dc=com" # the basedn for groups
|
||||
bind_dn = "cn=read-only-admin,dc=example,dc=com" # the dn to use when binding
|
||||
bind_password = "password" # passwd to use when binding
|
||||
state = "active-public" # make sure the new auth-method is available to everyone
|
||||
enable_groups = true # this turns-on the discovery of a user's groups
|
||||
discover_dn = true # this turns-on the discovery of an authenticating user's dn
|
||||
}
|
||||
|
||||
resource "boundary_account_ldap" "einstein" {
|
||||
auth_method_id = boundary_auth_method_ldap.forumsys_ldap.id
|
||||
login_name = "einstein"
|
||||
name = "einstein"
|
||||
}
|
||||
|
||||
resource "boundary_user" "einstein" {
|
||||
name = "einstein"
|
||||
description = "User resource for einstein"
|
||||
scope_id = "global"
|
||||
account_ids = [boundary_account_ldap.einstein.id]
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
@@ -30,4 +67,10 @@ The account resource allows you to configure a Boundary account.
|
||||
|
||||
- `id` (String) The ID of the account.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import boundary_account_ldap.foo <my-id>
|
||||
```
|
||||
@@ -29,5 +29,3 @@ The account resource allows you to configure a Boundary account.
|
||||
### Read-Only
|
||||
|
||||
- `id` (String) The ID of the account.
|
||||
|
||||
|
||||
@@ -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>
|
||||
```
|
||||
@@ -10,7 +10,31 @@ description: |-
|
||||
|
||||
The LDAP auth method resource allows you to configure a Boundary auth_method_ldap.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = "global"
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_ldap" "forumsys_ldap" {
|
||||
name = "forumsys public LDAP"
|
||||
scope_id = "global" # add the new auth method to the global scope
|
||||
urls = ["ldap://ldap.forumsys.com"] # the addr of the LDAP server
|
||||
user_dn = "dc=example,dc=com" # the basedn for users
|
||||
user_attr = "uid" # the user attribute
|
||||
group_dn = "dc=example,dc=com" # the basedn for groups
|
||||
bind_dn = "cn=read-only-admin,dc=example,dc=com" # the dn to use when binding
|
||||
bind_password = "password" # passwd to use when binding
|
||||
state = "active-public" # make sure the new auth-method is available to everyone
|
||||
enable_groups = true # this turns-on the discovery of a user's groups
|
||||
discover_dn = true # this turns-on the discovery of an authenticating user's dn
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
@@ -30,6 +54,7 @@ The LDAP auth method resource allows you to configure a Boundary auth_method_lda
|
||||
- `client_certificate` (String) PEM-encoded X.509 client certificate in ASN.1 DER form that can be used to authenticate against an LDAP server(optional).
|
||||
- `client_certificate_key` (String) PEM-encoded X.509 client certificate key in PKCS #8, ASN.1 DER form used with the client certificate (optional).
|
||||
- `client_certificate_key_hmac` (String) The HMAC of the client certificate key returned by the Boundary controller, which is used for comparison after initial setting of the value.
|
||||
- `dereference_aliases` (String) Control how aliases are dereferenced when performing the search. Can be one of: NeverDerefAliases, DerefInSearching, DerefFindingBaseObj, and DerefAlways (optional).
|
||||
- `description` (String) The auth method description.
|
||||
- `discover_dn` (Boolean) Use anon bind to discover the bind DN of a user (optional).
|
||||
- `enable_groups` (Boolean) Find the authenticated user's groups during authentication (optional).
|
||||
@@ -38,6 +63,7 @@ The LDAP auth method resource allows you to configure a Boundary auth_method_lda
|
||||
- `group_filter` (String) A go template used to construct a LDAP group search filter (optional).
|
||||
- `insecure_tls` (Boolean) Skip the LDAP server SSL certificate validation (optional) - insecure and use with caution.
|
||||
- `is_primary_for_scope` (Boolean) When true, makes this auth method the primary auth method for the scope in which it resides. The primary auth method for a scope means the the user will be automatically created when they login using an LDAP account.
|
||||
- `maximum_page_size` (Number) MaximumPageSize specifies a maximum search result size to use when retrieving the authenticated user's groups (optional).
|
||||
- `name` (String) The auth method name. Defaults to the resource name.
|
||||
- `start_tls` (Boolean) Issue StartTLS command after connecting (optional).
|
||||
- `state` (String) Can be one of 'inactive', 'active-private', or 'active-public'. Defaults to active-public.
|
||||
@@ -53,4 +79,10 @@ The LDAP auth method resource allows you to configure a Boundary auth_method_lda
|
||||
|
||||
- `id` (String) The ID of the auth method.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import boundary_auth_method_ldap.foo <my-id>
|
||||
```
|
||||
@@ -10,7 +10,52 @@ description: |-
|
||||
|
||||
The OIDC auth method resource allows you to configure a Boundary auth_method_oidc.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = "global"
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_oidc" "vault" {
|
||||
api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200"
|
||||
client_id = "eieio"
|
||||
client_secret = "hvo_secret_XO"
|
||||
description = "My Boundary OIDC Auth Method for Vault"
|
||||
issuer = "https://XO-XO-XO-XO-XOXOXO.vault.hashicorp.cloud:8200/v1/identity/oidc/provider/my-provider"
|
||||
scope_id = "global"
|
||||
signing_algorithms = ["RS256"]
|
||||
type = "oidc"
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_oidc" "oidc_with_one_prompt" {
|
||||
api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200"
|
||||
client_id = "eieio"
|
||||
client_secret = "hvo_secret_XO"
|
||||
description = "My Boundary OIDC Auth Method With Prompt"
|
||||
issuer = "https://sts.windows.net/TENANT_ID/"
|
||||
scope_id = "global"
|
||||
signing_algorithms = ["RS256"]
|
||||
prompts = ["select_account"]
|
||||
type = "oidc"
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_oidc" "oidc_with_multiple_prompts" {
|
||||
api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200"
|
||||
client_id = "eieio"
|
||||
client_secret = "hvo_secret_XO"
|
||||
description = "My Boundary OIDC Auth Method With Multiple Prompts"
|
||||
issuer = "https://sts.windows.net/TENANT_ID/"
|
||||
scope_id = "global"
|
||||
signing_algorithms = ["RS256"]
|
||||
prompts = ["consent", "select_account"]
|
||||
type = "oidc"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
@@ -36,6 +81,7 @@ The OIDC auth method resource allows you to configure a Boundary auth_method_oid
|
||||
- `issuer` (String) The issuer corresponding to the provider, which must match the issuer field in generated tokens.
|
||||
- `max_age` (Number) The max age to provide to the provider, indicating how much time is allowed to have passed since the last authentication before the user is challenged again. A value of 0 sets an immediate requirement for all users to reauthenticate, and an unset maxAge results in a Terraform value of -1 and the default TTL of the chosen OIDC will be used.
|
||||
- `name` (String) The auth method name. Defaults to the resource name.
|
||||
- `prompts` (List of String) The prompts passed to the identity provider to determine whether to prompt the end-user for reauthentication, account selection or consent. Please note the values passed are case-sensitive. The valid values are: `none`, `login`, `consent` and `select_account`.
|
||||
- `signing_algorithms` (List of String) Allowed signing algorithms for the provider's issued tokens.
|
||||
- `state` (String) Can be one of 'inactive', 'active-private', or 'active-public'. Currently automatically set to active-public.
|
||||
- `type` (String) The type of auth method; hardcoded.
|
||||
@@ -44,4 +90,10 @@ The OIDC auth method resource allows you to configure a Boundary auth_method_oid
|
||||
|
||||
- `id` (String) The ID of the auth method.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import boundary_auth_method_oidc.foo <my-id>
|
||||
```
|
||||
@@ -30,5 +30,3 @@ The auth method resource allows you to configure a Boundary auth_method_password
|
||||
### Read-Only
|
||||
|
||||
- `id` (String) The ID of the account.
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ resource "boundary_credential_library_vault_ssh_certificate" "baz" {
|
||||
|
||||
### Optional
|
||||
|
||||
- `additional_valid_principals` (List of String) Principals to be signed as "valid_principles" in addition to username.
|
||||
- `critical_options` (Map of String) Specifies a map of the critical options that the certificate should be signed for.
|
||||
- `description` (String) The Vault credential library description.
|
||||
- `extensions` (Map of String) Specifies a map of the extensions that the certificate should be signed for.
|
||||
|
||||
@@ -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.
|
||||
- `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.
|
||||
- `worker_filter` (String) HCP Only. A filter used to control which PKI workers can handle dynamic host catalog requests.
|
||||
|
||||
### Read-Only
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ resource "boundary_host" "first" {
|
||||
description = "My first host!"
|
||||
address = "10.0.0.1"
|
||||
host_catalog_id = boundary_host_catalog.static.id
|
||||
scope_id = boundary_scope.project.id
|
||||
}
|
||||
|
||||
resource "boundary_host" "second" {
|
||||
@@ -48,7 +47,6 @@ resource "boundary_host" "second" {
|
||||
description = "My second host!"
|
||||
address = "10.0.0.2"
|
||||
host_catalog_id = boundary_host_catalog.static.id
|
||||
scope_id = boundary_scope.project.id
|
||||
}
|
||||
|
||||
resource "boundary_host_set" "web" {
|
||||
|
||||
@@ -51,7 +51,7 @@ resource "boundary_host_catalog_plugin" "aws_example" {
|
||||
resource "boundary_host_set_plugin" "web" {
|
||||
name = "My web host set plugin"
|
||||
host_catalog_id = boundary_host_catalog_plugin.aws_example.id
|
||||
attributes_json = jsonencode({ "filters" = "tag:service-type=web" })
|
||||
attributes_json = jsonencode({ "filters" = ["tag:service-type=web"] })
|
||||
}
|
||||
|
||||
resource "boundary_host_set_plugin" "foobar" {
|
||||
@@ -59,8 +59,7 @@ resource "boundary_host_set_plugin" "foobar" {
|
||||
host_catalog_id = boundary_host_catalog_plugin.aws_example.id
|
||||
preferred_endpoints = ["cidr:54.0.0.0/8"]
|
||||
attributes_json = jsonencode({
|
||||
"filters" = "tag-key=foo",
|
||||
"filters" = "tag-key=bar"
|
||||
"filters" = ["tag-key=foo", "tag-key=bar"]
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,8 +68,7 @@ resource "boundary_host_set_plugin" "launch" {
|
||||
host_catalog_id = boundary_host_catalog_plugin.aws_example.id
|
||||
sync_interval_seconds = 60
|
||||
attributes_json = jsonencode({
|
||||
"filters" = "tag:development=prod,dev",
|
||||
"filters" = "launch-time=2022-01-04T*"
|
||||
"filters" = ["tag:development=prod,dev", "launch-time=2022-01-04T*"]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -28,5 +28,3 @@ The managed group resource allows you to configure a Boundary group.
|
||||
### Read-Only
|
||||
|
||||
- `id` (String) The ID of the group.
|
||||
|
||||
|
||||
@@ -10,7 +10,38 @@ description: |-
|
||||
|
||||
The managed group resource allows you to configure a Boundary group.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = "global"
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_ldap" "forumsys_ldap" {
|
||||
name = "forumsys public LDAP"
|
||||
scope_id = "global" # add the new auth method to the global scope
|
||||
urls = ["ldap://ldap.forumsys.com"] # the addr of the LDAP server
|
||||
user_dn = "dc=example,dc=com" # the basedn for users
|
||||
user_attr = "uid" # the user attribute
|
||||
group_dn = "dc=example,dc=com" # the basedn for groups
|
||||
bind_dn = "cn=read-only-admin,dc=example,dc=com" # the dn to use when binding
|
||||
bind_password = "password" # passwd to use when binding
|
||||
state = "active-public" # make sure the new auth-method is available to everyone
|
||||
enable_groups = true # this turns-on the discovery of a user's groups
|
||||
discover_dn = true # this turns-on the discovery of an authenticating user's dn
|
||||
}
|
||||
|
||||
resource "boundary_managed_group_ldap" "forumsys_scientists" {
|
||||
name = "scientists"
|
||||
description = "forumsys scientists managed group"
|
||||
auth_method_id = boundary_auth_method_ldap.forumsys_ldap.id
|
||||
group_names = ["Scientists"]
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
@@ -29,4 +60,10 @@ The managed group resource allows you to configure a Boundary group.
|
||||
|
||||
- `id` (String) The ID of the group.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import boundary_managed_group_ldap.foo <my-id>
|
||||
```
|
||||
@@ -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.
|
||||
@@ -79,7 +79,7 @@ resource "boundary_role" "readonly" {
|
||||
name = "readonly"
|
||||
description = "A readonly role"
|
||||
principal_ids = [boundary_user.readonly.id]
|
||||
grant_strings = ["id=*;type=*;actions=read"]
|
||||
grant_strings = ["ids=*;type=*;actions=read"]
|
||||
scope_id = boundary_scope.org.id
|
||||
}
|
||||
```
|
||||
@@ -112,7 +112,7 @@ resource "boundary_role" "readonly" {
|
||||
name = "readonly"
|
||||
description = "A readonly role"
|
||||
principal_ids = [boundary_user.readonly.id]
|
||||
grant_strings = ["id=*;type=*;actions=read"]
|
||||
grant_strings = ["ids=*;type=*;actions=read"]
|
||||
scope_id = boundary_scope.project.id
|
||||
}
|
||||
```
|
||||
@@ -127,7 +127,7 @@ resource "boundary_role" "readonly" {
|
||||
### Optional
|
||||
|
||||
- `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.
|
||||
- `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.
|
||||
|
||||
@@ -53,10 +53,10 @@ resource "boundary_scope" "org" {
|
||||
}
|
||||
|
||||
resource "boundary_role" "org_admin" {
|
||||
scope_id = boundary_scope.global.id
|
||||
grant_scope_id = boundary_scope.org.id
|
||||
grant_strings = ["id=*;type=*;actions=*"]
|
||||
principal_ids = ["u_auth"]
|
||||
scope_id = boundary_scope.global.id
|
||||
grant_scope_ids = [boundary_scope.org.id]
|
||||
grant_strings = ["ids=*;type=*;actions=*"]
|
||||
principal_ids = ["u_auth"]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "boundary_storage_bucket Resource - terraform-provider-boundary"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
The storage bucket resource allows you to configure a Boundary storage bucket. A storage bucket can only belong to the Global scope or an Org scope. At this time, the only supported storage for storage buckets is AWS S3. This feature requires Boundary Enterprise or Boundary HCP.
|
||||
---
|
||||
|
||||
# boundary_storage_bucket (Resource)
|
||||
|
||||
The storage bucket resource allows you to configure a Boundary storage bucket. A storage bucket can only belong to the Global scope or an Org scope. At this time, the only supported storage for storage buckets is AWS S3. This feature requires Boundary Enterprise or Boundary HCP.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = boundary_scope.global.id
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_storage_bucket" "aws_static_credentials_example" {
|
||||
name = "My aws storage bucket with static credentials"
|
||||
description = "My first storage bucket!"
|
||||
scope_id = boundary_scope.org.id
|
||||
plugin_name = "aws"
|
||||
bucket_name = "mybucket"
|
||||
attributes_json = jsonencode({ "region" = "us-east-1" })
|
||||
|
||||
# recommended to pass in aws secrets using a file() or using environment variables
|
||||
# the secrets below must be generated in aws by creating a aws iam user with programmatic access
|
||||
secrets_json = jsonencode({
|
||||
"access_key_id" = "aws_access_key_id_value",
|
||||
"secret_access_key" = "aws_secret_access_key_value"
|
||||
})
|
||||
worker_filter = "\"pki\" in \"/tags/type\""
|
||||
}
|
||||
|
||||
resource "boundary_storage_bucket" "aws_dynamic_credentials_example" {
|
||||
name = "My aws storage bucket with dynamic credentials"
|
||||
description = "My first storage bucket!"
|
||||
scope_id = boundary_scope.org.id
|
||||
plugin_name = "aws"
|
||||
bucket_name = "mybucket"
|
||||
|
||||
# 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
|
||||
attributes_json = jsonencode({
|
||||
"region" = "us-east-1"
|
||||
"role_arn" = "arn:aws:iam::123456789012:role/S3Access"
|
||||
"disable_credential_rotation" = "true"
|
||||
})
|
||||
worker_filter = "\"pki\" in \"/tags/type\""
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `bucket_name` (String) The name of the bucket within the external object store service.
|
||||
- `scope_id` (String) The scope for this storage bucket.
|
||||
- `worker_filter` (String) Filters to the worker(s) that can handle requests for this storage bucket. The filter must match an existing worker in order to create a storage bucket.
|
||||
|
||||
### Optional
|
||||
|
||||
- `attributes_json` (String) The attributes for the storage bucket. The "region" attribute field is required when creating an AWS storage bucket. Values are either encoded with the "jsonencode" function, pre-escaped JSON string, or a file:// or env:// path. Set to a string "null" or remove the block to clear all attributes in the storage bucket.
|
||||
- `bucket_prefix` (String) The prefix used to organize the data held within the external object store.
|
||||
- `description` (String) The storage bucket description.
|
||||
- `name` (String) The storage bucket name. Defaults to the resource name.
|
||||
- `plugin_id` (String) The ID of the plugin that should back the resource. This or plugin_name must be defined.
|
||||
- `plugin_name` (String) The name of the plugin that should back the resource. This or plugin_id must be defined.
|
||||
- `secrets_json` (String, Sensitive) The secrets for the storage bucket. 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 storage bucket; this allows injecting secrets for one call, then removing them for storage.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (String) The ID of the storage bucket.
|
||||
- `internal_force_update` (String) Internal only. Used to force update so that we can always check the value of secrets.
|
||||
- `internal_hmac_used_for_secrets_config_hmac` (String) Internal only. The Boundary-provided HMAC used to calculate the current value of the HMAC'd config. Used for drift detection.
|
||||
- `internal_secrets_config_hmac` (String) Internal only. HMAC of (serverSecretsHmac + config secrets). Used for proper secrets handling.
|
||||
- `secrets_hmac` (String) The HMAC'd secrets value returned from the server.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import boundary_storage_bucket.foo <my-id>
|
||||
```
|
||||
@@ -61,7 +61,6 @@ resource "boundary_host" "foo" {
|
||||
type = "static"
|
||||
name = "foo"
|
||||
host_catalog_id = boundary_host_catalog.foo.id
|
||||
scope_id = boundary_scope.project.id
|
||||
address = "10.0.0.1"
|
||||
}
|
||||
|
||||
@@ -69,7 +68,6 @@ resource "boundary_host" "bar" {
|
||||
type = "static"
|
||||
name = "bar"
|
||||
host_catalog_id = boundary_host_catalog.foo.id
|
||||
scope_id = boundary_scope.project.id
|
||||
address = "10.0.0.1"
|
||||
}
|
||||
|
||||
@@ -84,6 +82,20 @@ resource "boundary_host_set" "foo" {
|
||||
]
|
||||
}
|
||||
|
||||
resource "boundary_storage_bucket" "aws_example" {
|
||||
name = "My aws storage bucket"
|
||||
description = "My first storage bucket!"
|
||||
scope_id = boundary_scope.org.id
|
||||
plugin_name = "aws"
|
||||
bucket_name = "mybucket"
|
||||
attributes_json = jsonencode({ "region" = "us-east-1" })
|
||||
secrets_json = jsonencode({
|
||||
"access_key_id" = "aws_access_key_id_value",
|
||||
"secret_access_key" = "aws_secret_access_key_value"
|
||||
})
|
||||
worker_filter = "\"pki\" in \"/tags/type\""
|
||||
}
|
||||
|
||||
resource "boundary_target" "foo" {
|
||||
name = "foo"
|
||||
description = "Foo target"
|
||||
@@ -112,6 +124,22 @@ resource "boundary_target" "ssh_foo" {
|
||||
]
|
||||
}
|
||||
|
||||
resource "boundary_target" "ssh_session_recording_foo" {
|
||||
name = "ssh_foo"
|
||||
description = "Ssh target"
|
||||
type = "ssh"
|
||||
default_port = "22"
|
||||
scope_id = boundary_scope.project.id
|
||||
host_source_ids = [
|
||||
boundary_host_set.foo.id
|
||||
]
|
||||
injected_application_credential_source_ids = [
|
||||
boundary_credential_library_vault.foo.id
|
||||
]
|
||||
enable_session_recording = true
|
||||
storage_bucket_id = boundary_storage_bucket.aws_example
|
||||
}
|
||||
|
||||
resource "boundary_target" "address_foo" {
|
||||
name = "address_foo"
|
||||
description = "Foo target with an address"
|
||||
@@ -138,12 +166,14 @@ resource "boundary_target" "address_foo" {
|
||||
- `default_port` (Number) The default port for this target.
|
||||
- `description` (String) The target description.
|
||||
- `egress_worker_filter` (String) Boolean expression to filter the workers used to access this target
|
||||
- `enable_session_recording` (Boolean) HCP/Ent Only. Enable sessions recording for this target. Only applicable for SSH targets.
|
||||
- `host_source_ids` (Set of String) A list of host source ID's. Cannot be used alongside address.
|
||||
- `ingress_worker_filter` (String) HCP Only. Boolean expression to filter the workers a user will connect to when initiating a session against this target
|
||||
- `injected_application_credential_source_ids` (Set of String) A list of injected application credential source ID's.
|
||||
- `name` (String) The target name. Defaults to the resource name.
|
||||
- `session_connection_limit` (Number)
|
||||
- `session_max_seconds` (Number)
|
||||
- `storage_bucket_id` (String) HCP/Ent Only. Storage bucket for this target. Only applicable for SSH targets.
|
||||
- `worker_filter` (String, Deprecated) Boolean expression to filter the workers for this target
|
||||
|
||||
### Read-Only
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "boundary_worker Resource - terraform-provider-boundary"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
@@ -12,16 +11,22 @@ The resource allows you to create a self-managed worker object.
|
||||
|
||||
## Example Usage
|
||||
|
||||
### Controller-led worker
|
||||
|
||||
```terraform
|
||||
resource "boundary_worker" "controller_led" {
|
||||
scope_id = "global"
|
||||
name = "worker 1"
|
||||
name = "controller-led-worker-1"
|
||||
description = "self managed worker with controller led auth"
|
||||
}
|
||||
```
|
||||
|
||||
resource "boundary_self_managed_worker" "worker_led" {
|
||||
### Worker-led worker
|
||||
|
||||
```terraform
|
||||
resource "boundary_worker" "worker_led" {
|
||||
scope_id = "global"
|
||||
name = "worker 2"
|
||||
name = "worker-led-worker-1"
|
||||
description = "self managed worker with worker led auth"
|
||||
worker_generated_auth_token = var.worker_generated_auth_token
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Retrieve the ID of a Boundary account
|
||||
data "boundary_account" "admin" {
|
||||
name = "admin"
|
||||
auth_method_id = "ampw_1234567890"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Retrieve an auth method from the global scope
|
||||
data "boundary_auth_method" "auth_method" {
|
||||
name = "password_auth_method"
|
||||
}
|
||||
|
||||
# Auth method from a org scope
|
||||
data "boundary_scope" "org" {
|
||||
name = "my-org"
|
||||
scope_id = "global"
|
||||
}
|
||||
|
||||
data "boundary_auth_method" "auth_method" {
|
||||
name = "password_auth_method"
|
||||
scope_id = data.boundary_scope.org.id
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Retrieve a user from the global scope
|
||||
data "boundary_group" "global_group" {
|
||||
name = "admin"
|
||||
}
|
||||
|
||||
# User from an org scope
|
||||
data "boundary_scope" "org" {
|
||||
name = "org"
|
||||
scope_id = "global"
|
||||
}
|
||||
|
||||
data "boundary_group" "org_group" {
|
||||
name = "username"
|
||||
scope_id = data.boundary_scope.org.id
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Retrieve the ID of a Boundary project
|
||||
data "boundary_scope" "org" {
|
||||
name = "SecOps"
|
||||
scope_id = "global"
|
||||
}
|
||||
|
||||
data "boundary_scope" "project" {
|
||||
name = "2111"
|
||||
scope_id = data.boundary_scope.id
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Retrieve a user from the global scope
|
||||
data "boundary_user" "global_scope_admin" {
|
||||
name = "admin"
|
||||
}
|
||||
|
||||
# User from a org scope
|
||||
data "boundary_user" "org_user" {
|
||||
name = "username"
|
||||
scope_id = data.boundary_scope.org.id
|
||||
}
|
||||
|
||||
data "boundary_scope" "org" {
|
||||
name = "my-org"
|
||||
scope_id = data.boundary_scope.org.id
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
provider "boundary" {
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_id = "ampw_1234567890" # changeme
|
||||
password_auth_method_login_name = "myuser" # changeme
|
||||
password_auth_method_password = "passpass" # changeme
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_id = "ampw_1234567890" # changeme
|
||||
auth_method_login_name = "myuser" # changeme
|
||||
auth_method_password = "passpass" # changeme
|
||||
}
|
||||
|
||||
provider "boundary" {
|
||||
addr = "http://127.0.0.1:9200"
|
||||
password_auth_method_login_name = "myuser"
|
||||
password_auth_method_password = "passpass"
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_login_name = "myuser"
|
||||
auth_method_password = "passpass"
|
||||
}
|
||||
|
||||
provider "boundary" {
|
||||
addr = "http://127.0.0.1:9200"
|
||||
password_auth_method_login_name = "myuser"
|
||||
password_auth_method_password = "passpass"
|
||||
scope_id = "s_1234567890"
|
||||
addr = "http://127.0.0.1:9200"
|
||||
auth_method_login_name = "myuser"
|
||||
auth_method_password = "passpass"
|
||||
scope_id = "s_1234567890"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import boundary_account_ldap.foo <my-id>
|
||||
@@ -0,0 +1,34 @@
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = "global"
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_ldap" "forumsys_ldap" {
|
||||
name = "forumsys public LDAP"
|
||||
scope_id = "global" # add the new auth method to the global scope
|
||||
urls = ["ldap://ldap.forumsys.com"] # the addr of the LDAP server
|
||||
user_dn = "dc=example,dc=com" # the basedn for users
|
||||
user_attr = "uid" # the user attribute
|
||||
group_dn = "dc=example,dc=com" # the basedn for groups
|
||||
bind_dn = "cn=read-only-admin,dc=example,dc=com" # the dn to use when binding
|
||||
bind_password = "password" # passwd to use when binding
|
||||
state = "active-public" # make sure the new auth-method is available to everyone
|
||||
enable_groups = true # this turns-on the discovery of a user's groups
|
||||
discover_dn = true # this turns-on the discovery of an authenticating user's dn
|
||||
}
|
||||
|
||||
resource "boundary_account_ldap" "einstein" {
|
||||
auth_method_id = boundary_auth_method_ldap.forumsys_ldap.id
|
||||
login_name = "einstein"
|
||||
name = "einstein"
|
||||
}
|
||||
|
||||
resource "boundary_user" "einstein" {
|
||||
name = "einstein"
|
||||
description = "User resource for einstein"
|
||||
scope_id = "global"
|
||||
account_ids = [boundary_account_ldap.einstein.id]
|
||||
}
|
||||
@@ -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 @@
|
||||
terraform import boundary_auth_method_ldap.foo <my-id>
|
||||
@@ -0,0 +1,21 @@
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = "global"
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_ldap" "forumsys_ldap" {
|
||||
name = "forumsys public LDAP"
|
||||
scope_id = "global" # add the new auth method to the global scope
|
||||
urls = ["ldap://ldap.forumsys.com"] # the addr of the LDAP server
|
||||
user_dn = "dc=example,dc=com" # the basedn for users
|
||||
user_attr = "uid" # the user attribute
|
||||
group_dn = "dc=example,dc=com" # the basedn for groups
|
||||
bind_dn = "cn=read-only-admin,dc=example,dc=com" # the dn to use when binding
|
||||
bind_password = "password" # passwd to use when binding
|
||||
state = "active-public" # make sure the new auth-method is available to everyone
|
||||
enable_groups = true # this turns-on the discovery of a user's groups
|
||||
discover_dn = true # this turns-on the discovery of an authenticating user's dn
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import boundary_auth_method_oidc.foo <my-id>
|
||||
@@ -0,0 +1,42 @@
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = "global"
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_oidc" "vault" {
|
||||
api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200"
|
||||
client_id = "eieio"
|
||||
client_secret = "hvo_secret_XO"
|
||||
description = "My Boundary OIDC Auth Method for Vault"
|
||||
issuer = "https://XO-XO-XO-XO-XOXOXO.vault.hashicorp.cloud:8200/v1/identity/oidc/provider/my-provider"
|
||||
scope_id = "global"
|
||||
signing_algorithms = ["RS256"]
|
||||
type = "oidc"
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_oidc" "oidc_with_one_prompt" {
|
||||
api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200"
|
||||
client_id = "eieio"
|
||||
client_secret = "hvo_secret_XO"
|
||||
description = "My Boundary OIDC Auth Method With Prompt"
|
||||
issuer = "https://sts.windows.net/TENANT_ID/"
|
||||
scope_id = "global"
|
||||
signing_algorithms = ["RS256"]
|
||||
prompts = ["select_account"]
|
||||
type = "oidc"
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_oidc" "oidc_with_multiple_prompts" {
|
||||
api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200"
|
||||
client_id = "eieio"
|
||||
client_secret = "hvo_secret_XO"
|
||||
description = "My Boundary OIDC Auth Method With Multiple Prompts"
|
||||
issuer = "https://sts.windows.net/TENANT_ID/"
|
||||
scope_id = "global"
|
||||
signing_algorithms = ["RS256"]
|
||||
prompts = ["consent", "select_account"]
|
||||
type = "oidc"
|
||||
}
|
||||
@@ -24,7 +24,6 @@ resource "boundary_host" "first" {
|
||||
description = "My first host!"
|
||||
address = "10.0.0.1"
|
||||
host_catalog_id = boundary_host_catalog.static.id
|
||||
scope_id = boundary_scope.project.id
|
||||
}
|
||||
|
||||
resource "boundary_host" "second" {
|
||||
@@ -33,7 +32,6 @@ resource "boundary_host" "second" {
|
||||
description = "My second host!"
|
||||
address = "10.0.0.2"
|
||||
host_catalog_id = boundary_host_catalog.static.id
|
||||
scope_id = boundary_scope.project.id
|
||||
}
|
||||
|
||||
resource "boundary_host_set" "web" {
|
||||
|
||||
@@ -36,7 +36,7 @@ resource "boundary_host_catalog_plugin" "aws_example" {
|
||||
resource "boundary_host_set_plugin" "web" {
|
||||
name = "My web host set plugin"
|
||||
host_catalog_id = boundary_host_catalog_plugin.aws_example.id
|
||||
attributes_json = jsonencode({ "filters" = "tag:service-type=web" })
|
||||
attributes_json = jsonencode({ "filters" = ["tag:service-type=web"] })
|
||||
}
|
||||
|
||||
resource "boundary_host_set_plugin" "foobar" {
|
||||
@@ -44,8 +44,7 @@ resource "boundary_host_set_plugin" "foobar" {
|
||||
host_catalog_id = boundary_host_catalog_plugin.aws_example.id
|
||||
preferred_endpoints = ["cidr:54.0.0.0/8"]
|
||||
attributes_json = jsonencode({
|
||||
"filters" = "tag-key=foo",
|
||||
"filters" = "tag-key=bar"
|
||||
"filters" = ["tag-key=foo", "tag-key=bar"]
|
||||
})
|
||||
}
|
||||
|
||||
@@ -54,8 +53,7 @@ resource "boundary_host_set_plugin" "launch" {
|
||||
host_catalog_id = boundary_host_catalog_plugin.aws_example.id
|
||||
sync_interval_seconds = 60
|
||||
attributes_json = jsonencode({
|
||||
"filters" = "tag:development=prod,dev",
|
||||
"filters" = "launch-time=2022-01-04T*"
|
||||
"filters" = ["tag:development=prod,dev", "launch-time=2022-01-04T*"]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
terraform import boundary_managed_group_ldap.foo <my-id>
|
||||
@@ -0,0 +1,28 @@
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = "global"
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_auth_method_ldap" "forumsys_ldap" {
|
||||
name = "forumsys public LDAP"
|
||||
scope_id = "global" # add the new auth method to the global scope
|
||||
urls = ["ldap://ldap.forumsys.com"] # the addr of the LDAP server
|
||||
user_dn = "dc=example,dc=com" # the basedn for users
|
||||
user_attr = "uid" # the user attribute
|
||||
group_dn = "dc=example,dc=com" # the basedn for groups
|
||||
bind_dn = "cn=read-only-admin,dc=example,dc=com" # the dn to use when binding
|
||||
bind_password = "password" # passwd to use when binding
|
||||
state = "active-public" # make sure the new auth-method is available to everyone
|
||||
enable_groups = true # this turns-on the discovery of a user's groups
|
||||
discover_dn = true # this turns-on the discovery of an authenticating user's dn
|
||||
}
|
||||
|
||||
resource "boundary_managed_group_ldap" "forumsys_scientists" {
|
||||
name = "scientists"
|
||||
description = "forumsys scientists managed group"
|
||||
auth_method_id = boundary_auth_method_ldap.forumsys_ldap.id
|
||||
group_names = ["Scientists"]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -23,6 +23,6 @@ resource "boundary_role" "readonly" {
|
||||
name = "readonly"
|
||||
description = "A readonly role"
|
||||
principal_ids = [boundary_user.readonly.id]
|
||||
grant_strings = ["id=*;type=*;actions=read"]
|
||||
grant_strings = ["ids=*;type=*;actions=read"]
|
||||
scope_id = boundary_scope.project.id
|
||||
}
|
||||
@@ -16,6 +16,6 @@ resource "boundary_role" "readonly" {
|
||||
name = "readonly"
|
||||
description = "A readonly role"
|
||||
principal_ids = [boundary_user.readonly.id]
|
||||
grant_strings = ["id=*;type=*;actions=read"]
|
||||
grant_strings = ["ids=*;type=*;actions=read"]
|
||||
scope_id = boundary_scope.org.id
|
||||
}
|
||||
@@ -5,8 +5,8 @@ resource "boundary_scope" "org" {
|
||||
}
|
||||
|
||||
resource "boundary_role" "org_admin" {
|
||||
scope_id = boundary_scope.global.id
|
||||
grant_scope_id = boundary_scope.org.id
|
||||
grant_strings = ["id=*;type=*;actions=*"]
|
||||
principal_ids = ["u_auth"]
|
||||
scope_id = boundary_scope.global.id
|
||||
grant_scope_ids = [boundary_scope.org.id]
|
||||
grant_strings = ["ids=*;type=*;actions=*"]
|
||||
principal_ids = ["u_auth"]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import boundary_storage_bucket.foo <my-id>
|
||||
@@ -0,0 +1,41 @@
|
||||
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_storage_bucket" "aws_static_credentials_example" {
|
||||
name = "My aws storage bucket with static credentials"
|
||||
description = "My first storage bucket!"
|
||||
scope_id = boundary_scope.org.id
|
||||
plugin_name = "aws"
|
||||
bucket_name = "mybucket"
|
||||
attributes_json = jsonencode({ "region" = "us-east-1" })
|
||||
|
||||
# recommended to pass in aws secrets using a file() or using environment variables
|
||||
# the secrets below must be generated in aws by creating a aws iam user with programmatic access
|
||||
secrets_json = jsonencode({
|
||||
"access_key_id" = "aws_access_key_id_value",
|
||||
"secret_access_key" = "aws_secret_access_key_value"
|
||||
})
|
||||
worker_filter = "\"pki\" in \"/tags/type\""
|
||||
}
|
||||
|
||||
resource "boundary_storage_bucket" "aws_dynamic_credentials_example" {
|
||||
name = "My aws storage bucket with dynamic credentials"
|
||||
description = "My first storage bucket!"
|
||||
scope_id = boundary_scope.org.id
|
||||
plugin_name = "aws"
|
||||
bucket_name = "mybucket"
|
||||
|
||||
# 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
|
||||
attributes_json = jsonencode({
|
||||
"region" = "us-east-1"
|
||||
"role_arn" = "arn:aws:iam::123456789012:role/S3Access"
|
||||
"disable_credential_rotation" = "true"
|
||||
})
|
||||
worker_filter = "\"pki\" in \"/tags/type\""
|
||||
}
|
||||
@@ -46,7 +46,6 @@ resource "boundary_host" "foo" {
|
||||
type = "static"
|
||||
name = "foo"
|
||||
host_catalog_id = boundary_host_catalog.foo.id
|
||||
scope_id = boundary_scope.project.id
|
||||
address = "10.0.0.1"
|
||||
}
|
||||
|
||||
@@ -54,7 +53,6 @@ resource "boundary_host" "bar" {
|
||||
type = "static"
|
||||
name = "bar"
|
||||
host_catalog_id = boundary_host_catalog.foo.id
|
||||
scope_id = boundary_scope.project.id
|
||||
address = "10.0.0.1"
|
||||
}
|
||||
|
||||
@@ -69,6 +67,20 @@ resource "boundary_host_set" "foo" {
|
||||
]
|
||||
}
|
||||
|
||||
resource "boundary_storage_bucket" "aws_example" {
|
||||
name = "My aws storage bucket"
|
||||
description = "My first storage bucket!"
|
||||
scope_id = boundary_scope.org.id
|
||||
plugin_name = "aws"
|
||||
bucket_name = "mybucket"
|
||||
attributes_json = jsonencode({ "region" = "us-east-1" })
|
||||
secrets_json = jsonencode({
|
||||
"access_key_id" = "aws_access_key_id_value",
|
||||
"secret_access_key" = "aws_secret_access_key_value"
|
||||
})
|
||||
worker_filter = "\"pki\" in \"/tags/type\""
|
||||
}
|
||||
|
||||
resource "boundary_target" "foo" {
|
||||
name = "foo"
|
||||
description = "Foo target"
|
||||
@@ -97,6 +109,22 @@ resource "boundary_target" "ssh_foo" {
|
||||
]
|
||||
}
|
||||
|
||||
resource "boundary_target" "ssh_session_recording_foo" {
|
||||
name = "ssh_foo"
|
||||
description = "Ssh target"
|
||||
type = "ssh"
|
||||
default_port = "22"
|
||||
scope_id = boundary_scope.project.id
|
||||
host_source_ids = [
|
||||
boundary_host_set.foo.id
|
||||
]
|
||||
injected_application_credential_source_ids = [
|
||||
boundary_credential_library_vault.foo.id
|
||||
]
|
||||
enable_session_recording = true
|
||||
storage_bucket_id = boundary_storage_bucket.aws_example
|
||||
}
|
||||
|
||||
resource "boundary_target" "address_foo" {
|
||||
name = "address_foo"
|
||||
description = "Foo target with an address"
|
||||
|
||||
@@ -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 controlled led auth"
|
||||
worker_generated_auth_token = var.worker_generated_auth_token
|
||||
}
|
||||
|
||||
resource "boundary_self_managed_worker" "worker_led" {
|
||||
scope_id = "global"
|
||||
name = "worker 2"
|
||||
description = "self managed worker with controlled led auth"
|
||||
worker_generated_auth_token = var.worker_generated_auth_token
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
resource "boundary_worker" "controller_led" {
|
||||
scope_id = "global"
|
||||
name = "worker 1"
|
||||
description = "self managed worker with controller led auth"
|
||||
}
|
||||
|
||||
resource "boundary_self_managed_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
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
resource "boundary_worker" "worker_led" {
|
||||
scope_id = "global"
|
||||
name = "worker-led-worker-1"
|
||||
description = "self managed worker with worker led auth"
|
||||
worker_generated_auth_token = var.worker_generated_auth_token
|
||||
}
|
||||
@@ -1,133 +1,143 @@
|
||||
module github.com/hashicorp/terraform-provider-boundary
|
||||
|
||||
go 1.20
|
||||
go 1.23.1
|
||||
|
||||
require (
|
||||
github.com/hashicorp/boundary v0.13.0
|
||||
github.com/hashicorp/boundary/api v0.0.39
|
||||
github.com/hashicorp/boundary/sdk v0.0.35
|
||||
github.com/hashicorp/cap v0.3.1
|
||||
github.com/hashicorp/cap/ldap v0.0.0-20230420150311-6d1e00a6c5e0
|
||||
github.com/YakDriver/regexache v0.23.0
|
||||
github.com/hashicorp/boundary v0.18.0
|
||||
github.com/hashicorp/boundary/api v0.0.53
|
||||
github.com/hashicorp/boundary/sdk v0.0.49
|
||||
github.com/hashicorp/cap v0.5.1-0.20240315182732-faa330bfb8df
|
||||
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-kms-wrapping/v2 v2.0.10-0.20230601143830-08d524b564ba
|
||||
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.10
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7
|
||||
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.4
|
||||
github.com/hashicorp/terraform-plugin-docs v0.14.1
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1
|
||||
github.com/jimlambrt/gldap v0.1.6
|
||||
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/parseutil v0.1.8
|
||||
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-sdk/v2 v2.33.0
|
||||
github.com/jimlambrt/gldap v0.1.11
|
||||
github.com/kr/pretty v0.3.1
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/crypto v0.9.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/crypto v0.25.0
|
||||
mvdan.cc/gofumpt v0.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // 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-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/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/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.0 // indirect
|
||||
github.com/agext/levenshtein v1.2.2 // indirect
|
||||
github.com/alessio/shellescape v1.4.1 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/alessio/shellescape v1.4.2 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/containerd/continuity v0.3.0 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.5.0 // indirect
|
||||
github.com/danieljoos/wincred v1.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/cli v23.0.1+incompatible // indirect
|
||||
github.com/docker/docker v23.0.3+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/containerd/continuity v0.4.3 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.9.0 // indirect
|
||||
github.com/danieljoos/wincred v1.2.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/cli v25.0.3+incompatible // indirect
|
||||
github.com/docker/docker v27.1.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.4 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/glebarez/sqlite v1.10.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.6 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-migrate/migrate/v4 v4.15.1 // indirect
|
||||
github.com/golang-migrate/migrate/v4 v4.17.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||
github.com/hashicorp/dbassert v0.0.0-20230405175854-2d88acd5134b // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
|
||||
github.com/hashicorp/cli v1.1.6 // indirect
|
||||
github.com/hashicorp/dbassert v0.0.0-20231012105025-1bc1bd88e22b // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/eventlogger v0.1.2-0.20230428153751-cca445805f24 // indirect
|
||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20230428153751-cca445805f24 // indirect
|
||||
github.com/hashicorp/go-bexpr v0.1.12 // indirect
|
||||
github.com/hashicorp/eventlogger v0.2.9 // 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-checkpoint v0.5.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-dbw v0.0.0-20230605211904-d40e5c9d5995 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20221122211539-47c893099f13 // indirect
|
||||
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.4 // indirect
|
||||
github.com/hashicorp/go-dbw v0.1.5-0.20240909162114-6cee92b3da36 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // 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.7 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.4.9 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 // 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-retryablehttp v0.7.7 // 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/gatedwriter v0.1.1 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/kv-builder v0.1.2 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.7 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.9 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/mlock v0.1.3 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/hc-install v0.5.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.16.2 // indirect
|
||||
github.com/hashicorp/hc-install v0.6.3 // indirect
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.19.1 // indirect
|
||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||
github.com/hashicorp/nodeenrollment v0.2.4 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.18.1 // indirect
|
||||
github.com/hashicorp/terraform-json v0.16.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-go v0.14.3 // indirect
|
||||
github.com/hashicorp/terraform-plugin-log v0.8.0 // indirect
|
||||
github.com/hashicorp/terraform-registry-address v0.1.0 // indirect
|
||||
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect
|
||||
github.com/hashicorp/vault/api v1.9.1 // indirect
|
||||
github.com/hashicorp/vault/sdk v0.3.0 // indirect
|
||||
github.com/hashicorp/nodeenrollment v0.2.13 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.20.0 // indirect
|
||||
github.com/hashicorp/terraform-json v0.21.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-registry-address v0.2.3 // indirect
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
|
||||
github.com/hashicorp/vault/api v1.12.0 // indirect
|
||||
github.com/hashicorp/vault/sdk v0.11.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.14.0 // indirect
|
||||
github.com/jackc/pgconn v1.14.3 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgtype v1.14.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.18.1 // indirect
|
||||
github.com/jackc/pgx/v5 v5.3.1 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jefferai/go-libsecret v0.0.0-20210525195240-b53481abef97 // indirect
|
||||
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f // indirect
|
||||
github.com/jefferai/keyring v1.1.7-0.20220316160357-58a74bb55891 // indirect
|
||||
github.com/jinzhu/gorm v1.9.12 // indirect
|
||||
github.com/jinzhu/gorm v1.9.16 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lib/pq v1.10.7 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a // indirect
|
||||
github.com/mitchellh/cli v1.1.5 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
@@ -136,58 +146,68 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/pointerstructure v1.2.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // 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/mr-tron/base58 v1.2.0 // 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/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opencontainers/runc v1.1.5 // indirect
|
||||
github.com/ory/dockertest/v3 v3.9.1 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc6 // indirect
|
||||
github.com/opencontainers/runc v1.2.0-rc.3 // indirect
|
||||
github.com/ory/dockertest/v3 v3.10.0 // indirect
|
||||
github.com/pires/go-proxyproto v0.7.0 // 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/prometheus/client_golang v1.12.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/prometheus/client_golang v1.18.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.46.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/russross/blackfriday v1.6.0 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
github.com/sethvargo/go-diceware v0.3.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
|
||||
github.com/vmihailenco/tagparser v0.1.1 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xo/dburl v0.14.2 // indirect
|
||||
github.com/xo/dburl v0.23.1 // indirect
|
||||
github.com/yuin/goldmark v1.6.0 // indirect
|
||||
github.com/yuin/goldmark-meta v1.1.0 // indirect
|
||||
github.com/zalando/go-keyring v0.2.3 // indirect
|
||||
github.com/zclconf/go-cty v1.13.1 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/oauth2 v0.8.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/term v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/grpc v1.55.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
github.com/zclconf/go-cty v1.14.2 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/oauth2 v0.17.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/term v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect
|
||||
google.golang.org/grpc v1.61.1 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/postgres v1.5.2 // indirect
|
||||
gorm.io/driver/sqlite v1.5.1 // indirect
|
||||
gorm.io/gorm v1.25.1 // indirect
|
||||
gorm.io/driver/postgres v1.5.9 // indirect
|
||||
gorm.io/driver/sqlite v1.5.6 // indirect
|
||||
gorm.io/gorm v1.25.11 // indirect
|
||||
modernc.org/libc v1.41.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/sqlite v1.28.0 // indirect
|
||||
)
|
||||
@@ -44,4 +44,20 @@ const (
|
||||
// internalForceUpdateKey is used to force updates so we can always check
|
||||
// the value of secrets
|
||||
internalForceUpdateKey = "internal_force_update"
|
||||
// workerFilter is used for common "worker_filter" resource attribute
|
||||
WorkerFilterKey = "worker_filter"
|
||||
// LoginNameKey is used for common "login_name" resource attribute
|
||||
LoginNameKey = "login_name"
|
||||
// PrimaryAccountIdKey is used for common "primary_account_id" resource attribute
|
||||
PrimaryAccountIdKey = "primary_account_id"
|
||||
// ScopeKey is used for common "scope" resource attribute
|
||||
ScopeKey = "scope"
|
||||
// ParentScopeKey is used for common "parent_scope_id" resource attribute
|
||||
ParentScopeIdKey = "parent_scope_id"
|
||||
// GroupMemberIdsKey is used for common "member_ids" resource attribute
|
||||
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"
|
||||
)
|
||||
@@ -0,0 +1,139 @@
|
||||
// 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/accounts"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||
)
|
||||
|
||||
func dataSourceAccount() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Description: "The boundary_account data source allows you to find a Boundary account.",
|
||||
ReadContext: dataSourceAccountRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
NameKey: {
|
||||
Description: "The name of the account to retrieve.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
AuthMethodIdKey: {
|
||||
Description: "The auth method ID that will be queried for the account.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
IDKey: {
|
||||
Description: "The ID of the retrieved account.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
DescriptionKey: {
|
||||
Description: "The description of the retrieved account.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
TypeKey: {
|
||||
Description: "The type of the account",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
ScopeKey: {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
IDKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
NameKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
TypeKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
DescriptionKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
ParentScopeIdKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAccountRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
|
||||
md := meta.(*metaData)
|
||||
|
||||
name := d.Get(NameKey).(string)
|
||||
authMethodId := d.Get(AuthMethodIdKey).(string)
|
||||
|
||||
acl := accounts.NewClient(md.client)
|
||||
accountsList, err := acl.List(ctx, authMethodId,
|
||||
accounts.WithFilter(FilterWithItemNameMatches(name)),
|
||||
)
|
||||
if err != nil {
|
||||
return diag.Errorf("error calling list account: %v", err)
|
||||
}
|
||||
accounts := accountsList.GetItems()
|
||||
if accounts == nil {
|
||||
return diag.Errorf("no accounts found")
|
||||
}
|
||||
if len(accounts) == 0 {
|
||||
return diag.Errorf("no matching account found")
|
||||
}
|
||||
if len(accounts) > 1 {
|
||||
return diag.Errorf("error found more than 1 account")
|
||||
}
|
||||
|
||||
arr, err := acl.Read(ctx, accounts[0].Id)
|
||||
if err != nil {
|
||||
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return diag.Errorf("error calling read account: %v", err)
|
||||
}
|
||||
if arr == nil {
|
||||
return diag.Errorf("account nil after read")
|
||||
}
|
||||
|
||||
if err := setFromAccountRead(d, *arr.Item); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFromAccountRead(d *schema.ResourceData, account accounts.Account) error {
|
||||
if err := d.Set(NameKey, account.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(DescriptionKey, account.Description); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(TypeKey, account.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Set(ScopeKey, flattenScopeInfo(account.Scope))
|
||||
d.SetId(account.Id)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/testing/controller"
|
||||
"github.com/hashicorp/cap/oidc"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
var accountPasswordRead = `
|
||||
data "boundary_account" "acc_password" {
|
||||
depends_on = [ boundary_account_password.foo ]
|
||||
name = "test"
|
||||
auth_method_id = boundary_auth_method.foo.id
|
||||
}`
|
||||
|
||||
var accountLdapRead = fmt.Sprintf(`
|
||||
data "boundary_account" "acc_ldap" {
|
||||
depends_on = [ boundary_account_ldap.foo ]
|
||||
name = "%s"
|
||||
auth_method_id = boundary_auth_method_ldap.foo.id
|
||||
}`, testAccountLdapName)
|
||||
|
||||
var accountOidcRead = `
|
||||
data "boundary_account" "acc_oidc" {
|
||||
depends_on = [ boundary_account_oidc.foo ]
|
||||
name = "test"
|
||||
auth_method_id = boundary_auth_method_oidc.foo.id
|
||||
}`
|
||||
|
||||
func TestAccAccountReadPassword(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testConfig(url, fooOrg, fooAccountPassword, accountPasswordRead),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAccountResourceExists(provider, "boundary_account_password.foo"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_password", IDKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_password", AuthMethodIdKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_password", NameKey),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_password", DescriptionKey, fooAccountPasswordDesc),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_password", TypeKey, "password"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_password", "scope.0.id"),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_password", "scope.0.name", "org1"),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_password", "scope.0.type", "org"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAccountReadLdap(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testConfig(url, fooOrg, testAccountLdap, accountLdapRead),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAccountResourceExists(provider, "boundary_account_ldap.foo"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_ldap", IDKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_ldap", AuthMethodIdKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_ldap", NameKey),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_ldap", DescriptionKey, testAccountLdapDesc),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_ldap", TypeKey, "ldap"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_ldap", "scope.0.id"),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_ldap", "scope.0.name", "org1"),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_ldap", "scope.0.type", "org"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAccountReadOidc(t *testing.T) {
|
||||
tp := oidc.StartTestProvider(t)
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
tpCert := strings.TrimSpace(tp.CACert())
|
||||
createConfig := fmt.Sprintf(fooAccountOidc, tp.Addr(), tpCert, fooAccountOidcDesc, tp.ExpectedSubject(), tp.Addr())
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testConfig(url, fooOrg, createConfig, accountOidcRead),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAccountResourceExists(provider, "boundary_account_oidc.foo"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_oidc", IDKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_oidc", AuthMethodIdKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_oidc", NameKey),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_oidc", DescriptionKey, fooAccountOidcDesc),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_oidc", TypeKey, "oidc"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_account.acc_oidc", "scope.0.id"),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_oidc", "scope.0.name", "org1"),
|
||||
resource.TestCheckResourceAttr("data.boundary_account.acc_oidc", "scope.0.type", "org"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
// 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/authmethods"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||
)
|
||||
|
||||
func dataSourceAuthMethod() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Description: "The boundary_auth_method data source allows you to find a Boundary auth method.",
|
||||
ReadContext: dataSourceAuthMethodRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
NameKey: {
|
||||
Description: "The name of the auth method to retrieve.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
ScopeIdKey: {
|
||||
Description: "The scope ID in which the resource is created. Defaults `global` if unset.",
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "global",
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
IDKey: {
|
||||
Description: "The ID of the retrieved auth method.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
DescriptionKey: {
|
||||
Description: "The description of the retrieved auth method.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
TypeKey: {
|
||||
Description: "The type of the auth method",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
ScopeKey: {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
IDKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
NameKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
TypeKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
DescriptionKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
ParentScopeIdKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAuthMethodRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
|
||||
md := meta.(*metaData)
|
||||
|
||||
name := d.Get(NameKey).(string)
|
||||
scopeId := d.Get(ScopeIdKey).(string)
|
||||
|
||||
amcl := authmethods.NewClient(md.client)
|
||||
authMethodsList, err := amcl.List(ctx, scopeId,
|
||||
authmethods.WithFilter(FilterWithItemNameMatches(name)),
|
||||
)
|
||||
if err != nil {
|
||||
return diag.Errorf("error calling list auth method: %v", err)
|
||||
}
|
||||
authMethods := authMethodsList.GetItems()
|
||||
if authMethods == nil {
|
||||
return diag.Errorf("no auth methods found")
|
||||
}
|
||||
if len(authMethods) == 0 {
|
||||
return diag.Errorf("no matching auth method found")
|
||||
}
|
||||
if len(authMethods) > 1 {
|
||||
return diag.Errorf("error found more than 1 auth method")
|
||||
}
|
||||
|
||||
amrr, err := amcl.Read(ctx, authMethods[0].Id)
|
||||
if err != nil {
|
||||
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return diag.Errorf("error calling read auth method: %v", err)
|
||||
}
|
||||
if amrr == nil {
|
||||
return diag.Errorf("auth method nil after read")
|
||||
}
|
||||
|
||||
if err := setFromAuthMethodRead(d, *amrr.Item); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFromAuthMethodRead(d *schema.ResourceData, authMethod authmethods.AuthMethod) error {
|
||||
if err := d.Set(NameKey, authMethod.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(DescriptionKey, authMethod.Description); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(ScopeIdKey, authMethod.ScopeId); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(TypeKey, authMethod.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
d.Set(ScopeKey, flattenScopeInfo(authMethod.Scope))
|
||||
d.SetId(authMethod.Id)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/testing/controller"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
testAuthMethodName = "test_auth_method"
|
||||
)
|
||||
|
||||
var authMethodReadGlobal = fmt.Sprintf(`
|
||||
resource "boundary_auth_method" "auth_method" {
|
||||
name = "%s"
|
||||
description = "test"
|
||||
scope_id = "global"
|
||||
type = "password"
|
||||
depends_on = [boundary_role.org1_admin]
|
||||
}
|
||||
|
||||
data "boundary_auth_method" "auth_method" {
|
||||
depends_on = [ boundary_auth_method.auth_method ]
|
||||
name = "%s"
|
||||
}`, testAuthMethodName, testAuthMethodName)
|
||||
|
||||
var authMethodReadOrg = fmt.Sprintf(`
|
||||
resource "boundary_auth_method" "auth_method" {
|
||||
name = "%s"
|
||||
description = "test"
|
||||
scope_id = boundary_scope.org1.id
|
||||
type = "password"
|
||||
depends_on = [boundary_role.org1_admin]
|
||||
}
|
||||
|
||||
data "boundary_auth_method" "auth_method" {
|
||||
depends_on = [ boundary_auth_method.auth_method ]
|
||||
name = "%s"
|
||||
scope_id = boundary_scope.org1.id
|
||||
}`, testAuthMethodName, testAuthMethodName)
|
||||
|
||||
func TestAccAuthMethodReadGlobal(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testConfig(url, fooOrg, authMethodReadGlobal),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("data.boundary_auth_method.auth_method", IDKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_auth_method.auth_method", ScopeIdKey),
|
||||
resource.TestCheckResourceAttr("data.boundary_auth_method.auth_method", NameKey, testAuthMethodName),
|
||||
resource.TestCheckResourceAttr("data.boundary_auth_method.auth_method", TypeKey, "password"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_auth_method.auth_method", DescriptionKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_auth_method.auth_method", "scope.0.id"),
|
||||
resource.TestCheckResourceAttr("data.boundary_auth_method.auth_method", "scope.0.name", "global"),
|
||||
resource.TestCheckResourceAttr("data.boundary_auth_method.auth_method", "scope.0.type", "global"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAuthMethodReadOrg(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testConfig(url, fooOrg, authMethodReadOrg),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("data.boundary_auth_method.auth_method", IDKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_auth_method.auth_method", ScopeIdKey),
|
||||
resource.TestCheckResourceAttr("data.boundary_auth_method.auth_method", NameKey, testAuthMethodName),
|
||||
resource.TestCheckResourceAttr("data.boundary_auth_method.auth_method", TypeKey, "password"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_auth_method.auth_method", DescriptionKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_auth_method.auth_method", "scope.0.id"),
|
||||
resource.TestCheckResourceAttr("data.boundary_auth_method.auth_method", "scope.0.name", "org1"),
|
||||
resource.TestCheckResourceAttr("data.boundary_auth_method.auth_method", "scope.0.type", "org"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// 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/groups"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||
)
|
||||
|
||||
func dataSourceGroup() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Description: "The boundary_group data source allows you to find a Boundary group.",
|
||||
ReadContext: dataSourceGroupRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
NameKey: {
|
||||
Description: "The name of the group to retrieve.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
ScopeIdKey: {
|
||||
Description: "The scope ID in which the resource is created. Defaults `global` if unset.",
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "global",
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
IDKey: {
|
||||
Description: "The ID of the retrieved group.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
DescriptionKey: {
|
||||
Description: "The description of the retrieved group.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
ScopeKey: {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
IDKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
NameKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
TypeKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
DescriptionKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
ParentScopeIdKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GroupMemberIdsKey: {
|
||||
Description: "Resource IDs for group members, these are most likely boundary users.",
|
||||
Type: schema.TypeSet,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
|
||||
md := meta.(*metaData)
|
||||
|
||||
name := d.Get(NameKey).(string)
|
||||
scopeId := d.Get(ScopeIdKey).(string)
|
||||
|
||||
gcl := groups.NewClient(md.client)
|
||||
groupsList, err := gcl.List(ctx, scopeId,
|
||||
groups.WithFilter(FilterWithItemNameMatches(name)),
|
||||
)
|
||||
if err != nil {
|
||||
return diag.Errorf("error calling list group: %v", err)
|
||||
}
|
||||
groups := groupsList.GetItems()
|
||||
if groups == nil {
|
||||
return diag.Errorf("no groups found")
|
||||
}
|
||||
if len(groups) == 0 {
|
||||
return diag.Errorf("no matching group found")
|
||||
}
|
||||
if len(groups) > 1 {
|
||||
return diag.Errorf("error found more than 1 group")
|
||||
}
|
||||
|
||||
grr, err := gcl.Read(ctx, groups[0].Id)
|
||||
if err != nil {
|
||||
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return diag.Errorf("error calling read group: %v", err)
|
||||
}
|
||||
if grr == nil {
|
||||
return diag.Errorf("group nil after read")
|
||||
}
|
||||
|
||||
if err := setFromGroupRead(d, *grr.Item); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFromGroupRead(d *schema.ResourceData, group groups.Group) error {
|
||||
if err := d.Set(NameKey, group.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(DescriptionKey, group.Description); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(ScopeIdKey, group.ScopeId); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(GroupMemberIdsKey, group.MemberIds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Set(ScopeKey, flattenScopeInfo(group.Scope))
|
||||
d.SetId(group.Id)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/testing/controller"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
testGroupName = "test_group"
|
||||
)
|
||||
|
||||
var groupReadGlobal = fmt.Sprintf(`
|
||||
|
||||
resource "boundary_user" "user" {
|
||||
description = "user"
|
||||
scope_id = "global"
|
||||
depends_on = [boundary_role.org1_admin]
|
||||
}
|
||||
|
||||
resource "boundary_group" "group" {
|
||||
name = "%s"
|
||||
description = "test"
|
||||
scope_id = "global"
|
||||
member_ids = [boundary_user.user.id]
|
||||
depends_on = [boundary_user.user]
|
||||
}
|
||||
|
||||
data "boundary_group" "group" {
|
||||
depends_on = [ boundary_group.group ]
|
||||
name = "%s"
|
||||
}`, testGroupName, testGroupName)
|
||||
|
||||
var groupReadOrg = fmt.Sprintf(`
|
||||
resource "boundary_user" "user" {
|
||||
description = "user"
|
||||
scope_id = boundary_scope.org1.id
|
||||
depends_on = [boundary_role.org1_admin]
|
||||
}
|
||||
|
||||
resource "boundary_group" "group" {
|
||||
name = "%s"
|
||||
description = "test"
|
||||
scope_id = boundary_scope.org1.id
|
||||
member_ids = [boundary_user.user.id]
|
||||
depends_on = [boundary_user.user]
|
||||
}
|
||||
|
||||
data "boundary_group" "group" {
|
||||
depends_on = [ boundary_group.group ]
|
||||
name = "%s"
|
||||
scope_id = boundary_scope.org1.id
|
||||
}`, testGroupName, testGroupName)
|
||||
|
||||
func TestAccGroupReadGlobal(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testConfig(url, fooOrg, groupReadGlobal),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckGroupResourceExists(provider, "boundary_group.group"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", IDKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", ScopeIdKey),
|
||||
resource.TestCheckResourceAttr("data.boundary_group.group", NameKey, testGroupName),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", DescriptionKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", fmt.Sprintf("%s.#", GroupMemberIdsKey)),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", "scope.0.id"),
|
||||
resource.TestCheckResourceAttr("data.boundary_group.group", "scope.0.name", "global"),
|
||||
resource.TestCheckResourceAttr("data.boundary_group.group", "scope.0.type", "global"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccGroupReadOrg(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testConfig(url, fooOrg, groupReadOrg),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckGroupResourceExists(provider, "boundary_group.group"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", IDKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", ScopeIdKey),
|
||||
resource.TestCheckResourceAttr("data.boundary_group.group", NameKey, testGroupName),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", DescriptionKey),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", fmt.Sprintf("%s.#", GroupMemberIdsKey)),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_group.group", "scope.0.id"),
|
||||
resource.TestCheckResourceAttr("data.boundary_group.group", "scope.0.name", "org1"),
|
||||
resource.TestCheckResourceAttr("data.boundary_group.group", "scope.0.type", "org"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// 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/scopes"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceScope() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Description: "The scope data source allows you to discover an existing Boundary scope by name.",
|
||||
ReadContext: dataSourceScopeRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
IDKey: {
|
||||
Description: "The ID of the retrieved scope.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
NameKey: {
|
||||
Description: "The name of the scope to retrieve.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
DescriptionKey: {
|
||||
Description: "The description of the retrieved scope.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
ScopeIdKey: {
|
||||
Description: "The parent scope ID that will be queried for the scope.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceScopeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
|
||||
md := meta.(*metaData)
|
||||
opts := []scopes.Option{}
|
||||
|
||||
var name string
|
||||
if v, ok := d.GetOk(NameKey); ok {
|
||||
name = v.(string)
|
||||
} else {
|
||||
return diag.Errorf("no name provided")
|
||||
}
|
||||
|
||||
var scopeId string
|
||||
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
|
||||
scopeId = scopeIdVal.(string)
|
||||
} else {
|
||||
return diag.Errorf("no parent scope ID provided")
|
||||
}
|
||||
|
||||
scp := scopes.NewClient(md.client)
|
||||
|
||||
scpls, err := scp.List(ctx, scopeId, opts...)
|
||||
if err != nil {
|
||||
return diag.Errorf("error calling list scope: %v", err)
|
||||
}
|
||||
if scpls == nil {
|
||||
return diag.Errorf("no scopes found")
|
||||
}
|
||||
|
||||
var scopeIdRead string
|
||||
for _, scopeItem := range scpls.GetItems() {
|
||||
if scopeItem.Name == name {
|
||||
scopeIdRead = scopeItem.Id
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if scopeIdRead == "" {
|
||||
return diag.Errorf("scope name %v not found in scope list", err)
|
||||
}
|
||||
|
||||
srr, err := scp.Read(ctx, scopeIdRead)
|
||||
if err != nil {
|
||||
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return diag.Errorf("error calling read scope: %v", err)
|
||||
}
|
||||
if srr == nil {
|
||||
return diag.Errorf("scope nil after read")
|
||||
}
|
||||
|
||||
if err := setFromScopeReadResponseMap(d, srr.GetResponse().Map); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFromScopeReadResponseMap(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
|
||||
}
|
||||
|
||||
d.SetId(raw["id"].(string))
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/testing/controller"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
orgName = "test org scope"
|
||||
projectName = "test project scope"
|
||||
notProjectName = "test project scope with wrong name"
|
||||
scopeDesc = "created to test the scope datasource"
|
||||
)
|
||||
|
||||
var scopeCreateAndRead = fmt.Sprintf(`
|
||||
resource "boundary_scope" "global" {
|
||||
global_scope = true
|
||||
name = "global"
|
||||
description = "Global Scope"
|
||||
scope_id = "global"
|
||||
}
|
||||
|
||||
resource "boundary_scope" "org" {
|
||||
scope_id = boundary_scope.global.id
|
||||
name = "%s"
|
||||
description = "%s"
|
||||
}
|
||||
|
||||
resource "boundary_scope" "project" {
|
||||
depends_on = [boundary_role.org_admin]
|
||||
scope_id = boundary_scope.org.id
|
||||
name = "%s"
|
||||
description = "%s"
|
||||
}
|
||||
|
||||
resource "boundary_role" "org_admin" {
|
||||
scope_id = "global"
|
||||
grant_scope_ids = [boundary_scope.org.id]
|
||||
grant_strings = ["ids=*;type=*;actions=*"]
|
||||
principal_ids = ["u_auth"]
|
||||
}
|
||||
|
||||
data "boundary_scope" "org" {
|
||||
depends_on = [boundary_scope.org]
|
||||
scope_id = "global"
|
||||
name = "%s"
|
||||
}
|
||||
|
||||
data "boundary_scope" "project" {
|
||||
depends_on = [boundary_scope.project]
|
||||
scope_id = data.boundary_scope.org.id
|
||||
name = "%s"
|
||||
}`, orgName, scopeDesc, projectName, scopeDesc, orgName, projectName)
|
||||
|
||||
func TestAccScopeRead(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
CheckDestroy: testAccCheckScopeResourceDestroy(t, provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
// create and read
|
||||
Config: testConfig(url, scopeCreateAndRead),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckScopeResourceExists(provider, "boundary_scope.org"),
|
||||
resource.TestCheckResourceAttr("boundary_scope.org", "description", scopeDesc),
|
||||
resource.TestCheckResourceAttr("boundary_scope.org", "name", orgName),
|
||||
testAccCheckScopeResourceExists(provider, "boundary_scope.project"),
|
||||
resource.TestCheckResourceAttr("boundary_scope.project", "description", scopeDesc),
|
||||
resource.TestCheckResourceAttr("boundary_scope.project", "name", projectName),
|
||||
// Check attributes on the org datasource
|
||||
resource.TestCheckResourceAttrSet("data.boundary_scope.org", "scope_id"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_scope.org", "id"),
|
||||
resource.TestCheckResourceAttr("data.boundary_scope.org", "name", orgName),
|
||||
resource.TestCheckResourceAttr("data.boundary_scope.org", "description", scopeDesc),
|
||||
// Check attributes on the project datasource
|
||||
resource.TestCheckResourceAttrSet("data.boundary_scope.project", "scope_id"),
|
||||
resource.TestCheckResourceAttrSet("data.boundary_scope.project", "id"),
|
||||
resource.TestCheckResourceAttr("data.boundary_scope.project", "name", projectName),
|
||||
resource.TestCheckResourceAttr("data.boundary_scope.project", "description", scopeDesc),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/boundary/api/users"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||
)
|
||||
|
||||
func dataSourceUser() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Description: "The user data source allows you to find a Boundary user.",
|
||||
ReadContext: dataSourceUserRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
IDKey: {
|
||||
Description: "The ID of the user.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
NameKey: {
|
||||
Description: "The username to search for.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
DescriptionKey: {
|
||||
Description: "The user description.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
ScopeIdKey: {
|
||||
Description: "The scope ID in which the resource is created. Defaults `global` if unset.",
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "global",
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
userAccountIDsKey: {
|
||||
Description: "Account ID's to associate with this user resource.",
|
||||
Type: schema.TypeSet,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
ScopeKey: {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
IDKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
NameKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
TypeKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
DescriptionKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
ParentScopeIdKey: {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
authorizedActions: {
|
||||
Description: "A list of actions that the worker is entitled to perform.",
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
Computed: true,
|
||||
},
|
||||
LoginNameKey: {
|
||||
Description: "Login name for user.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
PrimaryAccountIdKey: {
|
||||
Description: "Primary account ID.",
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
|
||||
md := meta.(*metaData)
|
||||
usrs := users.NewClient(md.client)
|
||||
|
||||
opts := []users.Option{}
|
||||
|
||||
// Get user ID using name
|
||||
name := d.Get(NameKey).(string)
|
||||
scopeID := d.Get(ScopeIdKey).(string)
|
||||
|
||||
opts = append(opts, users.WithFilter(FilterWithItemNameMatches(name)))
|
||||
|
||||
usersList, err := usrs.List(ctx, scopeID, opts...)
|
||||
if err != nil {
|
||||
return diag.Errorf("error calling list user: %v", err)
|
||||
}
|
||||
users := usersList.GetItems()
|
||||
|
||||
// check length, 0 means no user, > 1 means too many
|
||||
if len(users) == 0 {
|
||||
return diag.Errorf("no matching user found: %v", err)
|
||||
}
|
||||
|
||||
if len(users) > 1 {
|
||||
return diag.Errorf("error found more than 1 user: %v", err)
|
||||
}
|
||||
|
||||
if err := setFromUserItem(d, *users[0]); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFromUserItem(d *schema.ResourceData, user users.User) error {
|
||||
if err := d.Set(NameKey, user.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(DescriptionKey, user.Description); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(ScopeIdKey, user.ScopeId); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(userAccountIDsKey, user.AccountIds); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(authorizedActions, user.AuthorizedActions); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(LoginNameKey, user.LoginName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(PrimaryAccountIdKey, user.PrimaryAccountId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Set(ScopeKey, flattenScopeInfo(user.Scope))
|
||||
|
||||
d.SetId(user.Id)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/YakDriver/regexache"
|
||||
"github.com/hashicorp/boundary/testing/controller"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
orgUserDataSource = fmt.Sprintf(`
|
||||
resource "boundary_user" "org1" {
|
||||
name = "test"
|
||||
description = "%s"
|
||||
scope_id = boundary_scope.org1.id
|
||||
depends_on = [boundary_role.org1_admin]
|
||||
}
|
||||
data "boundary_user" "org1" {
|
||||
name = "test"
|
||||
scope_id = boundary_scope.org1.id
|
||||
depends_on = [boundary_user.org1]
|
||||
}`, fooDescription)
|
||||
|
||||
globalUserDataSource = `
|
||||
data "boundary_user" "admin" {
|
||||
name = "admin"
|
||||
depends_on = [boundary_role.org1_admin]
|
||||
}`
|
||||
)
|
||||
|
||||
// NOTE: this test also tests out the direct token auth mechanism.
|
||||
|
||||
func TestAccUserDataSource_basicOrgUser(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
token := tc.Token().Token
|
||||
|
||||
resourceName := "boundary_user.org1"
|
||||
dataSourceName := "data.boundary_user.org1"
|
||||
|
||||
var provider *schema.Provider
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
CheckDestroy: testAccCheckUserResourceDestroy(t, provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
// test create
|
||||
Config: testConfigWithToken(url, token, fooOrg, orgUserDataSource),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckUserResourceExists(provider, resourceName),
|
||||
resource.TestCheckResourceAttr(dataSourceName, DescriptionKey, fooDescription),
|
||||
resource.TestCheckResourceAttr(dataSourceName, NameKey, "test"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccUserDataSource_globalAdminUser(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
url := tc.ApiAddrs()[0]
|
||||
token := tc.Token().Token
|
||||
|
||||
dataSourceName := "data.boundary_user.admin"
|
||||
|
||||
var provider *schema.Provider
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProviderFactories: providerFactories(&provider),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testConfigWithToken(url, token, fooOrg, globalUserDataSource),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(dataSourceName, NameKey, "admin"),
|
||||
resource.TestCheckResourceAttr(dataSourceName, DescriptionKey, "Initial admin user within the \"global\" scope"),
|
||||
resource.TestCheckResourceAttr(dataSourceName, LoginNameKey, "testuser"),
|
||||
resource.TestMatchResourceAttr(dataSourceName, IDKey, regexache.MustCompile(`^u_.+`)),
|
||||
resource.TestMatchResourceAttr(dataSourceName, PrimaryAccountIdKey, regexache.MustCompile(`^acctpw_.+`)),
|
||||
resource.TestCheckResourceAttr(dataSourceName, "authorized_actions.#", "8"),
|
||||
resource.TestCheckResourceAttr(dataSourceName, "scope.0.name", "global"),
|
||||
resource.TestCheckResourceAttr(dataSourceName, "scope.0.id", "global"),
|
||||
resource.TestCheckResourceAttr(dataSourceName, "scope.0.type", "global"),
|
||||
resource.TestCheckResourceAttr(dataSourceName, "scope.0.description", "Global Scope"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import "fmt"
|
||||
|
||||
func FilterWithItemNameMatches(name string) string {
|
||||
return fmt.Sprintf("\"/item/name\" matches \"%s\"", name)
|
||||
}
|
||||
@@ -98,6 +98,7 @@ func New() *schema.Provider {
|
||||
"boundary_account_password": resourceAccountPassword(),
|
||||
"boundary_account_oidc": resourceAccountOidc(),
|
||||
"boundary_account_ldap": resourceAccountLdap(),
|
||||
"boundary_alias_target": resourceAliasTarget(),
|
||||
"boundary_auth_method": resourceAuthMethod(),
|
||||
"boundary_auth_method_password": resourceAuthMethodPassword(),
|
||||
"boundary_auth_method_oidc": resourceAuthMethodOidc(),
|
||||
@@ -120,12 +121,22 @@ func New() *schema.Provider {
|
||||
"boundary_host_set": resourceHostSet(),
|
||||
"boundary_host_set_static": resourceHostSetStatic(),
|
||||
"boundary_host_set_plugin": resourceHostSetPlugin(),
|
||||
"boundary_policy_storage": resourcePolicyStorage(),
|
||||
"boundary_scope_policy_attachment": resourceScopePolicyAttachment(),
|
||||
"boundary_role": resourceRole(),
|
||||
"boundary_scope": resourceScope(),
|
||||
"boundary_storage_bucket": resourceStorageBucket(),
|
||||
"boundary_target": resourceTarget(),
|
||||
"boundary_user": resourceUser(),
|
||||
"boundary_worker": resourceWorker(),
|
||||
},
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"boundary_account": dataSourceAccount(),
|
||||
"boundary_auth_method": dataSourceAuthMethod(),
|
||||
"boundary_group": dataSourceGroup(),
|
||||
"boundary_scope": dataSourceScope(),
|
||||
"boundary_user": dataSourceUser(),
|
||||
},
|
||||
}
|
||||
|
||||
p.ConfigureContextFunc = providerConfigure(p)
|
||||
|
||||
@@ -106,6 +106,19 @@ provider "boundary" {
|
||||
|
||||
func testConfigWithDefaultAuthMethod(url string, res ...string) string {
|
||||
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" {
|
||||
addr = "%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) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
|
||||
@@ -92,8 +92,8 @@ resource "boundary_user" "alice" {
|
||||
|
||||
resource "boundary_role" "ldap_principal" {
|
||||
scope_id = boundary_scope.global.id
|
||||
grant_scope_id = boundary_scope.global.id
|
||||
grant_strings = ["id=*;type=*;actions=*"]
|
||||
grant_scope_ids = [boundary_scope.global.id]
|
||||
grant_strings = ["ids=*;type=*;actions=*"]
|
||||
principal_ids = [boundary_user.alice.id]
|
||||
}`
|
||||
)
|
||||
|
||||
@@ -19,8 +19,7 @@ const (
|
||||
fooAccountOidcDescUpdate = "test account oidc update"
|
||||
)
|
||||
|
||||
var (
|
||||
fooAccountOidc = `
|
||||
var fooAccountOidc = `
|
||||
resource "boundary_auth_method_oidc" "foo" {
|
||||
name = "test"
|
||||
description = "test account oidc auth method"
|
||||
@@ -50,7 +49,6 @@ resource "boundary_account_oidc" "foo" {
|
||||
issuer = "%s"
|
||||
auth_method_id = boundary_auth_method_oidc.foo.id
|
||||
}`
|
||||
)
|
||||
|
||||
func TestAccOidcAccount(t *testing.T) {
|
||||
tp := oidc.StartTestProvider(t)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
@@ -38,6 +39,8 @@ const (
|
||||
authMethodLdapAccountAttrMapsField = "account_attribute_maps"
|
||||
authMethodLdapPrimaryAuthMethodForScopeField = "is_primary_for_scope"
|
||||
authMethodLdapStateField = "state"
|
||||
authMethodLdapMaxPageSizeField = "maximum_page_size"
|
||||
authMethodLdapDerefAliasesField = "dereference_aliases"
|
||||
|
||||
// computed-only parameters
|
||||
authMethodLdapClientCertKeyHmacKey = "client_certificate_key_hmac"
|
||||
@@ -195,6 +198,17 @@ func resourceAuthMethodLdap() *schema.Resource {
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
authMethodLdapMaxPageSizeField: {
|
||||
Description: "MaximumPageSize specifies a maximum search result size to use when retrieving the authenticated user's groups (optional).",
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
},
|
||||
authMethodLdapDerefAliasesField: {
|
||||
Description: "Control how aliases are dereferenced when performing the search. Can be one of: NeverDerefAliases, DerefInSearching, DerefFindingBaseObj, and DerefAlways (optional).",
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
// LDAP specific immutable and computed parameters
|
||||
authMethodLdapClientCertKeyHmacKey: {
|
||||
@@ -401,6 +415,18 @@ func setFromLdapAuthMethodResponseMap(d *schema.ResourceData, raw map[string]int
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := attrs[authMethodLdapMaxPageSizeField]; ok {
|
||||
if err := d.Set(authMethodLdapMaxPageSizeField, v.(json.Number)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := attrs[authMethodLdapDerefAliasesField]; ok {
|
||||
if err := d.Set(authMethodLdapDerefAliasesField, v.(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d.SetId(raw["id"].(string))
|
||||
@@ -506,6 +532,14 @@ func resourceAuthMethodLdapCreate(ctx context.Context, d *schema.ResourceData, m
|
||||
opts = append(opts, authmethods.WithLdapAuthMethodUseTokenGroups(useTokenGrps.(bool)))
|
||||
}
|
||||
|
||||
if maxPageSize, ok := d.GetOk(authMethodLdapMaxPageSizeField); ok {
|
||||
opts = append(opts, authmethods.WithLdapAuthMethodMaximumPageSize(uint32(maxPageSize.(int))))
|
||||
}
|
||||
|
||||
if derefAliases, ok := d.GetOk(authMethodLdapDerefAliasesField); ok {
|
||||
opts = append(opts, authmethods.WithLdapAuthMethodDereferenceAliases(derefAliases.(string)))
|
||||
}
|
||||
|
||||
if rawAccountAttrMaps, ok := d.GetOk(authMethodLdapAccountAttrMapsField); ok {
|
||||
accountAttrMaps := []string{}
|
||||
for _, u := range rawAccountAttrMaps.([]interface{}) {
|
||||
@@ -785,6 +819,20 @@ func resourceAuthMethodLdapUpdate(ctx context.Context, d *schema.ResourceData, m
|
||||
}
|
||||
}
|
||||
|
||||
if d.HasChange(authMethodLdapMaxPageSizeField) {
|
||||
opts = append(opts, authmethods.DefaultLdapAuthMethodMaximumPageSize())
|
||||
if maxPageSize, ok := d.GetOk(authMethodLdapMaxPageSizeField); ok {
|
||||
opts = append(opts, authmethods.WithLdapAuthMethodMaximumPageSize(uint32(maxPageSize.(int))))
|
||||
}
|
||||
}
|
||||
|
||||
if d.HasChange(authMethodLdapDerefAliasesField) {
|
||||
opts = append(opts, authmethods.DefaultLdapAuthMethodDereferenceAliases())
|
||||
if derefAliases, ok := d.GetOk(authMethodLdapDerefAliasesField); ok {
|
||||
opts = append(opts, authmethods.WithLdapAuthMethodDereferenceAliases(derefAliases.(string)))
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts) > 0 {
|
||||
opts = append(opts, authmethods.WithAutomaticVersioning(true))
|
||||
amur, err := amClient.Update(ctx, d.Id(), 0, opts...)
|
||||
|
||||
@@ -65,6 +65,8 @@ resource "boundary_auth_method_ldap" "test-ldap" {
|
||||
group_filter = "group-filter"
|
||||
bind_dn = "bind-dn"
|
||||
bind_password = "bind-password"
|
||||
maximum_page_size = 10
|
||||
dereference_aliases = "DerefAlways"
|
||||
use_token_groups = true
|
||||
certificates = [
|
||||
<<EOT
|
||||
@@ -112,6 +114,8 @@ resource "boundary_auth_method_ldap" "test-ldap" {
|
||||
bind_password = "bind-password-updated"
|
||||
use_token_groups = false
|
||||
state = "inactive"
|
||||
maximum_page_size = 100
|
||||
dereference_aliases = "NeverDerefAliases"
|
||||
certificates = [
|
||||
<<EOT
|
||||
%s
|
||||
@@ -160,6 +164,8 @@ func TestAccAuthMethodLdap(t *testing.T) {
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapBindDnField, "bind-dn"),
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapBindPasswordField, "bind-password"),
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapUseTokenGrpsField, "true"),
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapMaxPageSizeField, "10"),
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapDerefAliasesField, "DerefAlways"),
|
||||
testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_ldap.test-ldap", authMethodLdapCertificatesField, []string{tdCert}),
|
||||
),
|
||||
},
|
||||
@@ -187,6 +193,8 @@ func TestAccAuthMethodLdap(t *testing.T) {
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapBindDnField, "bind-dn-updated"),
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapBindPasswordField, "bind-password-updated"),
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapUseTokenGrpsField, "false"),
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapMaxPageSizeField, "100"),
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_ldap.test-ldap", authMethodLdapDerefAliasesField, "NeverDerefAliases"),
|
||||
testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_ldap.test-ldap", authMethodLdapCertificatesField, []string{testAuthMethodLdapCert}),
|
||||
testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method_ldap.test-ldap"),
|
||||
testAccIsPrimaryForScope(provider, "boundary_auth_method_ldap.test-ldap", true),
|
||||
|
||||
@@ -29,6 +29,7 @@ const (
|
||||
authmethodOidcIsPrimaryAuthMethodForScope = "is_primary_for_scope"
|
||||
authmethodOidcAccountClaimMapsKey = "account_claim_maps"
|
||||
authmethodOidcClaimsScopesKey = "claims_scopes"
|
||||
authmethodOidcPromptsKey = "prompts"
|
||||
|
||||
// computed-only parameters
|
||||
authmethodOidcCallbackUrlKey = "callback_url"
|
||||
@@ -147,6 +148,15 @@ func resourceAuthMethodOidc() *schema.Resource {
|
||||
},
|
||||
Optional: true,
|
||||
},
|
||||
authmethodOidcPromptsKey: {
|
||||
Description: "The prompts passed to the identity provider to determine whether to prompt the end-user for reauthentication, account selection or consent. " +
|
||||
"Please note the values passed are case-sensitive. The valid values are: `none`, `login`, `consent` and `select_account`. ",
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
// OIDC specific immutable and computed parameters
|
||||
authmethodOidcClientSecretHmacKey: {
|
||||
@@ -239,6 +249,10 @@ func setFromOidcAuthMethodResponseMap(d *schema.ResourceData, raw map[string]int
|
||||
if p, ok := attrs[authmethodOidcClaimsScopesKey]; ok {
|
||||
d.Set(authmethodOidcClaimsScopesKey, p.([]interface{}))
|
||||
}
|
||||
|
||||
if val, ok := attrs[authmethodOidcPromptsKey]; ok {
|
||||
d.Set(authmethodOidcPromptsKey, val.([]interface{}))
|
||||
}
|
||||
}
|
||||
|
||||
d.SetId(raw["id"].(string))
|
||||
@@ -321,6 +335,14 @@ func resourceAuthMethodOidcCreate(ctx context.Context, d *schema.ResourceData, m
|
||||
opts = append(opts, authmethods.WithOidcAuthMethodClaimsScopes(cList))
|
||||
}
|
||||
|
||||
if prompts, ok := d.GetOk(authmethodOidcPromptsKey); ok {
|
||||
promptList := []string{}
|
||||
for _, p := range prompts.([]interface{}) {
|
||||
promptList = append(promptList, p.(string))
|
||||
}
|
||||
opts = append(opts, authmethods.WithOidcAuthMethodPrompts(promptList))
|
||||
}
|
||||
|
||||
nameVal, ok := d.GetOk(NameKey)
|
||||
if ok {
|
||||
nameStr := nameVal.(string)
|
||||
@@ -536,6 +558,15 @@ func resourceAuthMethodOidcUpdate(ctx context.Context, d *schema.ResourceData, m
|
||||
opts = append(opts, authmethods.WithOidcAuthMethodClaimsScopes(claimsScopes))
|
||||
}
|
||||
}
|
||||
if d.HasChange(authmethodOidcPromptsKey) {
|
||||
if prompts, ok := d.GetOk(authmethodOidcPromptsKey); ok {
|
||||
var promptsList []string
|
||||
for _, prompt := range prompts.([]interface{}) {
|
||||
promptsList = append(promptsList, prompt.(string))
|
||||
}
|
||||
opts = append(opts, authmethods.WithOidcAuthMethodPrompts(promptsList))
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts) > 0 {
|
||||
opts = append(opts, authmethods.WithAutomaticVersioning(true))
|
||||
|
||||
@@ -67,6 +67,7 @@ EOT
|
||||
signing_algorithms = ["ES256"]
|
||||
account_claim_maps = ["oid=sub"]
|
||||
claims_scopes = ["profile"]
|
||||
prompts = ["select_account"]
|
||||
}`
|
||||
|
||||
fooAuthMethodOidcUpdate = `
|
||||
@@ -90,6 +91,7 @@ EOT
|
||||
signing_algorithms = ["ES256"]
|
||||
account_claim_maps = ["oid=sub"]
|
||||
claims_scopes = ["profile"]
|
||||
prompts = ["consent", "select_account"]
|
||||
|
||||
// we need to disable this validation, since the updated issuer isn't discoverable
|
||||
disable_discovered_config_validation = true
|
||||
@@ -125,6 +127,7 @@ func TestAccAuthMethodOidc(t *testing.T) {
|
||||
testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcSigningAlgorithmsKey, []string{"ES256"}),
|
||||
testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcAccountClaimMapsKey, []string{"oid=sub"}),
|
||||
testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcClaimsScopesKey, []string{"profile"}),
|
||||
testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcPromptsKey, []string{"select_account"}),
|
||||
testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method_oidc.foo"),
|
||||
testAccIsPrimaryForScope(provider, "boundary_auth_method_oidc.foo", false),
|
||||
),
|
||||
@@ -141,6 +144,7 @@ func TestAccAuthMethodOidc(t *testing.T) {
|
||||
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", authmethodOidcMaxAgeKey, "-1"),
|
||||
testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcIdpCaCertsKey, []string{fooAuthMethodOidcCaCerts}),
|
||||
testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcAllowedAudiencesKey, []string{"foo_aud_update"}),
|
||||
testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcPromptsKey, []string{"consent", "select_account"}),
|
||||
testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method_oidc.foo"),
|
||||
testAccIsPrimaryForScope(provider, "boundary_auth_method_oidc.foo", true),
|
||||
testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method_oidc.foo"),
|
||||
|
||||
@@ -14,15 +14,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
credentialLibraryVaultSshCertificateType = "vault-ssh-certificate"
|
||||
credentialLibraryVaultSshCertificatePathKey = "path"
|
||||
credentialLibraryVaultSshCertificateUsernameKey = "username"
|
||||
credentialLibraryVaultSshCertificateKeyTypeKey = "key_type"
|
||||
credentialLibraryVaultSshCertificateKeyBitsKey = "key_bits"
|
||||
credentialLibraryVaultSshCertificateTtlKey = "ttl"
|
||||
credentialLibraryVaultSshCertificateKeyIdKey = "key_id"
|
||||
credentialLibraryVaultSshCertificateCriticalOptionsKey = "critical_options"
|
||||
credentialLibraryVaultSshCertificateExtensionsKey = "extensions"
|
||||
credentialLibraryVaultSshCertificateType = "vault-ssh-certificate"
|
||||
credentialLibraryVaultSshCertificatePathKey = "path"
|
||||
credentialLibraryVaultSshCertificateUsernameKey = "username"
|
||||
credentialLibraryVaultSshCertificateKeyTypeKey = "key_type"
|
||||
credentialLibraryVaultSshCertificateKeyBitsKey = "key_bits"
|
||||
credentialLibraryVaultSshCertificateTtlKey = "ttl"
|
||||
credentialLibraryVaultSshCertificateKeyIdKey = "key_id"
|
||||
credentialLibraryVaultSshCertificateCriticalOptionsKey = "critical_options"
|
||||
credentialLibraryVaultSshCertificateExtensionsKey = "extensions"
|
||||
credentialLibraryVaultSshCertificateAdditionalValidPrincipalsKey = "additional_valid_principals"
|
||||
)
|
||||
|
||||
var libraryVaultSshCertificateAttrs = []string{
|
||||
@@ -34,6 +35,7 @@ var libraryVaultSshCertificateAttrs = []string{
|
||||
credentialLibraryVaultSshCertificateKeyIdKey,
|
||||
credentialLibraryVaultSshCertificateCriticalOptionsKey,
|
||||
credentialLibraryVaultSshCertificateExtensionsKey,
|
||||
credentialLibraryVaultSshCertificateAdditionalValidPrincipalsKey,
|
||||
}
|
||||
|
||||
func resourceCredentialLibraryVaultSshCertificate() *schema.Resource {
|
||||
@@ -110,6 +112,14 @@ func resourceCredentialLibraryVaultSshCertificate() *schema.Resource {
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
},
|
||||
credentialLibraryVaultSshCertificateAdditionalValidPrincipalsKey: {
|
||||
Description: "Principals to be signed as \"valid_principles\" in addition to username.",
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -185,6 +195,13 @@ func resourceCredentialLibraryCreateVaultSshCertificate(ctx context.Context, d *
|
||||
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryExtensions(e))
|
||||
}
|
||||
}
|
||||
if v, ok := d.GetOk(credentialLibraryVaultSshCertificateAdditionalValidPrincipalsKey); ok {
|
||||
avp := []string{}
|
||||
for _, vv := range v.([]interface{}) {
|
||||
avp = append(avp, vv.(string))
|
||||
}
|
||||
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryAdditionalValidPrincipals(avp))
|
||||
}
|
||||
|
||||
var credentialStoreId string
|
||||
cid, ok := d.GetOk(credentialStoreIdKey)
|
||||
@@ -375,6 +392,17 @@ func resourceCredentialLibraryUpdateVaultSshCertificate(ctx context.Context, d *
|
||||
}
|
||||
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryExtensions(e))
|
||||
}
|
||||
if d.HasChange(credentialLibraryVaultSshCertificateAdditionalValidPrincipalsKey) {
|
||||
// set defaults first in case the value was omitted and we want to remove it
|
||||
opts = append(opts, credentiallibraries.DefaultVaultSSHCertificateCredentialLibraryAdditionalValidPrincipals())
|
||||
if v, ok := d.GetOk(credentialLibraryVaultSshCertificateAdditionalValidPrincipalsKey); ok {
|
||||
avp := []string{}
|
||||
for _, vv := range v.([]interface{}) {
|
||||
avp = append(avp, vv.(string))
|
||||
}
|
||||
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryAdditionalValidPrincipals(avp))
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts) > 0 {
|
||||
opts = append(opts, credentiallibraries.WithAutomaticVersioning(true))
|
||||
|
||||
@@ -69,6 +69,9 @@ resource "boundary_credential_library_vault_ssh_certificate" "ext_co_example" {
|
||||
critical_options = {
|
||||
force-command = "/bin/foo"
|
||||
}
|
||||
additional_valid_principals = [
|
||||
"principal-1"
|
||||
]
|
||||
}`, vaultSshCertCredLibName,
|
||||
vaultSshCertCredLibDesc,
|
||||
vaultSshCertCredLibPath,
|
||||
@@ -86,6 +89,10 @@ resource "boundary_credential_library_vault_ssh_certificate" "ext_co_example" {
|
||||
permit-pty = ""
|
||||
permit-X11-forwarding = ""
|
||||
}
|
||||
additional_valid_principals = [
|
||||
"principal-2",
|
||||
"principal-3"
|
||||
]
|
||||
}`, vaultSshCertCredLibName,
|
||||
vaultSshCertCredLibDesc,
|
||||
vaultSshCertCredLibPath,
|
||||
@@ -161,13 +168,14 @@ func TestAccCredentialLibraryVaultSshCertificate(t *testing.T) {
|
||||
},
|
||||
importStep(vaultSshCertCredResc),
|
||||
{
|
||||
// create with extensions and critical options
|
||||
// create with extensions, critical options, and additional valid principals
|
||||
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultSshCertCredLibResourceExtensionsCriticalOpts),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, NameKey, vaultSshCertCredLibName),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, DescriptionKey, vaultSshCertCredLibDesc),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultPathKey, vaultSshCertCredLibPath),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateUsernameKey, vaultSshCertCredUsername),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateAdditionalValidPrincipalsKey+".#", "1"),
|
||||
|
||||
testAccCheckCredentialLibraryResourceExists(provider, vaultSshCertCredExtCOResc),
|
||||
),
|
||||
@@ -183,6 +191,7 @@ func TestAccCredentialLibraryVaultSshCertificate(t *testing.T) {
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateUsernameKey, vaultSshCertCredUsername),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateCriticalOptionsKey+".%", "0"),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateExtensionsKey+".%", "2"),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateAdditionalValidPrincipalsKey+".#", "2"),
|
||||
|
||||
testAccCheckCredentialLibraryResourceExists(provider, vaultSshCertCredExtCOResc),
|
||||
),
|
||||
@@ -198,6 +207,7 @@ func TestAccCredentialLibraryVaultSshCertificate(t *testing.T) {
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateUsernameKey, vaultSshCertCredUsername),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateCriticalOptionsKey+".%", "2"),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateExtensionsKey+".%", "1"),
|
||||
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateAdditionalValidPrincipalsKey+".#", "0"),
|
||||
|
||||
testAccCheckCredentialLibraryResourceExists(provider, vaultSshCertCredExtCOResc),
|
||||
),
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/credentialstores"
|
||||
"github.com/hashicorp/boundary/testing/controller"
|
||||
"github.com/hashicorp/boundary/testing/vault"
|
||||
@@ -93,7 +93,7 @@ func TestAccCredentialStoreVault(t *testing.T) {
|
||||
vaultCredStoreName+vaultCredStoreUpdate,
|
||||
vaultCredStoreDesc+vaultCredStoreUpdate,
|
||||
vaultCredStoreNamespace+vaultCredStoreUpdate,
|
||||
"www.updated.com",
|
||||
"localhost",
|
||||
tokenUpdate,
|
||||
false)
|
||||
|
||||
@@ -131,7 +131,7 @@ func TestAccCredentialStoreVault(t *testing.T) {
|
||||
resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultAddressKey, vcUpdate.Addr),
|
||||
resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultNamespaceKey, vaultCredStoreNamespace+vaultCredStoreUpdate),
|
||||
resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultCaCertKey, string(vcUpdate.CaCert)),
|
||||
resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTlsServerNameKey, "www.updated.com"),
|
||||
resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTlsServerNameKey, "localhost"),
|
||||
resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTlsSkipVerifyKey, "false"),
|
||||
resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTokenKey, tokenUpdate),
|
||||
resource.TestCheckResourceAttr(vaultCredStoreResc, credentialStoreVaultTokenHmacKey, tHmacUpdate),
|
||||
|
||||
@@ -13,10 +13,6 @@ import (
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
groupMemberIdsKey = "member_ids"
|
||||
)
|
||||
|
||||
func resourceGroup() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Description: "The group resource allows you to configure a Boundary group.",
|
||||
@@ -51,7 +47,7 @@ func resourceGroup() *schema.Resource {
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
groupMemberIdsKey: {
|
||||
GroupMemberIdsKey: {
|
||||
Description: "Resource IDs for group members, these are most likely boundary users.",
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
@@ -71,7 +67,7 @@ func setFromGroupResponseMap(d *schema.ResourceData, raw map[string]interface{})
|
||||
if err := d.Set(ScopeIdKey, raw["scope_id"]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(groupMemberIdsKey, raw["member_ids"]); err != nil {
|
||||
if err := d.Set(GroupMemberIdsKey, raw["member_ids"]); err != nil {
|
||||
return err
|
||||
}
|
||||
d.SetId(raw["id"].(string))
|
||||
@@ -118,7 +114,7 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter
|
||||
}
|
||||
}()
|
||||
|
||||
if val, ok := d.GetOk(groupMemberIdsKey); ok {
|
||||
if val, ok := d.GetOk(GroupMemberIdsKey); ok {
|
||||
list := val.(*schema.Set).List()
|
||||
memberIds := make([]string, 0, len(list))
|
||||
for _, i := range list {
|
||||
@@ -209,9 +205,9 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
|
||||
|
||||
// The above call may not actually happen, so we use d.Id() and automatic
|
||||
// versioning here
|
||||
if d.HasChange(groupMemberIdsKey) {
|
||||
if d.HasChange(GroupMemberIdsKey) {
|
||||
var memberIds []string
|
||||
if membersVal, ok := d.GetOk(groupMemberIdsKey); ok {
|
||||
if membersVal, ok := d.GetOk(GroupMemberIdsKey); ok {
|
||||
members := membersVal.(*schema.Set).List()
|
||||
for _, member := range members {
|
||||
memberIds = append(memberIds, member.(string))
|
||||
@@ -222,7 +218,7 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
|
||||
if err != nil {
|
||||
return diag.Errorf("error updating members in group: %v", err)
|
||||
}
|
||||
if err := d.Set(groupMemberIdsKey, memberIds); err != nil {
|
||||
if err := d.Set(GroupMemberIdsKey, memberIds); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,11 @@ func resourceHostCatalogPlugin() *schema.Resource {
|
||||
Optional: 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: {
|
||||
Description: "Internal only. HMAC of (serverSecretsHmac + config secrets). Used for proper secrets handling.",
|
||||
Type: schema.TypeString,
|
||||
@@ -294,6 +299,9 @@ func setFromHostCatalogPluginResponseMap(d *schema.ResourceData, raw map[string]
|
||||
if err := d.Set(ScopeIdKey, raw[ScopeIdKey]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(WorkerFilterKey, raw[WorkerFilterKey]); err != nil {
|
||||
return err
|
||||
}
|
||||
// Plugin stuff
|
||||
{
|
||||
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))
|
||||
}
|
||||
|
||||
workerFilterVal, ok := d.GetOk(WorkerFilterKey)
|
||||
if ok {
|
||||
workerFilterStr := workerFilterVal.(string)
|
||||
opts = append(opts, hostcatalogs.WithWorkerFilter(workerFilterStr))
|
||||
}
|
||||
|
||||
attrsVal, ok := d.GetOk(AttributesJsonKey)
|
||||
if ok {
|
||||
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) {
|
||||
attrsVal, ok := d.GetOk(AttributesJsonKey)
|
||||
if ok {
|
||||
|
||||
@@ -22,6 +22,9 @@ const (
|
||||
testPluginHostCatalogDescription = "bar"
|
||||
testPluginHostCatalogDescriptionUpdate = "bar 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
|
||||
@@ -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 {
|
||||
return func(s *terraform.State) error {
|
||||
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
|
||||
}
|
||||
@@ -5,7 +5,12 @@ package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/roles"
|
||||
@@ -14,9 +19,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
roleGrantScopeIdKey = "grant_scope_id"
|
||||
rolePrincipalIdsKey = "principal_ids"
|
||||
roleGrantStringsKey = "grant_strings"
|
||||
roleGrantScopeIdsKey = "grant_scope_ids"
|
||||
rolePrincipalIdsKey = "principal_ids"
|
||||
roleGrantStringsKey = "grant_strings"
|
||||
)
|
||||
|
||||
func resourceRole() *schema.Resource {
|
||||
@@ -60,15 +65,17 @@ func resourceRole() *schema.Resource {
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
roleGrantStringsKey: {
|
||||
Description: " A list of stringified grants for the role.",
|
||||
Description: "A list of stringified grants for the role.",
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
roleGrantScopeIdKey: {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
roleGrantScopeIdsKey: {
|
||||
Description: `A list of scopes for which the grants in this role should apply, which can include the special values "this", "children", or "descendants"`,
|
||||
Type: schema.TypeSet,
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
d.SetId(raw["id"].(string))
|
||||
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)
|
||||
|
||||
var scopeId string
|
||||
@@ -121,10 +129,13 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
|
||||
opts = append(opts, roles.WithDescription(descStr))
|
||||
}
|
||||
|
||||
grantScopeIdVal, ok := d.GetOk(roleGrantScopeIdKey)
|
||||
if ok {
|
||||
grantScopeIdStr := grantScopeIdVal.(string)
|
||||
opts = append(opts, roles.WithGrantScopeId(grantScopeIdStr))
|
||||
var grantScopeIds []string
|
||||
if grantScopeIdsVal, ok := d.GetOk(roleGrantScopeIdsKey); ok {
|
||||
list := grantScopeIdsVal.(*schema.Set).List()
|
||||
grantScopeIds = make([]string, 0, len(list))
|
||||
for _, i := range list {
|
||||
grantScopeIds = append(grantScopeIds, i.(string))
|
||||
}
|
||||
}
|
||||
|
||||
var principalIds []string
|
||||
@@ -141,6 +152,16 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
|
||||
list := grantStringsVal.(*schema.Set).List()
|
||||
grantStrings = make([]string, 0, len(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))
|
||||
}
|
||||
}
|
||||
@@ -157,7 +178,7 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
|
||||
apiResponse := tcr.GetResponse().Map
|
||||
defer func() {
|
||||
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))
|
||||
switch {
|
||||
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:
|
||||
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:
|
||||
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))
|
||||
switch {
|
||||
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:
|
||||
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:
|
||||
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 {
|
||||
@@ -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 {
|
||||
opts = append(opts, roles.WithAutomaticVersioning(true))
|
||||
_, 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)
|
||||
}
|
||||
}
|
||||
if d.HasChange(roleGrantScopeIdKey) {
|
||||
if err := d.Set(roleGrantScopeIdKey, grantScopeId); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
var diags diag.Diagnostics
|
||||
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 {
|
||||
grants := grantStringsVal.(*schema.Set).List()
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -325,3 +367,35 @@ func resourceRoleDelete(ctx context.Context, d *schema.ResourceData, meta interf
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -146,9 +146,9 @@ resource "boundary_role" "with_groups" {
|
||||
depends_on = [boundary_role.proj1_admin]
|
||||
}`
|
||||
|
||||
readonlyGrant = "id=*;type=*;actions=read"
|
||||
readonlyGrantUpdate = "id=*;type=*;actions=read,create"
|
||||
invalidGrant = "id=*;type=*;actions=badaction"
|
||||
readonlyGrant = "ids=*;type=*;actions=read"
|
||||
readonlyGrantUpdate = "ids=*;type=*;actions=read,create"
|
||||
invalidGrant = "ids=*;type=*;actions=badaction"
|
||||
|
||||
projRoleWithGrants = fmt.Sprintf(`
|
||||
resource "boundary_role" "with_grants" {
|
||||
@@ -189,7 +189,7 @@ resource "boundary_role" "with_grants" {
|
||||
|
||||
func TestAccRoleToOrgToProject(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
t.Cleanup(tc.Shutdown)
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
@@ -243,7 +243,7 @@ func TestAccRoleToOrgToProject(t *testing.T) {
|
||||
|
||||
func TestAccRoleWithGrants(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
t.Cleanup(tc.Shutdown)
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
@@ -302,7 +302,7 @@ func TestAccRoleWithGrants(t *testing.T) {
|
||||
|
||||
func TestAccRoleWithPrincipals(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
t.Cleanup(tc.Shutdown)
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
@@ -365,7 +365,7 @@ func TestAccRoleWithPrincipals(t *testing.T) {
|
||||
|
||||
func TestAccRoleWithGroups(t *testing.T) {
|
||||
tc := controller.NewTestController(t, tcConfig...)
|
||||
defer tc.Shutdown()
|
||||
t.Cleanup(tc.Shutdown)
|
||||
url := tc.ApiAddrs()[0]
|
||||
|
||||
var provider *schema.Provider
|
||||
|
||||
Loaded 100 of 123 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user