Cloud
banshee/cloud-collection/cloud/aws/https/
health
The open AWS service issues, scheduled changes and account notifications of an AWS account, by the AWS Health API.
by banshee0 installations1 version
Published on October 27, 2026. First version.
// The AWS Health events of an AWS account, by the AWS Health API with an access key: how many AWS service issues
// are open, how many scheduled changes and account notifications are open or upcoming, and which services the
// issues touch. The AWS Health API serves only an account with a Business, Enterprise On-Ramp or Enterprise
// support plan; any other account is told so.
// Reads POST https://health.us-east-1.amazonaws.com/, the action DescribeEvents, page after page, signed by
// Signature Version 4.
need('protocol:http');
need('protocol:aws');
$regions = variable('aws:health-regions', 'text', array(
'label' => 'Regions',
'help' => 'Only the events of these regions, separated by commas, global for the services without a region: eu-west-3, global. Empty, the events of every region.',
'required' => false,
));
output('issues', 'number', array('label' => 'Open service issues'));
output('scheduled', 'number', array('label' => 'Open or upcoming scheduled changes'));
output('notifications', 'number', array('label' => 'Open or upcoming account notifications'));
output('issue-names', 'text', array('label' => 'Services touched by the open issues'));
watch.above('issue', 'AWS service issue', 'issues', array('warning' => 0));
watch.above('scheduled', 'AWS scheduled change', 'scheduled', array('warning' => 0, 'enabled' => false));
watch.above('notification', 'AWS account notification', 'notifications', array('warning' => 0, 'enabled' => false));
// The regions set on the probe, as the list the filter takes.
$filter = array('eventStatusCodes' => array('open', 'upcoming'));
if ($regions != '') {
$names = array();
foreach (text.split($regions, ',') as $name) {
if (text.trim($name) != '') {
$names = list.add($names, text.trim($name));
}
}
$filter['regions'] = $names;
}
// {"events": [{"arn": "…", "service": "EC2", "eventTypeCode": "AWS_EC2_OPERATIONAL_ISSUE", "eventTypeCategory":
// "issue", "region": "eu-west-3", "statusCode": "open", "startTime": 1793107815.0, …}], "nextToken": "…"}.
// eventTypeCategory is issue, investigation, scheduledChange or accountNotification; statusCode open, closed or
// upcoming. The API speaks the JSON protocol of AWS: one POST, its action named by X-Amz-Target, the body
// signed with the headers. The body carries no secret, and neither does the answer.
$url = 'https://health.us-east-1.amazonaws.com/';
$issues = 0;
$scheduled = 0;
$notifications = 0;
$touched = array();
$token = '';
while (true) {
$request = array('filter' => $filter, 'maxResults' => 100);
if ($token != '') {
$request['nextToken'] = $token;
}
$body = json.write($request);
$headers = array('Content-Type' => 'application/x-amz-json-1.1', 'X-Amz-Target' => 'AWSHealth_20160804.DescribeEvents');
$answer = http.post($url, $body, headers=aws.sign('POST', $url, $headers, $body, 'us-east-1', 'health'));
if ($answer['status'] != 200) {
if (text.contains($answer['body'], 'SubscriptionRequiredException')) {
fail('misconfigured', 'The account has no support plan that opens the AWS Health API',
'AWS answers SubscriptionRequiredException: the AWS Health API serves only a Business, Enterprise On-Ramp or Enterprise support plan.');
}
if (text.contains($answer['body'], 'ThrottlingException') || $answer['status'] >= 500) {
fail('unreachable', 'AWS Health answers the reading of the events with code ' . $answer['status']);
}
if ($answer['status'] == 400 || $answer['status'] == 401 || $answer['status'] == 403) {
$type = text.match($answer['body'], '"__type"\s*:\s*"([^"]+)"');
$code = '';
if (list.count($type) > 0) {
$code = $type[1];
}
fail('misconfigured', 'AWS Health refuses to read the events, code ' . $answer['status'] . ' ' . $code,
'Check the access key and the permissions of its user or role.');
}
fail('unreachable', 'AWS Health answers the reading of the events with code ' . $answer['status']);
}
$page = json.read($answer['body']);
foreach (list.get($page, 'events', array()) as $event) {
$category = list.get($event, 'eventTypeCategory', '');
$status = list.get($event, 'statusCode', '');
if (($category == 'issue' || $category == 'investigation') && $status == 'open') {
$issues = $issues + 1;
$touched = list.add($touched, list.get($event, 'service', '') . ' ' . list.get($event, 'eventTypeCode', '')
. ' (' . list.get($event, 'region', '') . ')');
} elseif ($category == 'scheduledChange') {
$scheduled = $scheduled + 1;
} elseif ($category == 'accountNotification') {
$notifications = $notifications + 1;
}
}
$token = list.get($page, 'nextToken', '');
if ($token == '') {
break;
}
}
value('issues', $issues);
value('scheduled', $scheduled);
value('notifications', $notifications);
value('issue-names', text.join($touched, ' | '));