Overview
This Guidance uses AWS IoT Core and Amazon Kinesis Video Streams APIs to delete a device and its associated resources created by the StartCreateDevice workflow.
Operation | IoT/KVS Action | Respective CreateDevice Workflow Step |
---|---|---|
Delete thing shadows | iot:DeleteThingShadow | SetLoggerConfig |
Detach cert from thing | iot:DetachThingPrincipal | CreateDevice |
Detach policies from cert | iot:DetachPolicy | AttachKvsAccessToCert |
Delete KVS stream | kinesisvideo:DeleteStream | KVSResourceCreateLambda |
Delete KVS channels | kinesisvideo:DeleteSignalingChannel | KVSResourceCreateLambda |
Delete thing | iot:DeleteThing | CreateDevice |
Delete certificate | iot:DeleteCertificate | StartCreateDevice API call pre-req |
It is recommended to delete the X.509 certificate (certificateId argument passed into StartCreateDevice) after detaching it from the device Ref. The reason why StartCreateDevice is not recommended to handle creating the keys and certificate within the workflow is to avoid storage of keys/certs in DDB. Note: orphan certificates do not incur any costs; the recommendation is purely from security standpoint.
Assumptions:
- Device has been created with
StartCreateDevice
.
Instructions to delete the device
- Execute the Java code below
[Tip] Customers can choose to 1/embed the Java code provided here to an existing application or 2/implement the Java code following this guidance’s server-less pattern of APIGW → Lambda
for synchronous operation or APIGW -> Lambda -> DDB -> StepFunction
for asynchronous operations.
import software.amazon.awssdk.services.iot.IotClient;
import software.amazon.awssdk.services.iot.model.*;
import software.amazon.awssdk.services.iotdataplane.IotDataPlaneClient;
import software.amazon.awssdk.services.iotdataplane.model.DeleteThingShadowRequest;
import software.amazon.awssdk.services.iotdataplane.model.GetThingShadowRequest;
import software.amazon.awssdk.services.iotdataplane.model.IotDataPlaneException;
import software.amazon.awssdk.services.kinesisvideo.KinesisVideoClient;
import software.amazon.awssdk.services.kinesisvideo.model.DeleteSignalingChannelRequest;
import software.amazon.awssdk.services.kinesisvideo.model.DescribeSignalingChannelRequest;
import software.amazon.awssdk.services.kinesisvideo.model.DescribeSignalingChannelResponse;
import software.amazon.awssdk.services.kinesisvideo.model.DeleteStreamRequest;
import software.amazon.awssdk.services.kinesisvideo.model.DescribeStreamRequest;
import software.amazon.awssdk.services.kinesisvideo.model.DescribeStreamResponse;
import software.amazon.awssdk.services.kinesisvideo.model.KinesisVideoException;
import software.amazon.awssdk.arns.Arn;
import java.util.Arrays;
import java.util.List;
public void deleteDevice(
final String thingName,
final IotClient iotClient,
final IotDataPlaneClient iotDataPlaneClient,
final KinesisVideoClient kvsClient
) {
// Validate if device exists
System.out.println("Checking if device exist.");
try {
iotClient.describeThing(DescribeThingRequest.builder().thingName(deviceId).build());
} catch (ResourceNotFoundException e){
System.out.printf("Thing %s doesn't exist.%n", deviceId);
return;
}
System.out.println("Deleting thing shadows");
// null represents classic shadow which does not have a name
// If more shadows are created for additional features, add them here
List<String> shadowNames = Arrays.asList(null, "provision", "snapshot", "videoEncoder");
for (String shadowName: shadowNames) {
try {
GetThingShadowRequest getThingShadowRequest = GetThingShadowRequest.builder()
.thingName(deviceId)
.shadowName(shadowName)
.build();
iotDataPlaneClient.getThingShadow(getThingShadowRequest);
// if ResourceNotFoundException is not thrown, shadow exists
try {
DeleteThingShadowRequest deleteThingShadowRequest = DeleteThingShadowRequest.builder()
.thingName(deviceId)
.shadowName(shadowName)
.build();
iotDataPlaneClient.deleteThingShadow(deleteThingShadowRequest);
} catch (IotDataPlaneException e) {
System.out.println(e);
}
} catch (software.amazon.awssdk.services.iotdataplane.model.ResourceNotFoundException e) {
// shadow does not exist, no-op
}
}
ListThingPrincipalsRequest listThingPrincipalsRequest = ListThingPrincipalsRequest.builder()
.thingName(deviceId)
.build();
try {
List<String> principals = iotClient.listThingPrincipals(listThingPrincipalsRequest).principals();
System.out.println("Detaching cert from device");
for (String principal: principals) {
DetachThingPrincipalRequest detachThingPrincipalRequest = DetachThingPrincipalRequest.builder()
.principal(principal)
.thingName(deviceId)
.build();
iotClient.detachThingPrincipal(detachThingPrincipalRequest);
Arn arn = Arn.fromString(principal);
ArnResource arnResource = arn.resource();
if (arnResource.resourceType().orElse("").equals("cert")) {
ListAttachedPoliciesRequest listAttachedPoliciesRequest = ListAttachedPoliciesRequest.builder()
.target(principal)
.build();
List<Policy> policies = iotClient.listAttachedPolicies(listAttachedPoliciesRequest).policies();
System.out.println("Detaching policies from cert");
for (Policy policy: policies) {
DetachPolicyRequest detachPolicyRequest = DetachPolicyRequest.builder()
.policyName(policy.policyName())
.target(principal)
.build();
iotClient.detachPolicy(detachPolicyRequest);
}
// Certificate must be deactivated before being deleted
System.out.println("Deactivating cert");
String certificateId = arnResource.resource();
UpdateCertificateRequest updateCertificateRequest = UpdateCertificateRequest.builder()
.certificateId(certificateId)
.newStatus(CertificateStatus.INACTIVE)
.build();
iotClient.updateCertificate(updateCertificateRequest);
System.out.println("Deleting cert");
DeleteCertificateRequest deleteCertificateRequest = DeleteCertificateRequest.builder()
.certificateId(certificateId)
.build();
iotClient.deleteCertificate(deleteCertificateRequest);
}
}
} catch (IotException e) {
System.out.println(e);
}
System.out.println("Deleting KVS stream");
// By default, only 1 KVS stream (name=deviceId) is created.
// If more are created, add them here
try {
DescribeStreamRequest describeStreamRequest = DescribeStreamRequest.builder()
.streamName(deviceId)
.build();
DescribeStreamResponse describeStreamResponse = kvsClient.describeStream(describeStreamRequest);
DeleteStreamRequest deleteStreamRequest = DeleteStreamRequest.builder()
.streamARN(describeStreamResponse.streamInfo().streamARN())
.build();
kvsClient.deleteStream(deleteStreamRequest);
} catch (KinesisVideoException e) {
System.out.println(e);
}
System.out.println("Deleting KVS signaling channels");
// By default, only 2 KVS signaling channels are created with deviceId and the following suffixes.
// If more are created, add them here
List<String> channelSuffixes = List.of("%s-LiveStreamSignalingChannel", "%s-PlaybackSignalingChannel");
for (String suffix: channelSuffixes) {
String signalingChannelName = String.format(suffix, deviceId);
try {
DescribeSignalingChannelRequest describeSignalingChannelRequest = DescribeSignalingChannelRequest.builder()
.channelName(signalingChannelName)
.build();
DescribeSignalingChannelResponse describeSignalingChannelResponse = kvsClient.describeSignalingChannel(describeSignalingChannelRequest);
DeleteSignalingChannelRequest deleteSignalingChannelRequest = DeleteSignalingChannelRequest.builder()
.channelARN(describeSignalingChannelResponse.channelInfo().channelARN())
.build();
kvsClient.deleteSignalingChannel(deleteSignalingChannelRequest);
} catch (KinesisVideoException e) {
System.out.println(e);
}
}
System.out.println("Deleting thing");
DeleteThingRequest deleteThingRequest = DeleteThingRequest.builder()
.thingName(deviceId)
.build();
try {
iotClient.deleteThing(deleteThingRequest);
} catch (IotException e) {
System.out.println(e);
}
}
Verify:
- Sign in to AWS IoT console. In the menu bar on the left under Manage, find All devices > Things. The device that was deleted should not be there.
- In the menu bar on the left under Manage, find Security > Certificates. The certificate used to create the device should not be there.
- Sign in to AWS KVS console. In the menu bar on the left, find Video streams. The stream for the device should not be there or the status should be “Deleting” (can take around 30 seconds to delete).
- In the menu bar on the left, find Signaling channels. The signaling channels for the device should not be there.