{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# AWS IoT Greengrass Workshop\n", "\n", "[Requirements for Greengrass on an EC2 instance](https://docs.aws.amazon.com/greengrass/latest/developerguide/module1.html#setup-filter.ec2)\n", "\n", "**This workshop is currently broken when starting the greengrass devices on the EC2 instance.**\n", "\n", "### Initialize Environment" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import boto3\n", "import sys\n", "import os\n", "import json\n", "import base64\n", "import project_path # path to helper methods\n", "\n", "from lib import workshop\n", "from botocore.exceptions import ClientError\n", "project_name = 'iot-greengrass-workshop'\n", "\n", "ec2_client = boto3.client('ec2')\n", "ec2 = boto3.resource('ec2')\n", "\n", "gg = boto3.client('greengrass')\n", "iot = boto3.client('iot')\n", "cfn = boto3.client('cloudformation')\n", "\n", "session = boto3.session.Session()\n", "region = session.region_name\n", "account_id = boto3.client('sts').get_caller_identity().get('Account')\n", "\n", "stack_name = 'GGDEC2InstanceStack'\n", "instance_size = 'm4.large'\n", "ggc_name = 'tracker-core'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Create S3 Bucket](https://docs.aws.amazon.com/AmazonS3/latest/gsg/CreatingABucket.html)\n", "\n", "We will create an S3 bucket that will be used throughout the workshop for storing our data.\n", "\n", "[s3.create_bucket](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.create_bucket) boto3 documentation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bucket = workshop.create_bucket_name('gg-')\n", "session.resource('s3').create_bucket(Bucket=bucket, CreateBucketConfiguration={'LocationConstraint': region})\n", "print(bucket)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Getting Started with Greengrass](https://docs.aws.amazon.com/greengrass/latest/developerguide/gg-gs.html)\n", "\n", "In order to help simplify the setup process we will be using a setup utility available on Github called [aws-greengrass-group-setup\n", "](https://github.com/awslabs/aws-greengrass-group-setup). This allows us to take a file based approach to the entire Greengrass group for our health tracker application. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%cd health_tracker" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!source activate JupyterSystemEnv\n", "!pip install -r requirements.txt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Create Greengrass Core](https://docs.aws.amazon.com/greengrass/latest/developerguide/module2.html)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!cp tracker/cfg_template.json tracker/cfg.json" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fill in configuration document\n", "\n", "We will be using the gg_group helper class to populate the below `cfg.json` file with the appropriate certificates, evices, and gg core." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!cat tracker/cfg.json" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./group_setup.py create-core --thing-name $ggc_name --config-file tracker/cfg.json --cert-dir tracker/certs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Validate creation\n", "Once the `group_setup.py` runs successfully it will have create the `tracker-core` IoT thing, the client certificate, and policy for the device. To validate in the AWS Console click the link below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('https://{0}.console.aws.amazon.com/iot/home?region={0}#/thinghub'.format(region))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dowload root certs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#!curl -o root-ca.pem https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem\n", "!curl -o root-ca.pem https://www.amazontrust.com/repository/AmazonRootCA1.pem\n", "!echo tracker/certs tracker/ggd/certs | xargs -n 1 cp root-ca.pem\n", "!rm root-ca.pem" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Create IoT Core Devices](https://docs.aws.amazon.com/iot/latest/developerguide/configure-iot.html)\n", "\n", "Devices connected to AWS IoT are represented by `_things_` in the registry. The registry allows you to keep a record of all of the devices that are connected to your AWS IoT account. In the first cell we will create the IoT Core devices for the heart rate, heartbeat, and web devices that will be used on the device. The second cell will create the health tracker \"brain\" that will be used to control and monitor the IoT core devices on the EC2 instance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./group_setup.py create-devices --thing-names '[heartrate_ggd,heartbeat_ggd,web_ggd]' --config-file tracker/cfg.json --cert-dir tracker/ggd/certs\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./group_setup.py create-devices --thing-names '[tracker_brain]' --config-file tracker/cfg.json \\\n", "--cert-dir tracker/ggd/certs --append True --cloud_sync True\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Create Greengrass Lambda Functions](https://docs.aws.amazon.com/greengrass/latest/developerguide/config-lambda.html)\n", "\n", "We will create the lambda functions that will be used with greengrass core on the device to control the flow of messages on the tracker brain and provide for a central error detector." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./lambda_setup.py create lambda/TrackerBrain/cfg_lambda.json" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./lambda_setup.py create lambda/TrackerErrorDetector/cfg_lambda.json" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Associate IoT Core devices to Lambda Functions](https://docs.aws.amazon.com/greengrass/latest/developerguide/lambda-group-config.html)\n", "\n", "Now we want to associate the lambda functions created above with the tracker configuration on the device" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./group_setup.py associate-lambda ./tracker/cfg.json ./lambda/TrackerBrain/cfg_lambda.json" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./group_setup.py associate-lambda ./tracker/cfg.json ./lambda/TrackerErrorDetector/cfg_lambda.json" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Deploy Greengrass Core" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./group_setup.py create tracker ./tracker/cfg.json --group_name tracker" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Edit cfg.json\n", "\n", "The gg group creation script grabs the legacy IoT Endpoint. We will use the latest IoT endpoint using the Amazon root cert.\n", "\n", "[iot.describe_endpoint](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot.html#IoT.Client.describe_endpoint)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "resp = iot.describe_endpoint(\n", " endpointType='iot:Data-ATS'\n", ")\n", "\n", "iot_endpoint = resp['endpointAddress']\n", "print(iot_endpoint)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!cat tracker/cfg.json" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./group_setup.py deploy ./tracker/cfg.json" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create the Greengrass config.json for the Tracker\n", "\n", "Replace the `{{region}}` and `{{iot_endpoint}}` with values in the `cfg.json` file above. The `{{iot_endpoint}}` is in the `misc` section of the document." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('Account Id: {}'.format(account_id))\n", "print('Region: {}'.format(region))\n", "print('IoT Endpoint: {}'.format(iot_endpoint))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%writefile tracker/gg_config.json\n", "\n", "{\n", " \"coreThing\": {\n", " \"caPath\": \"root-ca.pem\",\n", " \"certPath\": \"tracker-core.pem\",\n", " \"keyPath\": \"tracker-core.prv\",\n", " \"thingArn\": \"arn:aws:iot:us-west-2:649037252677:thing/tracker-core\",\n", " \"iotHost\": \"alkm2mcwkgpv7-ats.iot.us-west-2.amazonaws.com\",\n", " \"ggHost\": \"greengrass-ats.iot.us-west-2.amazonaws.com\",\n", " \"keepAlive\": 600\n", " },\n", " \"runtime\": {\n", " \"cgroup\": {\n", " \"useSystemd\": \"yes\"\n", " }\n", " },\n", " \"managedRespawn\": false,\n", " \"crypto\" : {\n", " \"principals\" : {\n", " \"SecretsManager\" : {\n", " \"privateKeyPath\" : \"file:///greengrass/certs/tracker-core.prv\"\n", " },\n", " \"IoTCertificate\" : {\n", " \"privateKeyPath\" : \"file:///greengrass/certs/tracker-core.prv\",\n", " \"certificatePath\" : \"file:///greengrass/certs/tracker-core.pem\"\n", " }\n", " },\n", " \"caPath\" : \"file:///greengrass/certs/root-ca.pem\"\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Zip device software" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!zip -r tracker.zip tracker/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Upload to S3](https://docs.aws.amazon.com/AmazonS3/latest/dev/Welcome.html)\n", "\n", "Next, we will upload the json file created above to S3 to be used later in the workshop.\n", "\n", "[s3.upload_file](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.upload_file) boto3 documentation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "file_name = 'tracker.zip'\n", "session.resource('s3').Bucket(bucket).Object(os.path.join('device', file_name)).upload_file(file_name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Create VPC](https://docs.aws.amazon.com/vpc/index.html) \n", "\n", "In order to simulate a Greengrass device on an EC2 instance we will create a new VPC with a public subnet by running the code below. As you can see to make a subnet public an Internet Gateway is attached to the VPC and a routing table is created with and entry to route all traffic at `0.0.0.0/0` to the Internet gateway. We will store the VPC and Subnet Id's to be used later in the notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vpc, subnet1, subnet2 = workshop.create_and_configure_vpc()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vpc_id = vpc.id\n", "subnet1_id = subnet1.id\n", "subnet2_id = subnet2.id\n", "print(vpc_id)\n", "print(subnet1_id)\n", "print(subnet2_id)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Create Security Group for EC2 instance](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html)\n", "\n", "A security group acts as a virtual firewall for your instance to control inbound and outbound traffic. When you launch an instance in a VPC, you can assign up to five security groups to the instance. Security groups act at the instance level, not the subnet level. Therefore, each instance in a subnet in your VPC could be assigned to a different set of security groups. If you don't specify a particular group at launch time, the instance is automatically assigned to the default security group for the VPC.\n", "\n", "The security group will open ports `80` and `8883` respectively for HTTP and MQTT access.\n", "\n", "[ec2_client.create_security_group](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.create_security_group)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sec_group = ec2_client.create_security_group(\n", " Description='Security Group for EC2 instance acting as IoT Greengrass device',\n", " GroupName=project_name+'-sg',\n", " VpcId=vpc_id\n", ")\n", "\n", "sec_group_id=sec_group[\"GroupId\"]\n", "print(sec_group_id)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Authorizing Inbound Traffic for Your Linux Instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/authorizing-access-to-an-instance.html)\n", "\n", "Security groups enable you to control traffic to your instance, including the kind of traffic that can reach your instance. For example, you can allow computers from only your home network to access your instance using SSH. If your instance is a web server, you can allow all IP addresses to access your instance using HTTP or HTTPS, so that external users can browse the content on your web server.\n", "\n", "[ec2_client.authorize_security_group_ingress](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.authorize_security_group_ingress)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = ec2_client.authorize_security_group_ingress(\n", " GroupId=sec_group_id,\n", " IpPermissions=[\n", " {\n", " 'IpProtocol': 'tcp',\n", " 'FromPort': 8883,\n", " 'ToPort': 8883,\n", " 'IpRanges': [\n", " {\n", " 'CidrIp': '0.0.0.0/0',\n", " 'Description': 'MQTT access'\n", " },\n", " ]\n", " },\n", " {\n", " 'IpProtocol': 'tcp',\n", " 'FromPort': 443,\n", " 'ToPort': 443,\n", " 'IpRanges': [\n", " {\n", " 'CidrIp': '0.0.0.0/0',\n", " 'Description': 'Secure MQTT access'\n", " },\n", " ]\n", " },\n", " { \n", " 'IpProtocol': 'tcp',\n", " 'FromPort': 80,\n", " 'ToPort': 80,\n", " 'IpRanges': [\n", " {\n", " 'CidrIp': '0.0.0.0/0',\n", " 'Description': 'HTTP access'\n", " },\n", " ]\n", " }\n", " ]\n", ")\n", "\n", "print(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Get latest [Amazon Linux AMI](https://aws.amazon.com/amazon-linux-ami/) in the region\n", "\n", "The Amazon Linux AMI is a supported and maintained Linux image provided by Amazon Web Services for use on Amazon Elastic Compute Cloud (Amazon EC2). It is designed to provide a stable, secure, and high performance execution environment for applications running on Amazon EC2. It supports the latest EC2 instance type features and includes packages that enable easy integration with AWS. Amazon Web Services provides ongoing security and maintenance updates to all instances running the Amazon Linux AMI. The Amazon Linux AMI is provided at no additional charge to Amazon EC2 users.\n", "\n", "We will lookup the latest AMI version of the Amazon Linux OS to be used for the EC2 instance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ami = workshop.get_latest_amazon_linux()\n", "print(ami)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Launch EC2 instance and install [Greengrass](https://aws.amazon.com/greengrass/)\n", "\n", "The UserData section of the EC2 instance launch includes everything needed to configure and install Greengrass on the EC2 instance. View the UserData below to get an understanding of what's involved to configure Greengrass on devices. [Greengrass Core downloads](https://docs.aws.amazon.com/greengrass/latest/developerguide/what-is-gg.html#gg-core-download-tab) link provides the available devices and OS's available.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!cat greengrass-device.yaml" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Upload [CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/GettingStarted.html) template\n", "\n", "In the interest of time we will leverage CloudFormation to launch an EC2 instance that will install the appropriate Greengrass software to mock an edge device." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "file_name = 'greengrass-device.yaml'\n", "session.resource('s3').Bucket(bucket).Object(os.path.join('cfn', file_name)).upload_file(file_name)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create CloudFormation Stack for Greengrass EC2 instance" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cfn_template = 'https://s3-{0}.amazonaws.com/{1}/cfn/{2}'.format(region, bucket, file_name)\n", "print(cfn_template)\n", "response = cfn.create_stack(\n", " StackName=stack_name,\n", " TemplateURL=cfn_template,\n", " Capabilities = [\"CAPABILITY_NAMED_IAM\"],\n", " Parameters=[\n", " {\n", " 'ParameterKey': 'InstanceType',\n", " 'ParameterValue': instance_size\n", " },\n", " {\n", " 'ParameterKey': 'Subnet',\n", " 'ParameterValue': subnet1_id\n", " },\n", " {\n", " 'ParameterKey': 'ImageId',\n", " 'ParameterValue': ami\n", " },\n", " {\n", " 'ParameterKey': 'SG',\n", " 'ParameterValue': sec_group_id\n", " },\n", " {\n", " 'ParameterKey': 'S3BucketName',\n", " 'ParameterValue': bucket\n", " }\n", " ]\n", ")\n", "\n", "print(response)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Wait for CloudFormation template to complete" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "waiter = cfn.get_waiter('stack_create_complete')\n", "waiter.wait(\n", " StackName=stack_name\n", ")\n", "\n", "print('The wait is over for {0}'.format(stack_name))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Get EC2 instance id from template\n", "\n", "We will get the instance id of the EC2 instance to use in the next section from the `Output` section of the CloudFormation template." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = cfn.describe_stacks(\n", " StackName=stack_name\n", ")\n", "\n", "ec2_instance_id = response['Stacks'][0]['Outputs'][0]['OutputValue']\n", "\n", "print('https://{0}.console.aws.amazon.com/ec2/v2/home?region={0}#Instances:search={1};sort=tag:Name'.format(region, ec2_instance_id))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Update Greengrass Core local endpoint\n", "\n", "The local endpoint for all client devices to discover to needs to be updated to the `Public DNS (IPv4)` value of the EC2 instance created above. Update the `tracker-core` GGC in the link below.\n", "\n", "You will edit the core configuration and add the EC2 instances DNS information for the endpoint and `8883` for the port.\n", "![GG Connectivity](../../docs/assets/images/ggc-connectivity.png)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('https://{0}.console.aws.amazon.com/iot/home?region={0}#/greengrass/cores/{1}'.format(region, ggc_name))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Shell access to EC2 instance with [Systems Manager Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html)\n", "\n", "Session Manager is a fully managed AWS Systems Manager capability that lets you manage your Amazon EC2 instances through an interactive one-click browser-based shell or through the AWS CLI. Session Manager provides secure and auditable instance management without the need to open inbound ports, maintain bastion hosts, or manage SSH keys. Session Manager also makes it easy to comply with corporate policies that require controlled access to instances, strict security practices, and fully auditable logs with instance access details, while still providing end users with simple one-click cross-platform access to your Amazon EC2 instances.\n", "\n", "Execute the cell below and open the link in a new tab. You will now start shell access into the EC2 instance to complete the workshop. Select your instance id as noted above and click the `Start Session` button. \n", "\n", "![Session Manager](../../docs/assets/images/session_manager.png)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('https://{0}.console.aws.amazon.com/systems-manager/session-manager/start-session?region={0}'.format(region))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get shell access into the EC2 instance and run the final commands to start the GG Core and Devices" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### To start device operations of the Master host `ec2 instance`\n", "1. **Start GG Core** -- in the `ec2 instance` terminal execute:\n", " ```bash\n", " cd /greengrass/ggc/packages/ \n", " sudo ./greengrassd start\n", " ```\n", "1. **Start GG Devices** -- in the `ec2 instance` terminal execute:\n", " ```bash\n", " cd /health/groups/\n", " ./start_tracker.sh\n", " ```\n", " \n", "After starting the tracker devices, to determine success you should see three \n", "entries in the list of processes, similar to the following:\n", "```\n", "ec2 instance$ screen -ls\n", "There are screens on:\n", " 2540.web (Detached)\n", " 2537.heartbeat (Detached)\n", " 2534.heartrate (Detached)\n", "3 Sockets in /var/run/screen/S-root.\n", "```\n", "To view the output of any of the Greengrass Devices attach to the \n", "`screen` by using the command `screen -r `. Example that \n", "re-attaches to the `web` device process in the above list:\n", "```\n", "screen -r 8281\n", "```\n", ":warning: Remember to detach from the screen using `Ctrl-A, D` **not** `Ctrl-C`. \n", "Using `Ctrl-C` will exit the process being viewed.\n", "\n", "If the `hr` device started successfully you should see messages arriving to the IoT core through the MQTT topic.\n", "\n", "### To stop device operations of the Tracker host\n", "1. **Stop GG Devices** -- in `ec2 instance` Terminal execute:\n", " ```bash\n", " cd ~/groups/tracker\n", " ./stop_tracker.sh\n", " ```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cleanup" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = cfn.delete_stack(StackName=stack_name)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "waiter = cfn.get_waiter('stack_delete_complete')\n", "waiter.wait(\n", " StackName=stack_name\n", ")\n", "\n", "print('The wait is over for {0}'.format(stack_name))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%run ./group_setup.py clean-all ./tracker/cfg.json\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!aws lambda delete-function --function-name TrackerBrain" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!aws lambda delete-function --function-name TrackerErrorDetector" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!aws s3 rb s3://$bucket --force " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "workshop.vpc_cleanup(vpc_id)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "conda_python2", "language": "python", "name": "conda_python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.15" } }, "nbformat": 4, "nbformat_minor": 2 }