Start deploying devices! <\/li>\n<\/ol>\n\n\n\nSince we are lazy, we will use scripting and CloudFormation as much as possible. As a bonus point, we’ll create a golden virtual machine image with all the installed components we can use as a template to deploy at the edge.<\/p>\n\n\n\n
First, a script will create and save provisioning certificates using AWS CLI. We will save them using AWS SecretsManager, as you will see later, we use SecretsManager to automate their deployment on the golden virtual machine image.<\/p>\n\n\n\n
#!\/bin\/bash\nENV=${1}\n\n\nSECRETSMANAGER_PREFIX=\"MyAwesomeProjectSecret\"\nTHINGGROUP_NAME=\"MyThingGroup\"\n\n# Create the certificate\nCERTIFICATE_ARN=$(aws iot create-keys-and-certificate \\\n \t--certificate-pem-outfile \"claim-certs\/claim.pem.crt\" \\\n \t--public-key-outfile \"claim-certs\/claim.public.pem.key\" \\\n \t--private-key-outfile \"claim-certs\/claim.private.pem.key\" \\\n \t--set-as-active | jq -r .certificateArn)\n\nSECRET_STRING=$(echo \"$(cat claim-certs\/claim.pem.crt)|$(cat claim-certs\/claim.private.pem.key)\" | tr '\\n' ';' )\naws secretsmanager create-secret --name $SECRETSMANAGER_PREFIX\/$ENV\/claims-certificate --secret-string $SECRET_STRING\n\n# Attach the policy to the certificate\naws iot attach-policy --policy-name GreengrassProvisioningClaimPolicy --target $CERTIFICATE_ARN\naws iot create-thing-group --thing-group-name $THINGGROUP_NAME<\/code><\/pre>\n\n\n\nRun this script before deploying this CloudFormation template that will create the thing group, policies to associate it with claim certificates, and the required service roles.<\/p>\n\n\n\n
---\nAWSTemplateFormatVersion: '2010-09-09'\nDescription: GreenGrass with Fleet Manager provisioning template\n\nMetadata:\n\n 'AWS::CloudFormation::Interface':\n\tParameterGroups:\n \t- Label: {default: 'Required parameters'}\n \tParameters:\n \t- Env\n \t- ThingGroupName\n \t- Label: {default: 'Optional parameters'}\n \tParameters:\n \t- ProjectName\n\nParameters:\n\n Env:\n\tType: String\n\tDescription: \"Insert the environment\"\n\n ProjectName:\n\tType: String\n\tDescription: \"Insert the name of the project\"\n\n ThingGroupName:\n\tType: String\n\tDescription: \"Insert the Thing Group name\"\n\nResources:\n\n GreengrassTokenExchangeRole:\n\tType: AWS::IAM::Role\n\tProperties:\n \tAssumeRolePolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tPrincipal:\n \tService:\n \t- 'credentials.iot.amazonaws.com'\n \tAction:\n \t- 'sts:AssumeRole'\n \t- Effect: 'Allow'\n \tPolicies:\n \t- PolicyName: 'LogPersmission'\n \tPolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tAction:\n \t- \"logs:CreateLogGroup\"\n \t- \"logs:CreateLogStream\"\n \t- \"logs:PutLogEvents\"\n \t- \"logs:DescribeLogStreams\"\n \t- \"s3:GetBucketLocation\"\n \t- \"iam:PassRole\"\n \tResource: '*'\n \t \tRoleName: !Sub '${ProjectName}-${Env}-V2TokenExchangeRole'\n\n GreengrassTokenExchangeRoleAlias:\n\tType: AWS::IoT::RoleAlias\n\tProperties:\n \tRoleAlias: !Sub '${ProjectName}-${Env}-CoreTokenExchangeRoleAlias'\n \tRoleArn: !GetAtt GreengrassTokenExchangeRole.Arn\n \tTags:\n \t- Key: Name\n \tValue: !Sub \"${ProjectName}-${Env}-CoreTokenExchangeRoleAlias\"\n\n GreengrassV2IoTThingPolicy:\n\tType: AWS::IoT::Policy\n\tProperties:\n \tPolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:Publish\"\n \t- \"iot:Subscribe\"\n \t- \"iot:Receive\"\n \t- \"iot:Connect\"\n \t- \"greengrass:*\"\n \tResource: \"*\"\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:AssumeRoleWithCertificate\"\n \tResource: !GetAtt GreengrassTokenExchangeRoleAlias.RoleAliasArn\n \tPolicyName: V2IoTThingPolicy\n\n GreengrassFleetProvisioningRole:\n\tType: AWS::IAM::Role\n\tProperties:\n \tAssumeRolePolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tPrincipal:\n \tService:\n \t- 'iot.amazonaws.com'\n \tAction:\n \t- 'sts:AssumeRole'\n \tPolicies:\n \t- PolicyName: 'ECRAccess'\n \tPolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tAction:\n \t- \"logs:CreateLogGroup\"\n \t- \"logs:CreateLogStream\"\n \t- \"logs:PutLogEvents\"\n \t- \"logs:DescribeLogStreams\"\n \t- \"s3:GetBucketLocation\"\n \tResource: '*'\n \tRoleName: !Sub '${ProjectName}-${Env}-FleetProvisioningRole'\n\n GreengrassFleetProvisioningTemplate:\n\tType: AWS::IoT::ProvisioningTemplate\n\tProperties:\n \tDescription: \"template to provision iot fleet resources\"\n \tEnabled: true\n \tProvisioningRoleArn: !GetAtt GreengrassFleetProvisioningRole.Arn\n \tTemplateBody: |\n \t{\n \t\"Parameters\": {\n \t\"ThingName\": {\n \t\"Type\": \"String\"\n \t},\n \t\"ThingGroupName\": {\n \t\"Type\": \"String\"\n \t},\n \t\"AWS::IoT::Certificate::Id\": {\n \t\"Type\": \"String\"\n \t}\n \t},\n \t\"Resources\": {\n \t\"MyThing\": {\n \t\"OverrideSettings\": {\n \t\"AttributePayload\": \"REPLACE\",\n \t\"ThingGroups\": \"REPLACE\",\n \t\"ThingTypeName\": \"REPLACE\"\n \t},\n \t\"Properties\": {\n \t\"AttributePayload\": {},\n \t\"ThingGroups\": [\n \t{\n \t\"Ref\": \"ThingGroupName\"\n \t}\n \t],\n \t\"ThingName\": {\n \t\"Ref\": \"ThingName\"\n \t}\n \t},\n \t\"Type\": \"AWS::IoT::Thing\"\n \t},\n \t\"MyPolicy\": {\n \t\"Properties\": {\n \t\"PolicyName\": \"V2IoTThingPolicy\"\n \t},\n \t\"Type\": \"AWS::IoT::Policy\"\n \t},\n \t\"MyCertificate\": {\n \t\"Properties\": {\n \t\"CertificateId\": {\n \t\"Ref\": \"AWS::IoT::Certificate::Id\"\n \t},\n \t\"Status\": \"Active\"\n \t},\n \t\"Type\": \"AWS::IoT::Certificate\"\n \t}\n \t}\n \t}\n \tTemplateName: !Sub \"${ProjectName}-${Env}-FleetTemplate\"\n\n GreengrassProvisioningClaimPolicy:\n\tType: AWS::IoT::Policy\n\tProperties:\n \tPolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:Connect\"\n \tResource: \"*\"\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:Subscribe\"\n \tResource:\n \t- !Sub \"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topicfilter\/$aws\/certificates\/create\/*\"\n \t- !Sub \"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topicfilter\/$aws\/provisioning-templates\/${GreengrassFleetProvisioningTemplate}\/provision\/*\"\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:Publish\"\n \t- \"iot:Receive\"\n \tResource:\n \t- !Sub \"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic\/$aws\/certificates\/create\/*\"\n \t- !Sub \"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic\/$aws\/provisioning-templates\/${GreengrassFleetProvisioningTemplate}\/provision\/*\"\n \tPolicyName: GreengrassProvisioningClaimPolicy<\/code><\/pre>\n\n\n\nThe <\/p>\n\n\n\n
GreengrassFleetProvisioningTemplate <\/code><\/pre>\n\n\n\nresource will register our IoT device using a GroupName and a ThingName. You can personalize the template according to your needs. Later, you will see that “ThingName” will be our VM hostname.<\/p>\n\n\n\n
If you want to get notified and run custom actions when a device registers, you can add a rule like this and the corresponding SNS topics (I will not include the complete code since it’s a simple lambda function)<\/p>\n\n\n\n
ThingCreationIotRule:\n\tType: AWS::IoT::TopicRule\n\tProperties:\n \tRuleName: !Sub '${ProjectName}_${Env}_greengrass_rule'\n \tTopicRulePayload:\n \tRuleDisabled: 'false'\n \tSql: SELECT * FROM '$aws\/events\/thing\/#'\n \tActions:\n \t- Lambda:\n \tFunctionArn: !GetAtt RegisterThingLambda.Arn\n \tErrorAction:\n \tSns:\n \tTargetArn: !Ref SNSFailedNotificationTopic\n \tRoleArn: !GetAtt NotificationRole.Arn<\/code><\/pre>\n\n\n\nNow it’s time to create our golden virtual machine image using Ubuntu 22.04 as a base install; you use VirtualBox or VMware as a testing and development environment.<\/p>\n\n\n\n
The following script will install the required packages and retrieve claim certificates. To use it, you should set temporary AWS IAM credentials as environment variables or, for better, integrate everything in a packer template.<\/p>\n\n\n\n
#!\/bin\/bash\n\nENV=${1}\n\nSECRETSMANAGER_PREFIX=\"MyAwesomeProjectSecret\"\n\nmkdir -p claim-certs\nSECRET_STRING=$(aws secretsmanager get-secret-value --secret-id $SECRETSMANAGER_PREFIX\/$ENV\/claims-certificate --query SecretString --output text)\n\necho $SECRET_STRING | cut -d '|' -f 1 | tr ';' '\\n' > claim-certs\/claim.pem.crt\necho $SECRET_STRING | cut -d '|' -f 2 | tr ';' '\\n' > claim-certs\/claim.private.pem.key\n\n\nexport DEBIAN_FRONTEND=noninteractive\napt update && apt upgrade -y\napt -yq install python3-pip zip open-vm-tools default-jdk libgl1-mesa-glx\n\n\nmkdir -p \/greengrass\/v2\/installer\nmkdir \/greengrass\/v2\/claim-certs\n\n\nmv claim-certs\/claim.private.pem.key \/greengrass\/v2\/claim-certs\/\nmv \/claim-certs\/claim.pem.crt \/greengrass\/v2\/claim-certs\/\n\n\n\ncd \/greengrass\/v2\ncurl -o \/greengrass\/v2\/AmazonRootCA1.pem https:\/\/www.amazontrust.com\/repository\/AmazonRootCA1.pem\ncurl -s https:\/\/d2s8p88vqu9w66.cloudfront.net\/releases\/greengrass-nucleus-latest.zip > greengrass-nucleus-latest.zip\nunzip greengrass-nucleus-latest.zip -d installer && rm greengrass-nucleus-latest.zip\ncurl -s https:\/\/d2s8p88vqu9w66.cloudfront.net\/releases\/aws-greengrass-FleetProvisioningByClaim\/fleetprovisioningbyclaim-latest.jar > installer\/aws.greengrass.FleetProvisioningByClaim.jar<\/code><\/pre>\n\n\n\nAll the required AWS IoT Greengrass components are now installed. Let’s now add the ultimate lazy sysadmin touch to our solution: we definitely want to have something that, when started, configures itself without user intervention.\u00a0<\/p>\n\n\n\n
We will define a template for our AWS IoT Greengrass configuration and, taking advantage of <\/p>\n\n\n\n
systemd <\/code><\/pre>\n\n\n\nunit and scripting, use the virtual machine’s hostname as ThingName Create a file named config.yml in <\/p>\n\n\n\n
\/greengrass\/v2\/installer\/ <\/code><\/pre>\n\n\n\nspecifying that we will use the IoT fleet provisioning plugin.<\/p>\n\n\n\n
---\nservices:\n aws.greengrass.Nucleus:\n\tversion: \"2.9.1\"\n aws.greengrass.FleetProvisioningByClaim:\n\tconfiguration:\n \trootPath: \"\/greengrass\/v2\"\n \tawsRegion: \"eu-west-1\"\n \tiotDataEndpoint: \"endpoint.iot.region.amazonaws.com\"\n \tiotCredentialEndpoint: \"endpoint.credentials.iot.region.amazonaws.com\"\n \tiotRoleAlias: \"myproject-environment-CoreTokenExchangeRoleAlias\"\n \tprovisioningTemplate: \"myproject-environment-FleetTemplate\"\n \tclaimCertificatePath: \"\/greengrass\/v2\/claim-certs\/claim.pem.crt\"\n \tclaimCertificatePrivateKeyPath: \"\/greengrass\/v2\/claim-certs\/claim.private.pem.key\"\n \trootCaPath: \"\/greengrass\/v2\/AmazonRootCA1.pem\"\n \ttemplateParameters:\n \tThingName: \"REPLACEME\"\n \tThingGroupName: \"MyThingGroup\"<\/code><\/pre>\n\n\n\nChange this file according to your environment. You can obtain <\/p>\n\n\n\n
iotDataEndpoint <\/code><\/pre>\n\n\n\nand <\/p>\n\n\n\n
iotCredentialEndpoint <\/code><\/pre>\n\n\n\nvalues by issuing these commands:<\/p>\n\n\n\n
aws iot describe-endpoint --endpoint-type iot:Data-ATS\naws iot describe-endpoint --endpoint-type iot:CredentialProvider<\/code><\/pre>\n\n\n\nAs you can see, ThingName value is set as “REPLACEME<\/strong>“. A script <\/p>\n\n\n\n(\/greengrass\/configure-device.sh) <\/code><\/pre>\n\n\n\nwill replace this value and a systemd service will run it at the first boot.<\/p>\n\n\n\n
#!\/bin\/bash\n\nset -e\n\nsed -i s\/REPLACEME\/$(hostname)\/g \/greengrass\/v2\/installer\/config.yaml\necho \"********************** RUN SERVICE **************************\"\njava -Droot=\"\/greengrass\/v2\" -Dlog.store=FILE \\\n-jar \/greengrass\/v2\/installer\/lib\/Greengrass.jar \\\n--trusted-plugin \/greengrass\/v2\/installer\/aws.greengrass.FleetProvisioningByClaim.jar \\\n--init-config \/greengrass\/v2\/installer\/config.yaml \\\n--component-default-user ggc_user:ggc_group \\\n--setup-system-service true<\/code><\/pre>\n\n\n\nThe first line <\/p>\n\n\n\n
(sed -i s\/REPLACEME\/$(hostname)\/g \/greengrass\/v2\/installer\/config.yaml) <\/code><\/pre>\n\n\n\nreplaces the value with the virtual machine’s current hostname.<\/p>\n\n\n\n
Create now a unit file in the \/lib\/systemd\/system directory with a meaningful name (we used “greengrass-config.service”)<\/p>\n\n\n\n
[Unit]\nBefore=systemd-user-sessions.service\nWants=network-online.target\nAfter=network-online.target\nConditionPathExists=!\/greengrass\/greengrass_installed\n\n[Service]\nType=oneshot\nExecStart=\/greengrass\/configure-device.sh\nExecStartPost=\/usr\/bin\/touch \/greengrass\/greengrass_installed\nRemainAfterExit=yes\n\n[Install]\nWantedBy=multi-user.target<\/code><\/pre>\n\n\n\nThis is a pretty standard systemd unit, with an exception:<\/p>\n\n\n\n
ConditionPathExists=!\/greengrass\/greengrass_installed<\/code><\/pre>\n\n\n\ntells systemd to run this unit only if this file does not exist.<\/p>\n\n\n\n
ExecStartPost=\/usr\/bin\/touch \/greengrass\/greengrass_installed <\/code><\/pre>\n\n\n\ntells systemd to create the file that, so after the first time, the service will not run anymore.<\/p>\n\n\n\n
Activate service at boot with<\/p>\n\n\n\n
systemctl daemon-reload\nsystemctl enable greengrass-config.service<\/code><\/pre>\n\n\n\nOnce you finish this configuration, just export your virtual machine image in a format such as OVA (or create a VMware template) and deploy it! <\/p>\n\n\n\n
Remember: this is only a sample configuration; you may need to optimize and update your system to shrink disk size and disable unnecessary system services.<\/p>\n\n\n\n
Once you deploy this solution, you can run functions on your distributed environment and take advantage of all the unused computing resources you may have at your disposal on edge.<\/p>\n\n\n\n
Other AWS Services can use this solution as a base, like running managed SageMaker jobs at the edge, but let’s keep this topic for another time.<\/p>\n\n\n\n
Today we saw how automation could relieve us from the execution of boring tasks, even if, at first glance, everything seems to get more complicated.<\/p>\n\n\n\n
Did you ever find yourself in an automation frenzy to accommodate laziness? <\/p>\n\n\n\n
Let us know in the comments!<\/p>\n\n\n\n
See you in 14 days on #Proud2beCloud<\/strong><\/p>\n\n\n\n \n\n\n\nAbout Proud2beCloud<\/h4>\n\n\n\n Proud2beCloud is a blog by beSharp<\/a>, an Italian APN Premier Consulting Partner expert in designing, implementing, and managing complex Cloud infrastructures and advanced services on AWS. Before being writers, we are Cloud Experts working daily with AWS services since 2007. We are hungry readers, innovative builders, and gem-seekers. On Proud2beCloud, we regularly share our best AWS pro tips, configuration insights, in-depth news, tips&tricks, how-tos, and many other resources. Take part in the discussion!<\/p>\n","protected":false},"excerpt":{"rendered":"“If I had six hours to chop down a tree, I’d spend the first four hours sharpening the axe.” Abraham […]<\/p>\n","protected":false},"author":13,"featured_media":5406,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[478],"tags":[574,413],"yoast_head":"\n
How to onboard thousand of devices on AWS Greengrass and live happily - Proud2beCloud Blog<\/title>\n \n \n \n \n \n \n \n \n \n \n \n \n\t \n\t \n\t \n \n \n \n \n \n \n\t \n\t \n\t \n