View a markdown version of this page

Golden image procedure - AWS Prescriptive Guidance

Golden image procedure

The following sections describe the procedure for creating a golden image and writing that image to each device on the production line.

Create your golden device

Step 1: Install and provision Greengrass.

Install the Greengrass edge runtime on your golden device as you typically would, and provision it by using your preferred provisioning method.

Step 2: Deploy your components.

From the cloud, deploy your application components and configuration to your golden device, as you typically would.

Read the golden image from your golden device

You can now snapshot your golden device to create a golden image.

Step 1: Shut down Greengrass.

Shut down the Greengrass service on your golden device. For example:

sudo systemctl stop greengrass.service

Step 2: Snapshot the Greengrass file tree.

Depending on your preferred golden method, take a full or partial snapshot of the Greengrass file tree, and include it in your golden image.

Step 3: Copy your Docker images.

If your components use Docker images as artifacts, take a snapshot of the Docker image registry and include it in your golden image.

Alternatively, you can use docker save to create an archive of each Docker image, and copy these from the golden device. For example:

user@machine:~/environment $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE homeassistant/home-assistant 2024.6 2b4c9de733f1 9 days ago 1.6GB user@machine:~/environment $ docker save homeassistant/home-assistant | gzip > home-assistant.tar.gz

In general, this approach is slower than adding the Docker image registry to your golden image.

Write the golden image to your production devices

You now need to write the golden image to each of your production devices that come through your manufacturing line. The following steps would typically be automated by using scripting.

Step 1: Write the image.

Write your golden image to the production device. Make sure that it includes the full or partial snapshot of the Greengrass directory tree and the Docker image registry, as applicable.

Step 2: Load Docker images.

If you elected to use docker save to copy Docker image artifacts from your golden device, use docker load to load these images onto your production device. For example:

user@machine:~/environment $ docker load < home-assistant.tar.gz user@machine:~/environment $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE homeassistant/home-assistant 2024.6 2b4c9de733f1 9 days ago 1.6GB

In general, this approach is slower than adding the Docker image registry to your golden image.

Step 3: Add credentials.

If your device doesn't use fleet provisioning, install the unique X.509 certificate and private key for the production device, replacing the golden device credentials. The exact method of achieving this depends on whether you use a hardware security module (HSM) and your particular public key infrastructure (PKI).

Configure your production devices

Full Greengrass snapshot method

Follow these steps only if you're using the full Greengrass snapshot method.

Step 1: Edit the configuration.

Edit the config/effectiveConfig.yaml file . For example:

sudo nano /greengrass/v2/config/effectiveConfig.yaml

Step 2: Set the thing name.

The actions required to set the thing name of the new production device depend on whether you're using fleet provisioning. Make the correct modifications for your use case.

If you aren't using fleet provisioning, you can set the thing name of your production device by editing the thingName property to replace the golden device thing name with the production device thing name. For example:

system: certificateFilePath: "/greengrass/v2/thingCert.crt" privateKeyPath: "/greengrass/v2/privKey.key" rootCaPath: "/greengrass/v2/rootCA.pem" rootpath: "/greengrass/v2" thingName: "ProductionDeviceThingName1"

If you are using fleet provisioning, you must first erase part of the golden device configuration to ensure that the fleet provisioning plugin will run on the production device. You can do this by replacing the golden device thing name in the thingName property with an empty string. For example:

system: certificateFilePath: "/greengrass/v2/thingCert.crt" privateKeyPath: "/greengrass/v2/privKey.key" rootCaPath: "/greengrass/v2/rootCA.pem" rootpath: "/greengrass/v2" thingName: ""

Additionally, you must add the fleet provisioning plugin configuration to the services property, and pass the production device thing name as a property within the templateParameters property. The following example assumes that the fleet provisioning template has a parameter named ThingName, and therefore there's a ThingName property within the templateParameters of the plugin configuration:

