The Device Patcher module provides a scalable way to patch a physical device by remotely executing Ansible playbooks on the device.
The module utilizes SSM to activate the device as a hybrid instance and creates state manager associations which run Ansible playbooks to remotely patch the software on the physical device.
The following represents step-by-step walkthrough to setup a Greengrass V2 core device on a EC2 instance. Refer to the swagger for further details about each API and its options.
To successfully deploy Device Patcher software on the device there are a few pre-requisites that must be met.
- Ansible Playbook stored in S3 for SSM State Manager
- SSM Agent installed on the device/instance
- Ansible Agent installed on the device/instance
The host machine or edge device needs to be connected to the internet and have the prequisites installed.
NOTE: If the host machine is an EC2 instance, then the steps defined in Device Patcher Integration Testing can be used to create an AMI to launch an EC2 instance with pre-reqs installed.
In order to deploy an ansible Patch, the first step is to create a patch template. The template specifies configuration for a particular patch. The payload has 2 main parts, the ansible playbook file and the extraVars
to pass to the playbook itself. These extraVars
should be common to all patches. These can be overwritten when the actual patch is created.
curl --location --request POST '<endpoint>/patchTemplates' \
--header 'Accept: application/vnd.aws-cdf-v1.0+json' \
--header 'Content-type: application/vnd.aws-cdf-v1.0+json' \
--data '{"name": "sampleTemplate", "playbookFileContents": "LS0tCi0gaG9zdHM6IGFsbAogIHJlbW90ZV91c2VyOiBlYzJfdXNlcgogIGdhdGhlcl9mYWN0czogZmFsc2UKICBiZWNvbWU6IHllcwogIHRhc2tzOgogICAgLSBwaW5nOgo=", "patchType": "agentbased", "description": "Benji doing test"}'
{
"name": "sampleTemplate",
"playbookName": "sample-playbook.yml",
"playbookSource": {
"bucket": "xxxxxxxxxxxxxx",
"key": "device-patcher/playbooks/sampleTemplate___sample-playbook.yml"
},
"patchType": "agentbased",
"versionNo": 1,
"createdAt": "2022-04-14T20:48:47.410Z",
"updatedAt": "2022-04-14T20:48:58.748Z",
"enabled": true,
"description": "Sample Patch Template updated"
}
All templates are versioned within the system. The first POST
call will create the template, whereas subsequent PATCH
will update it.
In order to run a patch job on the device first needs to be activated as a hybrid instance within SSM. This can be done by generating an activation code for the device and then following the steps to stop/register/start the SSM agent on the device itself. Since this step is done on the device itself, connectivity to the device is required. This is the only time when a physical connection between the device and host needs to be made. The API call below will generate the activation code which can be taken to activate the SSM agent. This step will allow this device to be activated as a Hybrid instance in SSM.
curl --location --request POST '<endpoint>/activations' \
--header 'Content-Type: application/vnd.aws-cdf-v1.0+json' \
--header 'Accept: application/vnd.aws-cdf-v1.0+json' \
--data-raw '{
"deviceId": "ggv2-test-core-1"
}'
{
"activationId": "a1102421-922f-46d5-9a85-bdbad8d90d6c",
"activationCode": "nxj3IC1HBquDVxM14Oqo",
"activationRegion": "us-east-1"
}
In order to activate the device, the activation code needs to be passed to the device. This can be done by running the following command on the device:
# The following commands can be executed from the local machine on a host, provided the HOST endpoint, private key, and user allows the connection.
# The second command below requires the "$ACTIVATION_CODE", "$ACTIVATION_ID" & "$ACTIVATION_REGION" be replaced with the values from the response from Step 2.
# If the communication with host is via SSH, then the following commands can be used:
ssh -i ${HOST_PRIVATE_KEY_PATH} ${HOST_USER}@${HOST_DNS_ENDPOINT} 'sudo systemctl stop amazon-ssm-agent'
ssh -i ${HOST_PRIVATE_KEY_PATH} ${HOST_USER}@${HOST_DNS_ENDPOINT} 'sudo -E amazon-ssm-agent -register -code "$ACTIVATION_CODE" -id "$ACTIVATION_ID" -region "$ACTIVATION_REGION" -clear'
ssh -i ${HOST_PRIVATE_KEY_PATH} ${HOST_USER}@${HOST_DNS_ENDPOINT} 'sudo systemctl start amazon-ssm-agent'
# If the commands need to be ran directly on the host:
> sudo systemctl stop amazon-ssm-agent
> sudo -E amazon-ssm-agent -register -code "$ACTIVATION_CODE" -id "$ACTIVATION_ID" -region "$ACTIVATION_REGION" -clear
> sudo systemctl start amazon-ssm-agent
# Refer this link to activate the SSM agent for different platforms: https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-install-managed-linux.html
Upon successfully activating the device, The patch step can be executed. The following request will create a new SSM State Manager Association which will execute Ansible playbook on the device itself.
This particular endpoint is an asynchronous REST API. The patch gets queued which is processed at a later time. The payload requires a list of patches. The individual patch has required properties such as deviceId, PatchTemplateName, and extraVars for overriding playbook parameters defined in the patch template or unique to a particular patch.
curl --location --request POST '<endpoint>/patchTasks' \
--header 'Content-Type: application/vnd.aws-cdf-v1.0+json' \
--header 'Accept: application/vnd.aws-cdf-v1.0+json' \
--data-raw '{
"patches": [{
"deviceId": "<device-identifier>",
"patchTemplateName": "ggv2-ec2-installer-template",
"extraVars":{
"iot_device_config_url": "${aws:s3:presign:https://<bucket>/<prefix>?expiresIn=604800}",
"iot_device_cred_zip_url": "${aws:s3:presign:https://<bucket>/<prefix>?expiresIn=604800}"
}
}]
}'
HTTP: 200 OK
There are couple different ways the status of the patch can be checked. If the status of the whole patch task needs to be checked, then the following query can be made
# The {patchTaskId} can be obtained from the x-taskid response Header from Step 3.
curl --location --request GET '<endpoint>/patchTasks/{patchTaskId}/patches' \
--header 'Content-Type: application/vnd.aws-cdf-v1.0+json' \
--header 'Accept: application/vnd.aws-cdf-v1.0+json'
{
"patches": [
{
"deviceId": "ec2-ggv2core-device1",
"patchId": "2eb2dbc0-62e1-11ec-b3fe-919bdd9a87d1",
"taskId": "2e7c6270-62e1-11ec-b3fe-919bdd9a87d1",
"createdAt": "2021-12-22T04:39:43.228Z",
"updatedAt": "2021-12-22T04:39:55.017Z",
"patchTemplateName": "sample-playbook-template",
"patchStatus": "pending",
"patchType": "agentbased",
"statusMessage": "SSM:InProgress",
"extraVars": {
"iot_device_cred_zip_url": "${aws:s3:presign:https://cdf-xxxxxxxxxx-us-west-2/greengrass2/artifacts/ggv2-test-core-1/certs.zip?expiresIn=604800}",
"iot_device_config_url": "${aws:s3:presign:https://cdf-xxxxxxxxxx-us-west-2/greengrass2/artifacts/ggv2-test-core-1/installerConfig.yml?expiresIn=604800}"
},
"associationId": "b919b476-e507-49fd-a963-b5e0076daa8b"
},
{
"deviceId": "ec2-ggv2core-device1",
"patchId": "52240a20-62e1-11ec-b3fe-919bdd9a87d1",
"taskId": "51de75a0-62e1-11ec-b3fe-919bdd9a87d1",
"createdAt": "2021-12-22T04:40:42.690Z",
"updatedAt": "2021-12-22T17:24:33.818Z",
"patchTemplateName": "ggv2-ec2-installer-template",
"patchStatus": "failed",
"patchType": "agentbased",
"statusMessage": "SSM:Failed",
"extraVars": {
"iot_device_cred_zip_url": "${aws:s3:presign:https://cdf-xxxxxxxxxx-us-west-2/greengrass2/artifacts/ggv2-test-core-1/certs.zip?expiresIn=604800}",
"iot_device_config_url": "${aws:s3:presign:https://cdf-xxxxxxxxxx-us-west-2/greengrass2/artifacts/ggv2-test-core-1/installerConfig.yml?expiresIn=604800}"
},
"associationId": "032bbe91-281e-46eb-9f3f-564180a9eda3"
}
]
}
If the patch for a particular device needs to be checked, then the following query can be made to retrieve the patch for a particular device
curl --location --request GET '<endpoint>/devices/{deviceId}/patches' \
--header 'Content-Type: application/vnd.aws-cdf-v1.0+json' \
--header 'Accept: application/vnd.aws-cdf-v1.0+json'
{
"patches": [
{
"deviceId": "ec2-ggv2core-device1",
"patchId": "2eb2dbc0-62e1-11ec-b3fe-919bdd9a87d1",
"taskId": "2e7c6270-62e1-11ec-b3fe-919bdd9a87d1",
"createdAt": "2021-12-22T04:39:43.228Z",
"updatedAt": "2021-12-22T04:39:55.017Z",
"patchTemplateName": "sample-playbook-template",
"patchStatus": "pending",
"patchType": "agentbased",
"statusMessage": "SSM:InProgress",
"extraVars": {
"iot_device_cred_zip_url": "${aws:s3:presign:https://cdf-xxxxxxxxxx-us-west-2/greengrass2/artifacts/ggv2-test-core-1/certs.zip?expiresIn=604800}",
"iot_device_config_url": "${aws:s3:presign:https://cdf-xxxxxxxxxxx-us-west-2/greengrass2/artifacts/ggv2-test-core-1/installerConfig.yml?expiresIn=604800}"
},
"associationId": "b919b476-e507-49fd-a963-b5e0076daa8b"
}
]
}
curl --location --request PATCH '<endpoint>/patches/{patchId}' \
--header 'Content-Type: application/vnd.aws-cdf-v1.0+json' \
--header 'Accept: application/vnd.aws-cdf-v1.0+json' \
--data-raw '{
"patchStatus": "retry"
}'
HTTP: 200 OK