Cloud
banshee/cloud-collection/cloud/google/https/
compute-instances
The status of each Compute Engine instance of a Google Cloud project, every zone at once, by the Compute Engine API.
by banshee0 installations1 version
Published on October 27, 2026. First version.
// The Compute Engine instances of a Google Cloud project, every zone at once, by the Compute Engine API with a
// service account: the status of each instance.
// Reads POST https://oauth2.googleapis.com/token by google.token, then GET
// https://compute.googleapis.com/compute/v1/projects/<project>/aggregated/instances, page after page.
need('protocol:http');
need('protocol:google');
$project = variable('google:project', 'text', array(
'label' => 'Project',
'help' => 'The ID of the Google Cloud project, as the console writes it under Project ID: my-project-123456.',
));
per('instance', 'Instances');
output('status', 'text', array('label' => 'Status', 'per' => 'instance'));
watch.expect('not-running', 'Instance not running', 'status', array('critical' => array('running'), 'for' => '2 runs'));
watch.missing('missing', 'Instance missing', 'status', array('enabled' => false));
// The access token of the service account for this scope, asked by google.token and kept in the cache,
// encrypted on the poller, fifty minutes, under the hour Google gives it.
function access_token($scope) {
if (cache.has('token')) {
return cache.get('token');
}
$token = google.token($scope);
cache.set('token', $token, 3000);
return $token;
}
// One GET on a Google Cloud API with the token kept in the cache; a token Google no longer takes answers 401,
// and a new one is asked for, once.
function google_get($url, $scope, $what) {
$answer = http.get($url, headers=array('Authorization' => 'Bearer ' . access_token($scope)));
if ($answer['status'] == 401) {
cache.delete('token');
$answer = http.get($url, headers=array('Authorization' => 'Bearer ' . access_token($scope)));
}
if ($answer['status'] == 403) {
fail('misconfigured', 'The service account may not read ' . $what . ', code 403',
'Check its role on the project, and that the API is enabled on the project.');
}
if ($answer['status'] == 400 || $answer['status'] == 404) {
fail('misconfigured', 'Google Cloud refuses to read ' . $what . ', code ' . $answer['status'],
'Check the project and the names set on the probe.');
}
if ($answer['status'] != 200) {
fail('unreachable', 'Google Cloud answers the reading of ' . $what . ' with code ' . $answer['status']);
}
return json.read($answer['body']);
}
// {"items": {"zones/europe-west1-b": {"instances": [{"name": "web-1", "status": "RUNNING", "zone":
// "https://…/zones/europe-west1-b", …}]}, "zones/us-central1-a": {"warning": {"code": "NO_RESULTS_ON_PAGE", …}}},
// "nextPageToken": "…"}: a scope per zone, with its instances or a warning that it has none.
$base = 'https://compute.googleapis.com/compute/v1/projects/' . url.encode($project) . '/aggregated/instances';
$token = '';
while (true) {
$url = $base;
if ($token != '') {
$url = $base . '?' . url.query(array('pageToken' => $token));
}
$page = google_get($url, 'https://www.googleapis.com/auth/compute.readonly', 'the Compute Engine instances');
// An instance is named <zone>/<name>: a name is unique in its zone only.
foreach (list.get($page, 'items', array()) as $scope => $scoped) {
$zone = text.split($scope, '/');
foreach (list.get($scoped, 'instances', array()) as $instance) {
value('status', text.lower($instance['status']), $zone[list.count($zone) - 1] . '/' . $instance['name']);
}
}
$token = list.get($page, 'nextPageToken', '');
if ($token == '') {
break;
}
}