system: certificateFilePath: "/greengrass/v2/thingCert.crt" privateKeyPath: "/greengrass/v2/privKey.key" rootCaPath: "/greengrass/v2/rootCA.pem" rootpath: "/greengrass/v2" thingName: "" services: aws.greengrass.FleetProvisioningByClaim: configuration: rootPath: "/greengrass/v2" awsRegion: "us-east-1" iotDataEndpoint: "<prefix>-ats.iot.us-east-1.amazonaws.com" iotCredentialEndpoint: "<prefix>.credentials.iot.us-east-1.amazonaws.com" iotRoleAlias: "GreengrassCoreTokenExchangeRoleAlias" provisioningTemplate: "GreengrassFleetProvisioningTemplate" claimCertificatePath: "/greengrass/v2/claim-certs/claim.pem.crt" claimCertificatePrivateKeyPath: "/greengrass/v2/claim-certs/claim.private.pem.key" rootCaPath: "/greengrass/v2/rootCA.pem" templateParameters: ThingName: "ProductionDeviceThingName1"

Your use case might require some different fleet provisioning plugin configuration parameters than what's shown here. For more information, see Configure the AWS IoT fleet provisioning plugin in the AWS IoT Greengrass documentation.

Step 3: Update interpolated thing names.

If you have any components that use the iot:thingName recipe variable to interpolate the thing name, you will likely have access control statements in which the golden device thing name has been interpolated. For example, in the case of the Certificate Rotator community component:

services: aws.greengrass.labs.CertificateRotator: componentType: "GENERIC" configuration: accessControl: aws.greengrass.ipc.mqttproxy: aws.greengrass.labs.CertificateRotator:mqttproxy:1: operations: - "aws.greengrass#PublishToIoTCore" policyDescription: "Allows access to publish to relevant topics" resources: - "$aws/things/GoldenDeviceThingName/jobs/+/get" - "$aws/things/GoldenDeviceThingName/jobs/+/update" - "awslabs/things/GoldenDeviceThingName/certificate/create" - "awslabs/things/GoldenDeviceThingName/certificate/commit" aws.greengrass.labs.CertificateRotator:mqttproxy:2: operations: - "aws.greengrass#SubscribeToIoTCore" policyDescription: "Allows access to subscribe to relevant topics" resources: - "$aws/things/GoldenDeviceThingName/jobs/notify-next" - "$aws/things/GoldenDeviceThingName/jobs/+/get/accepted" - "$aws/things/GoldenDeviceThingName/jobs/+/get/rejected" - "$aws/things/GoldenDeviceThingName/jobs/+/update/accepted" - "$aws/things/GoldenDeviceThingName/jobs/+/update/rejected" - "awslabs/things/GoldenDeviceThingName/certificate/create/accepted" - "awslabs/things/GoldenDeviceThingName/certificate/create/rejected" - "awslabs/things/GoldenDeviceThingName/certificate/commit/accepted" - "awslabs/things/GoldenDeviceThingName/certificate/commit/rejected" keyAlgorithm: "RSA-2048" signingAlgorithm: "SHA256WITHRSA"

In all such cases, you must replace all instances of the golden device thing name with your production device thing name.

Step 4: Remove golden device secrets.

Examine effectiveConfig.yaml for any encrypted secrets that were stored by your golden device. These will be stored within the secret manager component configuration. For example:

aws.greengrass.SecretManager: componentType: "PLUGIN" configuration: cloudSecrets: - arn: "arn:aws:secretsmanager:us-east-1:111122223333:secret:greengrass-home-assistant-KIzJfZ" dependencies: - "aws.greengrass.Nucleus:SOFT" lifecycle: {} runtime: secretResponse: "{\"secrets\":[{\"arn\":\"arn:aws:secretsmanager:us-east-1:111122223333:secret:greengrass-home-assistant-KIzJfZ\"\ ,\"name\":\"greengrass-home-assistant\",\"versionId\":\"8e481177-9250-4458-9f1f-3690d28e4ae9\"\ ,\"encryptedSecretString\":\"AgV4Rpc9 ... MYeVALYQ==\"\ ,\"versionStages\":[\"AWSCURRENT\"],\"createdDate\":1660648425915}]}" version: "2.1.8"

