feat: Add support for vault ssh certificate credential libraries

This commit is contained in:
Timothy Messier committed 2023-02-15 10:54:21 -05:00
1 parent bd9cc97362
commit 55d8778edf
6 files changed
+874 -27

No files matched your search

@@ -0,0 +1,111 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_credential_library_vault_ssh_certificate Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The credential library for Vault resource allows you to configure a Boundary credential library for Vault.
---
# boundary_credential_library_vault_ssh_certificate (Resource)
The credential library for Vault resource allows you to configure a Boundary credential library for Vault.
## Example Usage
```terraform
resource "boundary_scope" "org" {
name = "organization_one"
description = "My first scope!"
scope_id = "global"
auto_create_admin_role = true
auto_create_default_role = true
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_credential_store_vault" "foo" {
name = "foo"
description = "My first Vault credential store!"
address = "http://127.0.0.1:8200" # change to Vault address
token = "s.0ufRo6XEGU2jOqnIr7OlFYP5" # change to valid Vault token
scope_id = boundary_scope.project.id
}
resource "boundary_credential_library_vault_ssh_certificate" "foo" {
name = "foo"
description = "My first Vault SSH certificate credential library!"
credential_store_id = boundary_credential_store_vault.foo.id
path = "ssh/sign/foo" # change to correct Vault endpoint and role
username = "foo" # change to valid username
}
resource "boundary_credential_library_vault_ssh_certificate" "bar" {
name = "bar"
description = "My second Vault SSH certificate credential library!"
credential_store_id = boundary_credential_store_vault.foo.id
path = "ssh/sign/foo" # change to correct Vault endpoint and role
username = "foo"
key_type = "ecdsa"
key_bits = 384
extensions = {
permit-pty = ""
}
}
resource "boundary_credential_library_vault_ssh_certificate" "baz" {
name = "baz"
description = "vault "
credential_store_id = boundary_credential_store_vault.foo.id
path = "ssh/issue/foo" # change to correct Vault endpoint and role
username = "foo"
key_type = "rsa"
key_bits = 4096
extensions = {
permit-pty = ""
permit-X11-forwarding = ""
}
critical_options = {
force-command = "/bin/some_script"
}
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `credential_store_id` (String) The ID of the credential store that this library belongs to.
- `path` (String) The path in Vault to request credentials from.
- `username` (String) The username to use with the certificate returned by the library.
### Optional
- `critical_options` (Map of String) Specifies a map of the critical options that the certificate should be signed for.
- `description` (String) The Vault credential library description.
- `extensions` (Map of String) Specifies a map of the extensions that the certificate should be signed for.
- `key_bits` (Number) Specifies the number of bits to use for the generated keys.
- `key_id` (String) Specifies the key id a certificate should have.
- `key_type` (String) Specifies the desired key type; must be ed25519, ecdsa, or rsa.
- `name` (String) The Vault credential library name. Defaults to the resource name.
- `ttl` (String) Specifies the requested time to live for a certificate returned from the library.
### Read-Only
- `id` (String) The ID of the Vault credential library.
## Import
Import is supported using the following syntax:
```shell
terraform import boundary_credential_library_vault_ssh_certificate.foo <my-id>
```
@@ -0,0 +1 @@
terraform import boundary_credential_library_vault_ssh_certificate.foo <my-id>
@@ -0,0 +1,63 @@
resource "boundary_scope" "org" {
name = "organization_one"
description = "My first scope!"
scope_id = "global"
auto_create_admin_role = true
auto_create_default_role = true
}
resource "boundary_scope" "project" {
name = "project_one"
description = "My first scope!"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
}
resource "boundary_credential_store_vault" "foo" {
name = "foo"
description = "My first Vault credential store!"
address = "http://127.0.0.1:8200" # change to Vault address
token = "s.0ufRo6XEGU2jOqnIr7OlFYP5" # change to valid Vault token
scope_id = boundary_scope.project.id
}
resource "boundary_credential_library_vault_ssh_certificate" "foo" {
name = "foo"
description = "My first Vault SSH certificate credential library!"
credential_store_id = boundary_credential_store_vault.foo.id
path = "ssh/sign/foo" # change to correct Vault endpoint and role
username = "foo" # change to valid username
}
resource "boundary_credential_library_vault_ssh_certificate" "bar" {
name = "bar"
description = "My second Vault SSH certificate credential library!"
credential_store_id = boundary_credential_store_vault.foo.id
path = "ssh/sign/foo" # change to correct Vault endpoint and role
username = "foo"
key_type = "ecdsa"
key_bits = 384
extensions = {
permit-pty = ""
}
}
resource "boundary_credential_library_vault_ssh_certificate" "baz" {
name = "baz"
description = "vault "
credential_store_id = boundary_credential_store_vault.foo.id
path = "ssh/issue/foo" # change to correct Vault endpoint and role
username = "foo"
key_type = "rsa"
key_bits = 4096
extensions = {
permit-pty = ""
permit-X11-forwarding = ""
}
critical_options = {
force-command = "/bin/some_script"
}
}
+28 -27
View File
@@ -71,33 +71,34 @@ func New() *schema.Provider {
},
},
ResourcesMap: map[string]*schema.Resource{
"boundary_account": resourceAccount(),
"boundary_account_password": resourceAccountPassword(),
"boundary_account_oidc": resourceAccountOidc(),
"boundary_auth_method": resourceAuthMethod(),
"boundary_auth_method_password": resourceAuthMethodPassword(),
"boundary_auth_method_oidc": resourceAuthMethodOidc(),
"boundary_credential_library_vault": resourceCredentialLibraryVault(),
"boundary_credential_store_vault": resourceCredentialStoreVault(),
"boundary_credential_store_static": resourceCredentialStoreStatic(),
"boundary_credential_username_password": resourceCredentialUsernamePassword(),
"boundary_credential_ssh_private_key": resourceCredentialSshPrivateKey(),
"boundary_credential_json": resourceCredentialJson(),
"boundary_managed_group": resourceManagedGroup(),
"boundary_group": resourceGroup(),
"boundary_host": resourceHost(),
"boundary_host_static": resourceHostStatic(),
"boundary_host_catalog": resourceHostCatalog(),
"boundary_host_catalog_static": resourceHostCatalogStatic(),
"boundary_host_catalog_plugin": resourceHostCatalogPlugin(),
"boundary_host_set": resourceHostSet(),
"boundary_host_set_static": resourceHostSetStatic(),
"boundary_host_set_plugin": resourceHostSetPlugin(),
"boundary_role": resourceRole(),
"boundary_scope": resourceScope(),
"boundary_target": resourceTarget(),
"boundary_user": resourceUser(),
"boundary_worker": resourceWorker(),
"boundary_account": resourceAccount(),
"boundary_account_password": resourceAccountPassword(),
"boundary_account_oidc": resourceAccountOidc(),
"boundary_auth_method": resourceAuthMethod(),
"boundary_auth_method_password": resourceAuthMethodPassword(),
"boundary_auth_method_oidc": resourceAuthMethodOidc(),
"boundary_credential_library_vault": resourceCredentialLibraryVault(),
"boundary_credential_library_vault_ssh_certificate": resourceCredentialLibraryVaultSshCertificate(),
"boundary_credential_store_vault": resourceCredentialStoreVault(),
"boundary_credential_store_static": resourceCredentialStoreStatic(),
"boundary_credential_username_password": resourceCredentialUsernamePassword(),
"boundary_credential_ssh_private_key": resourceCredentialSshPrivateKey(),
"boundary_credential_json": resourceCredentialJson(),
"boundary_managed_group": resourceManagedGroup(),
"boundary_group": resourceGroup(),
"boundary_host": resourceHost(),
"boundary_host_static": resourceHostStatic(),
"boundary_host_catalog": resourceHostCatalog(),
"boundary_host_catalog_static": resourceHostCatalogStatic(),
"boundary_host_catalog_plugin": resourceHostCatalogPlugin(),
"boundary_host_set": resourceHostSet(),
"boundary_host_set_static": resourceHostSetStatic(),
"boundary_host_set_plugin": resourceHostSetPlugin(),
"boundary_role": resourceRole(),
"boundary_scope": resourceScope(),
"boundary_target": resourceTarget(),
"boundary_user": resourceUser(),
"boundary_worker": resourceWorker(),
},
}
@@ -0,0 +1,404 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"net/http"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentiallibraries"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
credentialLibraryVaultSshCertificateType = "vault-ssh-certificate"
credentialLibraryVaultSshCertificatePathKey = "path"
credentialLibraryVaultSshCertificateUsernameKey = "username"
credentialLibraryVaultSshCertificateKeyTypeKey = "key_type"
credentialLibraryVaultSshCertificateKeyBitsKey = "key_bits"
credentialLibraryVaultSshCertificateTtlKey = "ttl"
credentialLibraryVaultSshCertificateKeyIdKey = "key_id"
credentialLibraryVaultSshCertificateCriticalOptionsKey = "critical_options"
credentialLibraryVaultSshCertificateExtensionsKey = "extensions"
)
var libraryVaultSshCertificateAttrs = []string{
credentialLibraryVaultSshCertificatePathKey,
credentialLibraryVaultSshCertificateUsernameKey,
credentialLibraryVaultSshCertificateKeyTypeKey,
credentialLibraryVaultSshCertificateKeyBitsKey,
credentialLibraryVaultSshCertificateTtlKey,
credentialLibraryVaultSshCertificateKeyIdKey,
credentialLibraryVaultSshCertificateCriticalOptionsKey,
credentialLibraryVaultSshCertificateExtensionsKey,
}
func resourceCredentialLibraryVaultSshCertificate() *schema.Resource {
return &schema.Resource{
Description: "The credential library for Vault resource allows you to configure a Boundary credential library for Vault.",
CreateContext: resourceCredentialLibraryCreateVaultSshCertificate,
ReadContext: resourceCredentialLibraryReadVaultSshCertificate,
UpdateContext: resourceCredentialLibraryUpdateVaultSshCertificate,
DeleteContext: resourceCredentialLibraryDeleteVaultSshCertificate,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
IDKey: {
Description: "The ID of the Vault credential library.",
Type: schema.TypeString,
Computed: true,
},
NameKey: {
Description: "The Vault credential library name. Defaults to the resource name.",
Type: schema.TypeString,
Optional: true,
},
DescriptionKey: {
Description: "The Vault credential library description.",
Type: schema.TypeString,
Optional: true,
},
credentialStoreIdKey: {
Description: "The ID of the credential store that this library belongs to.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
credentialLibraryVaultSshCertificatePathKey: {
Description: "The path in Vault to request credentials from.",
Type: schema.TypeString,
Required: true,
},
credentialLibraryVaultSshCertificateUsernameKey: {
Description: "The username to use with the certificate returned by the library.",
Type: schema.TypeString,
Required: true,
},
credentialLibraryVaultSshCertificateKeyTypeKey: {
Description: "Specifies the desired key type; must be ed25519, ecdsa, or rsa.",
Type: schema.TypeString,
Optional: true,
},
credentialLibraryVaultSshCertificateKeyBitsKey: {
Description: "Specifies the number of bits to use for the generated keys.",
Type: schema.TypeInt,
Optional: true,
},
credentialLibraryVaultSshCertificateTtlKey: {
Description: "Specifies the requested time to live for a certificate returned from the library.",
Type: schema.TypeString,
Optional: true,
},
credentialLibraryVaultSshCertificateKeyIdKey: {
Description: "Specifies the key id a certificate should have.",
Type: schema.TypeString,
Optional: true,
},
credentialLibraryVaultSshCertificateCriticalOptionsKey: {
Description: "Specifies a map of the critical options that the certificate should be signed for.",
Type: schema.TypeMap,
Optional: true,
},
credentialLibraryVaultSshCertificateExtensionsKey: {
Description: "Specifies a map of the extensions that the certificate should be signed for.",
Type: schema.TypeMap,
Optional: true,
},
},
}
}
func setFromVaultSshCertificateCredentialLibraryResponseMap(d *schema.ResourceData, raw map[string]interface{}) error {
if err := d.Set(NameKey, raw[NameKey]); err != nil {
return err
}
if err := d.Set(DescriptionKey, raw[DescriptionKey]); err != nil {
return err
}
if err := d.Set(credentialStoreIdKey, raw[credentialStoreIdKey]); err != nil {
return err
}
if attrsVal, ok := raw["attributes"]; ok {
attrs := attrsVal.(map[string]interface{})
for _, v := range libraryVaultSshCertificateAttrs {
if err := d.Set(v, attrs[v]); err != nil {
return err
}
}
}
d.SetId(raw["id"].(string))
return nil
}
func resourceCredentialLibraryCreateVaultSshCertificate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
var opts []credentiallibraries.Option
if v, ok := d.GetOk(NameKey); ok {
opts = append(opts, credentiallibraries.WithName(v.(string)))
}
if v, ok := d.GetOk(DescriptionKey); ok {
opts = append(opts, credentiallibraries.WithDescription(v.(string)))
}
if v, ok := d.GetOk(credentialLibraryVaultSshCertificatePathKey); ok {
opts = append(opts, credentiallibraries.WithVaultCredentialLibraryPath(v.(string)))
}
if v, ok := d.GetOk(credentialLibraryVaultSshCertificateUsernameKey); ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryUsername(v.(string)))
}
if v, ok := d.GetOk(credentialLibraryVaultSshCertificateKeyTypeKey); ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryKeyType(v.(string)))
}
if v, ok := d.GetOk(credentialLibraryVaultSshCertificateKeyBitsKey); ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryKeyBits(uint32(v.(int))))
}
if v, ok := d.GetOk(credentialLibraryVaultSshCertificateTtlKey); ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryTtl(v.(string)))
}
if v, ok := d.GetOk(credentialLibraryVaultSshCertificateKeyIdKey); ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryKeyId(v.(string)))
}
if v, ok := d.GetOk(credentialLibraryVaultSshCertificateCriticalOptionsKey); ok {
if vv, ok := v.(map[string]any); ok {
co := make(map[string]string, len(vv))
for k, vvv := range vv {
co[k] = vvv.(string)
}
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryCriticalOptions(co))
}
}
if v, ok := d.GetOk(credentialLibraryVaultSshCertificateExtensionsKey); ok {
if vv, ok := v.(map[string]any); ok {
e := make(map[string]string, len(vv))
for k, vvv := range vv {
e[k] = vvv.(string)
}
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryExtensions(e))
}
}
var credentialStoreId string
cid, ok := d.GetOk(credentialStoreIdKey)
if ok {
credentialStoreId = cid.(string)
} else {
return diag.Errorf("no credential store ID is set")
}
client := credentiallibraries.NewClient(md.client)
cr, err := client.Create(ctx, credentialLibraryVaultSshCertificateType, credentialStoreId, opts...)
if err != nil {
return diag.Errorf("error creating credential library: %v", err)
}
if cr == nil {
return diag.Errorf("nil credential library after create")
}
if err := setFromVaultSshCertificateCredentialLibraryResponseMap(d, cr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceCredentialLibraryReadVaultSshCertificate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentiallibraries.NewClient(md.client)
cr, err := client.Read(ctx, d.Id())
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
d.SetId("")
return nil
}
return diag.Errorf("error reading credential library: %v", err)
}
if cr == nil {
return diag.Errorf("credential library nil after read")
}
if err := setFromVaultSshCertificateCredentialLibraryResponseMap(d, cr.GetResponse().Map); err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceCredentialLibraryUpdateVaultSshCertificate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentiallibraries.NewClient(md.client)
var opts []credentiallibraries.Option
if d.HasChange(NameKey) {
opts = append(opts, credentiallibraries.DefaultName())
nameVal, ok := d.GetOk(NameKey)
if ok {
opts = append(opts, credentiallibraries.WithName(nameVal.(string)))
}
}
if d.HasChange(DescriptionKey) {
opts = append(opts, credentiallibraries.DefaultDescription())
descVal, ok := d.GetOk(DescriptionKey)
if ok {
opts = append(opts, credentiallibraries.WithDescription(descVal.(string)))
}
}
if d.HasChange(credentialLibraryVaultSshCertificatePathKey) {
v, ok := d.GetOk(credentialLibraryVaultSshCertificatePathKey)
if ok {
opts = append(opts, credentiallibraries.WithVaultCredentialLibraryPath(v.(string)))
}
}
if d.HasChange(credentialLibraryVaultSshCertificateUsernameKey) {
v, ok := d.GetOk(credentialLibraryVaultSshCertificateUsernameKey)
if ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryUsername(v.(string)))
}
}
if d.HasChange(credentialLibraryVaultSshCertificateKeyTypeKey) {
v, ok := d.GetOk(credentialLibraryVaultSshCertificateKeyTypeKey)
if ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryKeyType(v.(string)))
}
}
if d.HasChange(credentialLibraryVaultSshCertificateKeyBitsKey) {
v, ok := d.GetOk(credentialLibraryVaultSshCertificateKeyBitsKey)
if ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryKeyBits(uint32(v.(int))))
}
}
if d.HasChange(credentialLibraryVaultSshCertificateTtlKey) {
v, ok := d.GetOk(credentialLibraryVaultSshCertificateTtlKey)
if ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryTtl(v.(string)))
}
}
if d.HasChange(credentialLibraryVaultSshCertificateKeyIdKey) {
v, ok := d.GetOk(credentialLibraryVaultSshCertificateKeyIdKey)
if ok {
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryKeyId(v.(string)))
}
}
if d.HasChange(credentialLibraryVaultSshCertificateCriticalOptionsKey) {
var oldCriticalOptions, newCriticalOptions map[string]interface{}
old, new := d.GetChange(credentialLibraryVaultSshCertificateCriticalOptionsKey)
if old == nil {
old = map[string]interface{}{}
}
oldCriticalOptions = old.(map[string]interface{})
if new == nil {
new = map[string]interface{}{}
}
newCriticalOptions = new.(map[string]interface{})
newAttrs := []string{}
for k := range newCriticalOptions {
newAttrs = append(newAttrs, k)
}
for oldAttr := range oldCriticalOptions {
isRemoved := true
for _, newAttr := range newAttrs {
if oldAttr == newAttr {
isRemoved = false
break
}
}
if isRemoved {
newCriticalOptions[oldAttr] = nil
}
}
co := make(map[string]string, 0)
for k, v := range newCriticalOptions {
if v != nil {
co[k] = v.(string)
}
}
if len(co) == 0 {
co = nil
}
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryCriticalOptions(co))
}
if d.HasChange(credentialLibraryVaultSshCertificateExtensionsKey) {
var oldExtensions, newExtensions map[string]interface{}
old, new := d.GetChange(credentialLibraryVaultSshCertificateExtensionsKey)
if old == nil {
old = map[string]interface{}{}
}
oldExtensions = old.(map[string]interface{})
if new == nil {
new = map[string]interface{}{}
}
newExtensions = new.(map[string]interface{})
newAttrs := []string{}
for k := range newExtensions {
newAttrs = append(newAttrs, k)
}
for oldAttr := range oldExtensions {
isRemoved := true
for _, newAttr := range newAttrs {
if oldAttr == newAttr {
isRemoved = false
break
}
}
if isRemoved {
newExtensions[oldAttr] = nil
}
}
e := make(map[string]string, 0)
for k, v := range newExtensions {
if v != nil {
e[k] = v.(string)
}
}
if len(e) == 0 {
e = nil
}
opts = append(opts, credentiallibraries.WithVaultSSHCertificateCredentialLibraryExtensions(e))
}
if len(opts) > 0 {
opts = append(opts, credentiallibraries.WithAutomaticVersioning(true))
aur, err := client.Update(ctx, d.Id(), 0, opts...)
if err != nil {
return diag.Errorf("error updating credential library: %v", err)
}
if err := setFromVaultSshCertificateCredentialLibraryResponseMap(d, aur.GetResponse().Map); err != nil {
return diag.Errorf("error setting credential library from response: %v", err)
}
}
return nil
}
func resourceCredentialLibraryDeleteVaultSshCertificate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
client := credentiallibraries.NewClient(md.client)
_, err := client.Delete(ctx, d.Id())
if err != nil {
return diag.Errorf("error deleting credential library: %v", err)
}
return nil
}
@@ -0,0 +1,267 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"fmt"
"net/http"
"strconv"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentiallibraries"
"github.com/hashicorp/boundary/testing/controller"
"github.com/hashicorp/boundary/testing/vault"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)
const (
vaultSshCertCredResc = "boundary_credential_library_vault_ssh_certificate.example"
vaultSshCertCredExtCOResc = "boundary_credential_library_vault_ssh_certificate.ext_co_example"
vaultSshCertCredLibName = "foo"
vaultSshCertCredLibDesc = "the foo"
vaultSshCertCredLibPath = "/ssh/sign/foo"
vaultSshCertCredUsername = "bar"
vaultSshCertCredLibStringUpdate = "_random"
vaultSshCertCredKeyType = "ecdsa"
vaultSshCertCredKeyBits = 256
)
var vaultSshCertCredLibResource = fmt.Sprintf(`
resource "boundary_credential_library_vault_ssh_certificate" "example" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
username = "%s"
key_type = "ed25519"
}`, vaultSshCertCredLibName,
vaultSshCertCredLibDesc,
vaultSshCertCredLibPath,
vaultSshCertCredUsername)
var vaultSshCertCredLibResourceUpdate = fmt.Sprintf(`
resource "boundary_credential_library_vault_ssh_certificate" "example" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
username = "%s"
key_type = "%s"
key_bits = %d
}`, vaultSshCertCredLibName+vaultSshCertCredLibStringUpdate,
vaultSshCertCredLibDesc+vaultSshCertCredLibStringUpdate,
vaultSshCertCredLibPath+vaultSshCertCredLibStringUpdate,
vaultSshCertCredUsername+vaultSshCertCredLibStringUpdate,
vaultSshCertCredKeyType,
vaultSshCertCredKeyBits)
var vaultSshCertCredLibResourceExtensionsCriticalOpts = fmt.Sprintf(`
resource "boundary_credential_library_vault_ssh_certificate" "ext_co_example" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
username = "%s"
key_type = "ed25519"
extensions = {
permit-pty = ""
}
critical_options = {
force-command = "/bin/foo"
}
}`, vaultSshCertCredLibName,
vaultSshCertCredLibDesc,
vaultSshCertCredLibPath,
vaultSshCertCredUsername)
var vaultSshCertCredLibResourceExtensionsCriticalOptsUpdate = fmt.Sprintf(`
resource "boundary_credential_library_vault_ssh_certificate" "ext_co_example" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
username = "%s"
key_type = "ed25519"
extensions = {
permit-pty = ""
permit-X11-forwarding = ""
}
}`, vaultSshCertCredLibName,
vaultSshCertCredLibDesc,
vaultSshCertCredLibPath,
vaultSshCertCredUsername)
var vaultSshCertCredLibResourceExtensionsCriticalOptsUpdate2 = fmt.Sprintf(`
resource "boundary_credential_library_vault_ssh_certificate" "ext_co_example" {
name = "%s"
description = "%s"
credential_store_id = boundary_credential_store_vault.example.id
path = "%s"
username = "%s"
key_type = "ed25519"
extensions = {
permit-pty = ""
}
critical_options = {
force-command = "/bin/foo"
source-address = "10.10.0.0/16"
}
}`, vaultSshCertCredLibName,
vaultSshCertCredLibDesc,
vaultSshCertCredLibPath,
vaultSshCertCredUsername)
func TestAccCredentialLibraryVaultSshCertificate(t *testing.T) {
tc := controller.NewTestController(t, tcConfig...)
defer tc.Shutdown()
url := tc.ApiAddrs()[0]
vc := vault.NewTestVaultServer(t)
_, token := vc.CreateToken(t)
credStoreRes := vaultCredStoreResource(vc,
vaultCredStoreName,
vaultCredStoreDesc,
vaultCredStoreNamespace,
"www.original.com",
token,
true)
var provider *schema.Provider
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProviderFactories: providerFactories(&provider),
CheckDestroy: testAccCheckCredentialLibraryVaultSshCertificateResourceDestroy(t, provider),
Steps: []resource.TestStep{
{
// create
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultSshCertCredLibResource),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultSshCertCredResc, NameKey, vaultSshCertCredLibName),
resource.TestCheckResourceAttr(vaultSshCertCredResc, DescriptionKey, vaultSshCertCredLibDesc),
resource.TestCheckResourceAttr(vaultSshCertCredResc, credentialLibraryVaultPathKey, vaultSshCertCredLibPath),
resource.TestCheckResourceAttr(vaultSshCertCredResc, credentialLibraryVaultSshCertificateUsernameKey, vaultSshCertCredUsername),
testAccCheckCredentialLibraryVaultSshCertificateResourceExists(provider, vaultSshCertCredResc),
),
},
importStep(vaultSshCertCredResc),
{
// update
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultSshCertCredLibResourceUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultSshCertCredResc, NameKey, vaultSshCertCredLibName+vaultSshCertCredLibStringUpdate),
resource.TestCheckResourceAttr(vaultSshCertCredResc, DescriptionKey, vaultSshCertCredLibDesc+vaultSshCertCredLibStringUpdate),
resource.TestCheckResourceAttr(vaultSshCertCredResc, credentialLibraryVaultPathKey, vaultSshCertCredLibPath+vaultSshCertCredLibStringUpdate),
resource.TestCheckResourceAttr(vaultSshCertCredResc, credentialLibraryVaultSshCertificateUsernameKey, vaultSshCertCredUsername+vaultSshCertCredLibStringUpdate),
resource.TestCheckResourceAttr(vaultSshCertCredResc, credentialLibraryVaultSshCertificateKeyTypeKey, vaultSshCertCredKeyType),
resource.TestCheckResourceAttr(vaultSshCertCredResc, credentialLibraryVaultSshCertificateKeyBitsKey, strconv.Itoa(vaultSshCertCredKeyBits)),
testAccCheckCredentialLibraryVaultSshCertificateResourceExists(provider, vaultSshCertCredResc),
),
},
importStep(vaultSshCertCredResc),
{
// create with extensions and critical options
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultSshCertCredLibResourceExtensionsCriticalOpts),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, NameKey, vaultSshCertCredLibName),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, DescriptionKey, vaultSshCertCredLibDesc),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultPathKey, vaultSshCertCredLibPath),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateUsernameKey, vaultSshCertCredUsername),
testAccCheckCredentialLibraryVaultSshCertificateResourceExists(provider, vaultSshCertCredExtCOResc),
),
},
importStep(vaultSshCertCredExtCOResc),
{
// update with extensions and remove critical options
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultSshCertCredLibResourceExtensionsCriticalOptsUpdate),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, NameKey, vaultSshCertCredLibName),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, DescriptionKey, vaultSshCertCredLibDesc),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultPathKey, vaultSshCertCredLibPath),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateUsernameKey, vaultSshCertCredUsername),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateCriticalOptionsKey+".%", "0"),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateExtensionsKey+".%", "2"),
testAccCheckCredentialLibraryVaultSshCertificateResourceExists(provider, vaultSshCertCredExtCOResc),
),
},
importStep(vaultSshCertCredExtCOResc),
{
// update with extensions and remove critical options
Config: testConfig(url, fooOrg, firstProjectFoo, credStoreRes, vaultSshCertCredLibResourceExtensionsCriticalOptsUpdate2),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, NameKey, vaultSshCertCredLibName),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, DescriptionKey, vaultSshCertCredLibDesc),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultPathKey, vaultSshCertCredLibPath),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateUsernameKey, vaultSshCertCredUsername),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateCriticalOptionsKey+".%", "2"),
resource.TestCheckResourceAttr(vaultSshCertCredExtCOResc, credentialLibraryVaultSshCertificateExtensionsKey+".%", "1"),
testAccCheckCredentialLibraryVaultSshCertificateResourceExists(provider, vaultSshCertCredExtCOResc),
),
},
importStep(vaultSshCertCredExtCOResc),
},
})
}
func testAccCheckCredentialLibraryVaultSshCertificateResourceExists(testProvider *schema.Provider, name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("not found: %s", name)
}
id := rs.Primary.ID
if id == "" {
return fmt.Errorf("no ID is set")
}
md := testProvider.Meta().(*metaData)
c := credentiallibraries.NewClient(md.client)
if _, err := c.Read(context.Background(), id); err != nil {
return fmt.Errorf("got an error reading %q: %w", id, err)
}
return nil
}
}
func testAccCheckCredentialLibraryVaultSshCertificateResourceDestroy(t *testing.T, testProvider *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
if testProvider.Meta() == nil {
t.Fatal("got nil provider metadata")
}
md := testProvider.Meta().(*metaData)
for _, rs := range s.RootModule().Resources {
switch rs.Type {
case "boundary_credential_library_vault":
id := rs.Primary.ID
c := credentiallibraries.NewClient(md.client)
_, err := c.Read(context.Background(), id)
if apiErr := api.AsServerError(err); apiErr == nil || apiErr.Response().StatusCode() != http.StatusNotFound {
return fmt.Errorf("didn't get a 404 when reading destroyed vault credential library %q: %v", id, err)
}
default:
continue
}
}
return nil
}
}