diff --git a/docs/index.md b/docs/index.md index 2d3a96ab..a432b87c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -12,6 +12,8 @@ The Redshift provider provides configuration management resources for ## Example Usage +### Authentication using fixed password + ```terraform provider "redshift" { host = var.redshift_host @@ -20,6 +22,18 @@ provider "redshift" { } ``` +### Authentication using temporary credentials + +```terraform +provider "redshift" { + host = var.redshift_host + username = var.redshift_user + temporary_credentials { + cluster_identifier = "my-cluster" + } +} +``` + ## Schema @@ -31,4 +45,18 @@ provider "redshift" { - **password** (String, Sensitive) Password to be used if the Redshift server demands password authentication. - **port** (Number) The Redshift port number to connect to at the server host. - **sslmode** (String) This option determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the Redshift server. Valid values are `require` (default, always SSL, also skip verification), `verify-ca` (always SSL, verify that the certificate presented by the server was signed by a trusted CA), `verify-full` (always SSL, verify that the certification presented by the server was signed by a trusted CA and the server host name matches the one in the certificate), `disable` (no SSL). +- **temporary_credentials** (Block List, Max: 1) Configuration for obtaining a temporary password using redshift:GetClusterCredentials (see [below for nested schema](#nestedblock--temporary_credentials)) - **username** (String) Redshift user name to connect as. + + +### Nested Schema for `temporary_credentials` + +Required: + +- **cluster_identifier** (String) The unique identifier of the cluster that contains the database for which your are requesting credentials. This parameter is case sensitive. + +Optional: + +- **auto_create_user** (Boolean) Create a database user with the name specified for the user if one does not exist. +- **db_groups** (Set of String) A list of the names of existing database groups that the user will join for the current session, in addition to any group memberships for an existing user. If not specified, a new user is added only to PUBLIC. +- **duration_seconds** (Number) The number of seconds until the returned temporary password expires. diff --git a/examples/provider/provider_using_temporary_credentials.tf b/examples/provider/provider_using_temporary_credentials.tf new file mode 100644 index 00000000..aeaa93bf --- /dev/null +++ b/examples/provider/provider_using_temporary_credentials.tf @@ -0,0 +1,7 @@ +provider "redshift" { + host = var.redshift_host + username = var.redshift_user + temporary_credentials { + cluster_identifier = "my-cluster" + } +} diff --git a/go.mod b/go.mod index 9839edb1..df06430d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,10 @@ module github.com/brainly/terraform-provider-redshift go 1.16 require ( + github.com/aws/aws-sdk-go-v2 v1.7.0 + github.com/aws/aws-sdk-go-v2/config v1.4.1 + github.com/aws/aws-sdk-go-v2/service/redshift v1.8.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.5.0 github.com/hashicorp/terraform-plugin-docs v0.4.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.6.1 github.com/lib/pq v1.10.2 diff --git a/go.sum b/go.sum index 1fef55b4..6c8f1e37 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,10 @@ github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f/go.mod h1:k8feO4+kXDxro6ErPXBRTJ/ro2mf0SsFG8s7doP9kJE= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= @@ -61,10 +63,31 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6 github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/aws/aws-sdk-go v1.25.3 h1:uM16hIw9BotjZKMZlX05SN2EFtaWfi/NonPKIARiBLQ= github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v1.7.0 h1:UYGnoIPIzed+ycmgw8Snb/0HK+KlMD+SndLTneG8ncE= +github.com/aws/aws-sdk-go-v2 v1.7.0/go.mod h1:tb9wi5s61kTDA5qCkcDbt3KRVV74GGslQkl/DRdX/P4= +github.com/aws/aws-sdk-go-v2/config v1.4.1 h1:PcGp9Kf+1dHJmP3EIDZJmAmWfGABFTU0obuvYQNzWH8= +github.com/aws/aws-sdk-go-v2/config v1.4.1/go.mod h1:HCDWZ/oeY59TPtXslxlbkCqLQBsVu6b09kiG43tdP+I= +github.com/aws/aws-sdk-go-v2/credentials v1.3.0 h1:vXxTINCsHn6LKhR043jwSLd6CsL7KOEU7b1woMr1K1A= +github.com/aws/aws-sdk-go-v2/credentials v1.3.0/go.mod h1:tOcv+qDZ0O+6Jk2beMl5JnZX6N0H7O8fw9UsD3bP7GI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.2.0 h1:ucExzYCoAiL9GpKOsKkQLsa43wTT23tcdP4cDTSbZqY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.2.0/go.mod h1:XvzoGzuS0kKPzCQtJCC22Xh/mMgVAzfGo/0V+mk/Cu0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.1.0 h1:DJq/vXXF+LAFaa/kQX9C6arlf4xX4uaaqGWIyAKOCpM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.1.0/go.mod h1:qGQ/9IfkZonRNSNLE99/yBJ7EPA/h8jlWEqtJCcaj+Q= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.0 h1:g2npzssI/6XsoQaPYCxliMFeC5iNKKvO0aC+/wWOE0A= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.0/go.mod h1:a7XLWNKuVgOxjssEF019IiHPv35k8KHBaWv/wJAfi2A= +github.com/aws/aws-sdk-go-v2/service/redshift v1.8.0 h1:6G/WN/1nx+MKf/OhlmZeZfRd8/bMyWlQ5EPi5EoHI7c= +github.com/aws/aws-sdk-go-v2/service/redshift v1.8.0/go.mod h1:Vo3zqEXYYt2a7Us70mdXYSD8CW3Fo3NnRmTALZK1NfU= +github.com/aws/aws-sdk-go-v2/service/sso v1.3.0 h1:DMi9w+TpUam7eJ8ksL7svfzpqpqem2MkDAJKW8+I2/k= +github.com/aws/aws-sdk-go-v2/service/sso v1.3.0/go.mod h1:qWR+TUuvfji9udM79e4CPe87C5+SjMEb2TFXkZaI0Vc= +github.com/aws/aws-sdk-go-v2/service/sts v1.5.0 h1:Y1K9dHE2CYOWOvaJSIITq4mJfLX43iziThTvqs5FqOg= +github.com/aws/aws-sdk-go-v2/service/sts v1.5.0/go.mod h1:HjDKUmissf6Mlut+WzG2r35r6LeTKmLEDJ6p9NryzLg= +github.com/aws/smithy-go v1.5.0 h1:2grDq7LxZlo8BZUDeqRfQnQWLZpInmh2TLPPkJku3YM= +github.com/aws/smithy-go v1.5.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= @@ -88,7 +111,9 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= @@ -96,6 +121,7 @@ github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI github.com/go-git/go-billy/v5 v5.1.0 h1:4pl5BV4o7ZG/lterP4S6WzJ6xr49Ba5ET9ygheTYahk= github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM= github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= @@ -141,6 +167,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= @@ -227,6 +254,10 @@ github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyX github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -288,6 +319,7 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -460,6 +492,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -603,6 +636,7 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/redshift/provider.go b/redshift/provider.go index a7e05062..fc10d4dc 100644 --- a/redshift/provider.go +++ b/redshift/provider.go @@ -1,6 +1,13 @@ package redshift import ( + "context" + "fmt" + "log" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/redshift" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) @@ -30,6 +37,9 @@ func Provider() *schema.Provider { DefaultFunc: schema.EnvDefaultFunc("REDSHIFT_PASSWORD", nil), Description: "Password to be used if the Redshift server demands password authentication.", Sensitive: true, + ConflictsWith: []string{ + "temporary_credentials", + }, }, "port": { Type: schema.TypeInt, @@ -62,6 +72,48 @@ func Provider() *schema.Provider { Description: "Maximum number of connections to establish to the database. Zero means unlimited.", ValidateFunc: validation.IntAtLeast(-1), }, + "temporary_credentials": { + Type: schema.TypeList, + Optional: true, + Description: "Configuration for obtaining a temporary password using redshift:GetClusterCredentials", + MaxItems: 1, + ConflictsWith: []string{ + "password", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cluster_identifier": { + Type: schema.TypeString, + Required: true, + Description: "The unique identifier of the cluster that contains the database for which your are requesting credentials. This parameter is case sensitive.", + ValidateFunc: validation.StringLenBetween(1, 2147483647), + }, + "auto_create_user": { + Type: schema.TypeBool, + Optional: true, + Description: "Create a database user with the name specified for the user if one does not exist.", + Default: false, + }, + "db_groups": { + Type: schema.TypeSet, + Set: schema.HashString, + Optional: true, + Description: "A list of the names of existing database groups that the user will join for the current session, in addition to any group memberships for an existing user. If not specified, a new user is added only to PUBLIC.", + MaxItems: 2147483647, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: dbGroupValidate, + }, + }, + "duration_seconds": { + Type: schema.TypeInt, + Optional: true, + Description: "The number of seconds until the returned temporary password expires.", + ValidateFunc: validation.IntBetween(900, 3600), + }, + }, + }, + }, }, ResourcesMap: map[string]*schema.Resource{ "redshift_user": redshiftUser(), @@ -75,16 +127,95 @@ func Provider() *schema.Provider { } func providerConfigure(d *schema.ResourceData) (interface{}, error) { + username, password, err := resolveCredentials(d) + if err != nil { + return nil, err + } config := Config{ Host: d.Get("host").(string), Port: d.Get("port").(int), - Username: d.Get("username").(string), - Password: d.Get("password").(string), + Username: username, + Password: password, Database: d.Get("database").(string), SSLMode: d.Get("sslmode").(string), MaxConns: d.Get("max_connections").(int), } + log.Println("[DEBUG] creating database client") client := config.NewClient(d.Get("database").(string)) + log.Println("[DEBUG] created database client") return client, nil } + +func resolveCredentials(d *schema.ResourceData) (string, string, error) { + username, ok := d.GetOk("username") + if (!ok) || username == nil { + return "", "", fmt.Errorf("Username is required") + } + if password, passwordIsSet := d.GetOk("password"); passwordIsSet { + if password.(string) != "" { + log.Println("[DEBUG] using password authentication") + return username.(string), password.(string), nil + } + } + log.Println("[DEBUG] using temporary credentials authentication") + dbUser, password, err := temporaryCredentials(username.(string), d) + log.Printf("[DEBUG] got temporary credentials with username %s\n", dbUser) + return dbUser, password, err +} + +// temporaryCredentials gets temporary credentials using GetClusterCredentials +func temporaryCredentials(username string, d *schema.ResourceData) (string, string, error) { + sdkClient, err := redshiftSdkClient(d) + if err != nil { + return "", "", err + } + + config, ok := d.Get("temporary_credentials").([]interface{}) + if (!ok) || config == nil { + return "", "", fmt.Errorf("temporary_credentials not configured") + } + c := config[0].(map[string]interface{}) + input := &redshift.GetClusterCredentialsInput{ + ClusterIdentifier: aws.String(c["cluster_identifier"].(string)), + DbName: aws.String(d.Get("database").(string)), + DbUser: aws.String(username), + } + if autoCreateUser, ok := c["auto_create_user"]; ok && autoCreateUser != nil { + input.AutoCreate = aws.Bool(autoCreateUser.(bool)) + } + if dbGroups, ok := c["db_groups"]; ok { + if dbGroups != nil { + dbGroupsList := dbGroups.(*schema.Set).List() + if len(dbGroupsList) > 0 { + var groups []string + for _, group := range dbGroupsList { + if group.(string) != "" { + groups = append(groups, group.(string)) + } + } + input.DbGroups = groups + } + } + } + if durationSeconds, ok := c["duration_seconds"]; ok { + duration := durationSeconds.(int) + if duration > 0 { + input.DurationSeconds = aws.Int32(int32(duration)) + } + } + log.Println("[DEBUG] making GetClusterCredentials request") + response, err := sdkClient.GetClusterCredentials(context.TODO(), input) + if err != nil { + return "", "", err + } + return aws.ToString(response.DbUser), aws.ToString(response.DbPassword), nil +} + +func redshiftSdkClient(d *schema.ResourceData) (*redshift.Client, error) { + config, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + return nil, err + } + return redshift.NewFromConfig(config), nil +} diff --git a/redshift/provider_test.go b/redshift/provider_test.go index 9c755947..11fbe614 100644 --- a/redshift/provider_test.go +++ b/redshift/provider_test.go @@ -1,10 +1,15 @@ package redshift import ( + "context" + "fmt" "os" "testing" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) var ( @@ -38,3 +43,62 @@ func testAccPreCheck(t *testing.T) { t.Fatal("REDSHIFT_USER must be set for acceptance tests") } } + +func initTemporaryCredentialsProvider(t *testing.T, provider *schema.Provider) { + var clusterIdentifier string + if clusterIdentifier = os.Getenv("REDSHIFT_CLUSTER_IDENTIFIER"); clusterIdentifier == "" { + t.Skip("REDSHIFT_CLUSTER_IDENTIFIER must be set for acceptance tests") + } + + sdkClient, err := stsClient(t) + if err != nil { + t.Skip(fmt.Sprintf("Unable to load STS client due to: %s", err)) + } + + response, err := sdkClient.GetCallerIdentity(context.TODO(), nil) + if err != nil { + t.Skip(fmt.Sprintf("Unable to get current STS identity due to: %s", err)) + } + if response == nil { + t.Skip("Unable to get current STS identity. Empty response.") + } + + config := map[string]interface{}{ + "temporary_credentials": []interface{}{ + map[string]interface{}{ + "cluster_identifier": clusterIdentifier, + }, + }, + } + diagnostics := provider.Configure(context.Background(), terraform.NewResourceConfigRaw(config)) + if diagnostics != nil { + if diagnostics.HasError() { + t.Fatalf("Failed to configure temporary credentials provider: %v", diagnostics) + } + } +} + +func stsClient(t *testing.T) (*sts.Client, error) { + config, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + return nil, err + } + return sts.NewFromConfig(config), nil +} + +func TestAccRedshiftTemporaryCredentials(t *testing.T) { + provider := Provider() + redshift_password := os.Getenv("REDSHIFT_PASSWORD") + defer os.Setenv("REDSHIFT_PASSWORD", redshift_password) + os.Unsetenv("REDSHIFT_PASSWORD") + initTemporaryCredentialsProvider(t, provider) + client, ok := provider.Meta().(*Client) + if !ok { + t.Fatal("Unable to initialize client") + } + db, err := client.Connect() + if err != nil { + t.Fatalf("Unable to connect to database: %s", err) + } + defer db.Close() +} diff --git a/redshift/validation.go b/redshift/validation.go new file mode 100644 index 00000000..6348c58a --- /dev/null +++ b/redshift/validation.go @@ -0,0 +1,176 @@ +package redshift + +import ( + "regexp" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +var reservedWords = []string{ + "aes128", + "aes256", + "all", + "allowoverwrite", + "analyse", + "analyze", + "and", + "any", + "array", + "as", + "asc", + "authorization", + "az64", + "backup", + "between", + "binary", + "blanksasnull", + "both", + "bytedict", + "bzip2", + "case", + "cast", + "check", + "collate", + "column", + "constraint", + "create", + "credentials", + "cross", + "current_date", + "current_time", + "current_timestamp", + "current_user", + "current_user_id", + "default", + "deferrable", + "deflate", + "defrag", + "delta", + "delta32k", + "desc", + "disable", + "distinct", + "do", + "else", + "emptyasnull", + "enable", + "encode", + "encrypt ", + "encryption", + "end", + "except", + "explicit", + "false", + "for", + "foreign", + "freeze", + "from", + "full", + "globaldict256", + "globaldict64k", + "grant", + "group", + "gzip", + "having", + "identity", + "ignore", + "ilike", + "in", + "initially", + "inner", + "intersect", + "into", + "is", + "isnull", + "join", + "language", + "leading", + "left", + "like", + "limit", + "localtime", + "localtimestamp", + "lun", + "luns", + "lzo", + "lzop", + "minus", + "mostly16", + "mostly32", + "mostly8", + "natural", + "new", + "not", + "notnull", + "null", + "nulls", + "off", + "offline", + "offset", + "oid", + "old", + "on", + "only", + "open", + "or", + "order", + "outer", + "overlaps", + "parallel", + "partition", + "percent", + "permissions", + "placing", + "primary", + "raw", + "readratio", + "recover", + "references", + "respect", + "rejectlog", + "resort", + "restore", + "right", + "select", + "session_user", + "similar", + "snapshot ", + "some", + "sysdate", + "system", + "table", + "tag", + "tdes", + "text255", + "text32k", + "then", + "timestamp", + "to", + "top", + "trailing", + "true", + "truncatecolumns", + "union", + "unique", + "user", + "using", + "verbose", + "wallet", + "when", + "where", + "with", + "without", +} + +var dbGroupAcceptableCharacters = regexp.MustCompile("[a-z1-9_+.@-]{1,64}") +var startsWithLetter = regexp.MustCompile("[a-zA-Z].*") + +// Validation rules are specified at https://docs.aws.amazon.com/redshift/latest/APIReference/API_GetClusterCredentials.html +// admittedly, some of these are a bit redundant +var dbGroupValidate = validation.All( + validation.StringLenBetween(1, 64), + validation.StringMatch(dbGroupAcceptableCharacters, "Must contain only lowercase letters, numbers, underscore, plus sign, period (dot), at symbol (@), or hyphen."), + validation.StringMatch(startsWithLetter, "First character must be a letter."), + validation.StringDoesNotContainAny(":/"), + validation.StringNotInSlice(reservedWords, true), +) diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index 5b30e22c..54eda9ce 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -12,6 +12,12 @@ The Redshift provider provides configuration management resources for ## Example Usage +### Authentication using fixed password + {{ tffile "examples/provider/provider.tf" }} +### Authentication using temporary credentials + +{{ tffile "examples/provider/provider_using_temporary_credentials.tf" }} + {{ .SchemaMarkdown | trimspace }}