You should delete this secret to ensure that your production device will not attempt to decrypt a secret that was encrypted by the golden device. Delete the secretResponse property. For example:

aws.greengrass.SecretManager: componentType: "PLUGIN" configuration: cloudSecrets: - arn: "arn:aws:secretsmanager:us-east-1:111122223333:secret:greengrass-home-assistant-KIzJfZ" dependencies: - "aws.greengrass.Nucleus:SOFT" lifecycle: {} runtime: version: "2.1.8"

Step 5: Apply the altered configuration.

Apply the altered configuration, to update config/config.tlog. For example:

sudo java -Droot="/greengrass/v2" \ -jar /greengrass/v2/alts/current/distro/lib/Greengrass.jar \ --start false \ --init-config /greengrass/v2/config/effectiveConfig.yaml

Partial Greengrass snapshot method

Configuration of your production devices is trivial if you use the partial Greengrass snapshot method. The procedure is identical to the normal bootstrap partial configuration procedure when you install Greengrass, except that the initial configuration is passed to the Greengrass runtime that's already installed.

Follow these steps only if you're using the partial Greengrass snapshot method.

Step 1: Create the configuration.

The required partial configuration depends on whether you're using fleet provisioning. Be sure to make the correct modifications for your use case.

If you aren't using fleet provisioning, you can create a partial configuration file that doesn't include any configuration for the fleet provisioning plugin. Set the thingName property to your production device thing name. For example:

system: certificateFilePath: "/greengrass/v2/device.pem.crt" privateKeyPath: "/greengrass/v2/private.pem.key" rootCaPath: "/greengrass/v2/AmazonRootCA1.pem" rootpath: "/greengrass/v2" thingName: "ProductionDeviceThingName1" services: aws.greengrass.Nucleus: componentType: "NUCLEUS" version: "2.12.6" configuration: awsRegion: "us-east-1" iotRoleAlias: "GreengrassCoreTokenExchangeRoleAlias" iotDataEndpoint: "<prefix>-ats.iot.us-east-1.amazonaws.com" iotCredEndpoint: "<prefix>.credentials.iot.us-east-1.amazonaws.com"

If you are using fleet provisioning, the partial configuration should include configuration for the fleet provisioning plugin. This configuration should pass the production device thing name as a property within the templateParameters property. The following example assumes that the fleet provisioning template has a parameter named ThingName, and therefore there's a ThingName property within the templateParameters of the plugin configuration:

services: aws.greengrass.Nucleus: version: "2.12.6" aws.greengrass.FleetProvisioningByClaim: configuration: rootPath: "/greengrass/v2" awsRegion: "us-east-1" iotDataEndpoint: "<prefix>-ats.iot.us-east-1.amazonaws.com" iotCredentialEndpoint: "<prefix>.credentials.iot.us-east-1.amazonaws.com" iotRoleAlias: "GreengrassCoreTokenExchangeRoleAlias" provisioningTemplate: "GreengrassFleetProvisioningTemplate" claimCertificatePath: "/greengrass/v2/claim-certs/claim.pem.crt" claimCertificatePrivateKeyPath: "/greengrass/v2/claim-certs/claim.private.pem.key" rootCaPath: "/greengrass/v2/AmazonRootCA1.pem" templateParameters: ThingName: "ProductionDeviceThingName1"

Your use case might require different fleet provisioning plugin configuration parameters from what's shown here. For more information, see Configure the AWS IoT fleet provisioning plugin in the AWS IoT Greengrass documentation.

Step 2: Apply the configuration.

Apply the configuration to create config/config.tlog. For example:

sudo java -Droot="/greengrass/v2" \ -jar /greengrass/v2/alts/current/distro/lib/Greengrass.jar \ --start false \ --init-config config.yaml