diff --git a/examples/README.md b/examples/README.md index 14343ab..44f093f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,6 +4,18 @@ This directory contains example Terraform configurations demonstrating the usage ## Available Examples +### Authentication Examples (`authentication/`) + +Dedicated examples showing both authentication methods with detailed documentation. + +**Features:** +- Token-based authentication example +- Username/password authentication example +- Validation rules and error messages +- Best practices and recommendations + +**See:** [authentication/README.md](authentication/README.md) for detailed authentication documentation. + ### 1. Basic Example (`basic/`) The simplest example showing how to create a single application. @@ -66,7 +78,9 @@ terraform apply ## Provider Configuration -All examples require the following provider configuration: +The provider supports two authentication methods: + +### Option 1: Token-based Authentication ```hcl provider "edge-connect" { @@ -75,10 +89,24 @@ provider "edge-connect" { } ``` +### Option 2: Username/Password Authentication + +```hcl +provider "edge-connect" { + endpoint = "https://api.edge-connect.example.com" + username = var.edge_connect_username + password = var.edge_connect_password +} +``` + ### Configuration Options - `endpoint` (required): The Edge Connect API endpoint URL -- `token` (required, sensitive): Authentication token for the Edge Connect API +- `token` (optional, sensitive): Authentication token for the Edge Connect API. Required if username/password are not provided. +- `username` (optional): Username for the Edge Connect API. Required if token is not provided. +- `password` (optional, sensitive): Password for the Edge Connect API. Required if token is not provided. + +**Important:** You must use either token authentication OR username/password authentication, but not both. ## Resources diff --git a/examples/authentication/README.md b/examples/authentication/README.md new file mode 100644 index 0000000..dbeb5b3 --- /dev/null +++ b/examples/authentication/README.md @@ -0,0 +1,135 @@ +# Authentication Examples + +This directory contains examples demonstrating the different authentication methods supported by the Edge Connect Terraform provider. + +## Authentication Methods + +The provider supports two mutually exclusive authentication methods: + +### 1. Token-based Authentication + +Token authentication uses a pre-generated API token for authentication. + +**File:** `token-auth.tf` + +**Advantages:** +- More secure (tokens can be scoped and rotated) +- Better for CI/CD pipelines +- Recommended for production use + +**Usage:** +```hcl +provider "edge-connect" { + endpoint = "https://api.edge-connect.example.com" + token = var.edge_connect_token +} +``` + +**Environment Variable:** +You can also set credentials via environment variables: +```bash +export TF_VAR_edge_connect_token="your-token-here" +terraform apply +``` + +### 2. Username/Password Authentication + +Username/password authentication uses standard user credentials. + +**File:** `username-password-auth.tf` + +**Advantages:** +- Simpler for development and testing +- No token generation required +- Familiar authentication method + +**Usage:** +```hcl +provider "edge-connect" { + endpoint = "https://api.edge-connect.example.com" + username = var.edge_connect_username + password = var.edge_connect_password +} +``` + +**Environment Variables:** +You can also set credentials via environment variables: +```bash +export TF_VAR_edge_connect_username="your-username" +export TF_VAR_edge_connect_password="your-password" +terraform apply +``` + +## Important Notes + +1. **Mutual Exclusivity:** You must use EITHER token OR username/password, not both. The provider will return an error if both methods are provided. + +2. **Required Credentials:** + - For token auth: `endpoint` and `token` are required + - For username/password auth: `endpoint`, `username`, and `password` are required + +3. **Security Best Practices:** + - Never commit credentials to version control + - Use environment variables or secure secret management systems + - Mark sensitive variables with `sensitive = true` + - Use `.gitignore` to exclude `terraform.tfvars` files + +## Validation Rules + +The provider validates authentication credentials with the following rules: + +- At least one authentication method must be provided +- Both authentication methods cannot be used simultaneously +- For username/password auth, both username and password must be provided together + +## Error Messages + +### Missing Credentials +``` +Error: Missing Authentication Credentials + +The provider requires authentication credentials. Please provide either: + - token: API token for authentication, or + - username and password: Username and password for authentication +``` + +### Conflicting Methods +``` +Error: Conflicting Authentication Methods + +Both token and username/password authentication methods are provided. +Please use only one authentication method. +``` + +### Incomplete Credentials +``` +Error: Incomplete Username/Password Credentials + +Both username and password must be provided together for username/password authentication. +``` + +## Testing Examples + +To test token authentication: +```bash +cd examples/authentication +terraform init +terraform plan -var="edge_connect_token=your-token" -target=edge-connect_app.token_example +``` + +To test username/password authentication: +```bash +cd examples/authentication +terraform init +terraform plan \ + -var="edge_connect_username=your-username" \ + -var="edge_connect_password=your-password" \ + -target=edge-connect_app.userpass_example +``` + +## Recommendations + +- **Production Environments:** Use token-based authentication with properly scoped and rotated tokens +- **Development Environments:** Either method is acceptable, but token auth is still recommended +- **CI/CD Pipelines:** Use token-based authentication with secrets management (e.g., HashiCorp Vault, AWS Secrets Manager) +- **Local Development:** Consider using environment variables or `.tfvars` files (excluded from git) for credentials diff --git a/examples/authentication/token-auth.tf b/examples/authentication/token-auth.tf new file mode 100644 index 0000000..fd09f1c --- /dev/null +++ b/examples/authentication/token-auth.tf @@ -0,0 +1,33 @@ +# Example: Token-based Authentication +# +# This example demonstrates how to authenticate with the Edge Connect +# provider using an API token. + +terraform { + required_providers { + edge-connect = { + source = "local/edge-connect" + } + } +} + +provider "edge-connect" { + endpoint = "https://api.edge-connect.example.com" + token = var.edge_connect_token +} + +variable "edge_connect_token" { + description = "API token for Edge Connect" + type = string + sensitive = true +} + +# Example resource +resource "edge-connect_app" "token_example" { + name = "token-auth-app" + version = "1.0.0" +} + +output "app_id" { + value = edge-connect_app.token_example.id +} diff --git a/examples/authentication/username-password-auth.tf b/examples/authentication/username-password-auth.tf new file mode 100644 index 0000000..a8f3556 --- /dev/null +++ b/examples/authentication/username-password-auth.tf @@ -0,0 +1,39 @@ +# Example: Username/Password Authentication +# +# This example demonstrates how to authenticate with the Edge Connect +# provider using username and password credentials. + +terraform { + required_providers { + edge-connect = { + source = "local/edge-connect" + } + } +} + +provider "edge-connect" { + endpoint = "https://api.edge-connect.example.com" + username = var.edge_connect_username + password = var.edge_connect_password +} + +variable "edge_connect_username" { + description = "Username for Edge Connect API" + type = string +} + +variable "edge_connect_password" { + description = "Password for Edge Connect API" + type = string + sensitive = true +} + +# Example resource +resource "edge-connect_app" "userpass_example" { + name = "userpass-auth-app" + version = "1.0.0" +} + +output "app_id" { + value = edge-connect_app.userpass_example.id +} diff --git a/examples/basic/main.tf b/examples/basic/main.tf index 52e85ee..435d91c 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -6,11 +6,19 @@ terraform { } } +# Option 1: Token-based authentication provider "edge-connect" { endpoint = "https://api.edge-connect.example.com" token = var.edge_connect_token } +# Option 2: Username/password authentication (uncomment to use) +# provider "edge-connect" { +# endpoint = var.edge_connect_endpoint +# username = var.edge_connect_username +# password = var.edge_connect_password +# } + # Create a simple app resource "edge-connect_app" "example" { name = "my-app" diff --git a/examples/basic/terraform.tfvars.example b/examples/basic/terraform.tfvars.example index e0045a9..73db206 100644 --- a/examples/basic/terraform.tfvars.example +++ b/examples/basic/terraform.tfvars.example @@ -1,2 +1,8 @@ # Copy this file to terraform.tfvars and fill in your values + +# Option 1: Use token-based authentication edge_connect_token = "your-api-token-here" + +# Option 2: Use username/password authentication (uncomment and fill in) +# edge_connect_username = "your-username" +# edge_connect_password = "your-password" diff --git a/examples/basic/variables.tf b/examples/basic/variables.tf index 60a44e6..3f92160 100644 --- a/examples/basic/variables.tf +++ b/examples/basic/variables.tf @@ -1,5 +1,27 @@ +variable "edge_connect_endpoint" { + description = "Edge Connect API endpoint URL" + type = string + default = "https://api.edge-connect.example.com" +} + +# Token-based authentication variable "edge_connect_token" { - description = "Authentication token for Edge Connect API" + description = "Authentication token for Edge Connect API (use either token OR username/password)" type = string sensitive = true + default = "" +} + +# Username/password authentication +variable "edge_connect_username" { + description = "Username for Edge Connect API (use either token OR username/password)" + type = string + default = "" +} + +variable "edge_connect_password" { + description = "Password for Edge Connect API (use either token OR username/password)" + type = string + sensitive = true + default = "" } diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 0429de9..f67e388 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -6,11 +6,19 @@ terraform { } } +# Provider configuration with token authentication provider "edge-connect" { endpoint = var.edge_connect_endpoint token = var.edge_connect_token } +# Alternative: Username/password authentication (uncomment to use) +# provider "edge-connect" { +# endpoint = var.edge_connect_endpoint +# username = var.edge_connect_username +# password = var.edge_connect_password +# } + # Create an application resource "edge-connect_app" "web_app" { name = "web-application" diff --git a/examples/complete/terraform.tfvars.example b/examples/complete/terraform.tfvars.example index 8910caf..8da0f6e 100644 --- a/examples/complete/terraform.tfvars.example +++ b/examples/complete/terraform.tfvars.example @@ -1,3 +1,9 @@ # Copy this file to terraform.tfvars and fill in your values edge_connect_endpoint = "https://api.edge-connect.example.com" -edge_connect_token = "your-api-token-here" + +# Option 1: Use token-based authentication +edge_connect_token = "your-api-token-here" + +# Option 2: Use username/password authentication (uncomment and fill in) +# edge_connect_username = "your-username" +# edge_connect_password = "your-password" diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index c705711..3f92160 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -4,8 +4,24 @@ variable "edge_connect_endpoint" { default = "https://api.edge-connect.example.com" } +# Token-based authentication variable "edge_connect_token" { - description = "Authentication token for Edge Connect API" + description = "Authentication token for Edge Connect API (use either token OR username/password)" type = string sensitive = true + default = "" +} + +# Username/password authentication +variable "edge_connect_username" { + description = "Username for Edge Connect API (use either token OR username/password)" + type = string + default = "" +} + +variable "edge_connect_password" { + description = "Password for Edge Connect API (use either token OR username/password)" + type = string + sensitive = true + default = "" } diff --git a/examples/data-sources/main.tf b/examples/data-sources/main.tf index 02530cd..4d8cf2a 100644 --- a/examples/data-sources/main.tf +++ b/examples/data-sources/main.tf @@ -6,11 +6,19 @@ terraform { } } +# Provider configuration with token authentication provider "edge-connect" { endpoint = var.edge_connect_endpoint token = var.edge_connect_token } +# Alternative: Username/password authentication (uncomment to use) +# provider "edge-connect" { +# endpoint = var.edge_connect_endpoint +# username = var.edge_connect_username +# password = var.edge_connect_password +# } + # Data source: Look up an existing app by ID data "edge-connect_app" "existing_app" { id = var.app_id diff --git a/examples/data-sources/terraform.tfvars.example b/examples/data-sources/terraform.tfvars.example index 96229f6..a05898a 100644 --- a/examples/data-sources/terraform.tfvars.example +++ b/examples/data-sources/terraform.tfvars.example @@ -1,5 +1,13 @@ # Copy this file to terraform.tfvars and fill in your values edge_connect_endpoint = "https://api.edge-connect.example.com" -edge_connect_token = "your-api-token-here" -app_id = "my-existing-app" -app_instance_id = "my-existing-instance" + +# Option 1: Use token-based authentication +edge_connect_token = "your-api-token-here" + +# Option 2: Use username/password authentication (uncomment and fill in) +# edge_connect_username = "your-username" +# edge_connect_password = "your-password" + +# Data source lookup IDs +app_id = "my-existing-app" +app_instance_id = "my-existing-instance" diff --git a/examples/data-sources/variables.tf b/examples/data-sources/variables.tf index e45c7a1..e1e2ff2 100644 --- a/examples/data-sources/variables.tf +++ b/examples/data-sources/variables.tf @@ -4,10 +4,26 @@ variable "edge_connect_endpoint" { default = "https://api.edge-connect.example.com" } +# Token-based authentication variable "edge_connect_token" { - description = "Authentication token for Edge Connect API" + description = "Authentication token for Edge Connect API (use either token OR username/password)" type = string sensitive = true + default = "" +} + +# Username/password authentication +variable "edge_connect_username" { + description = "Username for Edge Connect API (use either token OR username/password)" + type = string + default = "" +} + +variable "edge_connect_password" { + description = "Password for Edge Connect API (use either token OR username/password)" + type = string + sensitive = true + default = "" } variable "app_id" { diff --git a/internal/provider/provider.go b/internal/provider/provider.go index e963239..6889e98 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -23,6 +23,8 @@ type EdgeConnectProvider struct { type EdgeConnectProviderModel struct { Endpoint types.String `tfsdk:"endpoint"` Token types.String `tfsdk:"token"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` } func (p *EdgeConnectProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { @@ -32,14 +34,24 @@ func (p *EdgeConnectProvider) Metadata(ctx context.Context, req provider.Metadat func (p *EdgeConnectProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ + MarkdownDescription: "Provider for Edge Connect API. Supports authentication via token or username/password.", Attributes: map[string]schema.Attribute{ "endpoint": schema.StringAttribute{ MarkdownDescription: "Edge Connect API endpoint", Required: true, }, "token": schema.StringAttribute{ - MarkdownDescription: "Edge Connect API token", - Required: true, + MarkdownDescription: "Edge Connect API token. Required if username/password are not provided.", + Optional: true, + Sensitive: true, + }, + "username": schema.StringAttribute{ + MarkdownDescription: "Edge Connect API username. Required if token is not provided.", + Optional: true, + }, + "password": schema.StringAttribute{ + MarkdownDescription: "Edge Connect API password. Required if token is not provided.", + Optional: true, Sensitive: true, }, }, @@ -57,7 +69,10 @@ func (p *EdgeConnectProvider) Configure(ctx context.Context, req provider.Config endpoint := data.Endpoint.ValueString() token := data.Token.ValueString() + username := data.Username.ValueString() + password := data.Password.ValueString() + // Validate endpoint if endpoint == "" { resp.Diagnostics.AddAttributeError( path.Root("endpoint"), @@ -66,11 +81,31 @@ func (p *EdgeConnectProvider) Configure(ctx context.Context, req provider.Config ) } - if token == "" { - resp.Diagnostics.AddAttributeError( - path.Root("token"), - "Missing Edge Connect API Token", - "The provider cannot create the Edge Connect API client as there is a missing or empty value for the Edge Connect API token.", + // Validate authentication: either token OR username/password must be provided + hasToken := token != "" + hasUsernamePassword := username != "" && password != "" + + if !hasToken && !hasUsernamePassword { + resp.Diagnostics.AddError( + "Missing Authentication Credentials", + "The provider requires authentication credentials. Please provide either:\n"+ + " - token: API token for authentication, or\n"+ + " - username and password: Username and password for authentication", + ) + } + + if hasToken && hasUsernamePassword { + resp.Diagnostics.AddError( + "Conflicting Authentication Methods", + "Both token and username/password authentication methods are provided. Please use only one authentication method.", + ) + } + + // Validate username/password pair + if (username != "" && password == "") || (username == "" && password != "") { + resp.Diagnostics.AddError( + "Incomplete Username/Password Credentials", + "Both username and password must be provided together for username/password authentication.", ) } @@ -79,12 +114,22 @@ func (p *EdgeConnectProvider) Configure(ctx context.Context, req provider.Config } ctx = tflog.SetField(ctx, "edge_connect_endpoint", endpoint) - ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "edge_connect_token") - tflog.Debug(ctx, "Creating Edge Connect client") + if hasToken { + ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "edge_connect_token") + tflog.Debug(ctx, "Creating Edge Connect client with token authentication") + } else { + ctx = tflog.SetField(ctx, "edge_connect_username", username) + ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "edge_connect_password") + tflog.Debug(ctx, "Creating Edge Connect client with username/password authentication") + } client := edgeclient.NewClient(endpoint) + // TODO: Configure client with authentication credentials + // If using token: client.SetToken(token) + // If using username/password: client.SetCredentials(username, password) + resp.DataSourceData = client resp.ResourceData = client