diff --git a/vault/provider.go b/vault/provider.go index ec5a34df8..76ae0f0e7 100644 --- a/vault/provider.go +++ b/vault/provider.go @@ -219,6 +219,10 @@ var ( } ResourceRegistry = map[string]*Description{ + "vault_alicloud_auth_backend_role": { + Resource: alicloudAuthBackendRoleResource(), + PathInventory: []string{"/auth/alicloud/role/{name}"}, + }, "vault_approle_auth_backend_login": { Resource: approleAuthBackendLoginResource(), PathInventory: []string{"/auth/approle/login"}, diff --git a/vault/resource_alicloud_auth_backend_role.go b/vault/resource_alicloud_auth_backend_role.go new file mode 100644 index 000000000..670e22dc9 --- /dev/null +++ b/vault/resource_alicloud_auth_backend_role.go @@ -0,0 +1,191 @@ +package vault + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/vault/api" +) + +func alicloudAuthBackendRoleResource() *schema.Resource { + fields := map[string]*schema.Schema{ + "role": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Name of the role. Must correspond with the name of the role reflected in the arn.", + }, + "arn": { + Type: schema.TypeString, + Required: true, + Description: "The role's arn.", + }, + "backend": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "alicloud", + Description: "Auth backend.", + StateFunc: func(v interface{}) string { + return strings.Trim(v.(string), "/") + }, + }, + } + + addTokenFields(fields, &addTokenFieldsConfig{}) + + return &schema.Resource{ + Create: alicloudAuthBackendRoleCreate, + Update: alicloudAuthBackendRoleUpdate, + Read: alicloudAuthBackendRoleRead, + Delete: alicloudAuthBackendRoleDelete, + Exists: alicloudAuthBackendRoleExists, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: fields, + } +} + +func alicloudAuthBackendRolePath(backend, role string) string { + return "auth/" + strings.Trim(backend, "/") + "/role/" + strings.Trim(role, "/") +} + +func alicloudAuthBackendFromPath(path string) (string, error) { + var parts = strings.Split(path, "/") + if len(parts) != 4 { + return "", fmt.Errorf("expected 4 parts in path '%s'", path) + } + return parts[1], nil +} + +func alicloudAuthRoleFromPath(path string) (string, error) { + var parts = strings.Split(path, "/") + if len(parts) != 4 { + return "", fmt.Errorf("expected 4 parts in path '%s'", path) + } + return parts[3], nil +} + +func alicloudAuthBackendRoleUpdateFields(d *schema.ResourceData, data map[string]interface{}, create bool) { + updateTokenFields(d, data, create) + + if v, ok := d.GetOk("role"); ok { + data["role"] = v + } + + if v, ok := d.GetOk("arn"); ok { + data["arn"] = v + } +} + +func alicloudAuthBackendRoleCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + + backend := d.Get("backend").(string) + role := d.Get("role").(string) + + path := alicloudAuthBackendRolePath(backend, role) + + data := map[string]interface{}{} + alicloudAuthBackendRoleUpdateFields(d, data, true) + + log.Printf("[DEBUG] Writing role %q to AliCloud auth backend", path) + d.SetId(path) + _, err := client.Logical().Write(path, data) + if err != nil { + d.SetId("") + return fmt.Errorf("error writing AliCloud auth role %q: %s", path, err) + } + log.Printf("[DEBUG] Wrote role %q to AliCloud auth backend", path) + + return alicloudAuthBackendRoleRead(d, meta) +} + +func alicloudAuthBackendRoleUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + path := d.Id() + + data := map[string]interface{}{} + alicloudAuthBackendRoleUpdateFields(d, data, false) + + log.Printf("[DEBUG] Updating role %q in AliCloud auth backend", path) + _, err := client.Logical().Write(path, data) + if err != nil { + return fmt.Errorf("error updating AliCloud auth role %q: %s", path, err) + } + log.Printf("[DEBUG] Updated role %q to AliCloud auth backend", path) + + return alicloudAuthBackendRoleRead(d, meta) +} + +func alicloudAuthBackendRoleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + path := d.Id() + + log.Printf("[DEBUG] Reading AliCloud role %q", path) + resp, err := client.Logical().Read(path) + if err != nil { + return fmt.Errorf("error reading AliCloud role %q: %s", path, err) + } + log.Printf("[DEBUG] Read AliCloud role %q", path) + + if resp == nil { + log.Printf("[WARN] AliCloud role %q not found, removing from state", path) + d.SetId("") + return nil + } + + backend, err := alicloudAuthBackendFromPath(path) + if err != nil { + return fmt.Errorf("invalid path %q for AliCloud auth backend role: %s", path, err) + } + d.Set("backend", backend) + role, err := alicloudAuthRoleFromPath(path) + if err != nil { + return fmt.Errorf("invalid path %q for AliCloud auth backend role: %s", path, err) + } + d.Set("role", role) + + readTokenFields(d, resp) + + for _, k := range []string{"arn"} { + if v, ok := resp.Data[k]; ok { + if err := d.Set(k, v); err != nil { + return fmt.Errorf("error reading %s for AliCloud Auth Backend Role %q: %q", k, path, err) + } + } + } + + return nil +} + +func alicloudAuthBackendRoleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + path := d.Id() + + log.Printf("[DEBUG] Deleting AliCloud role %q", path) + _, err := client.Logical().Delete(path) + if err != nil { + return fmt.Errorf("error deleting AliCloud role %q", path) + } + log.Printf("[DEBUG] Deleted AliCloud role %q", path) + + return nil +} + +func alicloudAuthBackendRoleExists(d *schema.ResourceData, meta interface{}) (bool, error) { + client := meta.(*api.Client) + path := d.Id() + + log.Printf("[DEBUG] Checking if AliCloud Auth Backend role %q exists", path) + resp, err := client.Logical().Read(path) + if err != nil { + return true, fmt.Errorf("error checking for existence of AliCloud Auth Backend resource config %q: %s", path, err) + } + log.Printf("[DEBUG] Checked if AliCloud Auth Backend role %q exists", path) + + return resp != nil, nil +} diff --git a/vault/resource_alicloud_auth_backend_role_test.go b/vault/resource_alicloud_auth_backend_role_test.go new file mode 100644 index 000000000..b798adf53 --- /dev/null +++ b/vault/resource_alicloud_auth_backend_role_test.go @@ -0,0 +1,84 @@ +package vault + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/hashicorp/vault/api" +) + +func TestAlicloudAuthBackendRole_basic(t *testing.T) { + backend := acctest.RandomWithPrefix("tf-test-alicloud-backend") + name := acctest.RandomWithPrefix("tf-test-alicloud-role") + arn := acctest.RandomWithPrefix("acs:ram:123456:tf:role/") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testProviders, + CheckDestroy: testAlicloudAuthBackedRoleDestroy, + Steps: []resource.TestStep{ + { + Config: testAlicloudAuthBackedRoleConfig_basic(backend, name, arn), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("vault_alicloud_auth_backend_role.test", + "arn", arn), + resource.TestCheckResourceAttr("vault_alicloud_auth_backend_role.test", + "role", name), + ), + }, + { + Config: testAlicloudAuthBackedRoleConfig_basic(backend, name+"updated", arn+"updated"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("vault_alicloud_auth_backend_role.test", + "arn", arn+"updated"), + resource.TestCheckResourceAttr("vault_alicloud_auth_backend_role.test", + "role", name+"updated"), + ), + }, + { + ResourceName: "vault_alicloud_auth_backend_role.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAlicloudAuthBackedRoleDestroy(s *terraform.State) error { + client := testProvider.Meta().(*api.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "vault_alicloud_auth_backend_role" { + continue + } + secret, err := client.Logical().Read(rs.Primary.ID) + if err != nil { + return fmt.Errorf("Error checking for AliCloud Auth Backend role %q: %s", rs.Primary.ID, err) + } + if secret != nil { + return fmt.Errorf("AliCloud Auth Backend role %q still exists", rs.Primary.ID) + } + } + return nil +} + +func testAlicloudAuthBackedRoleConfig_basic(backend, name, arn string) string { + + return fmt.Sprintf(` + +resource "vault_auth_backend" "alicloud" { + path = "%s" + type = "alicloud" +} + +resource "vault_alicloud_auth_backend_role" "test" { + backend = vault_auth_backend.alicloud.path + role = "%s" + arn = "%s" + +} +`, backend, name, arn) +} diff --git a/website/docs/r/alicloud_auth_backend_role.md b/website/docs/r/alicloud_auth_backend_role.md new file mode 100644 index 000000000..ca5ed1736 --- /dev/null +++ b/website/docs/r/alicloud_auth_backend_role.md @@ -0,0 +1,93 @@ +--- +layout: "vault" +page_title: "Vault: vault_alicloud_auth_backend_role resource" +sidebar_current: "docs-vault-resource-alicloud-auth-backend-role" +description: |- + Managing roles in an AliCloud auth backend in Vault +--- + +# vault\_alicloud\_auth\_backend\_role + +Provides a resource to create a role in an [AliCloud auth backend within Vault](https://www.vaultproject.io/docs/auth/alicloud.html). + +## Example Usage + +```hcl +resource "vault_auth_backend" "alicloud" { + type = "alicloud" + path = "alicloud" +} + +resource "vault_alicloud_auth_backend_role" "alicloud" { + backend = vault_auth_backend.alicloud.path + role = "example" + arn = "acs:ram:123456:tf:role/foobar" + +} +``` + +## Argument Reference + +The following arguments are supported: + +* `role` - (Required; Forces new resource) Name of the role. Must correspond with the name of + the role reflected in the arn. + +* `arn` - (Required) The role's arn. + +* `backend` - (Optional; Forces new resource) Path to the mounted AliCloud auth backend. + Defaults to `alicloud` + +For more details on the usage of each argument consult the [Vault AliCloud API documentation](https://www.vaultproject.io/api/auth/alicloud/index.html). + +### Common Token Arguments + +These arguments are common across several Authentication Token resources since Vault 1.2. + +* `token_ttl` - (Optional) The incremental lifetime for generated tokens in number of seconds. + Its current value will be referenced at renewal time. + +* `token_max_ttl` - (Optional) The maximum lifetime for generated tokens in number of seconds. + Its current value will be referenced at renewal time. + +* `token_period` - (Optional) If set, indicates that the + token generated using this role should never expire. The token should be renewed within the + duration specified by this value. At each renewal, the token's TTL will be set to the + value of this field. Specified in seconds. + +* `token_policies` - (Optional) List of policies to encode onto generated tokens. Depending + on the auth method, this list may be supplemented by user/group/other values. + +* `token_bound_cidrs` - (Optional) List of CIDR blocks; if set, specifies blocks of IP + addresses which can authenticate successfully, and ties the resulting token to these blocks + as well. + +* `token_explicit_max_ttl` - (Optional) If set, will encode an + [explicit max TTL](https://www.vaultproject.io/docs/concepts/tokens.html#token-time-to-live-periodic-tokens-and-explicit-max-ttls) + onto the token in number of seconds. This is a hard cap even if `token_ttl` and + `token_max_ttl` would otherwise allow a renewal. + +* `token_no_default_policy` - (Optional) If set, the default policy will not be set on + generated tokens; otherwise it will be added to the policies set in token_policies. + +* `token_num_uses` - (Optional) The + [period](https://www.vaultproject.io/docs/concepts/tokens.html#token-time-to-live-periodic-tokens-and-explicit-max-ttls), + if any, in number of seconds to set on the token. + +* `token_type` - (Optional) The type of token that should be generated. Can be `service`, + `batch`, or `default` to use the mount's tuned default (which unless changed will be + `service` tokens). For token store roles, there are two additional possibilities: + `default-service` and `default-batch` which specify the type to return unless the client + requests a different type at generation time. + +## Attribute Reference + +No additional attributes are exposed by this resource. + +## Import + +Alicloud authentication roles can be imported using the `path`, e.g. + +``` +$ terraform import vault_alicloud_auth_backend_role.my_role auth/alicloud/role/my_role +``` diff --git a/website/docs/r/gcp_auth_backend_role.html.md b/website/docs/r/gcp_auth_backend_role.html.md index f4bb7b16d..02a96e1a0 100644 --- a/website/docs/r/gcp_auth_backend_role.html.md +++ b/website/docs/r/gcp_auth_backend_role.html.md @@ -1,6 +1,6 @@ --- layout: "vault" -page_title: "Vault: vault_auth_backend resource" +page_title: "Vault: vault_gcp_auth_backend_role resource" sidebar_current: "docs-vault-resource-gcp-auth-backend-role" description: |- Managing roles in an GCP auth backend in Vault @@ -19,7 +19,7 @@ resource "vault_auth_backend" "gcp" { } resource "vault_gcp_auth_backend_role" "gcp" { - backend = vault_auth_backend.cert.path + backend = vault_auth_backend.gcp.path project_id = "foo-bar-baz" bound_service_accounts = ["database-server@foo-bar-baz.iam.gserviceaccount.com"] token_policies = ["database-server"] diff --git a/website/vault.erb b/website/vault.erb index 583be5067..79bb09cc8 100644 --- a/website/vault.erb +++ b/website/vault.erb @@ -59,6 +59,9 @@ > Resources