Cloud
banshee/cloud-collection/cloud/aws/https/
rds
The status of each Amazon RDS database instance of an AWS region, by the RDS API.
by banshee0 installations1 version
Published on October 27, 2026. First version.
// The database instances of Amazon RDS in an AWS region, by the RDS API with an access key: the status of each
// instance, as RDS gives it.
// Reads GET https://rds.<region>.amazonaws.com/?Action=DescribeDBInstances, page after page, signed by
// Signature Version 4.
need('protocol:http');
need('protocol:aws');
$region = variable('aws:region', 'text', array(
'label' => 'Region',
'help' => 'The AWS region of the databases, as AWS writes it in an API: eu-west-3, us-east-1.',
));
per('database', 'Databases');
output('status', 'text', array('label' => 'Status', 'per' => 'database'));
watch.state('failing', 'Database failing', 'status', array(
'help' => 'Raises on a status in which RDS cannot serve or recover the database by itself: failed, storage full, encryption key out of reach, incompatible settings.',
'critical' => array('failed', 'storage-full', 'inaccessible-encryption-credentials',
'inaccessible-encryption-credentials-recoverable', 'incompatible-create', 'incompatible-network',
'incompatible-option-group', 'incompatible-parameters', 'incompatible-restore', 'insufficient-capacity',
'restore-error', 'upgrade-failed'),
));
watch.state('stopped', 'Database stopped', 'status', array('warning' => array('stopped', 'stopping')));
watch.missing('missing', 'Database missing', 'status', array('enabled' => false));
// One signed GET on an AWS API of the Query protocol (EC2, RDS, CloudWatch): the parameters in the address, the
// answer in XML. aws.sign gives the headers that carry the signature: the keys never leave. A refusal is told
// by the error code AWS writes in its answer; a throttled request or a fault of AWS is not one.
function aws_get($service, $region, $query, $what) {
$url = 'https://' . $service . '.' . $region . '.amazonaws.com/?' . url.query($query);
$answer = http.get($url, headers=aws.sign('GET', $url, array(), '', $region, $service));
if ($answer['status'] != 200) {
$code = '';
$found = text.match($answer['body'], '<Code>([^<]+)</Code>');
if (list.count($found) > 0) {
$code = $found[1];
}
if ($code == 'Throttling' || $code == 'RequestLimitExceeded' || $answer['status'] >= 500) {
fail('unreachable', 'AWS answers the reading of ' . $what . ' with code ' . $answer['status'] . ' ' . $code);
}
if ($answer['status'] == 400 || $answer['status'] == 401 || $answer['status'] == 403) {
fail('misconfigured', 'AWS refuses to read ' . $what . ', code ' . $answer['status'] . ' ' . $code,
'Check the access key, the permissions of its user or role, and the region.');
}
fail('unreachable', 'AWS answers the reading of ' . $what . ' with code ' . $answer['status']);
}
return xml.read($answer['body']);
}
// The first child of that name, array() when it is absent or empty. An empty element, such as a list without
// members written <name/>, reads as the text '' and not as an array.
function element($parent, $name) {
$children = list.get($parent, $name, array());
if (list.count($children) == 0 || !list.is($children[0])) {
return array();
}
return $children[0];
}
// The text of the first child of that name, '' when it is absent.
function child_text($parent, $name) {
$children = list.get($parent, $name, array());
if (list.count($children) == 0) {
return '';
}
return $children[0];
}
// The region goes into the name of the host: it must be a name.
if (list.count(text.match($region, '^[a-z0-9-]+$')) == 0) {
fail('misconfigured', 'The region holds other characters than lower-case letters, digits and hyphens');
}
// <DescribeDBInstancesResponse><DescribeDBInstancesResult><DBInstances><DBInstance><DBInstanceIdentifier>mysqldb
// </DBInstanceIdentifier><DBInstanceStatus>available</DBInstanceStatus><Engine>mysql</Engine>…</DBInstance>
// </DBInstances><Marker>…</Marker></DescribeDBInstancesResult>…</DescribeDBInstancesResponse>, a page of 100 at
// most. An instance is named by its identifier, unique in the account and the region.
$marker = '';
while (true) {
$query = array('Action' => 'DescribeDBInstances', 'Version' => '2014-10-31', 'MaxRecords' => 100);
if ($marker != '') {
$query['Marker'] = $marker;
}
$result = element(element(aws_get('rds', $region, $query, 'the RDS databases'), 'DescribeDBInstancesResponse'),
'DescribeDBInstancesResult');
foreach (list.get(element($result, 'DBInstances'), 'DBInstance', array()) as $database) {
value('status', child_text($database, 'DBInstanceStatus'), child_text($database, 'DBInstanceIdentifier'));
}
$marker = child_text($result, 'Marker');
if ($marker == '') {
break;
}
}