AWS IoT Core (Claim Provisioning)
This guide walks through AWS IoT Core Fleet Provisioning by claim certificate for Aether, based on AWS IoT Core documentation and console workflow.
It covers cloud-side setup and the Aether-side configuration needed for first-boot device provisioning.
Provisioning flow at a glance
- Device connects with a claim certificate (bootstrap identity).
- Device requests a unique certificate with
CreateKeysAndCertificate(orCreateCertificateFromCsr). - Device calls
RegisterThingusing your provisioning template. - AWS IoT creates/attaches Thing, certificate, and policy per template.
- Device reconnects using its permanent device certificate.
Prerequisites
- AWS account with permissions for IoT Core and IAM.
- OpenSSL installed.
- Aether SDK/runtime ready on the device.
- Chosen template name (examples use
Aether).
Collect required AWS values
You need region, account-id, and endpoint for policy ARNs and Nexus config.
Get region and account-id from console
- Open AWS Console.
- Read the top-right region selector -> this is
region. - Open account menu (top-right) -> copy Account ID -> this is
account-id.
Get region and account-id from CLI
aws configure get region
aws sts get-caller-identity --query Account --output text
Get IoT data endpoint (ATS)
Use console: AWS IoT Core -> Connect one device.
Or CLI:
aws iot describe-endpoint --endpoint-type iot:Data-ATS --query endpointAddress --output text
Expected format:
<your-iot-prefix>-ats.iot.<region>.amazonaws.com
ARN prefix format
Use this prefix in policy resources:
arn:aws:iot:<region>:<account-id>
1) Create customer CA and claim certificate (OpenSSL)
Create a local working directory:
mkdir -p aws && cd aws
Create customer CA key/certificate:
openssl genrsa -out customer-ca.key 2048
openssl req -x509 -new -nodes -key customer-ca.key -sha256 -days 3650 -out customer-ca.pem -subj "/CN=AetherCustomerCA"
Create claim key and CSR:
openssl genrsa -out claim.key 2048
openssl req -new -key claim.key -out claim.csr -subj "/CN=AetherClaimCert"
Sign claim certificate with your CA:
openssl x509 -req -in claim.csr -CA customer-ca.pem -CAkey customer-ca.key -CAcreateserial -out claim.pem -days 3650 -sha256
Verify certificate chain metadata:
openssl x509 -in customer-ca.pem -noout -subject -issuer -dates
openssl x509 -in claim.pem -noout -subject -issuer -dates
You should see:
- claim
issuermatches customer CAsubject - validity dates are correct
2) Register CA in AWS IoT Core (recommended for custom CAs)
If you use your own CA-signed claim certificate, register the CA in IoT Core.
In console:
- Go to AWS IoT Core -> Security -> CAs.
- Choose Register CA certificate.
- Upload
customer-ca.pem. - Prefer multi-account / SNI_ONLY mode (AWS recommendation), when applicable.
- Activate the CA certificate.
CLI alternative (SNI_ONLY mode):
aws iot register-ca-certificate --ca-certificate file://customer-ca.pem --certificate-mode SNI_ONLY
aws iot update-ca-certificate --certificate-id <ca-certificate-id> --new-status ACTIVE
3) Provisioning role in UI flow
When you create the template through Connect many devices -> Provisioning templates -> Provisioning devices with claim certificates, AWS IoT can create and configure the provisioning role for you in the wizard.
Use the auto-created role unless your organization requires a pre-existing custom IAM role.
Optional: advanced teams can still create/manage this role manually, but this guide assumes the UI-managed role path.
4) Create fleet provisioning template (console path)
In AWS Console:
- Go to AWS IoT Core.
- Open Connect many devices.
- Open Provisioning templates.
- Choose Create provisioning template.
- Select Provisioning devices with claim certificates.
- Set template name (for example
Aether). - In the role step, choose the option to let AWS IoT create/manage the provisioning role automatically.
- Continue to claim certificate and permissions steps.
5) Create claim-certificate policy (bootstrap permissions)
Attach this policy to the claim certificate. Replace:
<region><account-id><template-name>
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Receive"
],
"Resource": [
"arn:aws:iot:<region>:<account-id>:topic/$aws/certificates/create/*",
"arn:aws:iot:<region>:<account-id>:topic/$aws/provisioning-templates/<template-name>/provision/*"
]
},
{
"Effect": "Allow",
"Action": "iot:Subscribe",
"Resource": [
"arn:aws:iot:<region>:<account-id>:topicfilter/$aws/certificates/create/*",
"arn:aws:iot:<region>:<account-id>:topicfilter/$aws/provisioning-templates/<template-name>/provision/*"
]
}
]
}
This policy intentionally allows only bootstrap/provisioning topics.
6) Upload claim credentials in template wizard
In the claim certificate step:
- CA certificate:
customer-ca.pem - claim cert:
claim.pem - claim private key:
claim.key
Then ensure:
- claim certificate is ACTIVE
- claim certificate has claim policy from Step 5 attached
7) Configure device policy (for provisioned certificates)
In Set device permissions, create/use a device policy like:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": "arn:aws:iot:<region>:<account-id>:client/${iot:ClientId}"
},
{
"Effect": "Allow",
"Action": "iot:Publish",
"Resource": "arn:aws:iot:<region>:<account-id>:topic/${iot:ClientId}"
},
{
"Effect": "Allow",
"Action": "iot:Subscribe",
"Resource": "arn:aws:iot:<region>:<account-id>:topicfilter/${iot:ClientId}/*"
},
{
"Effect": "Allow",
"Action": "iot:Receive",
"Resource": "arn:aws:iot:<region>:<account-id>:topic/${iot:ClientId}/*"
}
]
}
This policy is attached to each newly provisioned device certificate.
8) Configure Aether Nexus
Configure Nexus with claim credentials and template name:
from aether.nexus import Nexus
nexus = Nexus(
opc_ua_url="opc.tcp://0.0.0.0:4840",
token="<your-aether-token>",
aws_iot_endpoint="<your-iot-prefix>-ats.iot.<region>.amazonaws.com",
aws_iot_template_name="Aether",
aws_iot_claim_cert="./aws/claim.pem",
aws_iot_claim_key="./aws/claim.key",
)
Required alignment:
aws_iot_endpointmust be your account ATS endpointaws_iot_template_namemust exactly match template name in IoT Core- claim cert/key paths must match files provisioned on disk
9) Sigil read/write MQTT event behavior
After provisioning completes and Nexus is running with AWS IoT enabled, each Sigil operation is emitted as an MQTT message:
- Every Sigil
readoperation publishes one event message. - Every Sigil
writeoperation publishes one event message. - Topic uses the device Thing name (for example
<thing-name>).
Event payload format:
class SigilEvent(BaseModel):
"""Event data for a sigil read or write event."""
sigil_id: str
node_id: str
value: Any
timestamp: datetime
operation: Literal["read", "write"]
Notes:
operationisreadfor reads andwritefor writes.timestampis the event generation time.node_idandsigil_ididentify which Sigil produced the event.- Ensure your device runtime uses the same naming convention for MQTT client identity and Thing topic routing so events are delivered under the expected Thing topic.
10) Why not use AWS IoT Core for real-time control
Use AWS IoT Core for telemetry, events, and asynchronous commands, not hard real-time control loops.
Why:
- Asynchronous brokered messaging, not deterministic control transport
MQTT in AWS IoT Core is pub/sub with broker mediation, retries, session state, and queueing behavior. This is excellent for resilience, but it does not provide deterministic command-execution timing guarantees. - Callback + queue processing model in device SDK/runtime
In practice, MQTT clients process inbound messages through callback handlers scheduled by background/event-loop workers. Commands are executed when they are dequeued and handled by your runtime, not at a guaranteed control-cycle deadline. - No enforceable execution deadline at broker level
You can tune QoS/timeouts, but you cannot force "execute exactly at T" semantics through AWS IoT Core. Network jitter, broker scheduling, reconnect behavior, and client-side processing all affect timing. - AWS IoT service characteristics are throughput/reliability oriented
AWS documents broker limits and asynchronous behavior (for example delayed processing when limits are exceeded, queued message replay rates, and ordering caveats in some flows). These characteristics are compatible with messaging systems, not hard real-time actuation.
Recommended architecture:
- Keep closed-loop control local (PLC/RT controller/edge process with deterministic scheduler).
- Use AWS IoT Core to publish state/events and receive high-level intents.
- Treat cloud-to-device actions as eventual commands, with local safety interlocks and timeout/fallback logic.
11) Complete example: periodic Sigil reads via Weave
The following script is a complete example that periodically reads one Sigil every 2 seconds.
Each await temperature.read() generates a Sigil read event, which is published to AWS IoT Core on the Thing-name topic.
from aether.nexus import Nexus
from aether.sigil import Sigil
from aether.weave import Weave
# 1) Configure Nexus with AWS IoT fleet provisioning (claim cert path)
nexus = Nexus(
opc_ua_url="opc.tcp://0.0.0.0:4840",
token="<your-aether-token>",
aws_iot_endpoint="<your-iot-prefix>-ats.iot.<region>.amazonaws.com",
aws_iot_template_name="Aether",
aws_iot_claim_cert="./aws/claim.pem",
aws_iot_claim_key="./aws/claim.key",
)
# 2) Define a Sigil that you want to poll periodically
temperature = Sigil(
node_id="ns=2;s=Plant.Temperature",
initial_value=25.0,
description="Process temperature value.",
)
# 3) Periodic task: read Sigil every cycle
async def publish_temperature_read_event():
value = await temperature.read()
print(f"Sigil read -> Plant.Temperature={value}")
# 4) Register weave at module scope (before nexus.start)
Weave(
label="temperature-read-poller",
cycle_time=2.0,
callback=publish_temperature_read_event,
description="Reads temperature every 2 seconds to emit Sigil read MQTT events.",
)
if __name__ == "__main__":
nexus.start()
What to verify:
- Run the script and wait for provisioning/reconnect to complete.
- Open AWS IoT Core -> MQTT test client.
- Subscribe to the Thing topic (for example
<thing-name>). - Confirm you receive periodic messages with:
operation: "read"node_id: "ns=2;s=Plant.Temperature"- updated
timestamp
12) Validate provisioning end-to-end
- Start Nexus/device process with claim cert.
- Confirm device can complete first bootstrap and reconnect.
- In IoT Core, verify:
- new Thing created
- new device certificate created and
ACTIVE - device policy attached to new certificate
- Trigger Sigil reads/writes and verify
SigilEventmessages are published under the Thing-name topic.
Troubleshooting
Not authorizedon bootstrap connect:- claim cert missing policy, inactive cert, or wrong endpoint.
RegisterThingrejected:- wrong template name, provisioning role not granted correctly, or malformed template parameters.
- ARN mismatches:
- re-check
<region>and<account-id>in both claim and device policies.
- re-check
- Provisioning works once then fails:
- claim cert deactivated or rotated without updating device files.
- No new thing appears:
- inspect IoT logs and CloudWatch logs for provisioning errors.
Security recommendations
- Protect private keys (
customer-ca.key,claim.key) with strict filesystem permissions. - Do not commit cert/key material to source control.
- Consider short-lived or batch-scoped claim cert strategy and rotation.
- Monitor claim-certificate usage via IoT logs/metrics; deactivate quickly on suspected misuse.
- For stricter onboarding control, add a pre-provisioning Lambda hook to validate serial/device attributes before allowing provisioning.
References
- AWS IoT Core: Provisioning by claim
- AWS IoT Core: Provisioning templates
- AWS IoT Core: Manage CA certificates
- AWS IoT Core: Pre-provisioning hooks
- AWS IoT Core: MQTT behavior and limits
- AWS IoT Core: Service quotas and broker limits
- AWS Blog: Time-critical messaging patterns on AWS IoT Core
- AWS CLI: describe-endpoint