Skip to content

Commit 8ea9492

Browse files
grom-42Jerome Malingeremyleonejerjakojtherin
authored
feat(iot): Add ability to set user device certificate (#859)
* feat(iot): add ability to set user device certificate * test(iot): add test for user device certificate * test(iot): use unique resource names in tests to prevent conflict between tests set * test(iot): update cassettes for iot-device-minimal * chore: add a SECURITY.md (#872) * feat(domain): add record resource (#854) Co-authored-by: Jeremy JACQUEMIN <[email protected]> * doc: fix default runtime on k8s_pool (#873) Co-authored-by: Jerome Malinge <[email protected]> Co-authored-by: Rémy Léone <[email protected]> Co-authored-by: jerjako <[email protected]> Co-authored-by: Jeremy JACQUEMIN <[email protected]> Co-authored-by: Jérémy THERIN <[email protected]> Co-authored-by: jaime Bernabe <[email protected]>
1 parent 5ab4929 commit 8ea9492

File tree

4 files changed

+2094
-388
lines changed

4 files changed

+2094
-388
lines changed

docs/resources/iot_device.md

+25-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@ resource scaleway_iot_device main {
2727
}
2828
```
2929

30+
### With custom certificate
31+
32+
```hcl
33+
resource scaleway_iot_hub main {
34+
name = "test-iot"
35+
product_plan = "plan_shared"
36+
}
37+
38+
data local_file device_cert {
39+
filename = "device-certificate.pem"
40+
}
41+
42+
resource scaleway_iot_device main {
43+
hub_id = scaleway_iot_hub.main.id
44+
name = "test-iot"
45+
certificate {
46+
crt = data.local_file.device_cert.content
47+
}
48+
}
49+
```
50+
3051
## Arguments Reference
3152

3253
The following arguments are supported:
@@ -55,6 +76,9 @@ The following arguments are supported:
5576
- `policy` (Optional) Same as publish rules.
5677
- `topics` (Optional) Same as publish rules.
5778

79+
- `certificate.crt` - (Optional) The certificate of the device, either generated by Scaleway or provided.
80+
81+
~> **Important:** Updates to `certificate.crt` will disconnect connected devices and the previous certificate will be deleted and won't be recoverable.
5882

5983
## Attributes Reference
6084

@@ -64,8 +88,7 @@ In addition to all arguments above, the following attributes are exported:
6488
- `created_at` - The date and time the device was created.
6589
- `updated_at` - The date and time the device resource was updated.
6690
- `certificate` - The certificate bundle of the device.
67-
- `crt` - The certificate of the device.
68-
- `key` - The private key of the device.
91+
- `key` - The private key of the device, in case it is generated by Scaleway.
6992
- `status` - The current status of the device.
7093
- `last_activity_at` - The last MQTT activity of the device.
7194
- `is_connected` - The current connection status of the device.

scaleway/resource_iot_device.go

+72-20
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,7 @@ func resourceScalewayIotDevice() *schema.Resource {
119119
},
120120
},
121121
},
122-
// Computed elements
123-
"region": regionSchema(),
124-
"created_at": {
125-
Type: schema.TypeString,
126-
Computed: true,
127-
Description: "The date and time of the creation of the device",
128-
},
129-
"updated_at": {
130-
Type: schema.TypeString,
131-
Computed: true,
132-
Description: "The date and time of the last update of the device",
133-
},
122+
// Provided or computed elements
134123
"certificate": {
135124
Type: schema.TypeList,
136125
MaxItems: 1,
@@ -141,18 +130,32 @@ func resourceScalewayIotDevice() *schema.Resource {
141130
Schema: map[string]*schema.Schema{
142131
"crt": {
143132
Type: schema.TypeString,
133+
Optional: true,
144134
Computed: true,
135+
Sensitive: true,
145136
Description: "X509 PEM encoded certificate of the device",
146137
},
147138
"key": {
148139
Type: schema.TypeString,
149140
Computed: true,
150-
Description: "X509 PEM encoded key of the device",
151141
Sensitive: true,
142+
Description: "X509 PEM encoded key of the device",
152143
},
153144
},
154145
},
155146
},
147+
// Computed elements
148+
"region": regionSchema(),
149+
"created_at": {
150+
Type: schema.TypeString,
151+
Computed: true,
152+
Description: "The date and time of the creation of the device",
153+
},
154+
"updated_at": {
155+
Type: schema.TypeString,
156+
Computed: true,
157+
Description: "The date and time of the last update of the device",
158+
},
156159
"status": {
157160
Type: schema.TypeString,
158161
Computed: true,
@@ -242,12 +245,26 @@ func resourceScalewayIotDeviceCreate(ctx context.Context, d *schema.ResourceData
242245

243246
d.SetId(newRegionalIDString(region, res.Device.ID))
244247

245-
// Certificate and Key cannot be retreived later
246-
cert := map[string]interface{}{
247-
"crt": res.Certificate.Crt,
248-
"key": res.Certificate.Key,
248+
// If user certifcate is provided.
249+
if devCrt, ok := d.GetOk("certificate.0.crt"); ok {
250+
// Set user certificate to device.
251+
// It cannot currently be added in the create device request.
252+
_, err := iotAPI.SetDeviceCertificate(&iot.SetDeviceCertificateRequest{
253+
Region: region,
254+
DeviceID: res.Device.ID,
255+
CertificatePem: devCrt.(string),
256+
}, scw.WithContext(ctx))
257+
if err != nil {
258+
return diag.FromErr(err)
259+
}
260+
} else {
261+
// Update certificate and key as they cannot be retreived later.
262+
cert := map[string]interface{}{
263+
"crt": res.Certificate.Crt,
264+
"key": res.Certificate.Key,
265+
}
266+
_ = d.Set("certificate", []map[string]interface{}{cert})
249267
}
250-
_ = d.Set("certificate", []map[string]interface{}{cert})
251268

252269
return resourceScalewayIotDeviceRead(ctx, d, meta)
253270
}
@@ -319,11 +336,32 @@ func resourceScalewayIotDeviceRead(ctx context.Context, d *schema.ResourceData,
319336
_ = d.Set("message_filters", []map[string]interface{}{mf})
320337
}
321338

339+
////
340+
// Read Device certificate
341+
////
342+
343+
// As we cannot read the key, we get back it from cache and do not change it.
344+
if devCrtKey, ok := d.GetOk("certificate.0.key"); ok {
345+
devCrt, err := iotAPI.GetDeviceCertificate(&iot.GetDeviceCertificateRequest{
346+
Region: region,
347+
DeviceID: deviceID,
348+
}, scw.WithContext(ctx))
349+
if err != nil {
350+
return diag.FromErr(err)
351+
}
352+
// Set device certificate.
353+
cert := map[string]interface{}{
354+
"crt": devCrt.CertificatePem,
355+
"key": devCrtKey.(string),
356+
}
357+
_ = d.Set("certificate", []map[string]interface{}{cert})
358+
}
359+
322360
return nil
323361
}
324362

325363
func resourceScalewayIotDeviceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
326-
iotAPI, region, hubID, err := iotAPIWithRegionAndID(meta, d.Id())
364+
iotAPI, region, deviceID, err := iotAPIWithRegionAndID(meta, d.Id())
327365
if err != nil {
328366
return diag.FromErr(err)
329367
}
@@ -333,7 +371,7 @@ func resourceScalewayIotDeviceUpdate(ctx context.Context, d *schema.ResourceData
333371
////
334372
updateRequest := &iot.UpdateDeviceRequest{
335373
Region: region,
336-
DeviceID: hubID,
374+
DeviceID: deviceID,
337375
}
338376

339377
if d.HasChange("allow_insecure") {
@@ -387,6 +425,20 @@ func resourceScalewayIotDeviceUpdate(ctx context.Context, d *schema.ResourceData
387425
return diag.FromErr(err)
388426
}
389427

428+
////
429+
// Set the device certificate if changed
430+
////
431+
if d.HasChange("certificate.0.crt") {
432+
_, err := iotAPI.SetDeviceCertificate(&iot.SetDeviceCertificateRequest{
433+
Region: region,
434+
DeviceID: deviceID,
435+
CertificatePem: d.Get("certificate.0.crt").(string),
436+
}, scw.WithContext(ctx))
437+
if err != nil {
438+
return diag.FromErr(err)
439+
}
440+
}
441+
390442
return resourceScalewayIotDeviceRead(ctx, d, meta)
391443
}
392444

0 commit comments

Comments
 (0)