Skip to content

Commit 2e52e90

Browse files
yfodilremyleone
andauthored
feat(iam): add iam ssh key resource (#1340)
Co-authored-by: Rémy Léone <[email protected]>
1 parent 4bfd7e9 commit 2e52e90

10 files changed

+3092
-1
lines changed

scaleway/data_source_iam_ssh_key.go

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package scaleway
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
10+
"github.com/scaleway/scaleway-sdk-go/scw"
11+
)
12+
13+
func dataSourceScalewayIamSSHKey() *schema.Resource {
14+
// Generate datasource schema from resource
15+
dsSchema := datasourceSchemaFromResourceSchema(resourceScalewayIamSSKKey().Schema)
16+
addOptionalFieldsToSchema(dsSchema, "name")
17+
18+
dsSchema["name"].ConflictsWith = []string{"ssh_key_id"}
19+
dsSchema["ssh_key_id"] = &schema.Schema{
20+
Type: schema.TypeString,
21+
Optional: true,
22+
Description: "The ID of the SSH key",
23+
ValidateFunc: validationUUID(),
24+
}
25+
26+
return &schema.Resource{
27+
ReadContext: dataSourceScalewayIamSSHKeyRead,
28+
Schema: dsSchema,
29+
}
30+
}
31+
32+
func dataSourceScalewayIamSSHKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
33+
iamAPI := iamAPI(meta)
34+
35+
sshKeyID, sshKeyIDExists := d.GetOk("ssh_key_id")
36+
if !sshKeyIDExists {
37+
res, err := iamAPI.ListSSHKeys(&iam.ListSSHKeysRequest{
38+
Name: expandStringPtr(d.Get("name")),
39+
ProjectID: expandStringPtr(d.Get("project_id")),
40+
}, scw.WithContext(ctx))
41+
if err != nil {
42+
return diag.FromErr(err)
43+
}
44+
for _, sshKey := range res.SSHKeys {
45+
if sshKey.Name == d.Get("name").(string) {
46+
if sshKeyID != "" {
47+
return diag.FromErr(fmt.Errorf("more than 1 SSH Key found with the same name %s", d.Get("name")))
48+
}
49+
sshKeyID = sshKey.ID
50+
}
51+
}
52+
if sshKeyID == "" {
53+
return diag.FromErr(fmt.Errorf("no SSH Key found with the name %s", d.Get("name")))
54+
}
55+
}
56+
57+
d.SetId(sshKeyID.(string))
58+
59+
err := d.Set("ssh_key_id", sshKeyID)
60+
if err != nil {
61+
return diag.FromErr(err)
62+
}
63+
64+
diags := resourceScalewayIamSSHKeyRead(ctx, d, meta)
65+
if diags != nil {
66+
return append(diags, diag.Errorf("failed to read iam ssh key state")...)
67+
}
68+
69+
if d.Id() == "" {
70+
return diag.Errorf("iam ssh key (%s) not found", sshKeyID)
71+
}
72+
73+
return nil
74+
}
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package scaleway
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8+
)
9+
10+
func TestAccScalewayDataSourceIamSSHKey_Basic(t *testing.T) {
11+
SkipBetaTest(t)
12+
dataSourceIamSSHKey := "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILHy/M5FVm5ydLGcal3e5LNcfTalbeN7QL/ZGCvDEdqJ [email protected]"
13+
sshKeyName := "TestAccScalewayDataSourceIamSSHKey_Basic"
14+
tt := NewTestTools(t)
15+
defer tt.Cleanup()
16+
resource.ParallelTest(t, resource.TestCase{
17+
PreCheck: func() { testAccPreCheck(t) },
18+
ProviderFactories: tt.ProviderFactories,
19+
CheckDestroy: testAccCheckScalewayIamSSHKeyDestroy(tt),
20+
Steps: []resource.TestStep{
21+
{
22+
Config: fmt.Sprintf(`
23+
resource "scaleway_iam_ssh_key" "main" {
24+
name = "%s"
25+
public_key = "%s"
26+
}
27+
`, sshKeyName, dataSourceIamSSHKey),
28+
},
29+
{
30+
Config: fmt.Sprintf(`
31+
resource "scaleway_iam_ssh_key" "main" {
32+
name = "%s"
33+
public_key = "%s"
34+
}
35+
36+
data "scaleway_iam_ssh_key" "prod" {
37+
name = "${scaleway_iam_ssh_key.main.name}"
38+
}
39+
40+
data "scaleway_iam_ssh_key" "stg" {
41+
ssh_key_id = "${scaleway_iam_ssh_key.main.id}"
42+
}`, sshKeyName, dataSourceIamSSHKey),
43+
Check: resource.ComposeTestCheckFunc(
44+
testAccCheckScalewayIamSSHKeyExists(tt, "data.scaleway_iam_ssh_key.prod"),
45+
resource.TestCheckResourceAttr("data.scaleway_iam_ssh_key.prod", "name", sshKeyName),
46+
resource.TestCheckResourceAttr("data.scaleway_iam_ssh_key.prod", "public_key", dataSourceIamSSHKey),
47+
testAccCheckScalewayIamSSHKeyExists(tt, "data.scaleway_iam_ssh_key.stg"),
48+
resource.TestCheckResourceAttr("data.scaleway_iam_ssh_key.stg", "name", sshKeyName),
49+
resource.TestCheckResourceAttr("data.scaleway_iam_ssh_key.stg", "public_key", dataSourceIamSSHKey),
50+
),
51+
},
52+
},
53+
})
54+
}

scaleway/provider.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ func addBetaResources(provider *schema.Provider) {
3232
return
3333
}
3434
betaResources := map[string]*schema.Resource{
35-
"scaleway_iam_application": resourceScalewayIamApplication(),
3635
"scaleway_iam_api_key": resourceScalewayIamAPIKey(),
36+
"scaleway_iam_application": resourceScalewayIamApplication(),
37+
"scaleway_iam_ssh_key": resourceScalewayIamSSKKey(),
3738
}
3839
betaDataSources := map[string]*schema.Resource{
40+
"scaleway_iam_ssh_key": dataSourceScalewayIamSSHKey(),
3941
"scaleway_iam_application": dataSourceScalewayIamApplication(),
4042
"scaleway_iam_user": dataSourceScalewayIamUser(),
4143
}

scaleway/resource_iam_ssh_key.go

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package scaleway
2+
3+
import (
4+
"context"
5+
"strings"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
10+
"github.com/scaleway/scaleway-sdk-go/scw"
11+
)
12+
13+
func resourceScalewayIamSSKKey() *schema.Resource {
14+
return &schema.Resource{
15+
CreateContext: resourceScalewayIamSSKKeyCreate,
16+
ReadContext: resourceScalewayIamSSHKeyRead,
17+
UpdateContext: resourceScalewayIamSSKKeyUpdate,
18+
DeleteContext: resourceScalewayIamSSKKeyDelete,
19+
Importer: &schema.ResourceImporter{
20+
StateContext: schema.ImportStatePassthroughContext,
21+
},
22+
SchemaVersion: 0,
23+
Schema: map[string]*schema.Schema{
24+
"name": {
25+
Type: schema.TypeString,
26+
Computed: true,
27+
Optional: true,
28+
Description: "The name of the iam SSH key",
29+
},
30+
"public_key": {
31+
Type: schema.TypeString,
32+
Required: true,
33+
ForceNew: true,
34+
Description: "The public SSH key",
35+
// We don't consider trailing \n as diff
36+
DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool {
37+
return strings.Trim(oldValue, "\n") == strings.Trim(newValue, "\n")
38+
},
39+
},
40+
"fingerprint": {
41+
Type: schema.TypeString,
42+
Computed: true,
43+
Description: "The fingerprint of the iam SSH key",
44+
},
45+
"created_at": {
46+
Type: schema.TypeString,
47+
Computed: true,
48+
Description: "The date and time of the creation of the iam SSH Key",
49+
},
50+
"updated_at": {
51+
Type: schema.TypeString,
52+
Computed: true,
53+
Description: "The date and time of the last update of the iam SSH Key",
54+
},
55+
"organization_id": organizationIDSchema(),
56+
"project_id": projectIDSchema(),
57+
"disabled": {
58+
Type: schema.TypeBool,
59+
Optional: true,
60+
Default: false,
61+
Description: "The SSH key status",
62+
},
63+
},
64+
}
65+
}
66+
67+
func resourceScalewayIamSSKKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
68+
iamAPI := iamAPI(meta)
69+
70+
res, err := iamAPI.CreateSSHKey(&iam.CreateSSHKeyRequest{
71+
Name: d.Get("name").(string),
72+
PublicKey: strings.Trim(d.Get("public_key").(string), "\n"),
73+
ProjectID: (d.Get("project_id")).(string),
74+
}, scw.WithContext(ctx))
75+
if err != nil {
76+
return diag.FromErr(err)
77+
}
78+
79+
if _, disabledExists := d.GetOk("disabled"); disabledExists {
80+
_, err = iamAPI.UpdateSSHKey(&iam.UpdateSSHKeyRequest{
81+
SSHKeyID: d.Id(),
82+
Disabled: expandBoolPtr(getBool(d, "disabled")),
83+
}, scw.WithContext(ctx))
84+
if err != nil {
85+
return diag.FromErr(err)
86+
}
87+
}
88+
89+
d.SetId(res.ID)
90+
91+
return resourceScalewayIamSSHKeyRead(ctx, d, meta)
92+
}
93+
94+
func resourceScalewayIamSSHKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
95+
iamAPI := iamAPI(meta)
96+
97+
res, err := iamAPI.GetSSHKey(&iam.GetSSHKeyRequest{
98+
SSHKeyID: d.Id(),
99+
}, scw.WithContext(ctx))
100+
if err != nil {
101+
if is404Error(err) {
102+
d.SetId("")
103+
return nil
104+
}
105+
return diag.FromErr(err)
106+
}
107+
108+
_ = d.Set("name", res.Name)
109+
_ = d.Set("public_key", res.PublicKey)
110+
_ = d.Set("fingerprint", res.Fingerprint)
111+
_ = d.Set("created_at", flattenTime(res.CreatedAt))
112+
_ = d.Set("updated_at", flattenTime(res.UpdatedAt))
113+
_ = d.Set("organization_id", res.OrganizationID)
114+
_ = d.Set("project_id", res.ProjectID)
115+
_ = d.Set("disabled", res.Disabled)
116+
117+
return nil
118+
}
119+
120+
func resourceScalewayIamSSKKeyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
121+
iamAPI := iamAPI(meta)
122+
123+
req := &iam.UpdateSSHKeyRequest{
124+
SSHKeyID: d.Id(),
125+
}
126+
127+
hasUpdated := false
128+
129+
if d.HasChange("name") {
130+
req.Name = expandStringPtr(d.Get("name"))
131+
hasUpdated = true
132+
}
133+
134+
if d.HasChange("disabled") {
135+
if _, disabledExists := d.GetOk("disabled"); !disabledExists {
136+
_, err := iamAPI.UpdateSSHKey(&iam.UpdateSSHKeyRequest{
137+
SSHKeyID: d.Id(),
138+
Disabled: expandBoolPtr(false),
139+
})
140+
if err != nil {
141+
return diag.FromErr(err)
142+
}
143+
} else {
144+
_, err := iamAPI.UpdateSSHKey(&iam.UpdateSSHKeyRequest{
145+
SSHKeyID: d.Id(),
146+
Disabled: expandBoolPtr(getBool(d, "disabled")),
147+
})
148+
if err != nil {
149+
return diag.FromErr(err)
150+
}
151+
}
152+
}
153+
154+
if hasUpdated {
155+
_, err := iamAPI.UpdateSSHKey(req, scw.WithContext(ctx))
156+
if err != nil {
157+
return diag.FromErr(err)
158+
}
159+
}
160+
161+
return resourceScalewayIamSSHKeyRead(ctx, d, meta)
162+
}
163+
164+
func resourceScalewayIamSSKKeyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
165+
iamAPI := iamAPI(meta)
166+
167+
err := iamAPI.DeleteSSHKey(&iam.DeleteSSHKeyRequest{
168+
SSHKeyID: d.Id(),
169+
}, scw.WithContext(ctx))
170+
if err != nil && !is404Error(err) {
171+
return diag.FromErr(err)
172+
}
173+
174+
return nil
175+
}

0 commit comments

Comments
 (0)