Identities
Load naming and paths from config/walkthrough.env (Prerequisites).
All commands assume the repo root as the current directory. This page is plain
AWS CLI
AWS Command Line Interface v2 — every cloud and deployment step in this walkthrough is a visible aws command, not a wrapper script. — no wrappers.
Load lab env
Section titled “Load lab env”set -a && source config/walkthrough.env && set +a(no output)What this page builds:
gg-edge-wt-dev-corecore certgg-edge-wt-dev-token-aliasgg-edge-wt-dev-token-exchangegg-edge-wt-dev-coresgg-edge-wt-dev-esp32-1gg-edge-wt-dev-gg-serviceaccount association · cloud-side GG ops- Core cert ≠ client cert.
- Token exchange is core-only: cert → role alias → IAM role → temp creds.
- Service role is account-level, not on either Thing.
Build order (plain CLI):
- 1
IoT data endpoint
describe-endpoint - 2
Core Thing + cert
create-thing · create-keys - 3
Thing group
create-thing-group - 4
Token exchange role
create-role · create-role-alias - 5
Client Thing + cert
create-thing · create-keys - 6
Greengrass service role
associate-service-role-to-account
aws iot / aws iam / aws greengrassv2calls, in order. Each resource is created before the installer needs it.1. IoT data endpoint
Section titled “1. IoT data endpoint”Look up data endpoint
Section titled “Look up data endpoint”MQTT data endpoint (ATS) for firmware and Nucleus config:
aws iot describe-endpoint --endpoint-type iot:Data-ATS \ --query endpointAddress --output textexample123abc-ats.iot.ap-southeast-2.amazonaws.comSave it as AWS_IOT_ENDPOINT in walkthrough.env for the
ESP32-S3
Espressif ESP32-S3 — Wi‑Fi microcontroller used here as the Greengrass client device (not a second core). firmware.
2. Core Thing and certificate
Section titled “2. Core Thing and certificate”Create cert directories
Section titled “Create cert directories”Local folders for core and client PEMs:
mkdir -p "$CORE_CERTS_DIR" "$CLIENT_CERTS_DIR"(no output)Create core Thing
Section titled “Create core Thing”Register the core
Greengrass core device — the Linux host running Nucleus (this lab: Intel NUC6CAY). Client devices discover and connect to it. as an IoT Thing:
aws iot create-thing --thing-name "$CORE_THING_NAME"{ "thingName": "gg-edge-wt-dev-core", "thingArn": "arn:aws:iot:ap-southeast-2:123456789012:thing/gg-edge-wt-dev-core", "thingId": "01234567-89ab-cdef-0123-456789abcdef"}Create core certificate
Section titled “Create core certificate”Create and activate the core device certificate; keep the ARN for attaches:
aws iot create-keys-and-certificate --set-as-active \ --certificate-pem-outfile "$CORE_CERTS_DIR/device.pem.crt" \ --public-key-outfile "$CORE_CERTS_DIR/public.pem.key" \ --private-key-outfile "$CORE_CERTS_DIR/private.pem.key" \ > "$CORE_CERTS_DIR/create-cert.json"CORE_CERT_ARN=$(jq -r .certificateArn "$CORE_CERTS_DIR/create-cert.json")echo "$CORE_CERT_ARN" | tee "$CORE_CERTS_DIR/certificateArn.txt"arn:aws:iot:ap-southeast-2:123456789012:cert/abcdef0123456789…Download Amazon Root CA
Section titled “Download Amazon Root CA”So the core can trust IoT Core:
curl -sS https://www.amazontrust.com/repository/AmazonRootCA1.pem \ -o "$CORE_CERTS_DIR/AmazonRootCA1.pem"(no output)Create core IoT policy
Section titled “Create core IoT policy”From this repo’s policy JSON:
aws iot create-policy \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-core-policy" \ --policy-document "file://artifacts/policies/core-device-policy.json"{ "policyName": "gg-edge-wt-dev-core-policy", "policyArn": "arn:aws:iot:ap-southeast-2:123456789012:policy/gg-edge-wt-dev-core-policy", "policyVersionId": "1"}Attach policy and Thing principal
Section titled “Attach policy and Thing principal”Attach the IoT policy
AWS IoT policy — JSON permissions attached to a certificate (core connect/publish; client greengrass:Discover and related actions). to the cert, then bind
the cert to the core Thing:
aws iot attach-policy \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-core-policy" \ --target "$CORE_CERT_ARN"aws iot attach-thing-principal \ --thing-name "$CORE_THING_NAME" \ --principal "$CORE_CERT_ARN"(no output)3. Thing group for deployments
Section titled “3. Thing group for deployments”So cloud deployments can target the core
Greengrass core device — the Linux host running Nucleus (this lab: Intel NUC6CAY). Client devices discover and connect to it..
Create Thing group
Section titled “Create Thing group”aws iot create-thing-group --thing-group-name "$CORE_THING_GROUP"{ "thingGroupName": "gg-edge-wt-dev-cores", "thingGroupArn": "arn:aws:iot:ap-southeast-2:123456789012:thinggroup/gg-edge-wt-dev-cores", "thingGroupId": "01234567-89ab-cdef-0123-456789abcdef"}Add core and capture group ARN
Section titled “Add core and capture group ARN”Put the core in the Thing group
AWS IoT Thing group — deployment target for Greengrass cloud deployments (this lab: the core's group). and keep
the ARN for later deployments:
aws iot add-thing-to-thing-group \ --thing-group-name "$CORE_THING_GROUP" \ --thing-name "$CORE_THING_NAME"CORE_THING_GROUP_ARN=$(aws iot describe-thing-group \ --thing-group-name "$CORE_THING_GROUP" \ --query thingGroupArn --output text)echo "$CORE_THING_GROUP_ARN"arn:aws:iot:ap-southeast-2:123456789012:thinggroup/gg-edge-wt-dev-cores4. Token exchange role (Nucleus credentials)
Section titled “4. Token exchange role (Nucleus credentials)”Nucleus
Greengrass Nucleus — the edge runtime on the core device. Installs components, talks to AWS, and hosts the local client-device stack. needs temporary AWS credentials via a
token exchange
IoT role alias + IAM role — lets Nucleus exchange its device certificate for temporary AWS credentials on the core. role alias (
IAM
AWS Identity and Access Management — roles for Nucleus token exchange and the Greengrass service role used with client devices. + IoT).
Set role and alias names
Section titled “Set role and alias names”TOKEN_ROLE_NAME="${PROJECT_NAME}-${ENVIRONMENT}-token-exchange"ROLE_ALIAS_NAME="${PROJECT_NAME}-${ENVIRONMENT}-token-alias"echo "$TOKEN_ROLE_NAME"echo "$ROLE_ALIAS_NAME"gg-edge-wt-dev-token-exchangegg-edge-wt-dev-token-aliasCreate token-exchange IAM role
Section titled “Create token-exchange IAM role”Role that IoT Credentials Provider can assume:
aws iam create-role \ --role-name "$TOKEN_ROLE_NAME" \ --assume-role-policy-document "file://artifacts/policies/token-exchange-trust.json"{ "Role": { "Path": "/", "RoleName": "gg-edge-wt-dev-token-exchange", "RoleId": "AROAEXAMPLETOKENEXCH", "Arn": "arn:aws:iam::123456789012:role/gg-edge-wt-dev-token-exchange", "CreateDate": "2026-10-04T10:34:39+00:00", … }}Attach access policy
Section titled “Attach access policy”Grant logs / S3 artifact access Nucleus needs; print the role ARN:
aws iam put-role-policy \ --role-name "$TOKEN_ROLE_NAME" \ --policy-name "${TOKEN_ROLE_NAME}-access" \ --policy-document "file://artifacts/policies/token-exchange-access.json"TOKEN_ROLE_ARN=$(aws iam get-role --role-name "$TOKEN_ROLE_NAME" --query Role.Arn --output text)echo "$TOKEN_ROLE_ARN"arn:aws:iam::123456789012:role/gg-edge-wt-dev-token-exchangeCreate IoT role alias
Section titled “Create IoT role alias”Points at the IAM role above:
aws iot create-role-alias \ --role-alias "$ROLE_ALIAS_NAME" \ --role-arn "$TOKEN_ROLE_ARN"{ "roleAlias": "gg-edge-wt-dev-token-alias", "roleAliasArn": "arn:aws:iot:ap-southeast-2:123456789012:rolealias/gg-edge-wt-dev-token-alias"}Resolve role-alias ARN
Section titled “Resolve role-alias ARN”Needed for the IoT policy document:
ROLE_ALIAS_ARN=$(aws iot describe-role-alias \ --role-alias "$ROLE_ALIAS_NAME" \ --query roleAliasDescription.roleAliasArn --output text)echo "$ROLE_ALIAS_ARN"arn:aws:iot:ap-southeast-2:123456789012:rolealias/gg-edge-wt-dev-token-aliasCreate token-exchange IoT policy
Section titled “Create token-exchange IoT policy”Allows the core certificate to assume the alias
(iot:AssumeRoleWithCertificate):
cat > /tmp/token-exchange-iot-policy.json <<EOF{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iot:AssumeRoleWithCertificate", "Resource": "${ROLE_ALIAS_ARN}" } ]}EOFaws iot create-policy \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-token-exchange-policy" \ --policy-document file:///tmp/token-exchange-iot-policy.json{ "policyName": "gg-edge-wt-dev-token-exchange-policy", "policyArn": "arn:aws:iot:ap-southeast-2:123456789012:policy/gg-edge-wt-dev-token-exchange-policy", "policyVersionId": "1"}Attach token-exchange policy to core cert
Section titled “Attach token-exchange policy to core cert”aws iot attach-policy \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-token-exchange-policy" \ --target "$CORE_CERT_ARN"(no output)5. Client Thing and certificate
Section titled “5. Client Thing and certificate”The client device
Greengrass client device — an IoT Thing (here the ESP32-S3) that discovers a core and uses local MQTT. It does not run Nucleus. gets its own
Thing
AWS IoT Thing — logical device identity bound to a certificate (core Thing on the NUC; client Thing on the ESP32-S3). and certificate (separate from the core).
Create client Thing
Section titled “Create client Thing”aws iot create-thing --thing-name "$CLIENT_THING_NAME"{ "thingName": "gg-edge-wt-dev-esp32-1", "thingArn": "arn:aws:iot:ap-southeast-2:123456789012:thing/gg-edge-wt-dev-esp32-1", "thingId": "fedcba98-7654-3210-fedc-ba9876543210"}Create client certificate
Section titled “Create client certificate”Create and activate; keep the ARN:
aws iot create-keys-and-certificate --set-as-active \ --certificate-pem-outfile "$CLIENT_CERTS_DIR/device.pem.crt" \ --public-key-outfile "$CLIENT_CERTS_DIR/public.pem.key" \ --private-key-outfile "$CLIENT_CERTS_DIR/private.pem.key" \ > "$CLIENT_CERTS_DIR/create-cert.json"CLIENT_CERT_ARN=$(jq -r .certificateArn "$CLIENT_CERTS_DIR/create-cert.json")echo "$CLIENT_CERT_ARN"arn:aws:iot:ap-southeast-2:123456789012:cert/fedcba9876543210…Download Root CA and create client policy
Section titled “Download Root CA and create client policy”curl -sS https://www.amazontrust.com/repository/AmazonRootCA1.pem \ -o "$CLIENT_CERTS_DIR/AmazonRootCA1.pem"aws iot create-policy \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-client-policy" \ --policy-document "file://artifacts/policies/client-device-policy.json"{ "policyName": "gg-edge-wt-dev-client-policy", "policyArn": "arn:aws:iot:ap-southeast-2:123456789012:policy/gg-edge-wt-dev-client-policy", "policyVersionId": "1"}Attach client policy and Thing principal
Section titled “Attach client policy and Thing principal”aws iot attach-policy \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-client-policy" \ --target "$CLIENT_CERT_ARN"aws iot attach-thing-principal \ --thing-name "$CLIENT_THING_NAME" \ --principal "$CLIENT_CERT_ARN"(no output)Optional — more ESP32-S3 zone clients
Section titled “Optional — more ESP32-S3 zone clients”Skip if you only have one board. One client is enough for the closed loop.
Uncomment in walkthrough.env (names must stay under CLIENT_THING_PREFIX so
Auth’s thingName: …-esp32* rule matches all):
CLIENT_THING_NAME_2="${PROJECT_NAME}-${ENVIRONMENT}-esp32-2"CLIENT_CERTS_DIR_2=certs/client-2# Optional third zone:# CLIENT_THING_NAME_3="${PROJECT_NAME}-${ENVIRONMENT}-esp32-3"# CLIENT_CERTS_DIR_3=certs/client-3(no output)Then create each extra Thing + cert (reuse the same client IoT policy). Example for zone 2:
mkdir -p "$CLIENT_CERTS_DIR_2"aws iot create-thing --thing-name "$CLIENT_THING_NAME_2"aws iot create-keys-and-certificate --set-as-active \ --certificate-pem-outfile "$CLIENT_CERTS_DIR_2/device.pem.crt" \ --public-key-outfile "$CLIENT_CERTS_DIR_2/public.pem.key" \ --private-key-outfile "$CLIENT_CERTS_DIR_2/private.pem.key" \ > "$CLIENT_CERTS_DIR_2/create-cert.json"CLIENT_CERT_ARN_2=$(jq -r .certificateArn "$CLIENT_CERTS_DIR_2/create-cert.json")curl -sS https://www.amazontrust.com/repository/AmazonRootCA1.pem \ -o "$CLIENT_CERTS_DIR_2/AmazonRootCA1.pem"aws iot attach-policy \ --policy-name "${PROJECT_NAME}-${ENVIRONMENT}-client-policy" \ --target "$CLIENT_CERT_ARN_2"aws iot attach-thing-principal \ --thing-name "$CLIENT_THING_NAME_2" \ --principal "$CLIENT_CERT_ARN_2"echo "$CLIENT_CERT_ARN_2"arn:aws:iot:ap-southeast-2:123456789012:cert/…Repeat with $CLIENT_THING_NAME_3 / $CLIENT_CERTS_DIR_3 for a third zone.
Associate and flash each board later with its own certs and Thing name.
6. Greengrass service role
Section titled “6. Greengrass service role”Required for client-device identity checks and connectivity info under
Greengrass V2
AWS IoT Greengrass V2 — edge runtime (Nucleus) plus cloud control plane for deployments, client devices, and local MQTT..
Render trust policy and name the role
Section titled “Render trust policy and name the role”Substitute account/region into the trust document:
sed -e "s/\${AWS_ACCOUNT_ID}/${AWS_ACCOUNT_ID}/g" \ -e "s/\${AWS_REGION}/${AWS_REGION}/g" \ artifacts/policies/greengrass-service-role-trust.json \ > /tmp/gg-service-role-trust.jsonGG_SERVICE_ROLE="${PROJECT_NAME}-${ENVIRONMENT}-gg-service"echo "$GG_SERVICE_ROLE"gg-edge-wt-dev-gg-serviceCreate Greengrass service IAM role
Section titled “Create Greengrass service IAM role”Account-level role for cloud-side Greengrass ops:
aws iam create-role \ --role-name "$GG_SERVICE_ROLE" \ --assume-role-policy-document file:///tmp/gg-service-role-trust.json{ "Role": { "Path": "/", "RoleName": "gg-edge-wt-dev-gg-service", "RoleId": "AROAEXAMPLEGGSERVICE", "Arn": "arn:aws:iam::123456789012:role/gg-edge-wt-dev-gg-service", "CreateDate": "2026-10-04T10:34:50+00:00", … }}Attach managed access policy
Section titled “Attach managed access policy”AWS’s AWSGreengrassResourceAccessRolePolicy; print the role ARN:
aws iam attach-role-policy \ --role-name "$GG_SERVICE_ROLE" \ --policy-arn arn:aws:iam::aws:policy/service-role/AWSGreengrassResourceAccessRolePolicyGG_SERVICE_ROLE_ARN=$(aws iam get-role --role-name "$GG_SERVICE_ROLE" --query Role.Arn --output text)echo "$GG_SERVICE_ROLE_ARN"arn:aws:iam::123456789012:role/gg-edge-wt-dev-gg-serviceAssociate role to the account
Section titled “Associate role to the account”aws greengrassv2 associate-service-role-to-account --role-arn "$GG_SERVICE_ROLE_ARN"{ "associatedAt": "2026-10-04T10:35:08Z"}Confirm association
Section titled “Confirm association”aws greengrassv2 get-service-role-for-account{ "associatedAt": "2026-10-04T10:35:08Z", "roleArn": "arn:aws:iam::123456789012:role/gg-edge-wt-dev-gg-service"}Checkpoint
Section titled “Checkpoint”Describe core Thing
Section titled “Describe core Thing”aws iot describe-thing --thing-name "$CORE_THING_NAME"{ "defaultClientId": "gg-edge-wt-dev-core", "thingName": "gg-edge-wt-dev-core", "thingId": "01234567-89ab-cdef-0123-456789abcdef", "thingArn": "arn:aws:iot:ap-southeast-2:123456789012:thing/gg-edge-wt-dev-core", "attributes": {}, "version": 1}Describe client Thing
Section titled “Describe client Thing”aws iot describe-thing --thing-name "$CLIENT_THING_NAME"{ "defaultClientId": "gg-edge-wt-dev-esp32-1", "thingName": "gg-edge-wt-dev-esp32-1", "thingId": "fedcba98-7654-3210-fedc-ba9876543210", "thingArn": "arn:aws:iot:ap-southeast-2:123456789012:thing/gg-edge-wt-dev-esp32-1", "attributes": {}, "version": 1}Re-check service role
Section titled “Re-check service role”aws greengrassv2 get-service-role-for-account{ "associatedAt": "2026-10-04T10:35:08Z", "roleArn": "arn:aws:iam::123456789012:role/gg-edge-wt-dev-gg-service"}Next: Prepare the core.