diff --git a/docs/resources/connection.md b/docs/resources/connection.md index a391d10e..2cc4fbd6 100644 --- a/docs/resources/connection.md +++ b/docs/resources/connection.md @@ -32,7 +32,7 @@ resource "trocco_connection" "bigquery" { } ``` -### Snowflake +### Snowflake ```terraform resource "trocco_connection" "snowflake" { @@ -161,18 +161,20 @@ resource "trocco_connection" "s3_with_assume_role" { - `aws_auth_type` (String) S3: The authentication type for the S3 connection. It must be one of `iam_user` or `assume_role`. - `aws_iam_user` (Attributes) S3: IAM User configuration. (see [below for nested schema](#nestedatt--aws_iam_user)) - `description` (String) The description of the connection. -- `gateway` (Attributes) MySQL: Whether to connect via SSH (see [below for nested schema](#nestedatt--gateway)) -- `host` (String) Snowflake: The host of a Snowflake account. -- `password` (String, Sensitive) Snowflake: The password for the Snowflake user. -- `port` (Number) MySQL: The port of the MySQL server. +- `driver` (String) PostgreSQL: The name of a PostgreSQL driver. +- `gateway` (Attributes) MySQL, PostgreSQL: Whether to connect via SSH (see [below for nested schema](#nestedatt--gateway)) +- `host` (String) Snowflake, PostgreSQL: The host of a (Snowflake, PostgreSQL) account. +- `password` (String, Sensitive) Snowflake, PostgreSQL: The password for the (Snowflake, PostgreSQL) user. +- `port` (Number) MySQL, PostgreSQL: The port of the (MySQL, PostgreSQL) server. - `private_key` (String, Sensitive) Snowflake: A private key for the Snowflake user. - `project_id` (String) BigQuery, GCS: A GCP project ID. - `resource_group_id` (Number) The ID of the resource group the connection belongs to. - `role` (String) Snowflake: A role attached to the Snowflake user. - `service_account_email` (String, Sensitive) GCS: A GCP service account email. - `service_account_json_key` (String, Sensitive) BigQuery: A GCP service account key. -- `ssl` (Attributes) MySQL: SSL configuration. (see [below for nested schema](#nestedatt--ssl)) -- `user_name` (String) Snowflake: The name of a Snowflake user. +- `ssl` (Attributes) MySQL, PostgreSQL: SSL configuration. (see [below for nested schema](#nestedatt--ssl)) +- `ssl_mode` (String) PostgreSQL: SSL connection mode. +- `user_name` (String) Snowflake, PostgreSQL: The name of a (Snowflake, PostgreSQL) user. ### Read-Only @@ -201,12 +203,12 @@ Optional: Optional: -- `host` (String, Sensitive) MySQL: SSH Host -- `key` (String, Sensitive) MySQL: SSH Private Key -- `key_passphrase` (String, Sensitive) MySQL: SSH Private Key Passphrase -- `password` (String, Sensitive) MySQL: SSH Password -- `port` (Number, Sensitive) MySQL: SSH Port -- `user_name` (String, Sensitive) MySQL: SSH User +- `host` (String, Sensitive) MySQL, PostgreSQL: SSH Host +- `key` (String, Sensitive) MySQL, PostgreSQL: SSH Private Key +- `key_passphrase` (String, Sensitive) MySQL, PostgreSQL: SSH Private Key Passphrase +- `password` (String, Sensitive) MySQL, PostgreSQL: SSH Password +- `port` (Number, Sensitive) MySQL, PostgreSQL: SSH Port +- `user_name` (String, Sensitive) MySQL, PostgreSQL: SSH User @@ -214,13 +216,59 @@ Optional: Optional: -- `ca` (String, Sensitive) MySQL: CA certificate -- `cert` (String, Sensitive) MySQL: Certificate (CRT file) -- `key` (String, Sensitive) MySQL: Key (KEY file) +- `ca` (String, Sensitive) MySQL, PostgreSQL: CA certificate +- `cert` (String, Sensitive) MySQL, PostgreSQL: Certificate (CRT file) +- `key` (String, Sensitive) MySQL, PostgreSQL: Key (KEY file) +### PostgreSQL + +```terraform +resource "trocco_connection" "postgresql" { + connection_type = "postgresql" + name = "PostgreSQL Example" + description = "This is a PostgreSQL connection example" + host = "db.example.com" + port = 5432 + user_name = "root" + password = "password" + ssl_mode = "require" + driver = "postgresql_42_5_1" + ssl = { + ca = <<-SSL_CA + -----BEGIN PRIVATE KEY----- + ...SSL CA... + -----END PRIVATE KEY----- + SSL_CA + cert = <<-SSL_CERT + -----BEGIN CERTIFICATE----- + ...SSL CRT... + -----END CERTIFICATE----- + SSL_CERT + key = <<-SSL_KEY + -----BEGIN PRIVATE KEY----- + ...SSL KEY... + -----END PRIVATE KEY----- + SSL_KEY + } + gateway = { + host = "gateway.example.com" + port = 1234 + user_name = "gateway-joe" + password = "gateway-joepass" + key = <<-GATEWAY_KEY + -----BEGIN PRIVATE KEY----- + ... GATEWAY KEY... + -----END PRIVATE KEY----- + GATEWAY_KEY + key_passphrase = "sample_passphrase" + } + resource_group_id = 1 +} +``` + ## Import Import is supported using the following syntax: diff --git a/examples/resources/trocco_connection/postgresql.tf b/examples/resources/trocco_connection/postgresql.tf new file mode 100644 index 00000000..a8827d11 --- /dev/null +++ b/examples/resources/trocco_connection/postgresql.tf @@ -0,0 +1,41 @@ +resource "trocco_connection" "postgresql" { + connection_type = "postgresql" + name = "PostgreSQL Example" + description = "This is a PostgreSQL connection example" + host = "db.example.com" + port = 5432 + user_name = "root" + password = "password" + ssl_mode = "require" + driver = "postgresql_42_5_1" + ssl = { + ca = <<-SSL_CA + -----BEGIN PRIVATE KEY----- + ...SSL CA... + -----END PRIVATE KEY----- + SSL_CA + cert = <<-SSL_CERT + -----BEGIN CERTIFICATE----- + ...SSL CRT... + -----END CERTIFICATE----- + SSL_CERT + key = <<-SSL_KEY + -----BEGIN PRIVATE KEY----- + ...SSL KEY... + -----END PRIVATE KEY----- + SSL_KEY + } + gateway = { + host = "gateway.example.com" + port = 1234 + user_name = "gateway-joe" + password = "gateway-joepass" + key = <<-GATEWAY_KEY + -----BEGIN PRIVATE KEY----- + ... GATEWAY KEY... + -----END PRIVATE KEY----- + GATEWAY_KEY + key_passphrase = "sample_passphrase" + } + resource_group_id = 1 +} diff --git a/internal/client/connection.go b/internal/client/connection.go index 9a1ec803..5bd58647 100644 --- a/internal/client/connection.go +++ b/internal/client/connection.go @@ -48,6 +48,9 @@ type Connection struct { AWSSecretAccessKey *string `json:"aws_secret_access_key,omitempty"` AWSAssumeRoleAccountID *string `json:"aws_assume_role_account_id,omitempty"` AWSAssumeRoleName *string `json:"aws_assume_role_name,omitempty"` + + // PostgreSQL Fields + SSLMode *string `json:"ssl_mode,omitempty"` } type GetConnectionsInput struct { @@ -97,6 +100,12 @@ type CreateConnectionInput struct { AWSSecretAccessKey *string `json:"aws_secret_access_key,omitempty"` AWSAssumeRoleAccountID *string `json:"aws_assume_role_account_id,omitempty"` AWSAssumeRoleName *string `json:"aws_assume_role_name,omitempty"` + + // PostgreSQL Fields + SSLClientCa *string `json:"ssl_client_ca,omitempty"` + SSLClientKey *string `json:"ssl_client_key,omitempty"` + SSLMode *string `json:"ssl_mode,omitempty"` + Driver *string `json:"driver,omitempty"` } type UpdateConnectionInput struct { @@ -141,6 +150,12 @@ type UpdateConnectionInput struct { AWSSecretAccessKey *string `json:"aws_secret_access_key,omitempty"` AWSAssumeRoleAccountID *string `json:"aws_assume_role_account_id,omitempty"` AWSAssumeRoleName *string `json:"aws_assume_role_name,omitempty"` + + // PostgreSQL Fields + SSLClientCa *string `json:"ssl_client_ca,omitempty"` + SSLClientKey *string `json:"ssl_client_key,omitempty"` + SSLMode *string `json:"ssl_mode,omitempty"` + Driver *string `json:"driver,omitempty"` } func (c *TroccoClient) GetConnections(connectionType string, in *GetConnectionsInput) (*ConnectionList, error) { diff --git a/internal/provider/connection_resource.go b/internal/provider/connection_resource.go index 6f2454be..6d3d12df 100644 --- a/internal/provider/connection_resource.go +++ b/internal/provider/connection_resource.go @@ -57,6 +57,10 @@ type connectionResourceModel struct { SSL *connection.SSL `tfsdk:"ssl"` Gateway *connection.Gateway `tfsdk:"gateway"` + // PostgreSQL Fields + SSLMode types.String `tfsdk:"ssl_mode"` + Driver types.String `tfsdk:"driver"` + // S3 Fields AWSAuthType types.String `tfsdk:"aws_auth_type"` AWSIAMUser *connection.AWSIAMUser `tfsdk:"aws_iam_user"` @@ -91,6 +95,10 @@ func (m *connectionResourceModel) ToCreateConnectionInput() *client.CreateConnec // S3 Fields AWSAuthType: m.AWSAuthType.ValueStringPointer(), + + // PostgreSQL Fields + SSLMode: m.SSLMode.ValueStringPointer(), + Driver: m.Driver.ValueStringPointer(), } // SSL Fields @@ -98,7 +106,9 @@ func (m *connectionResourceModel) ToCreateConnectionInput() *client.CreateConnec input.SSL = model.NewNullableBool(types.BoolValue(true)) input.SSLCA = m.SSL.CA.ValueStringPointer() input.SSLCert = m.SSL.Cert.ValueStringPointer() + input.SSLClientCa = m.SSL.Cert.ValueStringPointer() input.SSLKey = m.SSL.Key.ValueStringPointer() + input.SSLClientKey = m.SSL.Key.ValueStringPointer() } else { input.SSL = model.NewNullableBool(types.BoolValue(false)) } @@ -159,6 +169,10 @@ func (m *connectionResourceModel) ToUpdateConnectionInput() *client.UpdateConnec // S3 Fields AWSAuthType: m.AWSAuthType.ValueStringPointer(), + + // PostgreSQL Fields + SSLMode: m.SSLMode.ValueStringPointer(), + Driver: m.Driver.ValueStringPointer(), } // SSL Fields @@ -167,6 +181,8 @@ func (m *connectionResourceModel) ToUpdateConnectionInput() *client.UpdateConnec input.SSLCA = m.SSL.CA.ValueStringPointer() input.SSLCert = m.SSL.Cert.ValueStringPointer() input.SSLKey = m.SSL.Key.ValueStringPointer() + input.SSLClientCa = m.SSL.Cert.ValueStringPointer() + input.SSLClientKey = m.SSL.Key.ValueStringPointer() } else { input.SSL = model.NewNullableBool(types.BoolValue(false)) } @@ -252,7 +268,7 @@ func (r *connectionResource) Schema( stringplanmodifier.RequiresReplace(), }, Validators: []validator.String{ - stringvalidator.OneOf("bigquery", "snowflake", "gcs", "mysql", "s3"), + stringvalidator.OneOf("bigquery", "snowflake", "gcs", "mysql", "s3", "postgresql"), }, }, "id": schema.Int64Attribute{ @@ -306,14 +322,14 @@ func (r *connectionResource) Schema( // Snowflake Fields "host": schema.StringAttribute{ - MarkdownDescription: "Snowflake: The host of a Snowflake account.", + MarkdownDescription: "Snowflake, PostgreSQL: The host of a (Snowflake, PostgreSQL) account.", Optional: true, Validators: []validator.String{ stringvalidator.UTF8LengthAtLeast(1), }, }, "user_name": schema.StringAttribute{ - MarkdownDescription: "Snowflake: The name of a Snowflake user.", + MarkdownDescription: "Snowflake, PostgreSQL: The name of a (Snowflake, PostgreSQL) user.", Optional: true, Validators: []validator.String{ stringvalidator.UTF8LengthAtLeast(1), @@ -334,7 +350,7 @@ func (r *connectionResource) Schema( }, }, "password": schema.StringAttribute{ - MarkdownDescription: "Snowflake: The password for the Snowflake user.", + MarkdownDescription: "Snowflake, PostgreSQL: The password for the (Snowflake, PostgreSQL) user.", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -369,19 +385,20 @@ func (r *connectionResource) Schema( // MySQL Fields "port": schema.Int64Attribute{ - MarkdownDescription: "MySQL: The port of the MySQL server.", + MarkdownDescription: "MySQL, PostgreSQL: The port of the (MySQL, PostgreSQL) server.", Optional: true, Validators: []validator.Int64{ int64validator.AtLeast(1), int64validator.AtMost(65535), }, }, + "ssl": schema.SingleNestedAttribute{ - MarkdownDescription: "MySQL: SSL configuration.", + MarkdownDescription: "MySQL, PostgreSQL: SSL configuration.", Optional: true, Attributes: map[string]schema.Attribute{ "ca": schema.StringAttribute{ - MarkdownDescription: "MySQL: CA certificate", + MarkdownDescription: "MySQL, PostgreSQL: CA certificate", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -391,7 +408,7 @@ func (r *connectionResource) Schema( Default: stringdefault.StaticString(""), }, "cert": schema.StringAttribute{ - MarkdownDescription: "MySQL: Certificate (CRT file)", + MarkdownDescription: "MySQL, PostgreSQL: Certificate (CRT file)", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -401,7 +418,7 @@ func (r *connectionResource) Schema( Default: stringdefault.StaticString(""), }, "key": schema.StringAttribute{ - MarkdownDescription: "MySQL: Key (KEY file)", + MarkdownDescription: "MySQL, PostgreSQL: Key (KEY file)", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -413,11 +430,11 @@ func (r *connectionResource) Schema( }, }, "gateway": schema.SingleNestedAttribute{ - MarkdownDescription: "MySQL: Whether to connect via SSH", + MarkdownDescription: "MySQL, PostgreSQL: Whether to connect via SSH", Optional: true, Attributes: map[string]schema.Attribute{ "host": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH Host", + MarkdownDescription: "MySQL, PostgreSQL: SSH Host", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -425,7 +442,7 @@ func (r *connectionResource) Schema( }, }, "port": schema.Int64Attribute{ - MarkdownDescription: "MySQL: SSH Port", + MarkdownDescription: "MySQL, PostgreSQL: SSH Port", Optional: true, Sensitive: true, Validators: []validator.Int64{ @@ -434,7 +451,7 @@ func (r *connectionResource) Schema( }, }, "user_name": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH User", + MarkdownDescription: "MySQL, PostgreSQL: SSH User", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -442,21 +459,21 @@ func (r *connectionResource) Schema( }, }, "password": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH Password", + MarkdownDescription: "MySQL, PostgreSQL: SSH Password", Optional: true, Computed: true, Sensitive: true, Default: stringdefault.StaticString(""), }, "key": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH Private Key", + MarkdownDescription: "MySQL, PostgreSQL: SSH Private Key", Optional: true, Computed: true, Sensitive: true, Default: stringdefault.StaticString(""), }, "key_passphrase": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH Private Key Passphrase", + MarkdownDescription: "MySQL, PostgreSQL: SSH Private Key Passphrase", Optional: true, Computed: true, Sensitive: true, @@ -514,6 +531,25 @@ func (r *connectionResource) Schema( }, }, }, + + // PostgreSQL Fields + "ssl_mode": schema.StringAttribute{ + MarkdownDescription: "PostgreSQL: SSL connection mode.", + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.OneOf("require", "verify-ca"), + }, + }, + "driver": schema.StringAttribute{ + MarkdownDescription: "PostgreSQL: The name of a PostgreSQL driver.", + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.OneOf("postgresql_42_5_1", "postgresql_9_4_1205_jdbc41"), + }, + Default: stringdefault.StaticString("postgresql_42_5_1"), + }, }, } } @@ -578,6 +614,10 @@ func (r *connectionResource) Create( AWSAuthType: types.StringPointerValue(conn.AWSAuthType), AWSIAMUser: plan.AWSIAMUser, AWSAssumeRole: connection.NewAWSAssumeRole(conn), + + // PostgreSQL Fields + SSLMode: types.StringPointerValue(conn.SSLMode), + Driver: types.StringPointerValue(conn.Driver), } resp.Diagnostics.Append(resp.State.Set(ctx, newState)...) } @@ -659,6 +699,10 @@ func (r *connectionResource) Update( AWSAuthType: types.StringPointerValue(connection.AWSAuthType), AWSIAMUser: plan.AWSIAMUser, AWSAssumeRole: plan.AWSAssumeRole, + + // PostgreSQL Fields + SSLMode: types.StringPointerValue(connection.SSLMode), + Driver: types.StringPointerValue(connection.Driver), } resp.Diagnostics.Append(resp.State.Set(ctx, newState)...) } @@ -719,6 +763,10 @@ func (r *connectionResource) Read( AWSAuthType: types.StringPointerValue(conn.AWSAuthType), AWSIAMUser: state.AWSIAMUser, AWSAssumeRole: connection.NewAWSAssumeRole(conn), + + // PostgreSQL Fields + SSLMode: types.StringPointerValue(conn.SSLMode), + Driver: types.StringPointerValue(conn.Driver), } resp.Diagnostics.Append(resp.State.Set(ctx, newState)...) } @@ -847,6 +895,16 @@ func (r *connectionResource) ValidateConfig( validateRequiredString(plan.AWSAssumeRole.AccountRoleName, "aws_assume_role.account_role_name", "S3", resp) } } + case "postgresql": + validateRequiredString(plan.Host, "host", "PostgreSQL", resp) + validateRequiredInt(plan.Port, "port", "PostgreSQL", resp) + validateRequiredString(plan.UserName, "user_name", "PostgreSQL", resp) + validateRequiredString(plan.Password, "password", "PostgreSQL", resp) + if plan.Gateway != nil { + validateRequiredString(plan.Gateway.Host, "gateway.host", "PostgreSQL", resp) + validateRequiredInt(plan.Gateway.Port, "gateway.port", "PostgreSQL", resp) + validateRequiredString(plan.Gateway.UserName, "gateway.user_name", "PostgreSQL", resp) + } } } diff --git a/templates/resources/connection.md.tmpl b/templates/resources/connection.md.tmpl index 067eaf79..807ea7f8 100644 --- a/templates/resources/connection.md.tmpl +++ b/templates/resources/connection.md.tmpl @@ -15,7 +15,7 @@ description: |- {{codefile "terraform" "examples/resources/trocco_connection/bigquery.tf"}} -### Snowflake +### Snowflake {{codefile "terraform" "examples/resources/trocco_connection/snowflake.tf"}} @@ -33,6 +33,10 @@ description: |- {{.SchemaMarkdown}} +### PostgreSQL + +{{codefile "terraform" "examples/resources/trocco_connection/postgresql.tf"}} + ## Import Import is supported using the following syntax: