├── media
├── testfile
├── WX20200505-214220.png
├── WX20200505-214553.png
├── WX20200505-220505.png
├── WX20200508-221500.png
├── WX20200505-213724@2x.png
├── Screen Shot 2020-02-23 at 9.02.34 pm.png
├── Screen Shot 2020-02-23 at 9.30.48 pm.png
├── Screen Shot 2020-02-23 at 9.32.55 pm.png
├── Screen Shot 2020-02-23 at 9.34.54 pm.png
├── Screen Shot 2020-02-27 at 10.04.33 pm.png
├── Screen Shot 2020-02-27 at 10.04.52 pm.png
├── Screen Shot 2020-02-27 at 8.24.34 pm.png
├── Screen Shot 2020-02-27 at 8.35.19 pm.png
├── Screen Shot 2020-02-27 at 9.46.46 pm.png
├── Screen Shot 2020-02-27 at 9.52.27 pm.png
├── Screen Shot 2020-02-27 at 9.58.07 pm.png
├── Screen Shot 2020-03-06 at 4.02.42 pm.png
├── Screen Shot 2020-03-08 at 4.48.39 pm.png
├── Screen Shot 2020-03-08 at 4.56.48 pm.png
├── Screen Shot 2020-03-08 at 9.02.24 pm.png
├── Screen Shot 2020-03-08 at 9.13.36 pm.png
├── Screen Shot 2020-03-08 at 9.15.56 pm.png
├── Screen Shot 2020-03-08 at 9.16.11 pm.png
├── Screen Shot 2020-03-08 at 9.16.36 pm.png
├── Screen Shot 2020-03-08 at 9.49.24 pm.png
├── Screen Shot 2020-03-09 at 9.20.09 pm.png
├── Screen Shot 2020-03-09 at 9.22.56 pm.png
├── Screen Shot 2020-03-14 at 4.29.45 pm.png
├── Screen Shot 2020-03-14 at 4.37.33 pm.png
├── Screen Shot 2020-05-05 at 9.31.00 pm.png
├── Screen Shot 2020-05-05 at 9.33.32 pm.png
├── Screen Shot 2020-05-08 at 10.00.57 pm.png
├── Screen Shot 2020-05-08 at 10.01.05 pm.png
├── Screen Shot 2020-05-08 at 10.07.45 pm.png
├── Screen Shot 2020-05-08 at 10.08.25 pm.png
└── Screen Shot 2020-05-08 at 10.18.05 pm.png
├── manifest.json
├── aws-health-cfn-stack.yaml
├── health_org_demo.py
├── README.md
└── lambda_function.py
/media/testfile:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/media/WX20200505-214220.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/WX20200505-214220.png
--------------------------------------------------------------------------------
/media/WX20200505-214553.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/WX20200505-214553.png
--------------------------------------------------------------------------------
/media/WX20200505-220505.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/WX20200505-220505.png
--------------------------------------------------------------------------------
/media/WX20200508-221500.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/WX20200508-221500.png
--------------------------------------------------------------------------------
/media/WX20200505-213724@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/WX20200505-213724@2x.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-23 at 9.02.34 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-23 at 9.02.34 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-23 at 9.30.48 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-23 at 9.30.48 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-23 at 9.32.55 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-23 at 9.32.55 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-23 at 9.34.54 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-23 at 9.34.54 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-27 at 10.04.33 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-27 at 10.04.33 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-27 at 10.04.52 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-27 at 10.04.52 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-27 at 8.24.34 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-27 at 8.24.34 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-27 at 8.35.19 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-27 at 8.35.19 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-27 at 9.46.46 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-27 at 9.46.46 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-27 at 9.52.27 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-27 at 9.52.27 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-02-27 at 9.58.07 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-02-27 at 9.58.07 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-06 at 4.02.42 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-06 at 4.02.42 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-08 at 4.48.39 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-08 at 4.48.39 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-08 at 4.56.48 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-08 at 4.56.48 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-08 at 9.02.24 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-08 at 9.02.24 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-08 at 9.13.36 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-08 at 9.13.36 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-08 at 9.15.56 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-08 at 9.15.56 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-08 at 9.16.11 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-08 at 9.16.11 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-08 at 9.16.36 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-08 at 9.16.36 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-08 at 9.49.24 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-08 at 9.49.24 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-09 at 9.20.09 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-09 at 9.20.09 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-09 at 9.22.56 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-09 at 9.22.56 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-14 at 4.29.45 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-14 at 4.29.45 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-03-14 at 4.37.33 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-03-14 at 4.37.33 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-05-05 at 9.31.00 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-05-05 at 9.31.00 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-05-05 at 9.33.32 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-05-05 at 9.33.32 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-05-08 at 10.00.57 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-05-08 at 10.00.57 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-05-08 at 10.01.05 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-05-08 at 10.01.05 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-05-08 at 10.07.45 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-05-08 at 10.07.45 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-05-08 at 10.08.25 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-05-08 at 10.08.25 pm.png
--------------------------------------------------------------------------------
/media/Screen Shot 2020-05-08 at 10.18.05 pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/HEAD/media/Screen Shot 2020-05-08 at 10.18.05 pm.png
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileLocations": [
3 | {
4 | "URIs": [
5 |
6 | "https://bucket-name.s3.amazonaws.com/event_data_file.csv"
7 |
8 | ]
9 | },
10 | ],
11 | "globalUploadSettings": {
12 | "format": "CSV",
13 | "delimiter": ",",
14 | "containsHeader": "true"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/aws-health-cfn-stack.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Transform: AWS::Serverless-2016-10-31
4 | Description: AWS Cloudformation Template to launch S3 bucket, IAM Lambda Role and
5 | Health API polling Lambda.
6 | Parameters:
7 | S3BucketNameforHealthData:
8 | Description: Name of the new S3 Bucket to host AWS Health API data
9 | AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$
10 | Type: String
11 | Resources:
12 | AWSHealthApiDataHostingBucket:
13 | Type: AWS::S3::Bucket
14 | Properties:
15 | BucketName:
16 | Ref: S3BucketNameforHealthData
17 | CopyZips:
18 | Type: Custom::CopyZips
19 | Properties:
20 | ServiceToken: !GetAtt 'CopyZipsFunction.Arn'
21 | DestBucket: !Ref 'AWSHealthApiDataHostingBucket'
22 | SourceBucket: 'my-public-bucket-2021'
23 | Prefix: ''
24 | Objects:
25 | - aws-health-api-polling-ae1b9c80-aae2-44f2-9a3c-9c282db54ee3.zip
26 | DependsOn:
27 | - LambdaExeRole
28 | - AWSHealthApiDataHostingBucket
29 | LambdaExeRole:
30 | Type: AWS::IAM::Role
31 | Properties:
32 | RoleName: HealthPollingLambdaExecutionRole
33 | AssumeRolePolicyDocument:
34 | Version: '2012-10-17'
35 | Statement:
36 | - Effect: Allow
37 | Principal:
38 | Service: lambda.amazonaws.com
39 | Action:
40 | - sts:AssumeRole
41 | ManagedPolicyArns:
42 | - arn:aws:iam::aws:policy/AWSHealthFullAccess
43 | - arn:aws:iam::aws:policy/AmazonS3FullAccess
44 | - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
45 | awshealthapipolling:
46 | Type: AWS::Serverless::Function
47 | Properties:
48 | FunctionName: HealthAPIPollingLambdaFunction
49 | Description: This Lambda function is the main function to poll health data at organizations level
50 | Environment:
51 | Variables:
52 | s3_bucket_name:
53 | Ref: S3BucketNameforHealthData
54 | Handler: lambda_function.lambda_handler
55 | Runtime: python3.8
56 | CodeUri:
57 | Bucket:
58 | Ref: S3BucketNameforHealthData
59 | Key: aws-health-api-polling-ae1b9c80-aae2-44f2-9a3c-9c282db54ee3.zip
60 | Description: lambda function
61 | MemorySize: 256
62 | Timeout: 900
63 | Role: !GetAtt 'LambdaExeRole.Arn'
64 | Events:
65 | Schedule1:
66 | Type: Schedule
67 | Properties:
68 | Schedule: rate(10 minutes)
69 | DependsOn:
70 | - LambdaExeRole
71 | - AWSHealthApiDataHostingBucket
72 | - CopyZips
73 | CopyZipsFunction:
74 | Type: AWS::Lambda::Function
75 | Properties:
76 | Description: Copies objects from a source S3 bucket to a destination
77 | Handler: index.handler
78 | Runtime: python2.7
79 | Role: !GetAtt 'LambdaExeRole.Arn'
80 | Timeout: 240
81 | Code:
82 | ZipFile: |
83 | import json
84 | import logging
85 | import threading
86 | import boto3
87 | import cfnresponse
88 | def copy_objects(source_bucket, dest_bucket, prefix, objects):
89 | s3 = boto3.client('s3')
90 | for o in objects:
91 | key = prefix + o
92 | copy_source = {
93 | 'Bucket': source_bucket,
94 | 'Key': key
95 | }
96 | print('copy_source: %s' % copy_source)
97 | print('dest_bucket = %s'%dest_bucket)
98 | print('key = %s' %key)
99 | s3.copy_object(CopySource=copy_source, Bucket=dest_bucket,
100 | Key=key)
101 | def delete_objects(bucket, prefix, objects):
102 | s3 = boto3.client('s3')
103 | objects = {'Objects': [{'Key': prefix + o} for o in objects]}
104 | s3.delete_objects(Bucket=bucket, Delete=objects)
105 | def timeout(event, context):
106 | logging.error('Execution is about to time out, sending failure response to CloudFormation')
107 | cfnresponse.send(event, context, cfnresponse.FAILED, {}, None)
108 | def handler(event, context):
109 | # make sure we send a failure to CloudFormation if the function
110 | # is going to timeout
111 | timer = threading.Timer((context.get_remaining_time_in_millis()
112 | / 1000.00) - 0.5, timeout, args=[event, context])
113 | timer.start()
114 | print('Received event: %s' % json.dumps(event))
115 | status = cfnresponse.SUCCESS
116 | try:
117 | source_bucket = event['ResourceProperties']['SourceBucket']
118 | dest_bucket = event['ResourceProperties']['DestBucket']
119 | prefix = event['ResourceProperties']['Prefix']
120 | objects = event['ResourceProperties']['Objects']
121 | if event['RequestType'] == 'Delete':
122 | delete_objects(dest_bucket, prefix, objects)
123 | else:
124 | copy_objects(source_bucket, dest_bucket, prefix, objects)
125 | except Exception as e:
126 | logging.error('Exception: %s' % e, exc_info=True)
127 | status = cfnresponse.FAILED
128 | finally:
129 | timer.cancel()
130 | cfnresponse.send(event, context, status, {}, None)
--------------------------------------------------------------------------------
/health_org_demo.py:
--------------------------------------------------------------------------------
1 | ####################################################################################################################################################################################
2 | # Script Function: Demonstrate AWS Health API for Organization View
3 | # Author: JC
4 | # Time: 2020.11.03
5 | # Version: 1.2
6 | # Execution requirements:
7 | # Update the "bucketName" value following the instruction from Step 10 in https://github.com/JerryChenZeyun/aws-health-api-organization-view/blob/master/README.md#setup
8 | ####################################################################################################################################################################################
9 |
10 | import logging
11 | import datetime
12 | import boto3
13 | import json
14 | from botocore.exceptions import ClientError
15 | import os
16 | import pandas as pd
17 |
18 | ####################################################################################################################################################################################
19 | bucketName = 'YOUR-S3-BUCKET-NAME-HERE'
20 | ####################################################################################################################################################################################
21 |
22 | arn_list = []
23 | service_list = []
24 | eventTypeCode_list = []
25 | eventTypeCategory_list = []
26 | region_list = []
27 | startTime_list = []
28 | endTime_list = []
29 | lastUpdatedTime_list = []
30 | statusCode_list = []
31 | impactedAccount_List = []
32 | eventDescription_List = []
33 | impactedEntity_List = []
34 |
35 | # time encoder class
36 | class DatetimeEncoder(json.JSONEncoder):
37 | def default(self, obj):
38 | try:
39 | return super(DatetimeEncoder, obj).default(obj)
40 | except TypeError:
41 | return str(obj)
42 |
43 | ## Upload the organization events json message to S3 file
44 | def upload_to_s3(file_name, bucket, key):
45 | """Upload a file to an S3 bucket
46 | :param file_name: File to upload
47 | :param bucket: Bucket to upload to
48 | :param object_name: S3 object name. If not specified then file_name is used
49 | :return: True if file was uploaded, else False
50 | """
51 | s3 = boto3.resource('s3')
52 | try:
53 | s3.meta.client.upload_file(file_name, bucket, key)
54 | print("s3 upload success -- uploaded " + file_name + " to the bucket: " + bucket)
55 | except ClientError as e:
56 | logging.error(e)
57 | return False
58 | print("s3 upload error occurs", e)
59 | return True
60 |
61 | csvFileName = 'event_data_file.csv'
62 |
63 | # Store event data info into csv file
64 | def write_to_csv():
65 | whole_table = pd.DataFrame(
66 | {
67 | 'arn': arn_list,
68 | 'service': service_list,
69 | 'eventTypeCode': eventTypeCode_list,
70 | 'eventTypeCategory': eventTypeCategory_list,
71 | 'region': region_list,
72 | 'startTime': startTime_list,
73 | 'endTime': endTime_list,
74 | 'lastUpdatedTime': lastUpdatedTime_list,
75 | 'statusCode': statusCode_list,
76 | 'impactedAccount': impactedAccount_List,
77 | 'impactedEntity': impactedEntity_List,
78 | 'eventDescription': eventDescription_List
79 | }
80 | )
81 | event_data_file = open(csvFileName, "w")
82 | event_data_file.write(whole_table.to_csv(index=False))
83 | event_data_file.close()
84 | print("\n#########################################################################\n")
85 | print("Event data saved to CSV file.")
86 | print("\n#########################################################################\n")
87 |
88 | ## Create the Manifest file, and save it to the user specified S3 bucket
89 | def create_manifest():
90 | """Create a Manifest file to an user specified S3 bucket
91 | This is required when user need to visualise the event data hosted in S3 bucket via QuickSight
92 | """
93 | dirpath = os.getcwd()
94 | file_path_ori = dirpath + "/manifest.json"
95 | file_path_new = dirpath + "/manifests3.json"
96 |
97 | with open(file_path_ori, "rt") as fin:
98 | with open(file_path_new, "wt") as fout:
99 | for line in fin:
100 | fout.write(line.replace('bucket-name', bucketName))
101 |
102 | ## Enable the health service in organization level
103 | def enable_health_org():
104 | client = boto3.client('health', region_name='us-east-1')
105 | try:
106 | response = client.enable_health_service_access_for_organization()
107 | print("enable health api at organization level success")
108 | except ClientError as e:
109 | logging.error(e)
110 | print("enable health api at organization level error occurs:", e)
111 |
112 | print("\n#########################################################################\n")
113 | print(response)
114 | print("\n#########################################################################\n")
115 | print("Health Service has been enabled at AWS Organization Level -- Done!")
116 | print("\n#########################################################################\n")
117 |
118 | ## describe_health_service_status_for_organization
119 | def describe_health_service_status_for_org():
120 | client = boto3.client('health', region_name='us-east-1')
121 | response = client.describe_health_service_status_for_organization()
122 | print(response)
123 | print("\n#########################################################################\n")
124 |
125 | ## describe_events_for_organization(**kwargs)
126 | def describe_events_for_org():
127 | client = boto3.client('health', region_name='us-east-1')
128 | event_paginator = client.get_paginator('describe_events_for_organization')
129 | event_page_iterator = event_paginator.paginate()
130 |
131 | for event_page in event_page_iterator:
132 | json_event = json.dumps(event_page, cls=DatetimeEncoder)
133 | parsed_event = json.loads(json_event)
134 |
135 | events = parsed_event.get("events")
136 |
137 | for event in events:
138 | arn_list.append(event.get("arn"))
139 | service_list.append(event.get("service"))
140 | eventTypeCode_list.append(event.get("eventTypeCode"))
141 | eventTypeCategory_list.append(event.get("eventTypeCategory"))
142 | region_list.append(event.get("region"))
143 | startTime_list.append(event.get("startTime"))
144 | lastUpdatedTime_list.append(event.get("lastUpdatedTime"))
145 | statusCode_list.append(event.get("statusCode"))
146 | if (event.get("statusCode") == "open"):
147 | endTime_list.append("NULL")
148 | elif (event.get("statusCode") == "closed"):
149 | endTime_list.append(event.get("endTime"))
150 |
151 | response = client.describe_event_details(eventArns = [event.get("arn")], locale = "en")
152 | json_response = json.dumps(response, cls=DatetimeEncoder)
153 | parsed_response = json.loads (json_response)
154 | eventDescription_List.append(parsed_response["successfulSet"][0]["eventDescription"]["latestDescription"])
155 |
156 | print("\n#########################################################################\n")
157 | print("describe_events_for_organization response - Total events:", len(arn_list))
158 | print("\n")
159 | print("These events are related to the following services:\n", service_list)
160 | print("\n#########################################################################\n")
161 | return(True)
162 |
163 | ## describe_affected_accounts
164 | def describe_affected_accounts(event_arn):
165 | affectedAccounts = []
166 | client = boto3.client('health', region_name='us-east-1')
167 | event_accounts_paginator = client.get_paginator('describe_affected_accounts_for_organization')
168 |
169 | event_accounts_page_iterator = event_accounts_paginator.paginate(eventArn=event_arn)
170 |
171 | for event_accounts_page in event_accounts_page_iterator:
172 | json_event_accounts = json.dumps(event_accounts_page, cls=DatetimeEncoder)
173 | parsed_event_accounts = json.loads (json_event_accounts)
174 |
175 | if((parsed_event_accounts['affectedAccounts']) == "[]"):
176 | affectedAccounts = affectedAccounts +"[]"
177 | else:
178 | affectedAccounts = affectedAccounts + (parsed_event_accounts['affectedAccounts'])
179 | print("For service event arn: {}".format(arn))
180 | print("Affected accounts are: {}".format(affectedAccounts))
181 |
182 | return(affectedAccounts)
183 |
184 | ## Retrieve account id automatically
185 | def get_account_id():
186 | return(boto3.client('sts').get_caller_identity().get('Account'))
187 |
188 | ## describe_affected_entities_for_organization(**kwargs)
189 | def describe_affected_entities(event_arn):
190 | affectedEntities = []
191 | affectedEntities_sub_list = []
192 | client = boto3.client('health', region_name='us-east-1')
193 | affected_accounts = describe_affected_accounts(event_arn)
194 |
195 | for affected_account in affected_accounts:
196 | affected_account = ''.join(str(e) for e in affected_account)
197 |
198 | #If there's no affected account for this event, the 'affected account' will be filled by '[]'
199 | if(not affected_account):
200 | affected_account = '[]'
201 |
202 | print("affected_account: ", affected_account)
203 | print("affected account type:", type(affected_account))
204 |
205 | event_entities_paginator = client.get_paginator('describe_affected_entities_for_organization')
206 | event_entities_page_iterator = event_entities_paginator.paginate(
207 | organizationEntityFilters=[
208 | {
209 | 'awsAccountId': affected_account,
210 | 'eventArn': event_arn
211 | }
212 | ]
213 | )
214 |
215 | affectedEntities_sub_list = []
216 |
217 | for event_entities_page in event_entities_page_iterator:
218 | json_event_entities = json.dumps(event_entities_page, cls=DatetimeEncoder)
219 | parsed_event_entities = json.loads (json_event_entities)
220 | print("for event {} and affected account {}: ".format(event_arn, affected_account))
221 | print("event entities list are: {} \n".format(parsed_event_entities))
222 |
223 | for entity in parsed_event_entities['entities']:
224 |
225 | if((entity['entityValue']) != "[]"):
226 | affectedEntities_sub_list.append(entity['entityValue'])
227 |
228 | print("\n#########################################################################\n")
229 | print("affected entities list are:", affectedEntities_sub_list)
230 | print("\n#########################################################################\n")
231 |
232 | if(len(affectedEntities_sub_list) == 0):
233 | affectedEntities = affectedEntities + "[]"
234 | else:
235 | affectedEntities = affectedEntities + affectedEntities_sub_list
236 | return(affectedEntities)
237 |
238 |
239 | # Main part of Script
240 | # -------------------
241 | if __name__ == "__main__":
242 |
243 | ## Enable the health service in organization level
244 | enable_health_org()
245 |
246 | ## describe_health_service_status_for_organization
247 | describe_health_service_status_for_org()
248 |
249 | ## describe_events_for_organization(**kwargs)
250 | describe_events_for_org()
251 |
252 | ## describe_affected_accounts & describe_affected_entities
253 | for arn in arn_list:
254 | eventAffectedAccounts = describe_affected_accounts(arn)
255 | print ("eventAffectedAccounts:",eventAffectedAccounts)
256 | impactedAccount_List.append(eventAffectedAccounts)
257 |
258 | eventAffectedEntities = describe_affected_entities(arn)
259 | print ("eventAffectedEntities:",eventAffectedEntities)
260 | impactedEntity_List.append(eventAffectedEntities)
261 |
262 | print("complete impacted account list:", impactedAccount_List)
263 | print("complete impacted entity list:", impactedEntity_List)
264 |
265 | ## save event data to csv file
266 | write_to_csv()
267 |
268 | ## upload the csv file to S3 bucket
269 | upload_to_s3(file_name=csvFileName, bucket=bucketName, key=csvFileName)
270 |
271 | ## create manifest file, and save it to S3 bucket
272 | create_manifest()
273 | upload_to_s3(file_name="manifests3.json", bucket=bucketName, key="manifests3.json")
274 |
275 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Building Organization Service Health Check Solution with Cloud9 and QuickSight
2 |
3 | This lab is provided as part of [AWS Summit Online](https://aws.amazon.com/events/summits/online/), click [here](https://github.com/phonghuule/awssummmitonline) to explore the full list of hands-on labs.
4 | ℹ️ You will run this lab in your own AWS account. Please follow directions at the end of the lab to remove resources to avoid future costs.
5 | ℹ️ Make sure your AWS account has "Business" or "Enterprise" support plan, so as to be able to consume Health API at Organization level.
6 |
7 |
8 |
9 | This lab is intended to showcase the Health API Organization View feature. Organization View is intended to aggregate Personal Health Dashboard/Health events at the PAYER account level within an organization. Thus it requires an Organization hierarchy, including linked accounts. Furthermore, for the data required to be aggregated at the PAYER account level, it needs to be enabled prior to the Personal Health Dashboard notification being created.
10 |
11 | At minimum, user can use their own AWS account with Business or Enterprise support and with single account under the AWS organization to run this lab. The lab content and process can be best referenced for user account with multiple linked accounts under the AWS organization.
12 |
13 |
14 |
15 |
16 | # Introduction
17 | This lab aims to show users how easy it is to call AWS health API at organization level through Cloud9. The python code initially runs locally in the Cloud9 environment, where we will upload the health status data to S3 bucket, and then visualise the data using QuickSight. Optionally, user can consider to integrate email SNS to get notification upon the conditions setup by the operation team.
18 |
19 | 
20 |
21 |
22 | # How much will this lab cost?
23 | Base costs will be ( $USD in us-east-1):
24 | - t2.micro Cloud9 instances $0.0116 per Hour
25 | - S3 used by Cloud9 python script to store event data/manifest files with size less than 5KB
26 | - For QuickSight associated cost please refer to [Setting up QuickSight](#setting-up-quicksight) in appendix section
27 |
28 |
29 | # Setup
30 | 1. Goto to AWS Console, select Cloud9 service (In N.Virginia Region -- "us-east-1"). Or simply click the following link:
31 | https://console.aws.amazon.com/cloud9/home?region=us-east-1
32 |
33 | 2. Create a new Cloud9 environment by Clicking the "Create Environment" button
34 | 
35 |
36 | 3. Give a name to the new environment. In this demo, we can name it as "demo-env". Then click "Next step".
37 | 
38 |
39 | 4. Accept other default settings, click "Next step"
40 | 
41 |
42 | 5. In the "Review" page, review the environment configurations, and click "Create environment" to kickoff the creation process. It would take around 1 to 2 mins for the Cloud9 environment to be provisioned.
43 | 
44 |
45 | 6. Once the Cloud9 environment has been provisioned, use the following command to update the boto3 library -- as the current default library version (1.10.41) doesn't support the latest AWS health API for Organization.
46 |
47 | ec2-user:~/environment $ `python -m pip install boto3 -t ./`
48 |
49 | 7. As we need to use python pandas module to translate the json data into csv file, please use the following command to install pandas module
50 |
51 | ec2-user:~/environment $ `python -m pip install pandas --user`
52 |
53 | 8. Now the environment has almost ready. So please use the following command to download the project to Cloud9, and copy the "health_org_demo.py" and "manifest.json" files to the environment folder.
54 |
55 | ec2-user:~/environment $ `git clone https://github.com/JerryChenZeyun/aws-health-api-organization-view.git`
56 |
57 | ec2-user:~/environment $ `cp /home/ec2-user/environment/aws-health-api-organization-view/health_org_demo.py /home/ec2-user/environment/health_org_demo.py`
58 |
59 | ec2-user:~/environment $ `cp /home/ec2-user/environment/aws-health-api-organization-view/manifest.json /home/ec2-user/environment/manifest.json`
60 |
61 | 9. Create the S3 bucket to host the event data fed back from the organization health api call. Lab user can go to S3 console (`https://s3.console.aws.amazon.com/s3/home?region=us-east-1`) to create a new bucket. Simply give the new bucket a name, then accept all the default setting and keep on clicking "Next", and finally click "Create Bucket" to finalise S3 bucket creation. You may jot down the name which is needed in next step.
62 |
63 | For those user need more guidance on creating the new bucket, you may refer to the [Create new S3 bucket](#create-new-s3-bucket) in appendix section.
64 |
65 | 10. Use the Cloud9 editor environment to change the S3 bucket name in the "health_org_demo.py" file:
66 |
67 | "bucketName" - change it to your bucket name, which is used to host the data fetched by the health api. You can find your buckets via this link:
68 | https://s3.console.aws.amazon.com/s3/home
69 |
70 | Once the "bucketName" has been updated, you can simply save the python file.
71 | ("Command" + "S" for MAC, or "Control" + "S" for Windows)
72 |
73 | 
74 |
75 |
76 | For those who interested in digging out the api call functions, you may take a quick look at the python code.
77 |
78 |
79 |
80 | 11. Execute the python script -- Use the following command in Cloud9 to proceed:
81 |
82 | ec2-user:~/environment $ `python health_org_demo.py`
83 |
84 |
85 | Once the code has been executed successfully, You can see the output from Cloud9, showing the health status info summary, and detail health info in json format.
86 |
87 | 
88 |
89 | 12. Analyze the health event data printed at the Cloud9 terminal. In the Cloud9 terminal, we will find the json format data retrieved from health api call at organization level.
90 |
91 | Caveat: If the response for the script execution shows that there's no affected accounts/event details/entities, it's because the Lab account doesn't have any resource impacted by those service events. While Lab users should still be able to get the general service events data, including event arn, impacting regions, etc.
92 |
93 |
94 | # Visualize the health event csv data through QuickSight
95 |
96 | After the python script has been executed, the health status data would be stored in S3 bucket as csv file. We can utilise various tooling to visualise the dataset. In this Lab, we are going to use QuickSight.
97 |
98 | 1. Go to the following QuickSight link to generate the view of the dataset
99 | https://us-east-1.quicksight.aws.amazon.com/sn/start
100 |
101 | As a first time QuickSight user, you might need to sign up for QuickSight service. Please refer to [Setting up QuickSight](#setting-up-quicksight) in appendix section for the sign up process.
102 |
103 | 2. Enable QuickSight to be able to access the data file stored in S3 bucket you created
104 |
105 | a) Click the user icon on the top right corner of QuickSight page, then select "Manage QuickSight".
106 | 
107 |
108 | b) Select "Security & permissions" on the left, then click the "Add or remove" button under "QuickSight access to AWS services"
109 | 
110 |
111 | c) Click the "Details" link under "Amazon S3", then click the "Select S3 buckets" button
112 | 
113 |
114 | d) Check the box left to the bucket that you created for this lab in previous step, then click "Finish".
115 | 
116 |
117 | e) Click the "Update" button. Once this done, QuickSight will have access right to this bucket to visualise the data.
118 | 
119 |
120 |
121 | 3. Go back to the QuickSight front page (https://us-east-1.quicksight.aws.amazon.com/sn/start) and choose "New analysis" at the top left corner of the QuickSight page
122 |
123 | 
124 |
125 | 4. Choose "New data set" at the top left corner of the page
126 |
127 | 
128 |
129 | 5. Up till now, there's a "manifests3.json" file that has been stored in the S3 bucket you created for this Lab. Please jot down the URL for this json file. You can use this link (https://s3.console.aws.amazon.com/s3/object/) to go to your S3 and click on your bucket, and then click on "manifests3.json" file to copy its URL. The screenshot is for reference.
130 |
131 | 
132 |
133 | 6. Select S3, then fill in "Data source name" (e.g. event_data_file). Then choose the "URL" radio button. Then paste the URL you copied from last step in the URL blank.
134 |
135 | Then hit "Connect", and then click "Visualize" to proceed.
136 |
137 | 
138 |
139 | 7. Up till this step, you should be able to upload the file to QuickSight. If you meet with SPICE related error message, please take the following actions:
140 |
141 | a. Go to this link to increase the SPICE capacity in US east 1 region:https://us-east-1.quicksight.aws.amazon.com/sn/admin?#capacity.
142 |
143 | 
144 |
145 | b. Fill in "1" GB in the black, the click "Purchase SPICE capacity", then you can retry from step 3 to upload the data file from S3 to QuickSight.
146 |
147 | 
148 |
149 |
150 | 8. Experience the dataset visualization -- at this step, user can simply select or drag/drop via QuickSight GUI to visualize the dataset based on specific need. the screenshots show visualize the whole dataset, and only the "region" data for the health events within the organization.
151 |
152 | 
153 |
154 | 
155 |
156 |
157 | Congratulations! Till this step, you've accomplished this Lab.
158 | Please proceed to next step to clean up the resource.
159 |
160 |
161 |
162 |
163 | # Clean Up Step
164 |
165 | 1. Go to Cloud9 dashboard in AWS Console via the following link. Select your IDE environment and select "Delete".
166 | 'https://console.aws.amazon.com/cloud9/home?region=us-east-1'
167 |
168 | 2. Go to the follow link to delete the S3 bucket that you created for this lab use:
169 | 'https://s3.console.aws.amazon.com/s3/home?region=us-east-1'
170 |
171 | 3. Canceling Your Amazon QuickSight Subscription and Closing the Account -- Please refer to [Canceling Your Amazon QuickSight Subscription](#Canceling-Your-Amazon-QuickSight-Subscription) in appendix section.
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 | # Appendix
181 |
182 |
183 | # Create new S3 bucket
184 |
185 | This section give you more detailed guidance on how to create the S3 bucket to host data fed back from Organization Health API Call. Please follow these steps to complete the bucket creation:
186 |
187 | 1. Go to the following link to access AWS S3 service, then click "Create bucket".
188 | `https://s3.console.aws.amazon.com/s3/home?region=us-east-1`
189 |
190 | 
191 |
192 | 2. Give the new bucket a name, which cannot duplicate with other S3 bucket that have been existed. In this example, the new bucket is named as "my-health-api-lab-202003082100", and accept other default settings, Then click "Create bucket" to finish the creation process.
193 |
194 | 
195 |
196 |
197 |
198 | # Setting up QuickSight
199 |
200 | For those AWS accounts that use QuickSight for the first time, users need to explicitely sign up for QuickSight. Please follow the following steps:
201 |
202 | 1. Go to follow link to access AWS QuickSight service, then click "Sign up for QuickSight".
203 |
204 | `https://us-east-1.quicksight.aws.amazon.com/sn/start`
205 |
206 | 
207 |
208 | 2. In the QuickSight account selection page, please select "Standard" edition for this lab as shown below. Then click "Continue" at the bottom right corner.
209 |
210 | 
211 |
212 | 3. Give an Account Name to QuickSight service.
213 |
a. You might use "dev-lab" as shown in screenshot.
214 |b. Fill in an email address for the registration. For simplicity, you might use a fake email address "abc@gmail.com" to proceed.
215 |c. leave all other options as default settings, and click "Finish" to finalise the QuickSight sign up process.
216 | 217 |  218 | 219 | # Canceling Your Amazon QuickSight Subscription 220 | 221 | Here's the public guidance link: https://docs.aws.amazon.com/quicksight/latest/user/closing-account.html 222 | 223 | 1. Goes to this URL https://us-east-1.quicksight.aws.amazon.com/sn/admin?#permissions 224 | 2. Choose Unsubscribe. 225 |  226 |  227 | -------------------------------------------------------------------------------- /lambda_function.py: -------------------------------------------------------------------------------- 1 | #################################################################################################################################################################################### 2 | # Script Function: Demonstrate AWS Health API for Organization View 3 | # Author: JC 4 | # Time: 2020.12.24 5 | # Version: 1.4 6 | # Execution requirements: 7 | # Update the "bucketName" value following the instruction from Step 10 in https://github.com/JerryChenZeyun/aws-health-api-organization-view/blob/master/README.md#setup 8 | #################################################################################################################################################################################### 9 | import json 10 | import logging 11 | from datetime import datetime 12 | from dateutil import parser 13 | import boto3 14 | import json 15 | import csv 16 | from itertools import zip_longest 17 | from botocore.exceptions import ClientError 18 | import os 19 | import urllib.request 20 | import fileinput 21 | from time import sleep 22 | 23 | #################################################################################################################################################################################### 24 | bucketName = os.environ.get('s3_bucket_name') 25 | #################################################################################################################################################################################### 26 | 27 | csvFileName = 'event_data_file.csv' 28 | manifest_url = 'https://raw.githubusercontent.com/JerryChenZeyun/aws-health-api-organization-view/master/manifest.json' 29 | 30 | arn_list = [] 31 | service_list = [] 32 | eventTypeCode_list = [] 33 | eventTypeCategory_list = [] 34 | region_list = [] 35 | startTime_list = [] 36 | endTime_list = [] 37 | lastUpdatedTime_list = [] 38 | statusCode_list = [] 39 | impactedAccount_List = [] 40 | eventDescription_List = [] 41 | impactedEntity_List = [] 42 | 43 | def enable_health_org(): 44 | client = boto3.client('health', 'us-east-1') 45 | try: 46 | response = client.enable_health_service_access_for_organization() 47 | print("enable health api at organization level success") 48 | except ClientError as e: 49 | logging.error(e) 50 | print("enable health api at organization level error occurs:", e) 51 | print(response) 52 | print("\n#########################################################################\n") 53 | print("Health Service has been enabled at AWS Organization Level -- Done!") 54 | print("\n#########################################################################\n") 55 | 56 | # time encoder class 57 | class DatetimeEncoder(json.JSONEncoder): 58 | def default(self, obj): 59 | try: 60 | return super(DatetimeEncoder, obj).default(obj) 61 | except TypeError: 62 | return str(obj) 63 | 64 | ## Check if the health data has been stored in the S3 bucket. So lambda will know if it needs to pull the whole set of health data 65 | def get_s3_file_status(): 66 | s3 = boto3.resource('s3') 67 | try: 68 | s3.Object(bucketName, csvFileName).load() 69 | except ClientError as e: 70 | if e.response['Error']['Code'] == "404": 71 | # The object does not exist. 72 | print ("data file doesn't exist.") 73 | return (False) 74 | print ("data file does exist.") 75 | return (True) 76 | 77 | # Combine the latest health data with historical data 78 | def data_merge(file1, file2, combined_file): 79 | lambda_file1 = '/tmp/' + file1 80 | lambda_file2 = '/tmp/' + file2 81 | s3 = boto3.client("s3") 82 | s3.download_file(bucketName, file1, lambda_file1) 83 | s3.download_file(bucketName, file2, lambda_file2) 84 | reader1 = csv.reader(open(lambda_file1)) 85 | reader2 = csv.reader(open(lambda_file2)) 86 | file_name = "/tmp/" + combined_file 87 | f = open(file_name, "w") 88 | writer = csv.writer(f) 89 | 90 | for row in reader1: 91 | writer.writerow(row) 92 | for row in reader2: 93 | writer.writerow(row) 94 | f.close() 95 | 96 | tempfile = "/tmp/tmp_" + combined_file 97 | inFile = open(file_name,'r') 98 | outFile = open(tempfile,'w') 99 | listLines = [] 100 | 101 | for line in inFile: 102 | if line in listLines: 103 | continue 104 | else: 105 | outFile.write(line) 106 | listLines.append(line) 107 | outFile.close() 108 | inFile.close() 109 | 110 | ## Upload the organization events json message to S3 file 111 | def upload_to_s3(file_name, bucket, key): 112 | """Upload a file to an S3 bucket 113 | :param file_name: File to upload 114 | :param bucket: Bucket to upload to 115 | :param object_name: S3 object name. If not specified then file_name is used 116 | :return: True if file was uploaded, else False 117 | """ 118 | lambdaFileName = '/tmp/' + file_name 119 | s3 = boto3.resource('s3') 120 | try: 121 | s3.meta.client.upload_file(lambdaFileName, bucket, key) 122 | print("s3 upload success -- uploaded " + key + " to the bucket: " + bucket) 123 | except ClientError as e: 124 | logging.error(e) 125 | return False 126 | print("s3 upload error occurs", e) 127 | return True 128 | 129 | # Store event data info into csv file 130 | def write_to_csv(file_name): 131 | whole_table = [arn_list, service_list, eventTypeCode_list, eventTypeCategory_list, region_list, startTime_list, 132 | endTime_list, lastUpdatedTime_list, statusCode_list, impactedAccount_List, impactedEntity_List, eventDescription_List] 133 | export_data = zip_longest(*whole_table, fillvalue = '') 134 | 135 | lambdaFileName = '/tmp/' + file_name 136 | with open(lambdaFileName, 'w', encoding="utf-8", newline='') as myfile: 137 | wr = csv.writer(myfile) 138 | wr.writerow(("arn", "service", "eventTypeCode", "eventTypeCategory", "region", "startTime", "endTime", "lastUpdatedTime", 139 | "statusCode", "impactedAccount", "impactedEntity", "eventDescription")) 140 | wr.writerows(export_data) 141 | myfile.close() 142 | 143 | print("\n#########################################################################\n") 144 | print("Event data saved to CSV file.") 145 | print("\n#########################################################################\n") 146 | 147 | ## Create the Manifest file, and save it to the user specified S3 bucket 148 | def create_manifest(): 149 | """Create a Manifest file to an user specified S3 bucket 150 | This is required when user need to visualise the event data hosted in S3 bucket via QuickSight 151 | """ 152 | # download the manifest file template 153 | urllib.request.urlretrieve(manifest_url, '/tmp/manifest.json') 154 | 155 | file_path_ori = "/tmp" + "/manifest.json" 156 | file_path_new = "/tmp" + "/manifests3.json" 157 | 158 | with open(file_path_ori, "rt") as fin: 159 | with open(file_path_new, "wt") as fout: 160 | for line in fin: 161 | fout.write(line.replace('bucket-name', bucketName)) 162 | 163 | ## describe_health_service_status_for_organization 164 | def describe_health_service_status_for_org(): 165 | client = boto3.client('health', 'us-east-1') 166 | response = client.describe_health_service_status_for_organization() 167 | print(response) 168 | print("\n#########################################################################\n") 169 | 170 | ## describe_events_for_organization(**kwargs) 171 | def describe_events_for_org(start_time): 172 | client = boto3.client('health', 'us-east-1') 173 | event_paginator = client.get_paginator('describe_events_for_organization') 174 | event_page_iterator = event_paginator.paginate( 175 | filter={ 176 | 'lastUpdatedTime':{ 177 | 'from': start_time 178 | } 179 | } 180 | ) 181 | for event_page in event_page_iterator: 182 | json_event = json.dumps(event_page, cls=DatetimeEncoder) 183 | parsed_event = json.loads(json_event) 184 | events = parsed_event.get("events") 185 | 186 | for event in events: 187 | arn_list.append(event.get("arn")) 188 | service_list.append(event.get("service")) 189 | eventTypeCode_list.append(event.get("eventTypeCode")) 190 | eventTypeCategory_list.append(event.get("eventTypeCategory")) 191 | region_list.append(event.get("region")) 192 | startTime_list.append(event.get("startTime")) 193 | lastUpdatedTime_list.append(event.get("lastUpdatedTime")) 194 | statusCode_list.append(event.get("statusCode")) 195 | if (event.get("statusCode") == "open"): 196 | endTime_list.append("NULL") 197 | elif (event.get("statusCode") == "closed"): 198 | endTime_list.append(event.get("endTime")) 199 | else: 200 | endTime_list.append("NULL") 201 | 202 | response = client.describe_event_details(eventArns = [event.get("arn")], locale = "en") 203 | json_response = json.dumps(response, cls=DatetimeEncoder) 204 | parsed_response = json.loads (json_response) 205 | eventDescription_List.append(parsed_response["successfulSet"][0]["eventDescription"]["latestDescription"]) 206 | 207 | print("\n#########################################################################\n") 208 | print("describe_events_for_organization response - Total events:{}".format(len(arn_list))) 209 | print("These events are related to the following services:\n", service_list) 210 | print("\n#########################################################################\n") 211 | return(True) 212 | 213 | ## read the latest event time 214 | def check_latest_event(): 215 | last_update_time = [] 216 | lambdaFileName = '/tmp/' + csvFileName 217 | s3 = boto3.resource('s3') 218 | obj = s3.Object(bucketName, csvFileName) 219 | s3.Bucket(bucketName).download_file(csvFileName, lambdaFileName) 220 | 221 | with open(lambdaFileName, 'r') as file: 222 | reader = csv.reader(file) 223 | row1 = next(reader) 224 | column = 0 225 | 226 | for item in row1: 227 | if (item == 'lastUpdatedTime'): 228 | break 229 | else: 230 | column += 1 231 | 232 | for row in reader: 233 | if (row[column] != "lastUpdatedTime"): 234 | #date_time_obj = datetime.datetime.strptime(row[column], '%y-%m-%d %H:%M:%S.%f%z') 235 | strUpdate = parser.parse(row[column]) 236 | last_update_time.append(strUpdate) 237 | 238 | return(max(last_update_time)) 239 | 240 | ## describe_affected_accounts 241 | def describe_affected_accounts(event_arn): 242 | affectedAccounts = [] 243 | client = boto3.client('health', 'us-east-1') 244 | event_accounts_paginator = client.get_paginator('describe_affected_accounts_for_organization') 245 | 246 | event_accounts_page_iterator = event_accounts_paginator.paginate(eventArn=event_arn) 247 | 248 | for event_accounts_page in event_accounts_page_iterator: 249 | json_event_accounts = json.dumps(event_accounts_page, cls=DatetimeEncoder) 250 | parsed_event_accounts = json.loads (json_event_accounts) 251 | 252 | if((parsed_event_accounts['affectedAccounts']) == "[]"): 253 | affectedAccounts = affectedAccounts +"[]" 254 | else: 255 | affectedAccounts = affectedAccounts + (parsed_event_accounts['affectedAccounts']) 256 | print("For service event arn: {}".format(event_arn)) 257 | print("Affected accounts are: {}".format(affectedAccounts)) 258 | 259 | return(affectedAccounts) 260 | 261 | ## Retrieve account id automatically 262 | def get_account_id(): 263 | return(boto3.client('sts').get_caller_identity().get('Account')) 264 | 265 | ## describe_affected_entities_for_organization(**kwargs) 266 | def describe_affected_entities(event_arn): 267 | affectedEntities = [] 268 | affectedEntities_sub_list = [] 269 | client = boto3.client('health', 'us-east-1') 270 | affected_accounts = describe_affected_accounts(event_arn) 271 | 272 | for affected_account in affected_accounts: 273 | affected_account = ''.join(str(e) for e in affected_account) 274 | 275 | #If there's no affected account for this event, the 'affected account' will be filled by '[]' 276 | if(not affected_account): 277 | affected_account = '[]' 278 | 279 | print("affected_account: ", affected_account) 280 | print("affected account type:", type(affected_account)) 281 | 282 | event_entities_paginator = client.get_paginator('describe_affected_entities_for_organization') 283 | event_entities_page_iterator = event_entities_paginator.paginate( 284 | organizationEntityFilters=[ 285 | { 286 | 'awsAccountId': affected_account, 287 | 'eventArn': event_arn 288 | } 289 | ] 290 | ) 291 | 292 | affectedEntities_sub_list = [] 293 | 294 | for event_entities_page in event_entities_page_iterator: 295 | json_event_entities = json.dumps(event_entities_page, cls=DatetimeEncoder) 296 | parsed_event_entities = json.loads (json_event_entities) 297 | print("for event {} and affected account {}: ".format(event_arn, affected_account)) 298 | print("event entities list are: {} \n".format(parsed_event_entities)) 299 | 300 | for entity in parsed_event_entities['entities']: 301 | 302 | if((entity['entityValue']) != "[]"): 303 | affectedEntities_sub_list.append(entity['entityValue']) 304 | 305 | print("\n#########################################################################\n") 306 | print("affected entities list are:", affectedEntities_sub_list) 307 | print("\n#########################################################################\n") 308 | 309 | if(len(affectedEntities_sub_list) == 0): 310 | affectedEntities = affectedEntities + "[]" 311 | else: 312 | affectedEntities = affectedEntities + affectedEntities_sub_list 313 | return(affectedEntities) 314 | 315 | def lambda_handler(event, context): 316 | # TODO implement 317 | arn_list.clear() 318 | service_list.clear() 319 | eventTypeCode_list.clear() 320 | eventTypeCategory_list.clear() 321 | region_list.clear() 322 | startTime_list.clear() 323 | endTime_list.clear() 324 | lastUpdatedTime_list.clear() 325 | statusCode_list.clear() 326 | impactedAccount_List.clear() 327 | eventDescription_List.clear() 328 | impactedEntity_List.clear() 329 | 330 | print("boto3 version is ---", boto3.__version__) 331 | 332 | # If the data file not exist, then lambda pull out the whole health dataset. Otherwise, Lambda just pull the delta dataset. 333 | if (not (get_s3_file_status())): 334 | # Enable the health service in organization level 335 | enable_health_org() 336 | ## describe_health_service_status_for_organization 337 | describe_health_service_status_for_org() 338 | ## describe_events_for_organization(**kwargs) 339 | describe_events_for_org(datetime(2015,1,1)) 340 | ## describe_affected_accounts & describe_affected_entities 341 | for arn in arn_list: 342 | eventAffectedAccounts = describe_affected_accounts(arn) 343 | sleep(1) 344 | print ("eventAffectedAccounts:",eventAffectedAccounts) 345 | impactedAccount_List.append(eventAffectedAccounts) 346 | sleep(1) 347 | eventAffectedEntities = describe_affected_entities(arn) 348 | print ("eventAffectedEntities:",eventAffectedEntities) 349 | impactedEntity_List.append(eventAffectedEntities) 350 | 351 | print("complete impacted account list:", impactedAccount_List) 352 | print("complete impacted entity list:", impactedEntity_List) 353 | 354 | ## create manifest file, and save it to S3 bucket 355 | create_manifest() 356 | upload_to_s3(file_name="manifests3.json", bucket=bucketName, key="manifests3.json") 357 | 358 | ## save event data to csv file 359 | write_to_csv(csvFileName) 360 | 361 | ## upload the csv file to S3 bucket 362 | upload_to_s3(file_name=csvFileName, bucket=bucketName, key=csvFileName) 363 | 364 | else: 365 | latest_time = check_latest_event() 366 | print("latest_time is: ", latest_time ) 367 | ## describe_events_for_organization(**kwargs) 368 | describe_events_for_org(latest_time) 369 | 370 | ## Check if there's updated event data 371 | if (len(arn_list) >= 1): 372 | ## describe_affected_accounts & describe_affected_entities 373 | for arn in arn_list: 374 | eventAffectedAccounts = describe_affected_accounts(arn) 375 | print ("eventAffectedAccounts:",eventAffectedAccounts) 376 | impactedAccount_List.append(eventAffectedAccounts) 377 | sleep(1) 378 | 379 | eventAffectedEntities = describe_affected_entities(arn) 380 | print ("eventAffectedEntities:",eventAffectedEntities) 381 | impactedEntity_List.append(eventAffectedEntities) 382 | sleep(1) 383 | 384 | print("complete impacted account list:", impactedAccount_List) 385 | print("complete impacted entity list:", impactedEntity_List) 386 | 387 | ## save event data to csv file 388 | new_file = 'event_data_file_recent.csv' 389 | write_to_csv(new_file) 390 | 391 | ## upload the csv file to S3 bucket 392 | upload_to_s3(file_name=new_file, bucket=bucketName, key=new_file) 393 | 394 | ## merge the latest data with historical data into csvFileName 395 | data_merge(new_file, csvFileName, 'combined_new_file.csv') 396 | 397 | ## upload the csv file to S3 bucket 398 | upload_to_s3(file_name='tmp_combined_new_file.csv', bucket=bucketName, key=csvFileName) 399 | 400 | return { 401 | 'statusCode': 200, 402 | 'body': json.dumps('AWS Service Health Data has been polled and uploaded to the specified S3 bucket!') 403 | } 404 | --------------------------------------------------------------------------------