From c416d404634c6d2822f48ee4b0d0c0fc51f738f3 Mon Sep 17 00:00:00 2001 From: Maxime Guyot Date: Wed, 29 Sep 2021 10:57:08 +0200 Subject: [PATCH 1/2] Add Importer for tls_private_key --- internal/provider/fixtures/fixtures.go | 7 ++ internal/provider/resource_private_key.go | 27 +++++ .../provider/resource_private_key_test.go | 99 +++++++++++++++++++ templates/resources/private_key.md.tmpl | 8 ++ 4 files changed, 141 insertions(+) diff --git a/internal/provider/fixtures/fixtures.go b/internal/provider/fixtures/fixtures.go index b2a2d99c..17bebac6 100644 --- a/internal/provider/fixtures/fixtures.go +++ b/internal/provider/fixtures/fixtures.go @@ -22,6 +22,13 @@ rpxCHbX0xSJh0s8j7exRHMF8W16DHjjkc265YdWPXWo= -----END RSA PRIVATE KEY----- ` + TestPrivateKeyECDSA = ` +-----BEGIN EC PRIVATE KEY----- +MGgCAQEEHJEA3wyFdST8uuJeYAoW1DISVM8sqj/xhk3AJZegBwYFK4EEACGhPAM6 +AARnftbJ7ZLuRFxEeYJ8iVWLo45/87Nb4fxijONNCkDIIqOrBJ6LFhiYXZ0AOErw +6eZBkrwPQ2ADQA== +-----END EC PRIVATE KEY-----` + TestPrivateKeyOpenSSHPEM = ` -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn diff --git a/internal/provider/resource_private_key.go b/internal/provider/resource_private_key.go index 894c1eda..6f221464 100644 --- a/internal/provider/resource_private_key.go +++ b/internal/provider/resource_private_key.go @@ -11,9 +11,11 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "os" "strings" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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/int64default" @@ -33,6 +35,7 @@ type privateKeyResource struct{} var ( _ resource.Resource = (*privateKeyResource)(nil) _ resource.ResourceWithUpgradeState = (*privateKeyResource)(nil) + _ resource.ResourceWithImportState = (*privateKeyResource)(nil) ) func NewPrivateKeyResource() resource.Resource { @@ -367,6 +370,30 @@ func (r *privateKeyResource) Delete(ctx context.Context, _ resource.DeleteReques tflog.Debug(ctx, "Removing private key from state") } +func (r *privateKeyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + bytes, err := os.ReadFile(req.ID) + if err != nil { + resp.Diagnostics.AddError("Could not read file", err.Error()) + return + } + key, algo, err := parsePrivateKeyPEM(bytes) + if err != nil { + resp.Diagnostics.AddError("Error parsing private key", err.Error()) + } + + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("algorithm"), algo)...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("private_key_pem"), string(bytes))...) + resp.Diagnostics.Append(setPublicKeyAttributes(ctx, &resp.State, key)...) + switch k := key.(type) { + case *rsa.PrivateKey: + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("rsa_bits"), int64(k.N.BitLen()))...) + case *ecdsa.PrivateKey: + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("ecdsa_curve"), k.Curve.Params().Name)...) + case ed25519.PrivateKey: + // Nothing to do + } +} + func (r *privateKeyResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { schemaV1 := privateKeyResourceSchemaV1() diff --git a/internal/provider/resource_private_key_test.go b/internal/provider/resource_private_key_test.go index 9cabf7de..60938795 100644 --- a/internal/provider/resource_private_key_test.go +++ b/internal/provider/resource_private_key_test.go @@ -434,3 +434,102 @@ func TestOpenSSHComment(t *testing.T) { }, }) } + +// type keyLens struct { +// algorithm string +// rsa_bits int +// ecdsa_curve string +// } + +// var testAccProviders map[string]*schema.Provider +// var testAccProvider *schema.Provider + +// func init() { +// testAccProvider = New() +// testAccProviders = map[string]*schema.Provider{ +// "tls": testAccProvider, +// } +// } + +// func TestAccImportKey(t *testing.T) { +// r.UnitTest(t, r.TestCase{ +// PreCheck: func() {}, +// Providers: testAccProviders, +// Steps: []r.TestStep{ +// { +// Config: testAccResourceKeyConfig, +// Check: r.ComposeTestCheckFunc( +// testAccResourceKeyCheck("tls_private_key.rsa", &keyLens{ +// algorithm: "RSA", +// rsa_bits: 2048, +// ecdsa_curve: "P224", +// }), +// testAccResourceKeyCheck("tls_private_key.ecdsa", &keyLens{ +// algorithm: "ECDSA", +// rsa_bits: 2048, +// ecdsa_curve: "P224", +// }), +// ), +// }, +// { +// ResourceName: "tls_private_key.rsa", +// ImportState: true, +// ImportStateIdFunc: importStateIdFunc(t, fixtures.TestPrivateKeyPEM), +// }, +// { +// ResourceName: "tls_private_key.ecdsa", +// ImportState: true, +// ImportStateIdFunc: importStateIdFunc(t, fixtures.TestPrivateKeyECDSA), +// }, +// }, +// }) +// } +// func importStateIdFunc(t *testing.T, key string) func(*terraform.State) (string, error) { +// return func(state *terraform.State) (string, error) { +// file, err := os.CreateTemp(t.TempDir(), state.Lineage) +// file.Write([]byte(key)) +// if err != nil { +// return "", fmt.Errorf("could not write file: %w", err) +// } +// return file.Name(), nil +// } +// } +// func testAccResourceKeyCheck(id string, want *keyLens) r.TestCheckFunc { +// return func(s *terraform.State) error { +// rs, ok := s.RootModule().Resources[id] +// if !ok { +// return fmt.Errorf("Not found: %s", id) +// } +// if rs.Primary.ID == "" { +// return fmt.Errorf("No ID is set") +// } + +// algorithm := rs.Primary.Attributes["algorithm"] +// rsa_bits := rs.Primary.Attributes["rsa_bits"] +// ecdsa_curve := rs.Primary.Attributes["ecdsa_curve"] + +// if got, want := algorithm, want.algorithm; got != want { +// return fmt.Errorf("algorithm is %s; want %s", got, want) +// } +// if got, want := rsa_bits, want.rsa_bits; got != fmt.Sprint(want) { +// return fmt.Errorf("rsa_bits is %v; want %v", got, want) +// } +// if got, want := ecdsa_curve, want.ecdsa_curve; got != want { +// return fmt.Errorf("ecdsa_curve is %s; want %s", got, want) +// } + +// return nil +// } +// } + +// const ( +// testAccResourceKeyConfig = ` +// resource "tls_private_key" "rsa" { +// algorithm = "RSA" +// } + +// resource "tls_private_key" "ecdsa" { +// algorithm = "ECDSA" +// } +// ` +// ) diff --git a/templates/resources/private_key.md.tmpl b/templates/resources/private_key.md.tmpl index 12858773..328f1add 100644 --- a/templates/resources/private_key.md.tmpl +++ b/templates/resources/private_key.md.tmpl @@ -38,3 +38,11 @@ terraform taint tls_private_key.example ``` A new key will then be generated on the next ``terraform apply``. + +## Import + +Import is supported for private key files in PEM format using the following syntax: + +```shell +terraform import tls_private_key.example key.pem +``` \ No newline at end of file From 57fb6d9b52f5f5b00cb0a16033f8d78c660f4288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Este-Gracias?= Date: Sat, 2 Sep 2023 03:00:03 +0200 Subject: [PATCH 2/2] Fix default values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Este-Gracias --- internal/provider/resource_private_key.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/provider/resource_private_key.go b/internal/provider/resource_private_key.go index 6f221464..11954cf9 100644 --- a/internal/provider/resource_private_key.go +++ b/internal/provider/resource_private_key.go @@ -381,6 +381,11 @@ func (r *privateKeyResource) ImportState(ctx context.Context, req resource.Impor resp.Diagnostics.AddError("Error parsing private key", err.Error()) } + // Set the default values for the attributes + resp.State.SetAttribute(ctx, path.Root("rsa_bits"), 2048) + resp.State.SetAttribute(ctx, path.Root("ecdsa_curve"), P224.String()) + + // Set the attributes on the State resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("algorithm"), algo)...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("private_key_pem"), string(bytes))...) resp.Diagnostics.Append(setPublicKeyAttributes(ctx, &resp.State, key)...) @@ -388,7 +393,7 @@ func (r *privateKeyResource) ImportState(ctx context.Context, req resource.Impor case *rsa.PrivateKey: resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("rsa_bits"), int64(k.N.BitLen()))...) case *ecdsa.PrivateKey: - resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("ecdsa_curve"), k.Curve.Params().Name)...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("ecdsa_curve"), strings.Replace(k.Curve.Params().Name, "-", "", -1))...) case ed25519.PrivateKey: // Nothing to do }