Teardown
Teardown runs in reverse build order. Remove the cloud consumers first, from
Edge inference back to Deploy from the cloud, so nothing
keeps writing while you delete. Then remove the Greengrass identity:
association, deployment, Nucleus, certificates, and
Things
AWS IoT Thing — logical device identity bound to a certificate (core Thing on the NUC; client Thing on the ESP32-S3).. Commands end in || true where a
reader may have skipped that page.
Load env, mutation gate, and derived names
Section titled “Load env, mutation gate, and derived names”set -a && source config/walkthrough.env && set +aexport GG_EDGE_ALLOW_AWS=1AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)ARTIFACT_BUCKET="${PROJECT_NAME}-${ENVIRONMENT}-gg-artifacts-${AWS_ACCOUNT_ID}"DATA_BUCKET="${PROJECT_NAME}-${ENVIRONMENT}-gg-data-${AWS_ACCOUNT_ID}"RULE_PREFIX=$(echo "${PROJECT_NAME}_${ENVIRONMENT}" | tr '-' '_')IOT_RULE_ROLE="${PROJECT_NAME}-${ENVIRONMENT}-iot-rules"SAGEMAKER_ROLE="${PROJECT_NAME}-${ENVIRONMENT}-sagemaker-training"ZONE_TABLE="${ZONE_TABLE:-${PROJECT_NAME}-${ENVIRONMENT}-zone-state}"ALARM_TOPIC="${ALARM_TOPIC:-${PROJECT_NAME}-${ENVIRONMENT}-zone-alarms}"COMMAND_FUNCTION="${COMMAND_FUNCTION:-${PROJECT_NAME}-${ENVIRONMENT}-alarm-to-command}"ALARM_TOPIC_ARN="arn:aws:sns:${AWS_REGION}:${AWS_ACCOUNT_ID}:${ALARM_TOPIC}"(no output)1. Cloud commands automation
Section titled “1. Cloud commands automation”Alarms and SNS topic
Section titled “Alarms and SNS topic”Deleting the topic also deletes its email and Lambda subscriptions (DeleteTopic).
ALARMS=$(aws cloudwatch describe-alarms --alarm-name-prefix "${PROJECT_NAME}-${ENVIRONMENT}-hot-" \ --query "MetricAlarms[].AlarmName" --output text)[ -n "$ALARMS" ] && aws cloudwatch delete-alarms --alarm-names $ALARMSaws sns delete-topic --topic-arn "$ALARM_TOPIC_ARN" || true(no output)Lambda, its role, and its log group
Section titled “Lambda, its role, and its log group”aws lambda delete-function --function-name "$COMMAND_FUNCTION" || trueaws iam delete-role-policy --role-name "$COMMAND_FUNCTION" \ --policy-name iot-publish-command || trueaws iam detach-role-policy --role-name "$COMMAND_FUNCTION" \ --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole || trueaws iam delete-role --role-name "$COMMAND_FUNCTION" || trueaws logs delete-log-group --log-group-name "/aws/lambda/${COMMAND_FUNCTION}" || true(no output)2. IoT rules and their role
Section titled “2. IoT rules and their role”Delete every walkthrough rule
Section titled “Delete every walkthrough rule”for r in $(aws iot list-topic-rules \ --query "rules[?starts_with(ruleName, '${RULE_PREFIX}_')].ruleName" --output text); do aws iot delete-topic-rule --rule-name "$r" && echo "deleted $r"donedeleted gg_edge_wt_dev_ack_ddbdeleted gg_edge_wt_dev_actuator_ddbdeleted gg_edge_wt_dev_inference_ddbdeleted gg_edge_wt_dev_telemetry_cwdeleted gg_edge_wt_dev_telemetry_ddbdeleted gg_edge_wt_dev_telemetry_s3Delete the rule role
Section titled “Delete the rule role”for p in iot-rule-s3 iot-rule-dynamodb iot-rule-cloudwatch; do aws iam delete-role-policy --role-name "$IOT_RULE_ROLE" --policy-name "$p" || truedoneaws iam delete-role --role-name "$IOT_RULE_ROLE" || true(no output)The GgEdge/Greenhouse metrics cannot be deleted. They stop receiving data
once the rule is gone, and CloudWatch ages them out.
3. DynamoDB zone state
Section titled “3. DynamoDB zone state”aws dynamodb delete-table --table-name "$ZONE_TABLE" \ --query TableDescription.TableStatus --output text || trueaws dynamodb wait table-not-exists --table-name "$ZONE_TABLE"DELETING4. SageMaker role and training logs
Section titled “4. SageMaker role and training logs”Training jobs stay in the SageMaker history and cannot be deleted. They do not run or bill after completion. Local-train runs leave no SageMaker job.
aws iam delete-role-policy --role-name "$SAGEMAKER_ROLE" \ --policy-name zone-anomaly-training || trueaws iam delete-role --role-name "$SAGEMAKER_ROLE" || truefor s in $(aws logs describe-log-streams --log-group-name /aws/sagemaker/TrainingJobs \ --log-stream-name-prefix zone-anomaly- --query "logStreams[].logStreamName" --output text 2>/dev/null); do [ -n "$s" ] && aws logs delete-log-stream \ --log-group-name /aws/sagemaker/TrainingJobs --log-stream-name "$s"done(no output)5. Telemetry lake
Section titled “5. Telemetry lake”rb --force deletes every object, then the bucket.
aws s3 rb "s3://${DATA_BUCKET}" --force || true…remove_bucket: gg-edge-wt-dev-gg-data-1234567890126. Edge stack from the cloud
Section titled “6. Edge stack from the cloud”Disassociate clients
Section titled “Disassociate clients”aws greengrassv2 batch-disassociate-client-device-from-core-device \ --core-device-thing-name "$CORE_THING_NAME" \ --entries thingName="$CLIENT_THING_NAME"[ -n "${CLIENT_THING_NAME_2:-}" ] && \ aws greengrassv2 batch-disassociate-client-device-from-core-device \ --core-device-thing-name "$CORE_THING_NAME" \ --entries thingName="$CLIENT_THING_NAME_2"{ "disassociatedClientDevices": […], "errorEntries": []}Empty deployment (remove components from the group)
Section titled “Empty deployment (remove components from the group)”CORE_THING_GROUP_ARN=$(aws iot describe-thing-group \ --thing-group-name "$CORE_THING_GROUP" \ --query thingGroupArn --output text)aws greengrassv2 create-deployment \ --target-arn "$CORE_THING_GROUP_ARN" \ --deployment-name "gg-edge-empty" \ --components '{}'{ "deploymentId": "…"}Wait until list-effective-deployments shows gg-edge-empty → SUCCEEDED
before deleting component versions.
Private component versions
Section titled “Private component versions”for c in com.example.ZoneAnomaly:1.0.0 com.example.GgEdgeLoop:1.1.0; do aws greengrassv2 delete-component \ --arn "arn:aws:greengrass:${AWS_REGION}:${AWS_ACCOUNT_ID}:components:${c%%:*}:versions:${c##*:}" || truedoneaws greengrassv2 list-components --scope PRIVATE --query "components[].componentName" --output text(no output)Artifact bucket, core S3 access, and LogManager log groups
Section titled “Artifact bucket, core S3 access, and LogManager log groups”aws s3 rb "s3://${ARTIFACT_BUCKET}" --force || trueaws iam delete-role-policy \ --role-name "${PROJECT_NAME}-${ENVIRONMENT}-token-exchange" \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-gg-artifacts-s3" || truefor g in GreengrassSystemComponent/${AWS_REGION}/System \ UserComponent/${AWS_REGION}/com.example.GgEdgeLoop \ UserComponent/${AWS_REGION}/com.example.ZoneAnomaly; do aws logs delete-log-group --log-group-name "/aws/greengrass/$g" || truedoneremove_bucket: gg-edge-wt-dev-gg-artifacts-1234567890127. Stop Nucleus (on the NUC)
Section titled “7. Stop Nucleus (on the NUC)”sudo systemctl stop greengrass.servicesudo systemctl disable greengrass.service# Optional full wipe:# sudo rm -rf /greengrassRemoved "/etc/systemd/system/multi-user.target.wants/greengrass.service".Run this over SSH on the NUC if you still have access.
8. Detach and delete certificates
Section titled “8. Detach and delete certificates”For each of core and client (example for client):
Resolve client cert ARN and ID
Section titled “Resolve client cert ARN and ID”CLIENT_CERT_ARN=$(jq -r .certificateArn "$CLIENT_CERTS_DIR/create-cert.json")CLIENT_CERT_ID=$(jq -r .certificateId "$CLIENT_CERTS_DIR/create-cert.json")echo "$CLIENT_CERT_ARN"echo "$CLIENT_CERT_ID"arn:aws:iot:ap-southeast-2:123456789012:cert/fedcba9876543210…fedcba9876543210…Detach, inactivate, and delete the client cert
Section titled “Detach, inactivate, and delete the client cert”aws iot detach-thing-principal \ --thing-name "$CLIENT_THING_NAME" \ --principal "$CLIENT_CERT_ARN"aws iot detach-policy \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-client-policy" \ --target "$CLIENT_CERT_ARN"aws iot update-certificate --certificate-id "$CLIENT_CERT_ID" --new-status INACTIVEaws iot delete-certificate --certificate-id "$CLIENT_CERT_ID"(no output)Repeat with $CORE_CERTS_DIR / $CORE_THING_NAME (detach core + token
policies). Repeat for $CLIENT_CERTS_DIR_2 and $CLIENT_THING_NAME_2 before
deleting those Things.
9. Delete Things, group, policies, roles
Section titled “9. Delete Things, group, policies, roles”Delete the core device record, Things, and Thing group
Section titled “Delete the core device record, Things, and Thing group”aws greengrassv2 delete-core-device --core-device-thing-name "$CORE_THING_NAME" || trueaws iot delete-thing --thing-name "$CLIENT_THING_NAME"[ -n "${CLIENT_THING_NAME_2:-}" ] && aws iot delete-thing --thing-name "$CLIENT_THING_NAME_2"aws iot remove-thing-from-thing-group \ --thing-group-name "$CORE_THING_GROUP" \ --thing-name "$CORE_THING_NAME" || trueaws iot delete-thing --thing-name "$CORE_THING_NAME"aws iot delete-thing-group --thing-group-name "$CORE_THING_GROUP"(no output)Delete role alias, IoT policies, and token-exchange role
Section titled “Delete role alias, IoT policies, and token-exchange role”aws iot delete-role-alias \ --role-alias "${ROLE_ALIAS_NAME:-${PROJECT_NAME}-${ENVIRONMENT}-token-alias}" || truefor p in ${PROJECT_NAME}-${ENVIRONMENT}-core-policy \ ${PROJECT_NAME}-${ENVIRONMENT}-client-policy \ ${PROJECT_NAME}-${ENVIRONMENT}-token-exchange-policy; do aws iot delete-policy --policy-name "$p" || truedoneaws iam delete-role-policy \ --role-name "${PROJECT_NAME}-${ENVIRONMENT}-token-exchange" \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-token-exchange-access" || trueaws iam delete-role \ --role-name "${PROJECT_NAME}-${ENVIRONMENT}-token-exchange" || true(no output)Greengrass account service role (if this lab created it)
Section titled “Greengrass account service role (if this lab created it)”aws greengrassv2 disassociate-service-role-from-account || trueaws iam detach-role-policy \ --role-name "${PROJECT_NAME}-${ENVIRONMENT}-gg-service" \ --policy-arn arn:aws:iam::aws:policy/service-role/AWSGreengrassResourceAccessRolePolicy || trueaws iam delete-role --role-name "${PROJECT_NAME}-${ENVIRONMENT}-gg-service" || true(no output)10. Local cleanup
Section titled “10. Local cleanup”rm -rf certs/core/* certs/client/* certs/client-2/* certs/client-3/*aws greengrassv2 list-core-devices --status HEALTHY{ "coreDevices": []}Sanity check — all should be empty for this project prefix:
aws iot list-things --query "things[?starts_with(thingName, '${PROJECT_NAME}')].thingName" --output textaws iot list-topic-rules --query "rules[?starts_with(ruleName, '${RULE_PREFIX}_')].ruleName" --output textaws s3 ls | grep "${PROJECT_NAME}-${ENVIRONMENT}" || trueaws iam list-roles --query "Roles[?contains(RoleName, '${PROJECT_NAME}')].RoleName" --output text(no output)