Compare commits

..
23 Commits
Author SHA1 Message Date
tf-release-bot 38f1cf40e9 v1.0.2 2021-05-06 18:52:44 +00:00
Jeff Malnick 4f4318d005 v1.0.2 (#108)
v1.0.2 docs, go mod, and changelog
2021-05-05 10:02:53 -07:00
dependabot[bot] 5af7f8b662 build(deps): bump github.com/hashicorp/boundary/api from 0.0.9 to 0.0.10 (#107)
Bumps [github.com/hashicorp/boundary/api](https://github.com/hashicorp/boundary) from 0.0.9 to 0.0.10.
- [Release notes](https://github.com/hashicorp/boundary/releases)
- [Changelog](https://github.com/hashicorp/boundary/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/boundary/compare/api/v0.0.9...api/v0.0.10)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-05 09:10:32 -07:00
Jeff Malnick 3d42195a26 Add OIDC auth method and account resource (#105)
add OIDC auth method and account resources
2021-05-05 08:33:15 -07:00
Louis Ruch 9bc08d4c48 go mody tidy 2021-05-04 16:38:39 -07:00
Brian Flad 4de1075067 provider: Bump minimum Go version to 1.16 (#104) 2021-05-04 16:25:41 -04:00
Jim dc46913c63 bump deps and catch some unreported errors (#101) 2021-04-28 18:22:18 -04:00
Michele DeggesandJim Lambert bb85735e02 Update ssh key fingerprint (#98)
Co-authored-by: Jim Lambert <jlambert@hashicorp.com>
2021-04-28 15:26:19 -04:00
Jim f49ee1d5bc update TF plugin sdk -> related to GPG key rotation (#100) 2021-04-28 15:16:18 -04:00
Jeff Malnick b01f482fb2 Add contributing guide with pronoun expectations (#92)
* docs: add contributing guide with pronoun expectations
2021-03-30 16:57:11 -07:00
Louis Ruch b69e175687 Boundary 0.1.8 (#91)
* Bump boundary to 0.1.8
2021-03-10 11:58:06 -08:00
tf-release-bot 3f0d2b2acd v1.0.1 2021-02-02 17:41:22 +00:00
Louis Ruch a2c645304f Worker filter (#76)
* Add target worker filtering

* Update docs

* Update changelog
2021-02-02 08:58:10 -08:00
Louis Ruch c0aa9bf5b1 Fix changelog 2021-02-01 11:31:28 -08:00
Louis Ruch 7403e3648e Update boundary (#75)
* Update to latest boundary

* Update changelog
2021-02-01 11:04:08 -08:00
tf-release-bot b96e119b9e v1.0.0 2021-01-20 18:26:04 +00:00
Louis Ruch d94cd5a5ed Dont allow password change via tf apply (#70)
* Don't allow password change via tf apply

* Bump Terraform Boundary provider version to v1.0.0
2021-01-20 08:15:57 -08:00
dependabot[bot] 187d81d91d build(deps): bump tomhjp/gh-action-jira-comment from v0.1.0 to v0.2.0
Bumps [tomhjp/gh-action-jira-comment](https://github.com/tomhjp/gh-action-jira-comment) from v0.1.0 to v0.2.0.
- [Release notes](https://github.com/tomhjp/gh-action-jira-comment/releases)
- [Commits](https://github.com/tomhjp/gh-action-jira-comment/compare/v0.1.0...6eb6b9ead70221916b6badd118c24535ed220bd9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-14 23:41:55 +00:00
dependabot[bot] 15a46cc8c2 build(deps): bump atlassian/gajira-transition from v2.0.1 to v2.0.2
Bumps [atlassian/gajira-transition](https://github.com/atlassian/gajira-transition) from v2.0.1 to v2.0.2.
- [Release notes](https://github.com/atlassian/gajira-transition/releases)
- [Commits](https://github.com/atlassian/gajira-transition/compare/v2.0.1...0fb280dcc874710a4ac5eced8789e1def040e35d)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-14 23:41:43 +00:00
dependabot[bot] 2a880c05c5 build(deps): bump tomhjp/gh-action-jira-create from v0.1.3 to v0.2.0
Bumps [tomhjp/gh-action-jira-create](https://github.com/tomhjp/gh-action-jira-create) from v0.1.3 to v0.2.0.
- [Release notes](https://github.com/tomhjp/gh-action-jira-create/releases)
- [Commits](https://github.com/tomhjp/gh-action-jira-create/compare/v0.1.3...5f39ec38266014b4aacd129f0f69673b5efdb4db)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-14 23:41:27 +00:00
Louis Ruch 224c8f5849 Update docs (#65)
* Update go mod and include go:generate for docs

* go generate
2021-01-14 14:07:36 -08:00
Paul Tyng 39f8c9f501 Turn on dependabot for actions and Go 2021-01-14 16:50:43 -05:00
Louis Ruch 9cb3297615 Update CHANGELOG.md 2021-01-14 13:23:03 -08:00
62 changed files with 2844 additions and 820 deletions

No files matched your search

+2 -2
View File
@@ -7,8 +7,8 @@ jobs:
steps:
- add_ssh_keys:
fingerprints:
# hashicorp-ci github "machine" user, for private modules
- "c6:96:98:82:dc:04:6c:39:dd:ac:83:05:e3:15:1c:98"
# hc-github-team-secure-boundary github user,TF_ACC=1 ~/.go/bin/go test ./... -v $(TESTARGS) -timeout 120m to read private modules
- "d4:7f:b2:f7:6f:4d:e7:21:cc:d2:b7:6a:8b:68:e6:d4"
- checkout
- run:
name: "Run Acceptance Tests"
+17
View File
@@ -0,0 +1,17 @@
# See GitHub's docs for more information on this file:
# https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every weekday
interval: "daily"
# Maintain dependencies for Go modules
- package-ecosystem: "gomod"
directory: "/"
schedule:
# Check for updates to Go modules every weekday
interval: "daily"
+4 -4
View File
@@ -48,7 +48,7 @@ jobs:
- name: Create ticket
if: github.event.action == 'opened' && !steps.boundary-team-role.outputs.role
uses: tomhjp/gh-action-jira-create@v0.1.3
uses: tomhjp/gh-action-jira-create@v0.2.0
with:
project: ICU
issuetype: "GH Issue"
@@ -67,21 +67,21 @@ jobs:
- name: Sync comment
if: github.event.action == 'created' && steps.search.outputs.issue
uses: tomhjp/gh-action-jira-comment@v0.1.0
uses: tomhjp/gh-action-jira-comment@v0.2.0
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@v2.0.1
uses: atlassian/gajira-transition@v2.0.2
with:
issue: ${{ steps.search.outputs.issue }}
transition: Done
- name: Reopen ticket
if: github.event.action == 'reopened' && steps.search.outputs.issue
uses: atlassian/gajira-transition@v2.0.1
uses: atlassian/gajira-transition@v2.0.2
with:
issue: ${{ steps.search.outputs.issue }}
transition: "To Do"
+1 -1
View File
@@ -1 +1 @@
1.15.2
1.16.3
+29 -2
View File
@@ -1,7 +1,34 @@
## 1.0.2 (May 06, 2021)
* Adds OIDC account resource
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/105))
* Adds OIDC auth method resource
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/105))
* Deprecates fields on `resource_auth_method` that will be replaced in the future with generic `attributes` attribute.
## 1.0.1 (February 02, 2021)
### New and Improved
* Adds worker filter to target resource
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/76))
## 1.0.0 (January 20, 2021)
We are bumping the version of the Boundary Terraform provider to v1.0.0 and will release new versions of the provider at its own cadence instead of keeping it in lockstep with Boundary.
### Bug Fixes
* During `terraform apply`, do not update existing user account passwords when the password field is updated in the tf file.
([Issue](https://github.com/hashicorp/terraform-provider-boundary/issues/71))
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/70))
## 0.1.4 (January 14, 2021)
Boundary 0.1.4 release ([see boundary changelog](https://github.com/hashicorp/boundary/blob/main/CHANGELOG.md#014-20210105))
### New and Improved
## 0.1.0 (2020/10/14)
Update provider to handle new domain errors ([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/63))
## 0.1.0 (October 14, 2020)
Initial release!
+6
View File
@@ -0,0 +1,6 @@
# Contributing to Boundary
Thank you for contributing to Boundary! Here you can find common questions around reporting issues and opening
pull requests to our project.
When contributing in any way to the Boundary project (new issue, PR, etc), please be aware that our team identifies with many gender pronouns and to use non-binary pronouns when addressing our team. For more reading on our code of conduct, please see the [HashiCorp community guidelines](https://www.hashicorp.com/community-guidelines).
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.15
FROM golang:1.16
WORKDIR /go/src/terraform-provider-boundary
COPY . .
+1 -1
View File
@@ -9,7 +9,7 @@ Requirements
------------
- [Terraform](https://www.terraform.io/downloads.html) >= 0.12.x
- [Go](https://golang.org/doc/install) >= 1.15
- [Go](https://golang.org/doc/install) >= 1.16
Building The Provider
---------------------
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
VERSION="1.15"
VERSION="1.16"
[ -z "$GOROOT" ] && GOROOT="$HOME/.go"
[ -z "$GOPATH" ] && GOPATH="$HOME/go"
+7 -6
View File
@@ -22,16 +22,17 @@ provider "boundary" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **addr** (String, Required) The base url of the Boundary API, e.g. "http://127.0.0.1:9200". If not set, it will be read from the "BOUNDARY_ADDR" env var.
- **addr** (String) The base url of the Boundary API, e.g. "http://127.0.0.1:9200". If not set, it will be read from the "BOUNDARY_ADDR" env var.
### Optional
- **auth_method_id** (String, Optional) The auth method ID e.g. ampw_1234567890
- **password_auth_method_login_name** (String, Optional) The auth method login name for password-style auth methods
- **password_auth_method_password** (String, Optional) The auth method password for password-style auth methods
- **recovery_kms_hcl** (String, Optional) Can be a heredoc string or a path on disk. If set, the string/file will be parsed as HCL and used with the recovery KMS mechanism. While this is set, it will override any other authentication information; the KMS mechanism will always be used. See Boundary's KMS docs for examples: https://boundaryproject.io/docs/configuration/kms
- **token** (String, Optional) The Boundary token to use, as a string or path on disk containing just the string. If set, the token read here will be used in place of authenticating with the auth method specified in "auth_method_id", although the recovery KMS mechanism will still override this. Can also be set with the BOUNDARY_TOKEN environment variable.
- **auth_method_id** (String) The auth method ID e.g. ampw_1234567890
- **password_auth_method_login_name** (String) The auth method login name for password-style auth methods
- **password_auth_method_password** (String) The auth method password for password-style auth methods
- **recovery_kms_hcl** (String) Can be a heredoc string or a path on disk. If set, the string/file will be parsed as HCL and used with the recovery KMS mechanism. While this is set, it will override any other authentication information; the KMS mechanism will always be used. See Boundary's KMS docs for examples: https://boundaryproject.io/docs/configuration/kms
- **token** (String) The Boundary token to use, as a string or path on disk containing just the string. If set, the token read here will be used in place of authenticating with the auth method specified in "auth_method_id", although the recovery KMS mechanism will still override this. Can also be set with the BOUNDARY_TOKEN environment variable.
+11 -9
View File
@@ -1,11 +1,12 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_account Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The account resource allows you to configure a Boundary account.
---
# Resource `boundary_account`
# boundary_account (Resource)
The account resource allows you to configure a Boundary account.
@@ -33,23 +34,24 @@ resource "boundary_account" "jeff" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **auth_method_id** (String, Required) The resource ID for the auth method.
- **type** (String, Required) The resource type.
- **auth_method_id** (String) The resource ID for the auth method.
- **type** (String) The resource type.
### Optional
- **description** (String, Optional) The account description.
- **login_name** (String, Optional) The login name for this account.
- **name** (String, Optional) The account name. Defaults to the resource name.
- **password** (String, Optional) The account password.
- **description** (String) The account description.
- **login_name** (String, Deprecated) The login name for this account.
- **name** (String) The account name. Defaults to the resource name.
- **password** (String, Deprecated) The account password. Only set on create, changes will not be reflected when updating account.
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the account.
- **id** (String) The ID of the account.
## Import
+33
View File
@@ -0,0 +1,33 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_account_oidc Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The account resource allows you to configure a Boundary account.
---
# boundary_account_oidc (Resource)
The account resource allows you to configure a Boundary account.
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **auth_method_id** (String) The resource ID for the auth method.
### Optional
- **description** (String) The account description.
- **issuer** (String) The OIDC issuer.
- **name** (String) The account name. Defaults to the resource name.
- **subject** (String) The OIDC subject.
### Read-Only
- **id** (String) The ID of the account.
+34
View File
@@ -0,0 +1,34 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_account_password Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The account resource allows you to configure a Boundary account.
---
# boundary_account_password (Resource)
The account resource allows you to configure a Boundary account.
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **auth_method_id** (String) The resource ID for the auth method.
- **type** (String) The resource type.
### Optional
- **description** (String) The account description.
- **login_name** (String, Deprecated) The login name for this account.
- **name** (String) The account name. Defaults to the resource name.
- **password** (String, Deprecated) The account password. Only set on create, changes will not be reflected when updating account.
### Read-Only
- **id** (String) The ID of the account.
+11 -9
View File
@@ -1,11 +1,12 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_auth_method Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The auth method resource allows you to configure a Boundary auth_method.
---
# Resource `boundary_auth_method`
# boundary_auth_method (Resource)
The auth method resource allows you to configure a Boundary auth_method.
@@ -26,23 +27,24 @@ resource "boundary_auth_method" "password" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String, Required) The scope ID.
- **type** (String, Required) The resource type.
- **scope_id** (String) The scope ID.
- **type** (String) The resource type.
### Optional
- **description** (String, Optional) The auth method description.
- **min_login_name_length** (Number, Optional) The minimum login name length.
- **min_password_length** (Number, Optional) The minimum password length.
- **name** (String, Optional) The auth method name. Defaults to the resource name.
- **description** (String) The auth method description.
- **min_login_name_length** (Number, Deprecated) The minimum login name length.
- **min_password_length** (Number, Deprecated) The minimum password length.
- **name** (String) The auth method name. Defaults to the resource name.
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the account.
- **id** (String) The ID of the account.
## Import
+45
View File
@@ -0,0 +1,45 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_auth_method_oidc Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The OIDC auth method resource allows you to configure a Boundary authmethodoidc.
---
# boundary_auth_method_oidc (Resource)
The OIDC auth method resource allows you to configure a Boundary auth_method_oidc.
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String) The scope ID.
### Optional
- **allowed_audiences** (List of String) Audiences for which the provider responses will be allowed
- **api_url_prefix** (String) The API prefix to use when generating callback URLs for the provider. Should be set to an address at which the provider can reach back to the controller.
- **callback_url** (String) The URL that should be provided to the IdP for callbacks.
- **client_id** (String) The client ID assigned to this auth method from the provider.
- **client_secret** (String) The secret key assigned to this auth method from the provider. Once set, only the hash will be kept and the original value can be removed from configuration.
- **client_secret_hmac** (String) The HMAC of the client secret returned by the Boundary controller, which is used for comparison after initial setting of the value.
- **description** (String) The auth method description.
- **disable_discovered_config_validation** (Boolean) Disables validation logic ensuring that the OIDC provider's information from its discovery endpoint matches the information here. The validation is only performed at create or update time.
- **idp_ca_certs** (List of String) A list of CA certificates to trust when validating the IdP's token signatures.
- **is_primary_for_scope** (Boolean) When true, makes this auth method the primary auth method for the scope in which it resides. The primary auth method for a scope means the the user will be automatically created when they login using an OIDC account.
- **issuer** (String) The issuer corresponding to the provider, which must match the issuer field in generated tokens.
- **max_age** (Number) The max age to provide to the provider, indicating how much time is allowed to have passed since the last authentication before the user is challenged again.
- **name** (String) The auth method name. Defaults to the resource name.
- **signing_algorithms** (List of String) Allowed signing algorithms for the provider's issued tokens.
- **state** (String) Can be one of 'inactive', 'active-private', or 'active-public'. Currently automatically set to active-public.
- **type** (String) The type of auth method; hardcoded.
### Read-Only
- **id** (String) The ID of the auth method.
+34
View File
@@ -0,0 +1,34 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_auth_method_password Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The auth method resource allows you to configure a Boundary authmethodpassword.
---
# boundary_auth_method_password (Resource)
The auth method resource allows you to configure a Boundary auth_method_password.
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String) The scope ID.
### Optional
- **description** (String) The auth method description.
- **min_login_name_length** (Number) The minimum login name length.
- **min_password_length** (Number) The minimum password length.
- **name** (String) The auth method name. Defaults to the resource name.
- **type** (String) The resource type, hardcoded per resource
### Read-Only
- **id** (String) The ID of the account.
+11 -10
View File
@@ -45,10 +45,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_user" "foo" {
@@ -64,21 +64,22 @@ resource "boundary_group" "example" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String, Required) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- **scope_id** (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
### Optional
- **description** (String, Optional) The group description.
- **member_ids** (Set of String, Optional) Resource IDs for group members, these are most likely boundary users.
- **name** (String, Optional) The group name. Defaults to the resource name.
- **description** (String) The group description.
- **member_ids** (Set of String) Resource IDs for group members, these are most likely boundary users.
- **name** (String) The group name. Defaults to the resource name.
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the group.
- **id** (String) The ID of the group.
## Import
+14 -12
View File
@@ -1,11 +1,12 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_host Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The host resource allows you to configure a Boundary static host. Hosts are always part of a project, so a project resource should be used inline or you should have the project ID in hand to successfully configure a host.
---
# Resource `boundary_host`
# boundary_host (Resource)
The host resource allows you to configure a Boundary static host. Hosts are always part of a project, so a project resource should be used inline or you should have the project ID in hand to successfully configure a host.
@@ -21,10 +22,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_host" "example" {
@@ -35,22 +36,23 @@ resource "boundary_host" "example" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **host_catalog_id** (String, Required)
- **type** (String, Required)
- **host_catalog_id** (String)
- **type** (String)
### Optional
- **address** (String, Optional) The static address of the host resource as `<IP>` (note: port assignment occurs in the target resource definition, do not add :port here) or a domain name.
- **description** (String, Optional) The host description.
- **name** (String, Optional) The host name. Defaults to the resource name.
- **address** (String) The static address of the host resource as `<IP>` (note: port assignment occurs in the target resource definition, do not add :port here) or a domain name.
- **description** (String) The host description.
- **name** (String) The host name. Defaults to the resource name.
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the host.
- **id** (String) The ID of the host.
## Import
+13 -11
View File
@@ -1,11 +1,12 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_host_catalog Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The host catalog resource allows you to configure a Boundary host catalog. Host catalogs are always part of a project, so a project resource should be used inline or you should have the project ID in hand to successfully configure a host catalog.
---
# Resource `boundary_host_catalog`
# boundary_host_catalog (Resource)
The host catalog resource allows you to configure a Boundary host catalog. Host catalogs are always part of a project, so a project resource should be used inline or you should have the project ID in hand to successfully configure a host catalog.
@@ -21,10 +22,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_host_catalog" "example" {
@@ -35,21 +36,22 @@ resource "boundary_host_catalog" "example" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String, Required) The scope ID in which the resource is created.
- **type** (String, Required) The host catalog type. Only `Static` (yes, title case) is supported.
- **scope_id** (String) The scope ID in which the resource is created.
- **type** (String) The host catalog type. Only `Static` (yes, title case) is supported.
### Optional
- **description** (String, Optional) The host catalog description.
- **name** (String, Optional) The host catalog name. Defaults to the resource name.
- **description** (String) The host catalog description.
- **name** (String) The host catalog name. Defaults to the resource name.
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the host catalog.
- **id** (String) The ID of the host catalog.
## Import
+14 -12
View File
@@ -1,11 +1,12 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_host_set Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The host_set resource allows you to configure a Boundary host set. Host sets are always part of a host catalog, so a host catalog resource should be used inline or you should have the host catalog ID in hand to successfully configure a host set.
---
# Resource `boundary_host_set`
# boundary_host_set (Resource)
The host_set resource allows you to configure a Boundary host set. Host sets are always part of a host catalog, so a host catalog resource should be used inline or you should have the host catalog ID in hand to successfully configure a host set.
@@ -21,10 +22,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_host_catalog" "static" {
@@ -56,22 +57,23 @@ resource "boundary_host_set" "web" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **host_catalog_id** (String, Required) The catalog for the hostset.
- **type** (String, Required)
- **host_catalog_id** (String) The catalog for the hostset.
- **type** (String)
### Optional
- **description** (String, Optional) The hostset description.
- **host_ids** (Set of String, Optional) The list of host IDs contained in this set.
- **name** (String, Optional) The hostset name. Defaults to the resource name.
- **description** (String) The hostset description.
- **host_ids** (Set of String) The list of host IDs contained in this set.
- **name** (String) The hostset name. Defaults to the resource name.
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the hostset.
- **id** (String) The ID of the hostset.
## Import
+14 -13
View File
@@ -96,10 +96,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_user" "readonly" {
@@ -117,24 +117,25 @@ resource "boundary_role" "readonly" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String, Required) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- **scope_id** (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
### Optional
- **default_role** (Boolean, Optional) Indicates that the role containing this value is the default role (that is, has the id 'r_default'), which triggers some specialized behavior to allow it to be imported and managed.
- **description** (String, Optional) The role description.
- **grant_scope_id** (String, Optional)
- **grant_strings** (Set of String, Optional) A list of stringified grants for the role.
- **name** (String, Optional) The role name. Defaults to the resource name.
- **principal_ids** (Set of String, Optional) A list of principal (user or group) IDs to add as principals on the role.
- **default_role** (Boolean) Indicates that the role containing this value is the default role (that is, has the id 'r_default'), which triggers some specialized behavior to allow it to be imported and managed.
- **description** (String) The role description.
- **grant_scope_id** (String)
- **grant_strings** (Set of String) A list of stringified grants for the role.
- **name** (String) The role name. Defaults to the resource name.
- **principal_ids** (Set of String) A list of principal (user or group) IDs to add as principals on the role.
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the role.
- **id** (String) The ID of the role.
## Import
+9 -8
View File
@@ -60,23 +60,24 @@ resource "boundary_role" "org_admin" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String, Required) The scope ID containing the sub scope resource.
- **scope_id** (String) The scope ID containing the sub scope resource.
### Optional
- **auto_create_admin_role** (Boolean, Optional) If set, when a new scope is created, the provider will not disable the functionality that automatically creates a role in the new scope and gives permissions to manage the scope to the provider's user. Marking this true makes for simpler HCL but results in role resources that are unmanaged by Terraform.
- **auto_create_default_role** (Boolean, Optional) Only relevant when creating an org scope. If set, when a new scope is created, the provider will not disable the functionality that automatically creates a role in the new scope and gives listing of scopes and auth methods and the ability to authenticate to the anonymous user. Marking this true makes for simpler HCL but results in role resources that are unmanaged by Terraform.
- **description** (String, Optional) The scope description.
- **global_scope** (Boolean, Optional) Indicates that the scope containing this value is the global scope, which triggers some specialized behavior to allow it to be imported and managed.
- **name** (String, Optional) The scope name. Defaults to the resource name.
- **auto_create_admin_role** (Boolean) If set, when a new scope is created, the provider will not disable the functionality that automatically creates a role in the new scope and gives permissions to manage the scope to the provider's user. Marking this true makes for simpler HCL but results in role resources that are unmanaged by Terraform.
- **auto_create_default_role** (Boolean) Only relevant when creating an org scope. If set, when a new scope is created, the provider will not disable the functionality that automatically creates a role in the new scope and gives listing of scopes and auth methods and the ability to authenticate to the anonymous user. Marking this true makes for simpler HCL but results in role resources that are unmanaged by Terraform.
- **description** (String) The scope description.
- **global_scope** (Boolean) Indicates that the scope containing this value is the global scope, which triggers some specialized behavior to allow it to be imported and managed.
- **name** (String) The scope name. Defaults to the resource name.
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the scope.
- **id** (String) The ID of the scope.
## Import
+18 -15
View File
@@ -1,11 +1,12 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_target Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The target resource allows you to configure a Boundary target.
---
# Resource `boundary_target`
# boundary_target (Resource)
The target resource allows you to configure a Boundary target.
@@ -26,10 +27,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_host_catalog" "foo" {
@@ -74,25 +75,27 @@ resource "boundary_target" "foo" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String, Required) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- **type** (String, Required) The target resource type.
- **scope_id** (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- **type** (String) The target resource type.
### Optional
- **default_port** (Number, Optional) The default port for this target.
- **description** (String, Optional) The target description.
- **host_set_ids** (Set of String, Optional) A list of host set ID's.
- **name** (String, Optional) The target name. Defaults to the resource name.
- **session_connection_limit** (Number, Optional)
- **session_max_seconds** (Number, Optional)
- **default_port** (Number) The default port for this target.
- **description** (String) The target description.
- **host_set_ids** (Set of String) A list of host set ID's.
- **name** (String) The target name. Defaults to the resource name.
- **session_connection_limit** (Number)
- **session_max_seconds** (Number)
- **worker_filter** (String) Boolean expression to filter the workers for this target
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the target.
- **id** (String) The ID of the target.
## Import
+9 -7
View File
@@ -1,11 +1,12 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_user Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The user resource allows you to configure a Boundary user.
---
# Resource `boundary_user`
# boundary_user (Resource)
The user resource allows you to configure a Boundary user.
@@ -40,21 +41,22 @@ resource "boundary_user" "jeff" {
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **scope_id** (String, Required) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
- **scope_id** (String) The scope ID in which the resource is created. Defaults to the provider's `default_scope` if unset.
### Optional
- **account_ids** (Set of String, Optional) Account ID's to associate with this user resource.
- **description** (String, Optional) The user description.
- **name** (String, Optional) The username. Defaults to the resource name.
- **account_ids** (Set of String) Account ID's to associate with this user resource.
- **description** (String) The user description.
- **name** (String) The username. Defaults to the resource name.
### Read-only
### Read-Only
- **id** (String, Read-only) The ID of the user.
- **id** (String) The ID of the user.
## Import
@@ -7,10 +7,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_user" "foo" {
+4 -4
View File
@@ -7,10 +7,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_host" "example" {
@@ -7,10 +7,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_host_catalog" "example" {
@@ -7,10 +7,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_host_catalog" "static" {
@@ -7,10 +7,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_user" "readonly" {
@@ -12,10 +12,10 @@ resource "boundary_scope" "org" {
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_host_catalog" "foo" {
+10 -8
View File
@@ -1,6 +1,6 @@
module github.com/hashicorp/terraform-provider-boundary
go 1.15
go 1.16
require (
cloud.google.com/go v0.68.0 // indirect
@@ -17,15 +17,16 @@ require (
github.com/circonus-labs/circonusllhist v0.1.4 // indirect
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe // indirect
github.com/golang/snappy v0.0.2 // indirect
github.com/hashicorp/boundary v0.1.4
github.com/hashicorp/boundary/api v0.0.3
github.com/hashicorp/boundary/sdk v0.0.1
github.com/hashicorp/boundary v0.2.0
github.com/hashicorp/boundary/api v0.0.10
github.com/hashicorp/boundary/sdk v0.0.4
github.com/hashicorp/cap v0.0.0-20210408110729-0dd65efe5473
github.com/hashicorp/go-immutable-radix v1.3.0 // indirect
github.com/hashicorp/go-kms-wrapping v0.5.16
github.com/hashicorp/go-kms-wrapping v0.6.1
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl/v2 v2.6.0 // indirect
github.com/hashicorp/terraform-plugin-docs v0.2.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.0.5-0.20201016174159-3a1c6c51f716
github.com/hashicorp/terraform-plugin-docs v0.4.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.6.1
github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
@@ -34,6 +35,7 @@ require (
github.com/prometheus/common v0.14.0 // indirect
github.com/prometheus/procfs v0.2.0 // indirect
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c // indirect
github.com/zclconf/go-cty v1.6.1 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
+191 -299
View File
File diff suppressed because it is too large. Load diff
+20 -14
View File
@@ -25,7 +25,7 @@ func New() *schema.Provider {
"addr": {
Type: schema.TypeString,
Required: true,
Description: `The base url of the Boundary API, e.g. "http://127.0.0.1". If not set, it will be read from the "BOUNDARY_ADDR" env var.`,
Description: `The base url of the Boundary API, e.g. "http://127.0.0.1:9200". If not set, it will be read from the "BOUNDARY_ADDR" env var.`,
},
"token": {
Type: schema.TypeString,
@@ -54,16 +54,20 @@ func New() *schema.Provider {
},
},
ResourcesMap: map[string]*schema.Resource{
"boundary_account": resourceAccount(),
"boundary_auth_method": resourceAuthMethod(),
"boundary_group": resourceGroup(),
"boundary_host": resourceHost(),
"boundary_host_catalog": resourceHostCatalog(),
"boundary_host_set": resourceHostset(),
"boundary_role": resourceRole(),
"boundary_scope": resourceScope(),
"boundary_target": resourceTarget(),
"boundary_user": resourceUser(),
"boundary_account": resourceAccount(),
"boundary_account_password": resourceAccountPassword(),
"boundary_account_oidc": resourceAccountOidc(),
"boundary_auth_method": resourceAuthMethod(),
"boundary_auth_method_password": resourceAuthMethodPassword(),
"boundary_auth_method_oidc": resourceAuthMethodOidc(),
"boundary_group": resourceGroup(),
"boundary_host": resourceHost(),
"boundary_host_catalog": resourceHostCatalog(),
"boundary_host_set": resourceHostset(),
"boundary_role": resourceRole(),
"boundary_scope": resourceScope(),
"boundary_target": resourceTarget(),
"boundary_user": resourceUser(),
},
}
@@ -133,11 +137,11 @@ func providerAuthenticate(ctx context.Context, d *schema.ResourceData, md *metaD
am := authmethods.NewClient(md.client)
at, err := am.Authenticate(ctx, authMethodId.(string), credentials)
at, err := am.Authenticate(ctx, authMethodId.(string), "login", credentials)
if err != nil {
return err
}
md.client.SetToken(at.Item.Token)
md.client.SetToken(at.Attributes["token"].(string))
default:
return errors.New("no suitable auth method information found")
@@ -154,7 +158,9 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc {
}
if url, ok := d.GetOk("addr"); ok {
client.SetAddr(url.(string))
if err := client.SetAddr(url.(string)); err != nil {
return nil, diag.FromErr(err)
}
}
if client.Addr() == "" {
return nil, diag.Errorf(`"no valid address could be determined from "addr" or "BOUNDARY_ADDR" env var`)
+1 -1
View File
@@ -15,7 +15,7 @@ import (
)
var (
tcLoginName = "user"
tcLoginName = "testuser"
tcPassword = "passpass"
tcPAUM = "ampw_0000000000"
tcConfig = []controller.Option{
+56 -102
View File
@@ -10,12 +10,6 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
accountTypePassword = "password"
accountLoginNameKey = "login_name"
accountPasswordKey = "password"
)
func resourceAccount() *schema.Resource {
return &schema.Resource{
Description: "The account resource allows you to configure a Boundary account.",
@@ -60,31 +54,53 @@ func resourceAccount() *schema.Resource {
Description: "The login name for this account.",
Type: schema.TypeString,
Optional: true,
Deprecated: "Will be removed in favor of using attributes parameter",
},
accountPasswordKey: {
Description: "The account password.",
Description: "The account password. Only set on create, changes will not be reflected when updating account.",
Type: schema.TypeString,
Optional: true,
Deprecated: "Will be removed in favor of using attributes parameter",
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if d.Id() == "" {
// This is a new resource do not suppress password diff
return false
}
return true
},
},
},
}
}
func setFromAccountResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(AuthMethodIdKey, raw["auth_method_id"])
d.Set(TypeKey, raw["type"])
func setFromAccountResponseMap(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(AuthMethodIdKey, raw["auth_method_id"]); err != nil {
return err
}
if err := d.Set(TypeKey, raw["type"]); err != nil {
return err
}
// TODO(malnick) - remove after deprecation cycle in favor of attributes
switch raw["type"].(string) {
case accountTypePassword:
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
d.Set(accountLoginNameKey, attrs["login_name"])
if err := d.Set(accountLoginNameKey, attrs["login_name"]); err != nil {
return err
}
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -97,49 +113,24 @@ func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, meta int
return diag.Errorf("no auth method ID provided")
}
var loginName *string
if keyVal, ok := d.GetOk(accountLoginNameKey); ok {
key := keyVal.(string)
loginName = &key
}
var password *string
if keyVal, ok := d.GetOk(accountPasswordKey); ok {
key := keyVal.(string)
password = &key
}
opts := []accounts.Option{}
var typeStr string
if typeVal, ok := d.GetOk(TypeKey); ok {
typeStr = typeVal.(string)
} else {
return diag.Errorf("no type provided")
}
switch typeStr {
case accountTypePassword:
if loginName != nil {
opts = append(opts, accounts.WithPasswordAccountLoginName(*loginName))
}
if password != nil {
opts = append(opts, accounts.WithPasswordAccountPassword(*password))
d.Set(accountPasswordKey, *password)
}
default:
return diag.Errorf("invalid type provided")
if nameVal, ok := d.GetOk(NameKey); ok {
opts = append(opts, accounts.WithName(nameVal.(string)))
}
nameVal, ok := d.GetOk(NameKey)
if ok {
nameStr := nameVal.(string)
opts = append(opts, accounts.WithName(nameStr))
if descVal, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, accounts.WithDescription(descVal.(string)))
}
descVal, ok := d.GetOk(DescriptionKey)
if ok {
descStr := descVal.(string)
opts = append(opts, accounts.WithDescription(descStr))
// TODO(malnick) - remove after deprecation cycle
if name, ok := d.GetOk(accountLoginNameKey); ok {
opts = append(opts, accounts.WithPasswordAccountLoginName(name.(string)))
}
// TODO(malnick) - remove after deprecation cycle
if pass, ok := d.GetOk(accountPasswordKey); ok {
opts = append(opts, accounts.WithPasswordAccountPassword(pass.(string)))
}
aClient := accounts.NewClient(md.client)
@@ -152,7 +143,9 @@ func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, meta int
return diag.Errorf("nil account after create")
}
setFromAccountResponseMap(d, acr.GetResponseMap())
if err := setFromAccountResponseMap(d, acr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -163,7 +156,7 @@ func resourceAccountRead(ctx context.Context, d *schema.ResourceData, meta inter
arr, err := aClient.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.ResponseStatus() == http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -173,7 +166,9 @@ func resourceAccountRead(ctx context.Context, d *schema.ResourceData, meta inter
return diag.Errorf("account nil after read")
}
setFromAccountResponseMap(d, arr.GetResponseMap())
if err := setFromAccountResponseMap(d, arr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -184,79 +179,38 @@ func resourceAccountUpdate(ctx context.Context, d *schema.ResourceData, meta int
opts := []accounts.Option{}
var name *string
if d.HasChange(NameKey) {
opts = append(opts, accounts.DefaultName())
nameVal, ok := d.GetOk(NameKey)
if ok {
nameStr := nameVal.(string)
name = &nameStr
opts = append(opts, accounts.WithName(nameStr))
opts = append(opts, accounts.WithName(nameVal.(string)))
}
}
var desc *string
if d.HasChange(DescriptionKey) {
opts = append(opts, accounts.DefaultDescription())
descVal, ok := d.GetOk(DescriptionKey)
if ok {
descStr := descVal.(string)
desc = &descStr
opts = append(opts, accounts.WithDescription(descStr))
opts = append(opts, accounts.WithDescription(descVal.(string)))
}
}
var loginName *string
// TODO(malnick) - remove after deprecation cycle
if d.HasChange(accountLoginNameKey) {
switch d.Get(TypeKey).(string) {
case accountTypePassword:
opts = append(opts, accounts.DefaultPasswordAccountLoginName())
keyVal, ok := d.GetOk(accountLoginNameKey)
if ok {
keyStr := keyVal.(string)
loginName = &keyStr
opts = append(opts, accounts.WithPasswordAccountLoginName(keyStr))
}
default:
return diag.Errorf(`"login_name" cannot be used with this type of account`)
}
}
var password *string
if d.HasChange(accountPasswordKey) {
switch d.Get(TypeKey).(string) {
case accountTypePassword:
opts = append(opts, accounts.DefaultPasswordAccountPassword())
keyVal, ok := d.GetOk(accountPasswordKey)
if ok {
keyStr := keyVal.(string)
password = &keyStr
opts = append(opts, accounts.WithPasswordAccountPassword(keyStr))
}
default:
return diag.Errorf(`"password" cannot be used with this type of account`)
opts = append(opts, accounts.DefaultPasswordAccountLoginName())
if keyVal, ok := d.GetOk(accountLoginNameKey); ok {
opts = append(opts, accounts.WithPasswordAccountLoginName(keyVal.(string)))
}
}
if len(opts) > 0 {
opts = append(opts, accounts.WithAutomaticVersioning(true))
_, err := aClient.Update(ctx, d.Id(), 0, opts...)
aur, err := aClient.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating account: %v", err)
}
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
}
if d.HasChange(accountLoginNameKey) {
d.Set(accountLoginNameKey, loginName)
}
if d.HasChange(accountPasswordKey) {
d.Set(accountPasswordKey, password)
setFromAccountResponseMap(d, aur.GetResponse().Map)
}
return nil
+198
View File
@@ -0,0 +1,198 @@
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"
)
const (
accountTypeOidc = "password"
accountOidcIssuerKey = "issuer"
accountOidcSubjectKey = "subject"
)
func resourceAccountOidc() *schema.Resource {
return &schema.Resource{
Description: "The account resource allows you to configure a Boundary account.",
CreateContext: resourceAccountOidcCreate,
ReadContext: resourceAccountOidcRead,
UpdateContext: resourceAccountOidcUpdate,
DeleteContext: resourceAccountOidcDelete,
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 account name. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The account description.",
Type: schema.TypeString,
Optional: true,
},
AuthMethodIdKey: {
Description: "The resource ID for the auth method.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
accountOidcIssuerKey: {
Description: "The OIDC issuer.",
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
accountOidcSubjectKey: {
Description: "The OIDC subject.",
Type: schema.TypeString,
Optional: true,
},
},
}
}
func setFromAccountOidcResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(AuthMethodIdKey, raw["auth_method_id"])
d.Set(accountOidcIssuerKey, raw["issuer"])
d.Set(accountOidcSubjectKey, raw["subject"])
d.SetId(raw["id"].(string))
}
func resourceAccountOidcCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
var authMethodId string
if authMethodIdVal, ok := d.GetOk(AuthMethodIdKey); ok {
authMethodId = authMethodIdVal.(string)
} else {
return diag.Errorf("no auth method ID provided")
}
opts := []accounts.Option{}
if i, ok := d.GetOk(accountOidcIssuerKey); ok {
opts = append(opts, accounts.WithOidcAccountIssuer(i.(string)))
}
if s, ok := d.GetOk(accountOidcSubjectKey); ok {
opts = append(opts, accounts.WithOidcAccountSubject(s.(string)))
}
if n, ok := d.GetOk(NameKey); ok {
opts = append(opts, accounts.WithName(n.(string)))
}
if d, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, accounts.WithDescription(d.(string)))
}
aClient := accounts.NewClient(md.client)
acr, err := aClient.Create(ctx, authMethodId, opts...)
if err != nil {
return diag.Errorf("error creating account: %v", err)
}
if acr == nil {
return diag.Errorf("nil account after create")
}
setFromAccountOidcResponseMap(d, acr.GetResponse().Map)
return nil
}
func resourceAccountOidcRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
aClient := accounts.NewClient(md.client)
arr, err := aClient.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 account: %v", err)
}
if arr == nil {
return diag.Errorf("account nil after read")
}
setFromAccountOidcResponseMap(d, arr.GetResponse().Map)
return nil
}
func resourceAccountOidcUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
aClient := accounts.NewClient(md.client)
opts := []accounts.Option{}
if d.HasChange(NameKey) {
opts = append(opts, accounts.DefaultName())
if n, ok := d.GetOk(NameKey); ok {
opts = append(opts, accounts.WithName(n.(string)))
}
}
if d.HasChange(DescriptionKey) {
opts = append(opts, accounts.DefaultDescription())
if d, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, accounts.WithDescription(d.(string)))
}
}
if d.HasChange(accountOidcIssuerKey) {
opts = append(opts, accounts.DefaultOidcAccountIssuer())
if i, ok := d.GetOk(accountOidcIssuerKey); ok {
opts = append(opts, accounts.WithOidcAccountIssuer(i.(string)))
}
}
if d.HasChange(accountOidcSubjectKey) {
opts = append(opts, accounts.DefaultOidcAccountSubject())
if i, ok := d.GetOk(accountOidcSubjectKey); ok {
opts = append(opts, accounts.WithOidcAccountSubject(i.(string)))
}
}
if len(opts) > 0 {
opts = append(opts, accounts.WithAutomaticVersioning(true))
aur, err := aClient.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating account: %v", err)
}
setFromAccountOidcResponseMap(d, aur.GetResponse().Map)
}
return nil
}
func resourceAccountOidcDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
aClient := accounts.NewClient(md.client)
_, err := aClient.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting account: %v", err)
}
return nil
}
@@ -0,0 +1,266 @@
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"
)
const (
accountTypePassword = "password"
accountLoginNameKey = "login_name"
accountPasswordKey = "password"
)
func resourceAccountPassword() *schema.Resource {
return &schema.Resource{
Description: "The account resource allows you to configure a Boundary account.",
CreateContext: resourceAccountPasswordCreate,
ReadContext: resourceAccountPasswordRead,
UpdateContext: resourceAccountPasswordUpdate,
DeleteContext: resourceAccountPasswordDelete,
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 account name. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The account description.",
Type: schema.TypeString,
Optional: true,
},
AuthMethodIdKey: {
Description: "The resource ID for the auth method.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
TypeKey: {
Description: "The resource type.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
accountLoginNameKey: {
Description: "The login name for this account.",
Type: schema.TypeString,
Optional: true,
Deprecated: "Will be removed in favor of using attributes parameter",
},
accountPasswordKey: {
Description: "The account password. Only set on create, changes will not be reflected when updating account.",
Type: schema.TypeString,
Optional: true,
Deprecated: "Will be removed in favor of using attributes parameter",
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if d.Id() == "" {
// This is a new resource do not suppress password diff
return false
}
return true
},
},
},
}
}
func setFromAccountPasswordResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(AuthMethodIdKey, raw["auth_method_id"])
d.Set(TypeKey, raw["type"])
switch raw["type"].(string) {
case accountTypePassword:
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
d.Set(accountLoginNameKey, attrs["login_name"])
}
}
d.SetId(raw["id"].(string))
}
func resourceAccountPasswordCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
var authMethodId string
if authMethodIdVal, ok := d.GetOk(AuthMethodIdKey); ok {
authMethodId = authMethodIdVal.(string)
} else {
return diag.Errorf("no auth method ID provided")
}
var loginName *string
if keyVal, ok := d.GetOk(accountLoginNameKey); ok {
key := keyVal.(string)
loginName = &key
}
var password *string
if keyVal, ok := d.GetOk(accountPasswordKey); ok {
key := keyVal.(string)
password = &key
}
opts := []accounts.Option{}
var typeStr string
if typeVal, ok := d.GetOk(TypeKey); ok {
typeStr = typeVal.(string)
} else {
return diag.Errorf("no type provided")
}
switch typeStr {
case accountTypePassword:
if loginName != nil {
opts = append(opts, accounts.WithPasswordAccountLoginName(*loginName))
}
if password != nil {
opts = append(opts, accounts.WithPasswordAccountPassword(*password))
d.Set(accountPasswordKey, *password)
}
default:
return diag.Errorf("invalid type provided")
}
nameVal, ok := d.GetOk(NameKey)
if ok {
nameStr := nameVal.(string)
opts = append(opts, accounts.WithName(nameStr))
}
descVal, ok := d.GetOk(DescriptionKey)
if ok {
descStr := descVal.(string)
opts = append(opts, accounts.WithDescription(descStr))
}
aClient := accounts.NewClient(md.client)
acr, err := aClient.Create(ctx, authMethodId, opts...)
if err != nil {
return diag.Errorf("error creating account: %v", err)
}
if acr == nil {
return diag.Errorf("nil account after create")
}
setFromAccountPasswordResponseMap(d, acr.GetResponse().Map)
return nil
}
func resourceAccountPasswordRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
aClient := accounts.NewClient(md.client)
arr, err := aClient.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 account: %v", err)
}
if arr == nil {
return diag.Errorf("account nil after read")
}
setFromAccountPasswordResponseMap(d, arr.GetResponse().Map)
return nil
}
func resourceAccountPasswordUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
aClient := accounts.NewClient(md.client)
opts := []accounts.Option{}
var name *string
if d.HasChange(NameKey) {
opts = append(opts, accounts.DefaultName())
nameVal, ok := d.GetOk(NameKey)
if ok {
nameStr := nameVal.(string)
name = &nameStr
opts = append(opts, accounts.WithName(nameStr))
}
}
var desc *string
if d.HasChange(DescriptionKey) {
opts = append(opts, accounts.DefaultDescription())
descVal, ok := d.GetOk(DescriptionKey)
if ok {
descStr := descVal.(string)
desc = &descStr
opts = append(opts, accounts.WithDescription(descStr))
}
}
var loginName *string
if d.HasChange(accountLoginNameKey) {
switch d.Get(TypeKey).(string) {
case accountTypePassword:
opts = append(opts, accounts.DefaultPasswordAccountLoginName())
keyVal, ok := d.GetOk(accountLoginNameKey)
if ok {
keyStr := keyVal.(string)
loginName = &keyStr
opts = append(opts, accounts.WithPasswordAccountLoginName(keyStr))
}
default:
return diag.Errorf(`"login_name" cannot be used with this type of account`)
}
}
if len(opts) > 0 {
opts = append(opts, accounts.WithAutomaticVersioning(true))
_, err := aClient.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating account: %v", err)
}
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
}
if d.HasChange(accountLoginNameKey) {
d.Set(accountLoginNameKey, loginName)
}
return nil
}
func resourceAccountPasswordDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
aClient := accounts.NewClient(md.client)
_, err := aClient.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting account: %v", err)
}
return nil
}
@@ -0,0 +1,150 @@
package provider
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/accounts"
"github.com/hashicorp/boundary/testing/controller"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)
const (
fooAccountPasswordDesc = "test account"
fooAccountPasswordDescUpdate = "test account update"
)
var (
fooAccountPassword = fmt.Sprintf(`
resource "boundary_auth_method" "foo" {
name = "test"
description = "test account"
type = "password"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}
resource "boundary_account_password" "foo" {
name = "test"
description = "%s"
type = "password"
login_name = "foo"
password = "foofoofoo"
auth_method_id = boundary_auth_method.foo.id
}`, fooAccountPasswordDesc)
fooAccountPasswordUpdate = fmt.Sprintf(`
resource "boundary_auth_method" "foo" {
name = "test"
description = "test account"
type = "password"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}
resource "boundary_account_password" "foo" {
name = "test"
description = "%s"
type = "password"
login_name = "foo"
password = "foofoofoo"
auth_method_id = boundary_auth_method.foo.id
}`, fooAccountPasswordDescUpdate)
)
func TestAccAccount(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: testAccCheckAccountPasswordResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// create
Config: testConfig(url, fooOrg, fooAccountPassword),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("boundary_account_password.foo", "description", fooAccountPasswordDesc),
resource.TestCheckResourceAttr("boundary_account_password.foo", "name", "test"),
resource.TestCheckResourceAttr("boundary_account_password.foo", "type", "password"),
resource.TestCheckResourceAttr("boundary_account_password.foo", "login_name", "foo"),
resource.TestCheckResourceAttr("boundary_account_password.foo", "password", "foofoofoo"),
testAccCheckAccountPasswordResourceExists(provider, "boundary_account_password.foo"),
),
},
importStep("boundary_account_password.foo", "password"),
{
// update
Config: testConfig(url, fooOrg, fooAccountPasswordUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("boundary_account_password.foo", "description", fooAccountPasswordDescUpdate),
resource.TestCheckResourceAttr("boundary_account_password.foo", "name", "test"),
resource.TestCheckResourceAttr("boundary_account_password.foo", "type", "password"),
resource.TestCheckResourceAttr("boundary_account_password.foo", "login_name", "foo"),
resource.TestCheckResourceAttr("boundary_account_password.foo", "password", "foofoofoo"),
testAccCheckAccountPasswordResourceExists(provider, "boundary_account_password.foo"),
),
},
importStep("boundary_account_password.foo", "password"),
},
})
}
func testAccCheckAccountPasswordResourceExists(testProvider *schema.Provider, name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
id := rs.Primary.ID
if id == "" {
return fmt.Errorf("No ID is set")
}
md := testProvider.Meta().(*metaData)
amClient := accounts.NewClient(md.client)
if _, err := amClient.Read(context.Background(), id); err != nil {
return fmt.Errorf("Got an error when reading account %q: %v", id, err)
}
return nil
}
}
func testAccCheckAccountPasswordResourceDestroy(t *testing.T, testProvider *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
if testProvider.Meta() == nil {
t.Fatal("got nil provider metadata")
}
md := testProvider.Meta().(*metaData)
for _, rs := range s.RootModule().Resources {
switch rs.Type {
case "boundary_account_password":
id := rs.Primary.ID
amClient := accounts.NewClient(md.client)
_, err := amClient.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 account %q: %v", id, err)
}
default:
continue
}
}
return nil
}
}
+3 -3
View File
@@ -57,7 +57,7 @@ resource "boundary_account" "foo" {
}`, fooAccountDescUpdate)
)
func TestAccAccount(t *testing.T) {
func TestAccAccountBase(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
@@ -69,7 +69,7 @@ func TestAccAccount(t *testing.T) {
CheckDestroy: testAccCheckAccountResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
//create
// create
Config: testConfig(url, fooOrg, fooAccount),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("boundary_account.foo", "description", fooAccountDesc),
@@ -137,7 +137,7 @@ func testAccCheckAccountResourceDestroy(t *testing.T, testProvider *schema.Provi
amClient := accounts.NewClient(md.client)
_, err := amClient.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed account %q: %v", id, err)
}
+64 -107
View File
@@ -11,12 +11,6 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
authmethodTypePassword = "password"
authmethodMinLoginNameLengthKey = "min_login_name_length"
authmethodMinPasswordLengthKey = "min_password_length"
)
func resourceAuthMethod() *schema.Resource {
return &schema.Resource{
Description: "The auth method resource allows you to configure a Boundary auth_method.",
@@ -62,22 +56,32 @@ func resourceAuthMethod() *schema.Resource {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Deprecated: "Will be removed in favor of using attributes parameter",
},
authmethodMinPasswordLengthKey: {
Description: "The minimum password length.",
Type: schema.TypeInt,
Optional: true,
Computed: true,
Deprecated: "Will be removed in favor of using attributes parameter",
},
},
}
}
func setFromAuthMethodResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(ScopeIdKey, raw["scope_id"])
d.Set(TypeKey, raw["type"])
func setFromAuthMethodResponseMap(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(TypeKey, raw["type"]); err != nil {
return err
}
switch raw["type"].(string) {
case authmethodTypePassword:
@@ -86,15 +90,19 @@ func setFromAuthMethodResponseMap(d *schema.ResourceData, raw map[string]interfa
minLoginNameLength := attrs["min_login_name_length"].(json.Number)
minLoginNameLengthInt, _ := minLoginNameLength.Int64()
d.Set(authmethodMinLoginNameLengthKey, int(minLoginNameLengthInt))
if err := d.Set(authmethodMinLoginNameLengthKey, int(minLoginNameLengthInt)); err != nil {
return err
}
minPasswordLength := attrs["min_password_length"].(json.Number)
minPasswordLengthInt, _ := minPasswordLength.Int64()
d.Set(authmethodMinPasswordLengthKey, int(minPasswordLengthInt))
if err := d.Set(authmethodMinPasswordLengthKey, int(minPasswordLengthInt)); err != nil {
return err
}
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceAuthMethodCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -107,48 +115,31 @@ func resourceAuthMethodCreate(ctx context.Context, d *schema.ResourceData, meta
return diag.Errorf("no scope ID provided")
}
var minLoginNameLength *int
if minLengthVal, ok := d.GetOk(authmethodMinLoginNameLengthKey); ok {
minLength := minLengthVal.(int)
minLoginNameLength = &minLength
}
var minPasswordLength *int
if minLengthVal, ok := d.GetOk(authmethodMinPasswordLengthKey); ok {
minLength := minLengthVal.(int)
minPasswordLength = &minLength
}
opts := []authmethods.Option{}
var typeStr string
if typeVal, ok := d.GetOk(TypeKey); ok {
typeStr = typeVal.(string)
} else {
return diag.Errorf("no type provided")
}
switch typeStr {
case authmethodTypePassword:
if minLoginNameLength != nil {
opts = append(opts, authmethods.WithPasswordAuthMethodMinLoginNameLength(uint32(*minLoginNameLength)))
}
if minPasswordLength != nil {
opts = append(opts, authmethods.WithPasswordAuthMethodMinPasswordLength(uint32(*minPasswordLength)))
}
default:
return diag.Errorf("invalid type provided")
opts := []authmethods.Option{}
if nameVal, ok := d.GetOk(NameKey); ok {
opts = append(opts, authmethods.WithName(nameVal.(string)))
}
nameVal, ok := d.GetOk(NameKey)
if ok {
nameStr := nameVal.(string)
opts = append(opts, authmethods.WithName(nameStr))
if descVal, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, authmethods.WithDescription(descVal.(string)))
}
descVal, ok := d.GetOk(DescriptionKey)
if ok {
descStr := descVal.(string)
opts = append(opts, authmethods.WithDescription(descStr))
// TODO(malnick) - deprecate
if minLengthVal, ok := d.GetOk(authmethodMinLoginNameLengthKey); ok {
opts = append(opts, authmethods.WithPasswordAuthMethodMinLoginNameLength(uint32(minLengthVal.(int))))
}
// TODO(malnick) - deprecate
if minLengthVal, ok := d.GetOk(authmethodMinPasswordLengthKey); ok {
opts = append(opts, authmethods.WithPasswordAuthMethodMinPasswordLength(uint32(minLengthVal.(int))))
}
amClient := authmethods.NewClient(md.client)
@@ -161,7 +152,9 @@ func resourceAuthMethodCreate(ctx context.Context, d *schema.ResourceData, meta
return diag.Errorf("nil auth method after create")
}
setFromAuthMethodResponseMap(d, amcr.GetResponseMap())
if err := setFromAuthMethodResponseMap(d, amcr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -172,7 +165,7 @@ func resourceAuthMethodRead(ctx context.Context, d *schema.ResourceData, meta in
amrr, err := amClient.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr.ResponseStatus() == http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -182,7 +175,9 @@ func resourceAuthMethodRead(ctx context.Context, d *schema.ResourceData, meta in
return diag.Errorf("auth method nil after read")
}
setFromAuthMethodResponseMap(d, amrr.GetResponseMap())
if err := setFromAuthMethodResponseMap(d, amrr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -193,81 +188,43 @@ func resourceAuthMethodUpdate(ctx context.Context, d *schema.ResourceData, meta
opts := []authmethods.Option{}
var name *string
if d.HasChange(NameKey) {
opts = append(opts, authmethods.DefaultName())
nameVal, ok := d.GetOk(NameKey)
if ok {
nameStr := nameVal.(string)
name = &nameStr
opts = append(opts, authmethods.WithName(nameStr))
if nameVal, ok := d.GetOk(NameKey); ok {
opts = append(opts, authmethods.WithName(nameVal.(string)))
}
}
var desc *string
if d.HasChange(DescriptionKey) {
opts = append(opts, authmethods.DefaultDescription())
descVal, ok := d.GetOk(DescriptionKey)
if ok {
descStr := descVal.(string)
desc = &descStr
opts = append(opts, authmethods.WithDescription(descStr))
if descVal, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, authmethods.WithDescription(descVal.(string)))
}
}
var minLoginNameLength *int
if d.HasChange(authmethodMinLoginNameLengthKey) {
switch d.Get(TypeKey).(string) {
case authmethodTypePassword:
opts = append(opts, authmethods.DefaultPasswordAuthMethodMinLoginNameLength())
minLengthVal, ok := d.GetOk(authmethodMinLoginNameLengthKey)
if ok {
minLengthInt := minLengthVal.(int)
minLoginNameLength = &minLengthInt
opts = append(opts, authmethods.WithPasswordAuthMethodMinLoginNameLength(uint32(minLengthInt)))
}
default:
return diag.Errorf(`"min_login_name_length" cannot be used with this type of auth method`)
}
}
var minPasswordLength *int
// TODO(malnick) - deprecate
if d.HasChange(authmethodMinPasswordLengthKey) {
switch d.Get(TypeKey).(string) {
case authmethodTypePassword:
opts = append(opts, authmethods.DefaultPasswordAuthMethodMinPasswordLength())
minLengthVal, ok := d.GetOk(authmethodMinPasswordLengthKey)
if ok {
minLengthInt := minLengthVal.(int)
minPasswordLength = &minLengthInt
opts = append(opts, authmethods.WithPasswordAuthMethodMinPasswordLength(uint32(minLengthInt)))
}
default:
return diag.Errorf(`"min_password_length" cannot be used with this type of auth method`)
opts = append(opts, authmethods.DefaultPasswordAuthMethodMinPasswordLength())
if minLengthVal, ok := d.GetOk(authmethodMinPasswordLengthKey); ok {
opts = append(opts, authmethods.WithPasswordAuthMethodMinPasswordLength(uint32(minLengthVal.(int))))
}
}
if len(opts) > 0 {
opts = append(opts, authmethods.WithAutomaticVersioning(true))
_, err := amClient.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating auth method: %v", err)
}
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
}
// TODO(malnick) - deprecate
if d.HasChange(authmethodMinLoginNameLengthKey) {
d.Set(authmethodMinLoginNameLengthKey, minLoginNameLength)
opts = append(opts, authmethods.DefaultPasswordAuthMethodMinLoginNameLength())
if minLengthVal, ok := d.GetOk(authmethodMinLoginNameLengthKey); ok {
opts = append(opts, authmethods.WithPasswordAuthMethodMinLoginNameLength(uint32(minLengthVal.(int))))
}
}
if d.HasChange(authmethodMinPasswordLengthKey) {
d.Set(authmethodMinPasswordLengthKey, minPasswordLength)
opts = append(opts, authmethods.WithAutomaticVersioning(true))
amu, err := amClient.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating auth method: %v", err)
}
setFromAuthMethodResponseMap(d, amu.GetResponse().Map)
return nil
}
@@ -0,0 +1,511 @@
package provider
import (
"context"
"encoding/json"
"net/http"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/api/scopes"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
authmethodTypeOidc = "oidc"
authmethodOidcIssuerKey = "issuer"
authmethodOidcClientIdKey = "client_id"
authmethodOidcClientSecretKey = "client_secret"
authmethodOidcMaxAgeKey = "max_age"
authmethodOidcApiUrlPrefixKey = "api_url_prefix"
authmethodOidcIdpCaCertsKey = "idp_ca_certs"
authmethodOidcAllowedAudiencesKey = "allowed_audiences"
authmethodOidcDisableDiscoveredConfigValidationKey = "disable_discovered_config_validation"
authmethodOidcSigningAlgorithmsKey = "signing_algorithms"
authmethodOidcIsPrimaryAuthMethodForScope = "is_primary_for_scope"
// computed-only parameters
authmethodOidcCallbackUrlKey = "callback_url"
authmethodOidcClientSecretHmacKey = "client_secret_hmac"
authmethodOidcStateKey = "state"
)
func resourceAuthMethodOidc() *schema.Resource {
return &schema.Resource{
Description: "The OIDC auth method resource allows you to configure a Boundary auth_method_oidc.",
CreateContext: resourceAuthMethodOidcCreate,
ReadContext: resourceAuthMethodOidcRead,
UpdateContext: resourceAuthMethodOidcUpdate,
DeleteContext: resourceAuthMethodOidcDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
IDKey: {
Description: "The ID of the auth method.",
Type: schema.TypeString,
Computed: true,
},
NameKey: {
Description: "The auth method name. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The auth method description.",
Type: schema.TypeString,
Optional: true,
},
ScopeIdKey: {
Description: "The scope ID.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
// OIDC specific configurable parameters
authmethodOidcAllowedAudiencesKey: {
Description: "Audiences for which the provider responses will be allowed",
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
},
authmethodOidcApiUrlPrefixKey: {
Description: "The API prefix to use when generating callback URLs for the provider. Should be set to an address at which the provider can reach back to the controller.",
Type: schema.TypeString,
Optional: true,
},
authmethodOidcIdpCaCertsKey: {
Description: "A list of CA certificates to trust when validating the IdP's token signatures.",
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
},
authmethodOidcClientIdKey: {
Description: "The client ID assigned to this auth method from the provider.",
Type: schema.TypeString,
Optional: true,
},
authmethodOidcClientSecretKey: {
Description: "The secret key assigned to this auth method from the provider. Once set, only the hash will be kept and the original value can be removed from configuration.",
Type: schema.TypeString,
Optional: true,
},
authmethodOidcIssuerKey: {
Description: "The issuer corresponding to the provider, which must match the issuer field in generated tokens.",
Type: schema.TypeString,
Optional: true,
},
authmethodOidcDisableDiscoveredConfigValidationKey: {
Description: "Disables validation logic ensuring that the OIDC provider's information from its discovery endpoint matches the information here. The validation is only performed at create or update time.",
Type: schema.TypeBool,
Optional: true,
},
authmethodOidcMaxAgeKey: {
Description: "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.",
Type: schema.TypeInt,
Optional: true,
},
authmethodOidcSigningAlgorithmsKey: {
Description: "Allowed signing algorithms for the provider's issued tokens.",
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
},
// OIDC specific immutable and computed parameters
authmethodOidcClientSecretHmacKey: {
Description: "The HMAC of the client secret returned by the Boundary controller, which is used for comparison after initial setting of the value.",
Type: schema.TypeString,
Optional: true,
Computed: true,
},
authmethodOidcStateKey: {
Description: "Can be one of 'inactive', 'active-private', or 'active-public'. Currently automatically set to active-public.",
Type: schema.TypeString,
Optional: true,
Computed: true,
},
authmethodOidcCallbackUrlKey: {
Description: "The URL that should be provided to the IdP for callbacks.",
Type: schema.TypeString,
Optional: true,
Computed: true,
},
authmethodOidcIsPrimaryAuthMethodForScope: {
Description: "When true, makes this auth method the primary auth method for the scope in which it resides. The primary auth method for a scope means the the user will be automatically created when they login using an OIDC account.",
Type: schema.TypeBool,
Optional: true,
},
TypeKey: {
Description: "The type of auth method; hardcoded.",
Type: schema.TypeString,
Default: authmethodTypeOidc,
Optional: true,
},
},
}
}
func setFromOidcAuthMethodResponseMap(d *schema.ResourceData, raw map[string]interface{}) diag.Diagnostics {
d.Set(NameKey, raw[NameKey])
d.Set(DescriptionKey, raw[DescriptionKey])
d.Set(ScopeIdKey, raw[ScopeIdKey])
d.Set(TypeKey, raw[TypeKey])
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
// these are always set
d.Set(authmethodOidcStateKey, attrs[authmethodOidcStateKey].(string))
d.Set(authmethodOidcIssuerKey, attrs[authmethodOidcIssuerKey].(string))
d.Set(authmethodOidcClientIdKey, attrs[authmethodOidcClientIdKey].(string))
d.Set(authmethodOidcClientSecretHmacKey, attrs[authmethodOidcClientSecretHmacKey].(string))
if certs, ok := attrs[authmethodOidcIdpCaCertsKey]; ok {
d.Set(authmethodOidcIdpCaCertsKey, certs.([]interface{}))
}
if aud, ok := attrs[authmethodOidcAllowedAudiencesKey]; ok {
d.Set(authmethodOidcAllowedAudiencesKey, aud.([]interface{}))
}
if m, ok := attrs[authmethodOidcMaxAgeKey]; ok {
maxAge := m.(json.Number)
maxAgeInt, _ := maxAge.Int64()
d.Set(authmethodOidcMaxAgeKey, maxAgeInt)
}
if val, ok := attrs[authmethodOidcApiUrlPrefixKey]; ok {
d.Set(authmethodOidcApiUrlPrefixKey, val.(string))
}
if val, ok := attrs[authmethodOidcCallbackUrlKey]; ok {
d.Set(authmethodOidcCallbackUrlKey, val.(string))
}
if val, ok := attrs[authmethodOidcSigningAlgorithmsKey]; ok {
d.Set(authmethodOidcSigningAlgorithmsKey, val.([]interface{}))
}
if p, ok := attrs[authmethodOidcIsPrimaryAuthMethodForScope]; ok {
d.Set(authmethodOidcIsPrimaryAuthMethodForScope, p.(bool))
}
if p, ok := attrs[authmethodOidcDisableDiscoveredConfigValidationKey]; ok {
d.Set(authmethodOidcDisableDiscoveredConfigValidationKey, p.(bool))
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceAuthMethodOidcCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
var typeStr string
if typeVal, ok := d.GetOk(TypeKey); ok {
typeStr = typeVal.(string)
} else {
return diag.Errorf("no type provided")
}
opts := []authmethods.Option{}
opts = append(opts, authmethods.WithAutomaticVersioning(true))
if issuer, ok := d.GetOk(authmethodOidcIssuerKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodIssuer(issuer.(string)))
}
if clientId, ok := d.GetOk(authmethodOidcClientIdKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodClientId(clientId.(string)))
}
if clientSecret, ok := d.GetOk(authmethodOidcClientSecretKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodClientSecret(clientSecret.(string)))
}
if maxAge, ok := d.GetOk(authmethodOidcMaxAgeKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodMaxAge(uint32(maxAge.(int))))
}
if prefix, ok := d.GetOk(authmethodOidcApiUrlPrefixKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodApiUrlPrefix(prefix.(string)))
}
if certs, ok := d.GetOk(authmethodOidcIdpCaCertsKey); ok {
certList := []string{}
for _, c := range certs.([]interface{}) {
certList = append(certList, c.(string))
}
opts = append(opts, authmethods.WithOidcAuthMethodIdpCaCerts(certList))
}
if aud, ok := d.GetOk(authmethodOidcAllowedAudiencesKey); ok {
audList := []string{}
for _, c := range aud.([]interface{}) {
audList = append(audList, c.(string))
}
opts = append(opts, authmethods.WithOidcAuthMethodAllowedAudiences(audList))
}
if dis, ok := d.GetOk(authmethodOidcDisableDiscoveredConfigValidationKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodDisableDiscoveredConfigValidation(dis.(bool)))
}
if algos, ok := d.GetOk(authmethodOidcSigningAlgorithmsKey); ok {
algoList := []string{}
for _, c := range algos.([]interface{}) {
algoList = append(algoList, c.(string))
}
opts = append(opts, authmethods.WithOidcAuthMethodSigningAlgorithms(algoList))
}
nameVal, ok := d.GetOk(NameKey)
if ok {
nameStr := nameVal.(string)
opts = append(opts, authmethods.WithName(nameStr))
}
descVal, ok := d.GetOk(DescriptionKey)
if ok {
descStr := descVal.(string)
opts = append(opts, authmethods.WithDescription(descStr))
}
var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}
amClient := authmethods.NewClient(md.client)
amcr, err := amClient.Create(ctx, typeStr, scopeId, opts...)
if err != nil {
return diag.Errorf("error creating auth method: %v", err)
}
if amcr == nil {
return diag.Errorf("nil auth method after create")
}
// need to update state is create was successful to avoid loosing state if the change state or update
// scope step that follows errors out
if err := setFromOidcAuthMethodResponseMap(d, amcr.GetResponse().Map); err != nil {
return diag.Errorf("%v", err)
}
amid := amcr.GetResponse().Map["id"].(string)
// auto set to active-public state
_, err = amClient.ChangeState(ctx, amid, 0, "active-public", authmethods.WithAutomaticVersioning(true))
if err != nil {
return diag.Errorf("%v", err)
}
// update scope when set to primary
if p, ok := d.GetOk(authmethodOidcIsPrimaryAuthMethodForScope); ok {
if p.(bool) {
if err := updateScopeWithPrimaryAuthMethodId(ctx, scopeId, amid, meta); err != nil {
return diag.Errorf("%v", err)
}
amcr.GetResponse().Map[authmethodOidcIsPrimaryAuthMethodForScope] = true
}
}
return setFromOidcAuthMethodResponseMap(d, amcr.GetResponse().Map)
}
func updateScopeWithPrimaryAuthMethodId(ctx context.Context, scopeId, authmethodId string, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
scp := scopes.NewClient(md.client)
opts := []scopes.Option{}
opts = append(opts, scopes.WithAutomaticVersioning(true))
opts = append(opts, scopes.WithPrimaryAuthMethodId(authmethodId))
_, err := scp.Update(ctx, scopeId, 0, opts...)
if err != nil {
return diag.Errorf("error updating scope: %v", err)
}
return nil
}
func readScopeIsPrimaryAuthMethodId(ctx context.Context, scopeId, authmethodId string, meta interface{}) (diag.Diagnostics, bool) {
md := meta.(*metaData)
scp := scopes.NewClient(md.client)
srr, err := scp.Read(ctx, scopeId)
if err != nil {
return diag.Errorf("%s", err), false
}
if p, ok := srr.GetResponse().Map["primary_auth_method_id"]; ok {
if p == authmethodId {
return nil, true
}
}
return nil, false
}
func resourceAuthMethodOidcRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
amClient := authmethods.NewClient(md.client)
amrr, err := amClient.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 amrr == nil {
return diag.Errorf("auth method nil after read")
}
serr, isPrimary := readScopeIsPrimaryAuthMethodId(ctx, amrr.GetResponse().Map["scope_id"].(string), amrr.GetResponse().Map["id"].(string), meta)
if err != nil {
return diag.Errorf("%v", serr)
}
if isPrimary {
amrr.GetResponse().Map[authmethodOidcIsPrimaryAuthMethodForScope] = true
}
return setFromOidcAuthMethodResponseMap(d, amrr.GetResponse().Map)
}
func resourceAuthMethodOidcUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
amClient := authmethods.NewClient(md.client)
opts := []authmethods.Option{}
if d.HasChange(NameKey) {
opts = append(opts, authmethods.DefaultName())
nameVal, ok := d.GetOk(NameKey)
if ok {
opts = append(opts, authmethods.WithName(nameVal.(string)))
}
}
if d.HasChange(DescriptionKey) {
opts = append(opts, authmethods.DefaultDescription())
descVal, ok := d.GetOk(DescriptionKey)
if ok {
opts = append(opts, authmethods.WithDescription(descVal.(string)))
}
}
if d.HasChange(authmethodOidcIssuerKey) {
if issuer, ok := d.GetOk(authmethodOidcIssuerKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodIssuer(issuer.(string)))
}
}
if d.HasChange(authmethodOidcClientIdKey) {
if clientId, ok := d.GetOk(authmethodOidcClientIdKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodClientId(clientId.(string)))
}
}
if d.HasChange(authmethodOidcClientSecretKey) {
if clientSecret, ok := d.GetOk(authmethodOidcClientSecretKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodClientSecret(clientSecret.(string)))
}
}
if d.HasChange(authmethodOidcMaxAgeKey) {
if maxAge, ok := d.GetOk(authmethodOidcMaxAgeKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodMaxAge(uint32(maxAge.(int))))
}
}
if d.HasChange(authmethodOidcSigningAlgorithmsKey) {
if algos, ok := d.GetOk(authmethodOidcSigningAlgorithmsKey); ok {
var signingAlgs []string
for _, alg := range algos.([]interface{}) {
signingAlgs = append(signingAlgs, alg.(string))
}
opts = append(opts, authmethods.WithOidcAuthMethodSigningAlgorithms(signingAlgs))
}
}
if d.HasChange(authmethodOidcApiUrlPrefixKey) {
if prefix, ok := d.GetOk(authmethodOidcApiUrlPrefixKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodApiUrlPrefix(prefix.(string)))
}
}
if d.HasChange(authmethodOidcClientSecretHmacKey) {
if sec, ok := d.GetOk(authmethodOidcClientSecretHmacKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodClientSecret(sec.(string)))
}
}
if d.HasChange(authmethodOidcAllowedAudiencesKey) {
if val, ok := d.GetOk(authmethodOidcAllowedAudiencesKey); ok {
var audiences []string
for _, aud := range val.([]interface{}) {
audiences = append(audiences, aud.(string))
}
opts = append(opts, authmethods.WithOidcAuthMethodAllowedAudiences(audiences))
}
}
if d.HasChange(authmethodOidcIdpCaCertsKey) {
if val, ok := d.GetOk(authmethodOidcIdpCaCertsKey); ok {
certs := []string{}
for _, cert := range val.([]interface{}) {
certs = append(certs, cert.(string))
}
opts = append(opts, authmethods.WithOidcAuthMethodIdpCaCerts(certs))
}
}
if d.HasChange(authmethodOidcDisableDiscoveredConfigValidationKey) {
if val, ok := d.GetOk(authmethodOidcDisableDiscoveredConfigValidationKey); ok {
opts = append(opts, authmethods.WithOidcAuthMethodDisableDiscoveredConfigValidation(val.(bool)))
}
}
if len(opts) > 0 {
opts = append(opts, authmethods.WithAutomaticVersioning(true))
amur, err := amClient.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating auth method: %v", err)
}
if d.HasChange(authmethodOidcIsPrimaryAuthMethodForScope) {
if p, ok := d.GetOk(authmethodOidcIsPrimaryAuthMethodForScope); ok {
if p.(bool) {
if err := updateScopeWithPrimaryAuthMethodId(
ctx,
amur.GetResponse().Map["scope_id"].(string),
amur.GetResponse().Map["id"].(string),
meta); err != nil {
return diag.Errorf("%v", err)
}
amur.GetResponse().Map[authmethodOidcIsPrimaryAuthMethodForScope] = true
}
}
}
return setFromOidcAuthMethodResponseMap(d, amur.GetResponse().Map)
}
return nil
}
func resourceAuthMethodOidcDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
amClient := authmethods.NewClient(md.client)
_, err := amClient.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting auth method: %v", err)
}
return nil
}
@@ -0,0 +1,280 @@
package provider
import (
"context"
"fmt"
"net/http"
"strings"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/api/scopes"
"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"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)
const (
fooAuthMethodOidcDesc = "test auth method oidc"
fooAuthMethodOidcDescUpdate = "test auth method oidc update"
fooAuthMethodOidcCaCerts = `-----BEGIN CERTIFICATE-----
MIIDsjCCApoCCQCslgm7fAu/VzANBgkqhkiG9w0BAQsFADCBmjELMAkGA1UEBhMC
VVMxCzAJBgNVBAgMAldBMRMwEQYDVQQHDApCZWxsaW5naGFtMRIwEAYDVQQKDAlI
YXNoaUNvcnAxETAPBgNVBAsMCEJvdW5kYXJ5MRswGQYDVQQDDBJib3VuZGFyeXBy
b2plY3QuaW8xJTAjBgkqhkiG9w0BCQEWFmptYWxuaWNrQGhhc2hpY29ycC5jb20w
HhcNMjEwNDA2MjMzNTIxWhcNMjYwNDA1MjMzNTIxWjCBmjELMAkGA1UEBhMCVVMx
CzAJBgNVBAgMAldBMRMwEQYDVQQHDApCZWxsaW5naGFtMRIwEAYDVQQKDAlIYXNo
aUNvcnAxETAPBgNVBAsMCEJvdW5kYXJ5MRswGQYDVQQDDBJib3VuZGFyeXByb2pl
Y3QuaW8xJTAjBgkqhkiG9w0BCQEWFmptYWxuaWNrQGhhc2hpY29ycC5jb20wggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUa/ZLwYhTQ2hGzlwy/sB9xY9h
dM4qzY8DUF3Sin/+J2j59q3/vXZA7PS+o1GhoG1nW3J51zgyNFOY/EKHdtxVBken
YTTG+JswzrcTxsMV7/sYDgCLq6W8dPMV72gPH/3dRi3/0KUHtA6rBOf0Shf0f6Sz
7VmgTWcNmLvXpHKOs4YkjOL/tyflTgNm5j1dOa53TtwtMyvCcpGrB7PGL8m5+E2U
qxOzQ9kWfA6zr4Gl5rIm+Us8Ez3n1yGwjwFBteexk1Fot8zWKhoy7pZ3ZjWRpjwL
hfGs5eJs4kERQVAGONt39ZIR6OzOFxAsvI9WrMvxAsdCK63RtF2k4r0X21yDAgMB
AAEwDQYJKoZIhvcNAQELBQADggEBAJZcl7Zxjya23IcOV8jZDdCHtqnbcg9TcQb+
kpX1uEKJMFJoNmNK1q//nJxG1YBn3G8t9XtO6Kc6egdGHXWnOsM37N9hbYPJ2kW1
WWAwqWkQbV3wb0cc6MuU1S9xivOqwM046ZIcjrWR4T4tEUSUfYc3I+Yd8APdapn8
vePgWnmi/aSsx9RxVOUrzmVhzgN7rQJZGwnYYnxl4cwy2jxpysmXzg/grfXCZs/V
Kkc7Y5Ph6vRQ+vPCeB7QUxHxjlr8aq+rYDIaSiZ+/4+qyme0ergfvZmMSU8A3NNS
tYIMds5s2lIqVwOoyzpBEOjWBhUThH+aZu1A5c7Cb7s1eLSRX70=
-----END CERTIFICATE-----`
)
var (
fooAuthMethodOidc = `
resource "boundary_auth_method_oidc" "foo" {
name = "test"
description = "%s"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
issuer = "%s"
client_id = "foo_id"
client_secret = "foo_secret"
max_age = 10
api_url_prefix = "http://localhost:9200"
idp_ca_certs = [
<<EOT
%s
EOT
]
allowed_audiences = ["foo_aud"]
signing_algorithms = ["ES256"]
}`
fooAuthMethodOidcUpdate = `
resource "boundary_auth_method_oidc" "foo" {
name = "test"
description = "%s"
scope_id = boundary_scope.org1.id
is_primary_for_scope = true
depends_on = [boundary_role.org1_admin]
issuer = "https://test-update.com"
client_id = "foo_id_update"
client_secret = "foo_secret_update"
max_age = 1
api_url_prefix = "http://localhost:9200"
idp_ca_certs = [
<<EOT
%s
EOT
]
allowed_audiences = ["foo_aud_update"]
signing_algorithms = ["RS256"]
// we need to disable this validatin, since the updated issuer isn't discoverable
disable_discovered_config_validation = true
}`
)
func TestAccAuthMethodOidc(t *testing.T) {
tp := oidc.StartTestProvider(t)
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
tpCert := strings.TrimSpace(tp.CACert())
createConfig := fmt.Sprintf(fooAuthMethodOidc, fooAuthMethodOidcDesc, tp.Addr(), tpCert)
updateConfig := fmt.Sprintf(fooAuthMethodOidcUpdate, fooAuthMethodOidcDescUpdate, fooAuthMethodOidcCaCerts)
var provider *schema.Provider
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckAuthMethodOidcResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// create
Config: testConfig(url, fooOrg, createConfig),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", "description", fooAuthMethodOidcDesc),
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", "name", "test"),
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", authmethodOidcIssuerKey, tp.Addr()),
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", authmethodOidcClientIdKey, "foo_id"),
testAccCheckAuthMethodOidcAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcIdpCaCertsKey, []string{tpCert}),
testAccCheckAuthMethodOidcAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcAllowedAudiencesKey, []string{"foo_aud"}),
testAccCheckAuthMethodOidcAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcSigningAlgorithmsKey, []string{"ES256"}),
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", authmethodOidcMaxAgeKey, "10"),
testAccCheckAuthMethodOidcResourceExists(provider, "boundary_auth_method_oidc.foo"),
testAccIsPrimaryForScope(provider, "boundary_auth_method_oidc.foo", false),
),
},
importStep("boundary_auth_method_oidc.foo", "client_secret", "is_primary_for_scope"),
{
// update
Config: testConfig(url, fooOrg, updateConfig),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", "description", fooAuthMethodOidcDescUpdate),
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", "name", "test"),
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", authmethodOidcIssuerKey, "https://test-update.com"),
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", authmethodOidcClientIdKey, "foo_id_update"),
resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", authmethodOidcMaxAgeKey, "1"),
testAccCheckAuthMethodOidcAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcIdpCaCertsKey, []string{fooAuthMethodOidcCaCerts}),
testAccCheckAuthMethodOidcAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcAllowedAudiencesKey, []string{"foo_aud_update"}),
testAccCheckAuthMethodOidcResourceExists(provider, "boundary_auth_method_oidc.foo"),
testAccIsPrimaryForScope(provider, "boundary_auth_method_oidc.foo", true),
testAccCheckAuthMethodOidcResourceExists(provider, "boundary_auth_method_oidc.foo"),
),
},
importStep("boundary_auth_method_oidc.foo", "client_secret", "is_primary_for_scope"),
},
})
}
func testAccCheckAuthMethodOidcResourceExists(testProvider *schema.Provider, name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
id := rs.Primary.ID
if id == "" {
return fmt.Errorf("No ID is set")
}
md := testProvider.Meta().(*metaData)
amClient := authmethods.NewClient(md.client)
if _, err := amClient.Read(context.Background(), id); err != nil {
return fmt.Errorf("Got an error when reading auth method %q: %w", id, err)
}
return nil
}
}
func testAccCheckAuthMethodOidcResourceDestroy(t *testing.T, testProvider *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
if testProvider.Meta() == nil {
t.Fatal("got nil provider metadata")
}
md := testProvider.Meta().(*metaData)
for _, rs := range s.RootModule().Resources {
switch rs.Type {
case "boundary_auth_method_oidc":
id := rs.Primary.ID
amClient := authmethods.NewClient(md.client)
_, err := amClient.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 auth method %q: %v", id, err)
}
default:
continue
}
}
return nil
}
}
func testAccCheckAuthMethodOidcAttrAryValueSet(testProvider *schema.Provider, name string, key string, strAry []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("auth method resource not found: %s", name)
}
id := rs.Primary.ID
if id == "" {
return fmt.Errorf("auth method resource ID is not set")
}
md := testProvider.Meta().(*metaData)
amClient := authmethods.NewClient(md.client)
amr, err := amClient.Read(context.Background(), id)
if err != nil {
return fmt.Errorf("Got an error when reading auth method %q: %v", id, err)
}
for _, got := range amr.Item.Attributes[key].([]interface{}) {
ok := false
for _, expected := range strAry {
if strings.TrimSpace(got.(string)) == strings.TrimSpace(expected) {
ok = true
}
}
if !ok {
return fmt.Errorf("value not found in boundary\n %s: %s\n", key, got.(string))
}
}
return nil
}
}
func testAccIsPrimaryForScope(tp *schema.Provider, name string, shouldBePrimary bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("auth method resource not found: %s", name)
}
amId := rs.Primary.ID
if amId == "" {
return fmt.Errorf("auth method resource ID is not set")
}
md := tp.Meta().(*metaData)
amClient := authmethods.NewClient(md.client)
amr, err := amClient.Read(context.Background(), amId)
if err != nil {
return fmt.Errorf("Got an error when reading auth method %q: %v", amId, err)
}
amScopeId, ok := amr.GetResponse().Map["scope_id"]
if !ok {
return fmt.Errorf("scope_id unset on auth method resource")
}
scp := scopes.NewClient(md.client)
srr, err := scp.Read(context.Background(), amScopeId.(string))
if err != nil {
return err
}
primaryScopeAuthMethodId, ok := srr.GetResponse().Map["primary_auth_method_id"]
if !ok && shouldBePrimary {
return fmt.Errorf("primary_auth_method_id is not set on scope resource response")
}
if shouldBePrimary {
if primaryScopeAuthMethodId != amId {
return fmt.Errorf("auth method ('%s') should be primary for scope but scope returned '%s' for primary_auth_method_id", amId, primaryScopeAuthMethodId)
}
}
return nil
}
}
@@ -0,0 +1,240 @@
package provider
import (
"context"
"encoding/json"
"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"
)
const (
authmethodTypePassword = "password"
authmethodMinLoginNameLengthKey = "min_login_name_length"
authmethodMinPasswordLengthKey = "min_password_length"
)
func resourceAuthMethodPassword() *schema.Resource {
return &schema.Resource{
Description: "The auth method resource allows you to configure a Boundary auth_method_password.",
CreateContext: resourceAuthMethodPasswordCreate,
ReadContext: resourceAuthMethodPasswordRead,
UpdateContext: resourceAuthMethodPasswordUpdate,
DeleteContext: resourceAuthMethodPasswordDelete,
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 auth method name. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The auth method description.",
Type: schema.TypeString,
Optional: true,
},
ScopeIdKey: {
Description: "The scope ID.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
TypeKey: {
Description: "The resource type, hardcoded per resource",
Type: schema.TypeString,
Default: authmethodTypePassword,
Optional: true,
},
authmethodMinLoginNameLengthKey: {
Description: "The minimum login name length.",
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
authmethodMinPasswordLengthKey: {
Description: "The minimum password length.",
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
},
}
}
func setFromPasswordAuthMethodResponseMap(d *schema.ResourceData, raw map[string]interface{}) diag.Diagnostics {
d.Set(NameKey, raw[NameKey])
d.Set(DescriptionKey, raw[DescriptionKey])
d.Set(ScopeIdKey, raw[ScopeIdKey])
d.Set(TypeKey, raw[TypeKey])
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
minLoginNameLength := attrs[authmethodMinLoginNameLengthKey].(json.Number)
minLoginNameLengthInt, _ := minLoginNameLength.Int64()
d.Set(authmethodMinLoginNameLengthKey, int(minLoginNameLengthInt))
minPasswordLength := attrs[authmethodMinPasswordLengthKey].(json.Number)
minPasswordLengthInt, _ := minPasswordLength.Int64()
d.Set(authmethodMinPasswordLengthKey, int(minPasswordLengthInt))
}
d.SetId(raw["id"].(string))
return nil
}
func resourceAuthMethodPasswordCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
var typeStr string
if typeVal, ok := d.GetOk(TypeKey); ok {
typeStr = typeVal.(string)
} else {
return diag.Errorf("no type provided")
}
opts := []authmethods.Option{}
var minLoginNameLength *int
if minLengthVal, ok := d.GetOk(authmethodMinLoginNameLengthKey); ok {
minLength := minLengthVal.(int)
minLoginNameLength = &minLength
}
if minLoginNameLength != nil {
opts = append(opts, authmethods.WithPasswordAuthMethodMinLoginNameLength(uint32(*minLoginNameLength)))
}
var minPasswordLength *int
if minLengthVal, ok := d.GetOk(authmethodMinPasswordLengthKey); ok {
minLength := minLengthVal.(int)
minPasswordLength = &minLength
}
if minPasswordLength != nil {
opts = append(opts, authmethods.WithPasswordAuthMethodMinPasswordLength(uint32(*minPasswordLength)))
}
nameVal, ok := d.GetOk(NameKey)
if ok {
nameStr := nameVal.(string)
opts = append(opts, authmethods.WithName(nameStr))
}
descVal, ok := d.GetOk(DescriptionKey)
if ok {
descStr := descVal.(string)
opts = append(opts, authmethods.WithDescription(descStr))
}
var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}
amClient := authmethods.NewClient(md.client)
amcr, err := amClient.Create(ctx, typeStr, scopeId, opts...)
if err != nil {
return diag.Errorf("error creating auth method: %v", err)
}
if amcr == nil {
return diag.Errorf("nil auth method after create")
}
return setFromPasswordAuthMethodResponseMap(d, amcr.GetResponse().Map)
}
func resourceAuthMethodPasswordRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
amClient := authmethods.NewClient(md.client)
amrr, err := amClient.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 amrr == nil {
return diag.Errorf("auth method nil after read")
}
return setFromPasswordAuthMethodResponseMap(d, amrr.GetResponse().Map)
}
func resourceAuthMethodPasswordUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
amClient := authmethods.NewClient(md.client)
opts := []authmethods.Option{}
if d.HasChange(NameKey) {
opts = append(opts, authmethods.DefaultName())
nameVal, ok := d.GetOk(NameKey)
if ok {
opts = append(opts, authmethods.WithName(nameVal.(string)))
}
}
if d.HasChange(DescriptionKey) {
opts = append(opts, authmethods.DefaultDescription())
descVal, ok := d.GetOk(DescriptionKey)
if ok {
opts = append(opts, authmethods.WithDescription(descVal.(string)))
}
}
if d.HasChange(authmethodMinLoginNameLengthKey) {
opts = append(opts, authmethods.DefaultPasswordAuthMethodMinLoginNameLength())
minLengthVal, ok := d.GetOk(authmethodMinLoginNameLengthKey)
if ok {
opts = append(opts, authmethods.WithPasswordAuthMethodMinLoginNameLength(uint32(minLengthVal.(int))))
}
}
if d.HasChange(authmethodMinPasswordLengthKey) {
opts = append(opts, authmethods.DefaultPasswordAuthMethodMinPasswordLength())
minLengthVal, ok := d.GetOk(authmethodMinPasswordLengthKey)
if ok {
opts = append(opts, authmethods.WithPasswordAuthMethodMinPasswordLength(uint32(minLengthVal.(int))))
}
}
if len(opts) > 0 {
opts = append(opts, authmethods.WithAutomaticVersioning(true))
amur, err := amClient.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating auth method: %v", err)
}
return setFromPasswordAuthMethodResponseMap(d, amur.GetResponse().Map)
}
return nil
}
func resourceAuthMethodPasswordDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
amClient := authmethods.NewClient(md.client)
_, err := amClient.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting auth method: %v", err)
}
return nil
}
@@ -0,0 +1,127 @@
package provider
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/testing/controller"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)
const (
fooAuthMethodDesc = "test auth method"
fooAuthMethodDescUpdate = "test auth method update"
)
var (
fooAuthMethod = fmt.Sprintf(`
resource "boundary_auth_method_password" "foo" {
name = "test"
description = "%s"
type = "password"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}`, fooAuthMethodDesc)
fooAuthMethodUpdate = fmt.Sprintf(`
resource "boundary_auth_method_password" "foo" {
name = "test"
description = "%s"
type = "password"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}`, fooAuthMethodDescUpdate)
)
func TestAccAuthMethodPassword(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: testAccCheckAuthMethodResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// create
Config: testConfig(url, fooOrg, fooAuthMethod),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("boundary_auth_method_password.foo", "description", fooAuthMethodDesc),
resource.TestCheckResourceAttr("boundary_auth_method_password.foo", "name", "test"),
resource.TestCheckResourceAttr("boundary_auth_method_password.foo", "type", "password"),
testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method_password.foo"),
),
},
importStep("boundary_auth_method_password.foo"),
{
// update
Config: testConfig(url, fooOrg, fooAuthMethodUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("boundary_auth_method_password.foo", "description", fooAuthMethodDescUpdate),
resource.TestCheckResourceAttr("boundary_auth_method_password.foo", "name", "test"),
resource.TestCheckResourceAttr("boundary_auth_method_password.foo", "type", "password"),
testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method_password.foo"),
),
},
importStep("boundary_auth_method_password.foo"),
},
})
}
func testAccCheckAuthMethodResourceExists(testProvider *schema.Provider, name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
id := rs.Primary.ID
if id == "" {
return fmt.Errorf("No ID is set")
}
md := testProvider.Meta().(*metaData)
amClient := authmethods.NewClient(md.client)
if _, err := amClient.Read(context.Background(), id); err != nil {
return fmt.Errorf("Got an error when reading auth method %q: %w", id, err)
}
return nil
}
}
func testAccCheckAuthMethodResourceDestroy(t *testing.T, testProvider *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
if testProvider.Meta() == nil {
t.Fatal("got nil provider metadata")
}
md := testProvider.Meta().(*metaData)
for _, rs := range s.RootModule().Resources {
switch rs.Type {
case "boundary_auth_method_password":
id := rs.Primary.ID
amClient := authmethods.NewClient(md.client)
_, err := amClient.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 auth method %q: %v", id, err)
}
default:
continue
}
}
return nil
}
}
+20 -17
View File
@@ -15,31 +15,33 @@ import (
)
const (
fooAuthMethodDesc = "test auth method"
fooAuthMethodDescUpdate = "test auth method update"
fooBaseAuthMethodDesc = "test auth method"
fooBaseAuthMethodDescUpdate = "test auth method update"
)
// TODO(malnick) - remove these tests after deprecating use of password params in
// favor of attributes map
var (
fooAuthMethod = fmt.Sprintf(`
fooBaseAuthMethod = fmt.Sprintf(`
resource "boundary_auth_method" "foo" {
name = "test"
description = "%s"
type = "password"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}`, fooAuthMethodDesc)
}`, fooBaseAuthMethodDesc)
fooAuthMethodUpdate = fmt.Sprintf(`
fooBaseAuthMethodUpdate = fmt.Sprintf(`
resource "boundary_auth_method" "foo" {
name = "test"
description = "%s"
type = "password"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}`, fooAuthMethodDescUpdate)
}`, fooBaseAuthMethodDescUpdate)
)
func TestAccAuthMethod(t *testing.T) {
func TestAccBaseAuthMethodPassword(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
@@ -50,24 +52,25 @@ func TestAccAuthMethod(t *testing.T) {
CheckDestroy: testAccCheckAuthMethodResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
//create
Config: testConfig(url, fooOrg, fooAuthMethod),
// create
Config: testConfig(url, fooOrg, fooBaseAuthMethod),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("boundary_auth_method.foo", "description", fooAuthMethodDesc),
resource.TestCheckResourceAttr("boundary_auth_method.foo", "description", fooBaseAuthMethodDesc),
resource.TestCheckResourceAttr("boundary_auth_method.foo", "name", "test"),
resource.TestCheckResourceAttr("boundary_auth_method.foo", "type", "password"),
testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method.foo"),
testAccCheckBaseAuthMethodResourceExists(provider, "boundary_auth_method.foo"),
),
},
importStep("boundary_auth_method.foo"),
{
// update
Config: testConfig(url, fooOrg, fooAuthMethodUpdate),
Config: testConfig(url, fooOrg, fooBaseAuthMethodUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("boundary_auth_method.foo", "description", fooAuthMethodDescUpdate),
resource.TestCheckResourceAttr("boundary_auth_method.foo", "description", fooBaseAuthMethodDescUpdate),
resource.TestCheckResourceAttr("boundary_auth_method.foo", "name", "test"),
resource.TestCheckResourceAttr("boundary_auth_method.foo", "type", "password"),
testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method.foo"),
testAccCheckBaseAuthMethodResourceExists(provider, "boundary_auth_method.foo"),
),
},
importStep("boundary_auth_method.foo"),
@@ -75,7 +78,7 @@ func TestAccAuthMethod(t *testing.T) {
})
}
func testAccCheckAuthMethodResourceExists(testProvider *schema.Provider, name string) resource.TestCheckFunc {
func testAccCheckBaseAuthMethodResourceExists(testProvider *schema.Provider, name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
@@ -99,7 +102,7 @@ func testAccCheckAuthMethodResourceExists(testProvider *schema.Provider, name st
}
}
func testAccCheckAuthMethodResourceDestroy(t *testing.T, testProvider *schema.Provider) resource.TestCheckFunc {
func testAccCheckBaseAuthMethodResourceDestroy(t *testing.T, testProvider *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
if testProvider.Meta() == nil {
t.Fatal("got nil provider metadata")
@@ -114,7 +117,7 @@ func testAccCheckAuthMethodResourceDestroy(t *testing.T, testProvider *schema.Pr
amClient := authmethods.NewClient(md.client)
_, err := amClient.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed auth method %q: %v", id, err)
}
+35 -13
View File
@@ -58,12 +58,21 @@ func resourceGroup() *schema.Resource {
}
}
func setFromGroupResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(ScopeIdKey, raw["scope_id"])
d.Set(groupMemberIdsKey, raw["member_ids"])
func setFromGroupResponseMap(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(groupMemberIdsKey, raw["member_ids"]); err != nil {
return err
}
d.SetId(raw["id"].(string))
return nil
}
func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -99,7 +108,7 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter
if gcr == nil {
return diag.Errorf("group nil after create")
}
raw := gcr.GetResponseMap()
raw := gcr.GetResponse().Map
if val, ok := d.GetOk(groupMemberIdsKey); ok {
list := val.(*schema.Set).List()
@@ -114,10 +123,12 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter
if gcsmr == nil {
return diag.Errorf("group nil after setting members")
}
raw = gcsmr.GetResponseMap()
raw = gcsmr.GetResponse().Map
}
setFromGroupResponseMap(d, raw)
if err := setFromGroupResponseMap(d, raw); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -128,7 +139,7 @@ func resourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interfa
g, err := grps.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr.ResponseStatus() == http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -138,7 +149,9 @@ func resourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interfa
return diag.Errorf("group nil after read")
}
setFromGroupResponseMap(d, g.GetResponseMap())
if err := setFromGroupResponseMap(d, g.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -180,10 +193,16 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
if err := d.Set(NameKey, name); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
if err := d.Set(DescriptionKey, desc); err != nil {
return diag.FromErr(err)
}
}
// The above call may not actually happen, so we use d.Id() and automatic
@@ -201,7 +220,10 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
if err != nil {
return diag.Errorf("error updating members in group: %v", err)
}
d.Set(groupMemberIdsKey, memberIds)
if err := d.Set(groupMemberIdsKey, memberIds); err != nil {
return diag.FromErr(err)
}
}
return nil
+1 -1
View File
@@ -365,7 +365,7 @@ func testAccCheckGroupResourceDestroy(t *testing.T, testProvider *schema.Provide
id := rs.Primary.ID
_, err := grps.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed resource %q: %v", id, apiErr)
}
+33 -12
View File
@@ -63,21 +63,32 @@ func resourceHost() *schema.Resource {
}
}
func setFromHostResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(HostCatalogIdKey, raw["host_catalog_id"])
d.Set(TypeKey, raw["type"])
func setFromHostResponseMap(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(HostCatalogIdKey, raw["host_catalog_id"]); err != nil {
return err
}
if err := d.Set(TypeKey, raw["type"]); err != nil {
return err
}
switch raw["type"].(string) {
case hostTypeStatic:
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
d.Set(hostAddressKey, attrs["address"])
if err := d.Set(hostAddressKey, attrs["address"]); err != nil {
return err
}
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceHostCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -140,7 +151,9 @@ func resourceHostCreate(ctx context.Context, d *schema.ResourceData, meta interf
return diag.Errorf("host nil after create")
}
setFromHostResponseMap(d, hcr.GetResponseMap())
if err := setFromHostResponseMap(d, hcr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -151,7 +164,7 @@ func resourceHostRead(ctx context.Context, d *schema.ResourceData, meta interfac
hrr, err := hClient.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.ResponseStatus() == http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -161,7 +174,9 @@ func resourceHostRead(ctx context.Context, d *schema.ResourceData, meta interfac
return diag.Errorf("host nil after read")
}
setFromHostResponseMap(d, hrr.GetResponseMap())
if err := setFromHostResponseMap(d, hrr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -219,13 +234,19 @@ func resourceHostUpdate(ctx context.Context, d *schema.ResourceData, meta interf
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
if err := d.Set(NameKey, name); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
if err := d.Set(DescriptionKey, desc); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(hostAddressKey) {
d.Set(hostAddressKey, *address)
if err := d.Set(hostAddressKey, *address); err != nil {
return diag.FromErr(err)
}
}
return nil
+27 -10
View File
@@ -60,12 +60,21 @@ func resourceHostCatalog() *schema.Resource {
}
}
func setFromHostCatalogResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(ScopeIdKey, raw["scope_id"])
d.Set(TypeKey, raw["type"])
func setFromHostCatalogResponseMap(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(TypeKey, raw["type"]); err != nil {
return err
}
d.SetId(raw["id"].(string))
return nil
}
func resourceHostCatalogCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -114,7 +123,9 @@ func resourceHostCatalogCreate(ctx context.Context, d *schema.ResourceData, meta
return diag.Errorf("nil host catalog after create")
}
setFromHostCatalogResponseMap(d, hccr.GetResponseMap())
if err := setFromHostCatalogResponseMap(d, hccr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -125,7 +136,7 @@ func resourceHostCatalogRead(ctx context.Context, d *schema.ResourceData, meta i
hcrr, err := hcClient.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.ResponseStatus() == http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -135,7 +146,9 @@ func resourceHostCatalogRead(ctx context.Context, d *schema.ResourceData, meta i
return diag.Errorf("host catalog nil after read")
}
setFromHostCatalogResponseMap(d, hcrr.GetResponseMap())
if err := setFromHostCatalogResponseMap(d, hcrr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -177,10 +190,14 @@ func resourceHostCatalogUpdate(ctx context.Context, d *schema.ResourceData, meta
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
if err := d.Set(NameKey, name); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
if err := d.Set(DescriptionKey, desc); err != nil {
return diag.FromErr(err)
}
}
return nil
@@ -108,7 +108,7 @@ func testAccCheckHostCatalogResourceDestroy(t *testing.T, testProvider *schema.P
hcClient := hostcatalogs.NewClient(md.client)
_, err := hcClient.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed host catalog %q: %v", id, err)
}
+35 -14
View File
@@ -65,13 +65,24 @@ func resourceHostset() *schema.Resource {
}
}
func setFromHostSetResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(HostCatalogIdKey, raw["host_catalog_id"])
d.Set(TypeKey, raw["type"])
d.Set(hostsetHostIdsKey, raw["host_ids"])
func setFromHostSetResponseMap(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(HostCatalogIdKey, raw["host_catalog_id"]); err != nil {
return err
}
if err := d.Set(TypeKey, raw["type"]); err != nil {
return err
}
if err := d.Set(hostsetHostIdsKey, raw["host_ids"]); err != nil {
return err
}
d.SetId(raw["id"].(string))
return nil
}
func resourceHostsetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -130,7 +141,7 @@ func resourceHostsetCreate(ctx context.Context, d *schema.ResourceData, meta int
if hscr == nil {
return diag.Errorf("nil host set after create")
}
raw := hscr.GetResponseMap()
raw := hscr.GetResponse().Map
if hostIds != nil {
hsshr, err := hsClient.SetHosts(ctx, hscr.Item.Id, hscr.Item.Version, hostIds)
@@ -140,10 +151,12 @@ func resourceHostsetCreate(ctx context.Context, d *schema.ResourceData, meta int
if hsshr == nil {
return diag.Errorf("nil host set after setting hosts")
}
raw = hsshr.GetResponseMap()
raw = hsshr.GetResponse().Map
}
setFromHostSetResponseMap(d, raw)
if err := setFromHostSetResponseMap(d, raw); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -154,7 +167,7 @@ func resourceHostsetRead(ctx context.Context, d *schema.ResourceData, meta inter
hsrr, err := hsClient.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.ResponseStatus() == http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -164,7 +177,9 @@ func resourceHostsetRead(ctx context.Context, d *schema.ResourceData, meta inter
return diag.Errorf("host set nil after read")
}
setFromHostSetResponseMap(d, hsrr.GetResponseMap())
if err := setFromHostSetResponseMap(d, hsrr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -206,10 +221,14 @@ func resourceHostsetUpdate(ctx context.Context, d *schema.ResourceData, meta int
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
if err := d.Set(NameKey, name); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
if err := d.Set(DescriptionKey, desc); err != nil {
return diag.FromErr(err)
}
}
// The above call may not actually happen, so we use d.Id() and automatic
@@ -226,7 +245,9 @@ func resourceHostsetUpdate(ctx context.Context, d *schema.ResourceData, meta int
if err != nil {
return diag.Errorf("error updating hosts in host set: %v", err)
}
d.Set(hostsetHostIdsKey, hostIds)
if err := d.Set(hostsetHostIdsKey, hostIds); err != nil {
return diag.FromErr(err)
}
}
return nil
+1 -1
View File
@@ -201,7 +201,7 @@ func testAccCheckHostsetResourceDestroy(t *testing.T, testProvider *schema.Provi
hostsetsClient := hostsets.NewClient(md.client)
_, err := hostsetsClient.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed host set %q: %v", id, apiErr)
}
+1 -1
View File
@@ -171,7 +171,7 @@ func testAccCheckHostResourceDestroy(t *testing.T, testProvider *schema.Provider
hostsClient := hosts.NewClient(md.client)
_, err := hostsClient.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed host %q: %v", id, apiErr)
}
+45 -18
View File
@@ -77,14 +77,27 @@ func resourceRole() *schema.Resource {
}
}
func setFromRoleResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(ScopeIdKey, raw["scope_id"])
d.Set(rolePrincipalIdsKey, raw["principal_ids"])
d.Set(roleGrantStringsKey, raw["grant_strings"])
d.Set(roleGrantScopeIdKey, raw["grant_scope_id"])
func setFromRoleResponseMap(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(rolePrincipalIdsKey, raw["principal_ids"]); err != nil {
return err
}
if err := d.Set(roleGrantStringsKey, raw["grant_strings"]); err != nil {
return err
}
if err := d.Set(roleGrantScopeIdKey, raw["grant_scope_id"]); err != nil {
return err
}
d.SetId(raw["id"].(string))
return nil
}
func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -149,7 +162,7 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
if tcr == nil {
return diag.Errorf("nil role after create")
}
raw := tcr.GetResponseMap()
raw := tcr.GetResponse().Map
if principalIds != nil {
tspr, err := rc.SetPrincipals(ctx, tcr.Item.Id, 0, principalIds, roles.WithAutomaticVersioning(true))
@@ -159,7 +172,7 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
if tspr == nil {
return diag.Errorf("nil role after setting principal IDs")
}
raw = tspr.GetResponseMap()
raw = tspr.GetResponse().Map
}
if grantStrings != nil {
@@ -170,10 +183,12 @@ func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interf
if tsgr == nil {
return diag.Errorf("nil role after setting grant strings")
}
raw = tsgr.GetResponseMap()
raw = tsgr.GetResponse().Map
}
setFromRoleResponseMap(d, raw)
if err := setFromRoleResponseMap(d, raw); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -184,7 +199,7 @@ func resourceRoleRead(ctx context.Context, d *schema.ResourceData, meta interfac
trr, err := rc.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.ResponseStatus() == http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -194,7 +209,9 @@ func resourceRoleRead(ctx context.Context, d *schema.ResourceData, meta interfac
return diag.Errorf("role nil after read")
}
setFromRoleResponseMap(d, trr.GetResponseMap())
if err := setFromRoleResponseMap(d, trr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -247,13 +264,19 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
if err := d.Set(NameKey, name); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
if err := d.Set(DescriptionKey, desc); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(roleGrantScopeIdKey) {
d.Set(roleGrantScopeIdKey, grantScopeId)
if err := d.Set(roleGrantScopeIdKey, grantScopeId); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(roleGrantStringsKey) {
@@ -268,7 +291,9 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
if err != nil {
return diag.Errorf("error updating grant strings on role: %v", err)
}
d.Set(roleGrantStringsKey, grantStrings)
if err := d.Set(roleGrantStringsKey, grantStrings); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(rolePrincipalIdsKey) {
@@ -283,7 +308,9 @@ func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interf
if err != nil {
return diag.Errorf("error updating grant strings on role: %v", err)
}
d.Set(rolePrincipalIdsKey, principalIds)
if err := d.Set(rolePrincipalIdsKey, principalIds); err != nil {
return diag.FromErr(err)
}
}
return nil
+2 -2
View File
@@ -345,7 +345,7 @@ func testAccCheckRoleDestroyed(testProvider *schema.Provider, name string) resou
rolesClient := roles.NewClient(md.client)
_, err := rolesClient.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
errs = append(errs, fmt.Sprintf("Role not destroyed %q: %v", id, apiErr))
}
@@ -546,7 +546,7 @@ func testAccCheckRoleResourceDestroy(t *testing.T, testProvider *schema.Provider
rolesClient := roles.NewClient(md.client)
_, err := rolesClient.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed role %q: %v", id, err)
}
+30 -12
View File
@@ -69,15 +69,25 @@ func resourceScope() *schema.Resource {
}
}
func setFromScopeResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
if d.Id() == "global" {
d.Set(ScopeIdKey, "global")
} else {
d.Set(ScopeIdKey, raw["scope_id"])
func setFromScopeResponseMap(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 d.Id() == "global" {
if err := d.Set(ScopeIdKey, "global"); err != nil {
return err
}
} else {
if err := d.Set(ScopeIdKey, raw["scope_id"]); err != nil {
return err
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceScopeCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -136,7 +146,9 @@ func resourceScopeCreate(ctx context.Context, d *schema.ResourceData, meta inter
return diag.Errorf("scope nil after create")
}
setFromScopeResponseMap(d, scr.GetResponseMap())
if err := setFromScopeResponseMap(d, scr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -147,7 +159,7 @@ func resourceScopeRead(ctx context.Context, d *schema.ResourceData, meta interfa
srr, err := scp.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.ResponseStatus() == http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -157,7 +169,9 @@ func resourceScopeRead(ctx context.Context, d *schema.ResourceData, meta interfa
return diag.Errorf("scope nil after read")
}
setFromScopeResponseMap(d, srr.GetResponseMap())
if err := setFromScopeResponseMap(d, srr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -199,10 +213,14 @@ func resourceScopeUpdate(ctx context.Context, d *schema.ResourceData, meta inter
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
if err := d.Set(NameKey, name); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
if err := d.Set(DescriptionKey, desc); err != nil {
return diag.FromErr(err)
}
}
return nil
+1 -1
View File
@@ -166,7 +166,7 @@ func testAccCheckScopeResourceDestroy(t *testing.T, testProvider *schema.Provide
continue
}
_, err := scp.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed resource %q: %w", id, err)
}
default:
+84 -20
View File
@@ -16,6 +16,7 @@ const (
targetDefaultPortKey = "default_port"
targetSessionMaxSecondsKey = "session_max_seconds"
targetSessionConnectionLimitKey = "session_connection_limit"
targetWorkerFilterKey = "worker_filter"
targetTypeTcp = "tcp"
)
@@ -81,18 +82,40 @@ func resourceTarget() *schema.Resource {
Optional: true,
Computed: true,
},
targetWorkerFilterKey: {
Description: "Boolean expression to filter the workers for this target",
Type: schema.TypeString,
Optional: true,
},
},
}
}
func setFromTargetResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(ScopeIdKey, raw["scope_id"])
d.Set(TypeKey, raw["type"])
d.Set(targetHostSetIdsKey, raw["host_set_ids"])
d.Set(targetSessionMaxSecondsKey, raw["session_max_seconds"])
d.Set(targetSessionConnectionLimitKey, raw["session_connection_limit"])
func setFromTargetResponseMap(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(TypeKey, raw["type"]); err != nil {
return err
}
if err := d.Set(targetHostSetIdsKey, raw["host_set_ids"]); err != nil {
return err
}
if err := d.Set(targetSessionMaxSecondsKey, raw["session_max_seconds"]); err != nil {
return err
}
if err := d.Set(targetSessionConnectionLimitKey, raw["session_connection_limit"]); err != nil {
return err
}
if err := d.Set(targetWorkerFilterKey, raw["worker_filter"]); err != nil {
return err
}
switch raw["type"].(string) {
case targetTypeTcp:
@@ -100,12 +123,15 @@ func setFromTargetResponseMap(d *schema.ResourceData, raw map[string]interface{}
attrs := attrsVal.(map[string]interface{})
if defPort, ok := attrs["default_port"].(json.Number); ok {
defPortInt, _ := defPort.Int64()
d.Set(targetDefaultPortKey, int(defPortInt))
if err := d.Set(targetDefaultPortKey, int(defPortInt)); err != nil {
return err
}
}
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -180,6 +206,12 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte
}
}
workerFilterVal, ok := d.GetOk(targetWorkerFilterKey)
if ok {
workerFilterStr := workerFilterVal.(string)
opts = append(opts, targets.WithWorkerFilter(workerFilterStr))
}
tc := targets.NewClient(md.client)
tcr, err := tc.Create(ctx, typeStr, scopeId, opts...)
@@ -189,17 +221,19 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte
if tcr == nil {
return diag.Errorf("target nil after create")
}
raw := tcr.GetResponseMap()
raw := tcr.GetResponse().Map
if hostSetIds != nil {
tur, err := tc.SetHostSets(ctx, tcr.Item.Id, tcr.Item.Version, hostSetIds)
if err != nil {
return diag.Errorf("error setting host sets on target: %v", err)
}
raw = tur.GetResponseMap()
raw = tur.GetResponse().Map
}
setFromTargetResponseMap(d, raw)
if err := setFromTargetResponseMap(d, raw); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -210,7 +244,7 @@ func resourceTargetRead(ctx context.Context, d *schema.ResourceData, meta interf
trr, err := tc.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.ResponseStatus() == http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -220,7 +254,9 @@ func resourceTargetRead(ctx context.Context, d *schema.ResourceData, meta interf
return diag.Errorf("target nil after read")
}
setFromTargetResponseMap(d, trr.GetResponseMap())
if err := setFromTargetResponseMap(d, trr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -295,6 +331,17 @@ func resourceTargetUpdate(ctx context.Context, d *schema.ResourceData, meta inte
}
}
var workerFilter *string
if d.HasChange(targetWorkerFilterKey) {
opts = append(opts, targets.DefaultWorkerFilter())
workerFilterVal, ok := d.GetOk(targetWorkerFilterKey)
if ok {
workerFilterStr := workerFilterVal.(string)
workerFilter = &workerFilterStr
opts = append(opts, targets.WithWorkerFilter(workerFilterStr))
}
}
if len(opts) > 0 {
opts = append(opts, targets.WithAutomaticVersioning(true))
_, err := tc.Update(ctx, d.Id(), 0, opts...)
@@ -304,19 +351,34 @@ func resourceTargetUpdate(ctx context.Context, d *schema.ResourceData, meta inte
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
if err := d.Set(NameKey, name); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
if err := d.Set(DescriptionKey, desc); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(targetDefaultPortKey) {
d.Set(targetDefaultPortKey, defaultPort)
if err := d.Set(targetDefaultPortKey, defaultPort); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(targetSessionMaxSecondsKey) {
d.Set(targetSessionMaxSecondsKey, sessionMaxSeconds)
if err := d.Set(targetSessionMaxSecondsKey, sessionMaxSeconds); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(targetSessionConnectionLimitKey) {
d.Set(targetSessionConnectionLimitKey, sessionConnectionLimit)
if err := d.Set(targetSessionConnectionLimitKey, sessionConnectionLimit); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(targetWorkerFilterKey) {
if err := d.Set(targetWorkerFilterKey, workerFilter); err != nil {
return diag.FromErr(err)
}
}
// The above call may not actually happen, so we use d.Id() and automatic
@@ -333,7 +395,9 @@ func resourceTargetUpdate(ctx context.Context, d *schema.ResourceData, meta inte
if err != nil {
return diag.Errorf("error updating host sets in target: %v", err)
}
d.Set(targetHostSetIdsKey, hostSetIds)
if err := d.Set(targetHostSetIdsKey, hostSetIds); err != nil {
return diag.FromErr(err)
}
}
return nil
+5 -1
View File
@@ -66,6 +66,7 @@ resource "boundary_target" "foo" {
depends_on = [boundary_role.proj1_admin]
session_max_seconds = 6000
session_connection_limit = 6
worker_filter = "type == \"foo\""
}`, fooTargetDescription)
fooTargetUpdate = fmt.Sprintf(`
@@ -81,6 +82,7 @@ resource "boundary_target" "foo" {
depends_on = [boundary_role.proj1_admin]
session_max_seconds = 7000
session_connection_limit = 7
worker_filter = "type == \"bar\""
}`, fooTargetDescriptionUpdate)
)
@@ -105,6 +107,7 @@ func TestAccTarget(t *testing.T) {
resource.TestCheckResourceAttr("boundary_target.foo", targetDefaultPortKey, "22"),
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionMaxSecondsKey, "6000"),
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "6"),
resource.TestCheckResourceAttr("boundary_target.foo", targetWorkerFilterKey, `type == "foo"`),
),
},
importStep("boundary_target.foo"),
@@ -117,6 +120,7 @@ func TestAccTarget(t *testing.T) {
resource.TestCheckResourceAttr("boundary_target.foo", targetDefaultPortKey, "80"),
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionMaxSecondsKey, "7000"),
resource.TestCheckResourceAttr("boundary_target.foo", targetSessionConnectionLimitKey, "7"),
resource.TestCheckResourceAttr("boundary_target.foo", targetWorkerFilterKey, `type == "bar"`),
),
},
importStep("boundary_target.foo"),
@@ -220,7 +224,7 @@ func testAccCheckTargetResourceDestroy(t *testing.T, testProvider *schema.Provid
id := rs.Primary.ID
_, err := tgts.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed target %q: %v", id, apiErr)
}
+32 -13
View File
@@ -56,12 +56,21 @@ func resourceUser() *schema.Resource {
}
}
func setFromUserResponseMap(d *schema.ResourceData, raw map[string]interface{}) {
d.Set(NameKey, raw["name"])
d.Set(DescriptionKey, raw["description"])
d.Set(ScopeIdKey, raw["scope_id"])
d.Set(userAccountIDsKey, raw["account_ids"])
func setFromUserResponseMap(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(userAccountIDsKey, raw["account_ids"]); err != nil {
return err
}
d.SetId(raw["id"].(string))
return nil
}
func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -97,7 +106,7 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf
if ucr == nil {
return diag.Errorf("user nil after create")
}
raw := ucr.GetResponseMap()
raw := ucr.GetResponse().Map
if val, ok := d.GetOk(userAccountIDsKey); ok {
list := val.(*schema.Set).List()
@@ -112,10 +121,12 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf
if usrac == nil {
return diag.Errorf("user nil after setting accounts")
}
raw = usrac.GetResponseMap()
raw = usrac.GetResponse().Map
}
setFromUserResponseMap(d, raw)
if err := setFromUserResponseMap(d, raw); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -126,7 +137,7 @@ func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta interfac
urr, err := usrs.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.ResponseStatus() == http.StatusNotFound{
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
@@ -136,7 +147,9 @@ func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta interfac
return diag.Errorf("user nil after read")
}
setFromUserResponseMap(d, urr.GetResponseMap())
if err := setFromUserResponseMap(d, urr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
@@ -178,10 +191,14 @@ func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interf
}
if d.HasChange(NameKey) {
d.Set(NameKey, name)
if err := d.Set(NameKey, name); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(DescriptionKey) {
d.Set(DescriptionKey, desc)
if err := d.Set(DescriptionKey, desc); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange(userAccountIDsKey) {
@@ -197,7 +214,9 @@ func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interf
if err != nil {
return diag.Errorf("error updating accounts on user: %v", err)
}
d.Set(userAccountIDsKey, accountIds)
if err := d.Set(userAccountIDsKey, accountIds); err != nil {
return diag.FromErr(err)
}
}
return nil
+1 -1
View File
@@ -232,7 +232,7 @@ func testAccCheckUserResourceDestroy(t *testing.T, testProvider *schema.Provider
usrs := users.NewClient(md.client)
_, err := usrs.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.ResponseStatus() != http.StatusNotFound {
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed user %q: %v", id, err)
}
+10
View File
@@ -5,6 +5,16 @@ import (
"github.com/hashicorp/terraform-provider-boundary/internal/provider"
)
// Run "go generate" to format example terraform files and generate the docs for the registry/website
// If you do not have terraform installed, you can remove the formatting command, but its suggested to
// ensure the documentation is formatted properly.
//go:generate terraform fmt -recursive ./examples/
// Run the docs generation tool, check its repository for more information on how it works and how docs
// can be customized.
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
func main() {
plugin.Serve(&plugin.ServeOpts{ProviderFunc: provider.New})
}