blob: 5166f9271ea165798d2a887ce44174b3c2ada417 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"errors"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"golang.org/x/crypto/bcrypt"
"github.com/terraform-providers/terraform-provider-random/internal/diagnostics"
boolplanmodifiers "github.com/terraform-providers/terraform-provider-random/internal/planmodifiers/bool"
mapplanmodifiers "github.com/terraform-providers/terraform-provider-random/internal/planmodifiers/map"
stringplanmodifiers "github.com/terraform-providers/terraform-provider-random/internal/planmodifiers/string"
"github.com/terraform-providers/terraform-provider-random/internal/random"
)
var (
_ resource.Resource = (*passwordResource)(nil)
_ resource.ResourceWithImportState = (*passwordResource)(nil)
_ resource.ResourceWithUpgradeState = (*passwordResource)(nil)
)
func NewPasswordResource() resource.Resource {
return &passwordResource{}
}
type passwordResource struct{}
func (r *passwordResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_password"
}
func (r *passwordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = passwordSchemaV3()
}
func (r *passwordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan passwordModelV3
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
params := random.StringParams{
Length: plan.Length.ValueInt64(),
Upper: plan.Upper.ValueBool(),
MinUpper: plan.MinUpper.ValueInt64(),
Lower: plan.Lower.ValueBool(),
MinLower: plan.MinLower.ValueInt64(),
Numeric: plan.Numeric.ValueBool(),
MinNumeric: plan.MinNumeric.ValueInt64(),
Special: plan.Special.ValueBool(),
MinSpecial: plan.MinSpecial.ValueInt64(),
OverrideSpecial: plan.OverrideSpecial.ValueString(),
}
result, err := random.CreateString(params)
if err != nil {
resp.Diagnostics.Append(diagnostics.RandomReadError(err.Error())...)
return
}
hash, err := generateHash(string(result))
if err != nil {
resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...)
}
plan.BcryptHash = types.StringValue(hash)
plan.ID = types.StringValue("none")
plan.Result = types.StringValue(string(result))
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
}
// Read does not need to perform any operations as the state in ReadResourceResponse is already populated.
func (r *passwordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
}
// Update ensures the plan value is copied to the state to complete the update.
func (r *passwordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var model passwordModelV3
resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.State.Set(ctx, &model)...)
}
// Delete does not need to explicitly call resp.State.RemoveResource() as this is automatically handled by the
// [framework](https://github.com/hashicorp/terraform-plugin-framework/pull/301).
func (r *passwordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
}
func (r *passwordResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
id := req.ID
state := passwordModelV3{
ID: types.StringValue("none"),
Result: types.StringValue(id),
Length: types.Int64Value(int64(len(id))),
Special: types.BoolValue(true),
Upper: types.BoolValue(true),
Lower: types.BoolValue(true),
Number: types.BoolValue(true),
Numeric: types.BoolValue(true),
MinSpecial: types.Int64Value(0),
MinUpper: types.Int64Value(0),
MinLower: types.Int64Value(0),
MinNumeric: types.Int64Value(0),
Keepers: types.MapNull(types.StringType),
OverrideSpecial: types.StringNull(),
}
hash, err := generateHash(id)
if err != nil {
resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...)
}
state.BcryptHash = types.StringValue(hash)
diags := resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func (r *passwordResource) UpgradeState(context.Context) map[int64]resource.StateUpgrader {
schemaV0 := passwordSchemaV0()
schemaV1 := passwordSchemaV1()
schemaV2 := passwordSchemaV2()
return map[int64]resource.StateUpgrader{
0: {
PriorSchema: &schemaV0,
StateUpgrader: upgradePasswordStateV0toV3,
},
1: {
PriorSchema: &schemaV1,
StateUpgrader: upgradePasswordStateV1toV3,
},
2: {
PriorSchema: &schemaV2,
StateUpgrader: upgradePasswordStateV2toV3,
},
}
}
func upgradePasswordStateV0toV3(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
type modelV0 struct {
ID types.String `tfsdk:"id"`
Keepers types.Map `tfsdk:"keepers"`
Length types.Int64 `tfsdk:"length"`
Special types.Bool `tfsdk:"special"`
Upper types.Bool `tfsdk:"upper"`
Lower types.Bool `tfsdk:"lower"`
Number types.Bool `tfsdk:"number"`
MinNumeric types.Int64 `tfsdk:"min_numeric"`
MinUpper types.Int64 `tfsdk:"min_upper"`
MinLower types.Int64 `tfsdk:"min_lower"`
MinSpecial types.Int64 `tfsdk:"min_special"`
OverrideSpecial types.String `tfsdk:"override_special"`
Result types.String `tfsdk:"result"`
}
var passwordDataV0 modelV0
resp.Diagnostics.Append(req.State.Get(ctx, &passwordDataV0)...)
if resp.Diagnostics.HasError() {
return
}
// Setting fields that can contain null to non-null to prevent forced replacement.
// This can occur in cases where import has been used in provider versions v3.3.1 and earlier.
// If import has been used with v3.3.1, for instance then length, lower, number, special, upper,
// min_lower, min_numeric, min_special and min_upper attributes will all be null in state.
length := passwordDataV0.Length
if length.IsNull() {
length = types.Int64Value(int64(len(passwordDataV0.Result.ValueString())))
}
minNumeric := passwordDataV0.MinNumeric
if minNumeric.IsNull() {
minNumeric = types.Int64Value(0)
}
minUpper := passwordDataV0.MinUpper
if minUpper.IsNull() {
minUpper = types.Int64Value(0)
}
minLower := passwordDataV0.MinLower
if minLower.IsNull() {
minLower = types.Int64Value(0)
}
minSpecial := passwordDataV0.MinSpecial
if minSpecial.IsNull() {
minSpecial = types.Int64Value(0)
}
special := passwordDataV0.Special
if special.IsNull() {
special = types.BoolValue(true)
}
upper := passwordDataV0.Upper
if upper.IsNull() {
upper = types.BoolValue(true)
}
lower := passwordDataV0.Lower
if lower.IsNull() {
lower = types.BoolValue(true)
}
number := passwordDataV0.Number
if number.IsNull() {
number = types.BoolValue(true)
}
passwordDataV3 := passwordModelV3{
Keepers: passwordDataV0.Keepers,
Length: length,
Special: special,
Upper: upper,
Lower: lower,
Number: number,
Numeric: number,
MinNumeric: minNumeric,
MinUpper: minUpper,
MinLower: minLower,
MinSpecial: minSpecial,
OverrideSpecial: passwordDataV0.OverrideSpecial,
Result: passwordDataV0.Result,
ID: passwordDataV0.ID,
}
hash, err := generateHash(passwordDataV3.Result.ValueString())
if err != nil {
resp.Diagnostics.Append(diagnostics.HashGenerationError(err.Error())...)
return
}
passwordDataV3.BcryptHash = types.StringValue(hash)
diags := resp.State.Set(ctx, passwordDataV3)
resp.Diagnostics.Append(diags...)
}
func upgradePasswordStateV1toV3(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
type modelV1 struct {
ID types.String `tfsdk:"id"`
Keepers types.Map `tfsdk:"keepers"`
Length types.Int64 `tfsdk:"length"`
Special types.Bool `tfsdk:"special"`
Upper types.Bool `tfsdk:"upper"`
Lower types.Bool `tfsdk:"lower"`
Number types.Bool `tfsdk:"number"`
MinNumeric types.Int64 `tfsdk:"min_numeric"`
MinUpper types.Int64 `tfsdk:"min_upper"`
MinLower types.Int64 `tfsdk:"min_lower"`
MinSpecial types.Int64 `tfsdk:"min_special"`
OverrideSpecial types.String `tfsdk:"override_special"`
Result types.String `tfsdk:"result"`
BcryptHash types.String `tfsdk:"bcrypt_hash"`
}
var passwordDataV1 modelV1
resp.Diagnostics.Append(req.State.Get(ctx, &passwordDataV1)...)
if resp.Diagnostics.HasError() {
return
}
// Setting fields that can contain null to non-null to prevent forced replacement.
// This can occur in cases where import has been used in provider versions v3.3.1 and earlier.
// If import has been used with v3.3.1, for instance then length, lower, number, special, upper,
// min_lower, min_numeric, min_special and min_upper attributes will all be null in state.
length := passwordDataV1.Length
if length.IsNull() {
length = types.Int64Value(int64(len(passwordDataV1.Result.ValueString())))
}
minNumeric := passwordDataV1.MinNumeric
if minNumeric.IsNull() {
minNumeric = types.Int64Value(0)
}
minUpper := passwordDataV1.MinUpper
if minUpper.IsNull() {
minUpper = types.Int64Value(0)
}
minLower := passwordDataV1.MinLower
if minLower.IsNull() {
minLower = types.Int64Value(0)
}
minSpecial := passwordDataV1.MinSpecial
if minSpecial.IsNull() {
minSpecial = types.Int64Value(0)
}
special := passwordDataV1.Special
if special.IsNull() {
special = types.BoolValue(true)
}
upper := passwordDataV1.Upper
if upper.IsNull() {
upper = types.BoolValue(true)
}
lower := passwordDataV1.Lower
if lower.IsNull() {
lower = types.BoolValue(true)
}
number := passwordDataV1.Number
if number.IsNull() {
number = types.BoolValue(true)
}
passwordDataV3 := passwordModelV3{
Keepers: passwordDataV1.Keepers,
Length: length,
Special: special,
Upper: upper,
Lower: lower,
Number: number,
Numeric: number,
MinNumeric: minNumeric,
MinUpper: minUpper,
MinLower: minLower,
MinSpecial: minSpecial,
OverrideSpecial: passwordDataV1.OverrideSpecial,
BcryptHash: passwordDataV1.BcryptHash,
Result: passwordDataV1.Result,
ID: passwordDataV1.ID,
}
diags := resp.State.Set(ctx, passwordDataV3)
resp.Diagnostics.Append(diags...)
}
func upgradePasswordStateV2toV3(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
type passwordModelV2 struct {
ID types.String `tfsdk:"id"`
Keepers types.Map `tfsdk:"keepers"`
Length types.Int64 `tfsdk:"length"`
Special types.Bool `tfsdk:"special"`
Upper types.Bool `tfsdk:"upper"`
Lower types.Bool `tfsdk:"lower"`
Number types.Bool `tfsdk:"number"`
Numeric types.Bool `tfsdk:"numeric"`
MinNumeric types.Int64 `tfsdk:"min_numeric"`
MinUpper types.Int64 `tfsdk:"min_upper"`
MinLower types.Int64 `tfsdk:"min_lower"`
MinSpecial types.Int64 `tfsdk:"min_special"`
OverrideSpecial types.String `tfsdk:"override_special"`
Result types.String `tfsdk:"result"`
BcryptHash types.String `tfsdk:"bcrypt_hash"`
}
var passwordDataV2 passwordModelV2
resp.Diagnostics.Append(req.State.Get(ctx, &passwordDataV2)...)
if resp.Diagnostics.HasError() {
return
}
// Setting fields that can contain null to non-null to prevent forced replacement.
// This can occur in cases where import has been used in provider versions v3.3.1 and earlier.
// If import has been used with v3.3.1, for instance then length, lower, number, special, upper,
// min_lower, min_numeric, min_special and min_upper attributes will all be null in state.
length := passwordDataV2.Length
if length.IsNull() {
length = types.Int64Value(int64(len(passwordDataV2.Result.ValueString())))
}
minNumeric := passwordDataV2.MinNumeric
if minNumeric.IsNull() {
minNumeric = types.Int64Value(0)
}
minUpper := passwordDataV2.MinUpper
if minUpper.IsNull() {
minUpper = types.Int64Value(0)
}
minLower := passwordDataV2.MinLower
if minLower.IsNull() {
minLower = types.Int64Value(0)
}
minSpecial := passwordDataV2.MinSpecial
if minSpecial.IsNull() {
minSpecial = types.Int64Value(0)
}
special := passwordDataV2.Special
if special.IsNull() {
special = types.BoolValue(true)
}
upper := passwordDataV2.Upper
if upper.IsNull() {
upper = types.BoolValue(true)
}
lower := passwordDataV2.Lower
if lower.IsNull() {
lower = types.BoolValue(true)
}
number := passwordDataV2.Number
if number.IsNull() {
number = types.BoolValue(true)
}
numeric := passwordDataV2.Number
if numeric.IsNull() {
numeric = types.BoolValue(true)
}
// Schema version 2 to schema version 3 is a duplicate of the data,
// however the BcryptHash value may have been incorrectly generated.
//nolint:gosimple // V3 model will expand over time so all fields are written out to help future code changes.
passwordDataV3 := passwordModelV3{
BcryptHash: passwordDataV2.BcryptHash,
ID: passwordDataV2.ID,
Keepers: passwordDataV2.Keepers,
Length: length,
Lower: lower,
MinLower: minLower,
MinNumeric: minNumeric,
MinSpecial: minSpecial,
MinUpper: minUpper,
Number: number,
Numeric: numeric,
OverrideSpecial: passwordDataV2.OverrideSpecial,
Result: passwordDataV2.Result,
Special: special,
Upper: upper,
}
// Set the duplicated data now so we can easily return early below.
// The BcryptHash value will be adjusted later if it is incorrect.
resp.Diagnostics.Append(resp.State.Set(ctx, passwordDataV3)...)
if resp.Diagnostics.HasError() {
return
}
// If the BcryptHash value does not correctly verify against the Result
// value we should regenerate it.
err := bcrypt.CompareHashAndPassword([]byte(passwordDataV2.BcryptHash.ValueString()), []byte(passwordDataV2.Result.ValueString()))
// If the hash matched the password, there is nothing to do.
if err == nil {
return
}
if !errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
resp.Diagnostics.AddError(
"Version 3 State Upgrade Error",
"An unexpected error occurred when comparing the state version 2 password and bcrypt hash. "+
"This is always an issue in the provider and should be reported to the provider developers.\n\n"+
"Original Error: "+err.Error(),
)
return
}
// Regenerate the BcryptHash value.
newBcryptHash, err := bcrypt.GenerateFromPassword([]byte(passwordDataV2.Result.ValueString()), bcrypt.DefaultCost)
if err != nil {
resp.Diagnostics.AddError(
"Version 3 State Upgrade Error",
"An unexpected error occurred when generating a new password bcrypt hash. "+
"Check the error below and ensure the system executing Terraform can properly generate randomness.\n\n"+
"Original Error: "+err.Error(),
)
return
}
passwordDataV3.BcryptHash = types.StringValue(string(newBcryptHash))
resp.Diagnostics.Append(resp.State.Set(ctx, passwordDataV3)...)
}
// generateHash truncates strings that are longer than 72 bytes in
// order to avoid the error returned from bcrypt.GenerateFromPassword
// in versions v0.5.0 and above: https://pkg.go.dev/golang.org/x/crypto@v0.8.0/bcrypt#GenerateFromPassword
func generateHash(toHash string) (string, error) {
bytesHash := []byte(toHash)
bytesToHash := bytesHash
if len(bytesHash) > 72 {
bytesToHash = bytesHash[:72]
}
hash, err := bcrypt.GenerateFromPassword(bytesToHash, bcrypt.DefaultCost)
return string(hash), err
}
func passwordSchemaV3() schema.Schema {
return schema.Schema{
Version: 3,
Description: "Identical to [random_string](string.html) with the exception that the result is " +
"treated as sensitive and, thus, _not_ displayed in console output. Read more about sensitive " +
"data handling in the " +
"[Terraform documentation](https://www.terraform.io/docs/language/state/sensitive-data.html).\n\n" +
"This resource *does* use a cryptographic random number generator.",
Attributes: map[string]schema.Attribute{
"keepers": schema.MapAttribute{
Description: "Arbitrary map of values that, when changed, will trigger recreation of " +
"resource. See [the main provider documentation](../index.html) for more information.",
ElementType: types.StringType,
Optional: true,
PlanModifiers: []planmodifier.Map{
mapplanmodifiers.RequiresReplaceIfValuesNotNull(),
},
},
"length": schema.Int64Attribute{
Description: "The length of the string desired. The minimum value for length is 1 and, length " +
"must also be >= (`min_upper` + `min_lower` + `min_numeric` + `min_special`).",
Required: true,
PlanModifiers: []planmodifier.Int64{
int64planmodifier.RequiresReplace(),
},
Validators: []validator.Int64{
int64validator.AtLeast(1),
int64validator.AtLeastSumOf(
path.MatchRoot("min_upper"),
path.MatchRoot("min_lower"),
path.MatchRoot("min_numeric"),
path.MatchRoot("min_special"),
),
},
},
"special": schema.BoolAttribute{
Description: "Include special characters in the result. These are `!@#$%&*()-_=+[]{}<>:?`. Default value is `true`.",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(true),
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.RequiresReplace(),
},
},
"upper": schema.BoolAttribute{
Description: "Include uppercase alphabet characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(true),
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.RequiresReplace(),
}},
"lower": schema.BoolAttribute{
Description: "Include lowercase alphabet characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(true),
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.RequiresReplace(),
},
},
"number": schema.BoolAttribute{
Description: "Include numeric characters in the result. Default value is `true`. " +
"**NOTE**: This is deprecated, use `numeric` instead.",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.Bool{
boolplanmodifiers.NumberNumericAttributePlanModifier(),
boolplanmodifier.RequiresReplace(),
},
DeprecationMessage: "**NOTE**: This is deprecated, use `numeric` instead.",
},
"numeric": schema.BoolAttribute{
Description: "Include numeric characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.Bool{
boolplanmodifiers.NumberNumericAttributePlanModifier(),
boolplanmodifier.RequiresReplace(),
},
},
"min_numeric": schema.Int64Attribute{
Description: "Minimum number of numeric characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
Default: int64default.StaticInt64(0),
PlanModifiers: []planmodifier.Int64{
int64planmodifier.RequiresReplace(),
},
},
"min_upper": schema.Int64Attribute{
Description: "Minimum number of uppercase alphabet characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
Default: int64default.StaticInt64(0),
PlanModifiers: []planmodifier.Int64{
int64planmodifier.RequiresReplace(),
},
},
"min_lower": schema.Int64Attribute{
Description: "Minimum number of lowercase alphabet characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
Default: int64default.StaticInt64(0),
PlanModifiers: []planmodifier.Int64{
int64planmodifier.RequiresReplace(),
},
},
"min_special": schema.Int64Attribute{
Description: "Minimum number of special characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
Default: int64default.StaticInt64(0),
PlanModifiers: []planmodifier.Int64{
int64planmodifier.RequiresReplace(),
},
},
"override_special": schema.StringAttribute{
Description: "Supply your own list of special characters to use for string generation. This " +
"overrides the default character list in the special argument. The `special` argument must " +
"still be set to true for any overwritten characters to be used in generation.",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplaceIf(
stringplanmodifiers.RequiresReplaceUnlessEmptyStringToNull(),
"Replace on modification unless updating from empty string (\"\") to null.",
"Replace on modification unless updating from empty string (`\"\"`) to `null`.",
),
},
},
"result": schema.StringAttribute{
Description: "The generated random string.",
Computed: true,
Sensitive: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"bcrypt_hash": schema.StringAttribute{
Description: "A bcrypt hash of the generated random string. " +
"**NOTE**: If the generated random string is greater than 72 bytes in length, " +
"`bcrypt_hash` will contain a hash of the first 72 bytes.",
Computed: true,
Sensitive: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"id": schema.StringAttribute{
Description: "A static value used internally by Terraform, this should not be referenced in configurations.",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
}
}
func passwordSchemaV2() schema.Schema {
return schema.Schema{
Version: 2,
Description: "Identical to [random_string](string.html) with the exception that the result is " +
"treated as sensitive and, thus, _not_ displayed in console output. Read more about sensitive " +
"data handling in the " +
"[Terraform documentation](https://www.terraform.io/docs/language/state/sensitive-data.html).\n\n" +
"This resource *does* use a cryptographic random number generator.",
Attributes: map[string]schema.Attribute{
"keepers": schema.MapAttribute{
Description: "Arbitrary map of values that, when changed, will trigger recreation of " +
"resource. See [the main provider documentation](../index.html) for more information.",
ElementType: types.StringType,
Optional: true,
},
"length": schema.Int64Attribute{
Description: "The length of the string desired. The minimum value for length is 1 and, length " +
"must also be >= (`min_upper` + `min_lower` + `min_numeric` + `min_special`).",
Required: true,
},
"special": schema.BoolAttribute{
Description: "Include special characters in the result. These are `!@#$%&*()-_=+[]{}<>:?`. Default value is `true`.",
Optional: true,
Computed: true,
},
"upper": schema.BoolAttribute{
Description: "Include uppercase alphabet characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
},
"lower": schema.BoolAttribute{
Description: "Include lowercase alphabet characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
},
"number": schema.BoolAttribute{
Description: "Include numeric characters in the result. Default value is `true`. " +
"**NOTE**: This is deprecated, use `numeric` instead.",
Optional: true,
Computed: true,
DeprecationMessage: "**NOTE**: This is deprecated, use `numeric` instead.",
},
"numeric": schema.BoolAttribute{
Description: "Include numeric characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
},
"min_numeric": schema.Int64Attribute{
Description: "Minimum number of numeric characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"min_upper": schema.Int64Attribute{
Description: "Minimum number of uppercase alphabet characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"min_lower": schema.Int64Attribute{
Description: "Minimum number of lowercase alphabet characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"min_special": schema.Int64Attribute{
Description: "Minimum number of special characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"override_special": schema.StringAttribute{
Description: "Supply your own list of special characters to use for string generation. This " +
"overrides the default character list in the special argument. The `special` argument must " +
"still be set to true for any overwritten characters to be used in generation.",
Optional: true,
Computed: true,
},
"result": schema.StringAttribute{
Description: "The generated random string.",
Computed: true,
Sensitive: true,
},
"bcrypt_hash": schema.StringAttribute{
Description: "A bcrypt hash of the generated random string. " +
"**NOTE**: If the generated random string is greater than 72 bytes in length, " +
"`bcrypt_hash` will contain a hash of the first 72 bytes.",
Computed: true,
Sensitive: true,
},
"id": schema.StringAttribute{
Description: "A static value used internally by Terraform, this should not be referenced in configurations.",
Computed: true,
},
},
}
}
func passwordSchemaV1() schema.Schema {
return schema.Schema{
Version: 1,
Description: "Identical to [random_string](string.html) with the exception that the result is " +
"treated as sensitive and, thus, _not_ displayed in console output. Read more about sensitive " +
"data handling in the " +
"[Terraform documentation](https://www.terraform.io/docs/language/state/sensitive-data.html).\n\n" +
"This resource *does* use a cryptographic random number generator.",
Attributes: map[string]schema.Attribute{
"keepers": schema.MapAttribute{
Description: "Arbitrary map of values that, when changed, will trigger recreation of " +
"resource. See [the main provider documentation](../index.html) for more information.",
ElementType: types.StringType,
Optional: true,
},
"length": schema.Int64Attribute{
Description: "The length of the string desired. The minimum value for length is 1 and, length " +
"must also be >= (`min_upper` + `min_lower` + `min_numeric` + `min_special`).",
Required: true,
},
"special": schema.BoolAttribute{
Description: "Include special characters in the result. These are `!@#$%&*()-_=+[]{}<>:?`. Default value is `true`.",
Optional: true,
Computed: true,
},
"upper": schema.BoolAttribute{
Description: "Include uppercase alphabet characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
},
"lower": schema.BoolAttribute{
Description: "Include lowercase alphabet characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
},
"number": schema.BoolAttribute{
Description: "Include numeric characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
},
"min_numeric": schema.Int64Attribute{
Description: "Minimum number of numeric characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"min_upper": schema.Int64Attribute{
Description: "Minimum number of uppercase alphabet characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"min_lower": schema.Int64Attribute{
Description: "Minimum number of lowercase alphabet characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"min_special": schema.Int64Attribute{
Description: "Minimum number of special characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"override_special": schema.StringAttribute{
Description: "Supply your own list of special characters to use for string generation. This " +
"overrides the default character list in the special argument. The `special` argument must " +
"still be set to true for any overwritten characters to be used in generation.",
Optional: true,
Computed: true,
},
"result": schema.StringAttribute{
Description: "The generated random string.",
Computed: true,
Sensitive: true,
},
"bcrypt_hash": schema.StringAttribute{
Description: "A bcrypt hash of the generated random string. " +
"**NOTE**: If the generated random string is greater than 72 bytes in length, " +
"`bcrypt_hash` will contain a hash of the first 72 bytes.",
Computed: true,
Sensitive: true,
},
"id": schema.StringAttribute{
Description: "A static value used internally by Terraform, this should not be referenced in configurations.",
Computed: true,
},
},
}
}
func passwordSchemaV0() schema.Schema {
return schema.Schema{
Description: "Identical to [random_string](string.html) with the exception that the result is " +
"treated as sensitive and, thus, _not_ displayed in console output. Read more about sensitive " +
"data handling in the " +
"[Terraform documentation](https://www.terraform.io/docs/language/state/sensitive-data.html).\n\n" +
"This resource *does* use a cryptographic random number generator.",
Attributes: map[string]schema.Attribute{
"keepers": schema.MapAttribute{
Description: "Arbitrary map of values that, when changed, will trigger recreation of " +
"resource. See [the main provider documentation](../index.html) for more information.",
ElementType: types.StringType,
Optional: true,
},
"length": schema.Int64Attribute{
Description: "The length of the string desired. The minimum value for length is 1 and, length " +
"must also be >= (`min_upper` + `min_lower` + `min_numeric` + `min_special`).",
Required: true,
},
"special": schema.BoolAttribute{
Description: "Include special characters in the result. These are `!@#$%&*()-_=+[]{}<>:?`. Default value is `true`.",
Optional: true,
Computed: true,
},
"upper": schema.BoolAttribute{
Description: "Include uppercase alphabet characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
},
"lower": schema.BoolAttribute{
Description: "Include lowercase alphabet characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
},
"number": schema.BoolAttribute{
Description: "Include numeric characters in the result. Default value is `true`.",
Optional: true,
Computed: true,
},
"min_numeric": schema.Int64Attribute{
Description: "Minimum number of numeric characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"min_upper": schema.Int64Attribute{
Description: "Minimum number of uppercase alphabet characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"min_lower": schema.Int64Attribute{
Description: "Minimum number of lowercase alphabet characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"min_special": schema.Int64Attribute{
Description: "Minimum number of special characters in the result. Default value is `0`.",
Optional: true,
Computed: true,
},
"override_special": schema.StringAttribute{
Description: "Supply your own list of special characters to use for string generation. This " +
"overrides the default character list in the special argument. The `special` argument must " +
"still be set to true for any overwritten characters to be used in generation.",
Optional: true,
Computed: true,
},
"result": schema.StringAttribute{
Description: "The generated random string.",
Computed: true,
Sensitive: true,
},
"id": schema.StringAttribute{
Description: "A static value used internally by Terraform, this should not be referenced in configurations.",
Computed: true,
},
},
}
}
type passwordModelV3 struct {
ID types.String `tfsdk:"id"`
Keepers types.Map `tfsdk:"keepers"`
Length types.Int64 `tfsdk:"length"`
Special types.Bool `tfsdk:"special"`
Upper types.Bool `tfsdk:"upper"`
Lower types.Bool `tfsdk:"lower"`
Number types.Bool `tfsdk:"number"`
Numeric types.Bool `tfsdk:"numeric"`
MinNumeric types.Int64 `tfsdk:"min_numeric"`
MinUpper types.Int64 `tfsdk:"min_upper"`
MinLower types.Int64 `tfsdk:"min_lower"`
MinSpecial types.Int64 `tfsdk:"min_special"`
OverrideSpecial types.String `tfsdk:"override_special"`
Result types.String `tfsdk:"result"`
BcryptHash types.String `tfsdk:"bcrypt_hash"`
}