Sample to check if an ECS instance is stopped

This example demonstrates how to verify whether an Elastic Cloud Server (ECS) instance is in a stopped state using a custom rule.

Prerequisites

Before running this example, ensure you have the following:

FunctionGraph function

ecs-stopped/src/index.py
# -*- coding:utf-8 -*-

import http.client
import json
import time

import requests

requests.packages.urllib3.disable_warnings()


"""
Get the policy resource representation for RMS.
"""


def get_policy_resource(domain_id, resource):
    return {
        "domain_id": domain_id,
        "region_id": resource.get("region_id"),
        "resource_id": resource.get("id"),
        "resource_name": resource.get("name"),
        "resource_provider": resource.get("provider"),
        "resource_type": resource.get("type"),
    }


"""
The evaluation result of a rule will be either Compliant or NonCompliant.
In this example, if the properties.status of a resource matches the specified ECSstatus,
NonCompliant is returned. Otherwise, Compliant is returned.
"""


def evaluate_compliance(logger, resource, parameter):

    if resource.get("properties").get("status") == parameter.get("ECSstatus").get(
        "value"
    ):
        logger.info("NonCompliant")
        return "NonCompliant"
    else:
        logger.info("Compliant")
        return "Compliant"


"""
Update the policy state in RMS with the evaluation result.
"""


def update_policy_state(context, domain_id, evaluation):
    logger = context.getLogger()
    endpoint_url = context.getUserData("RMS_ENDPOINT_URL")
    url = f"{endpoint_url}/v1/resource-manager/domains/{domain_id}/policy-states"

    logger.info(
        "Updating policy state with URL: %s, payload: %s", url, json.dumps(evaluation)
    )

    return requests.put(
        url=url,
        headers={"X-Auth-Token": context.getToken()},
        json=evaluation,
        verify=False,
    )


"""
FunctionGraph handler to handle the incoming event and evaluate compliance for the resource.
"""


def handler(event, context):

    logger = context.getLogger()

    logger.debug("Invoking event: %s", json.dumps(event))

    resource = event.get("invoking_event", {})
    parameters = event.get("rule_parameter", {})

    compliance_state = evaluate_compliance(logger, resource, parameters)

    requests = {
        "policy_resource": get_policy_resource(event.get("domain_id"), resource),
        "trigger_type": event.get("trigger_type"),
        "compliance_state": compliance_state,
        "policy_assignment_id": event.get("policy_assignment_id"),
        "policy_assignment_name": event.get("policy_assignment_name"),
        "function_urn": event.get("function_urn"),
        "evaluation_time": event.get("evaluation_time"),
        "evaluation_hash": event.get("evaluation_hash"),
    }

    for retry in range(3):
        response = update_policy_state(context, event.get("domain_id"), requests)
        if response.status_code == http.client.TOO_MANY_REQUESTS:
            logger.error("TOO_MANY_REQUESTS: retry again, attempt %d", retry + 1)
            time.sleep(1)
        else:
            if response.status_code == http.client.OK:
                logger.info("Update policyState successfully.")
            else:
                logger.error("Failed to update policyState with response: %s", response.json())
            break

Note

The parameter ECSstatus is defined in the config deployment file in opentelekomcloud_rms_policy_assignment_v1.my_policy_assignment

parameters = {
  ECSstatus = "\"SHUTOFF\""
}

Terraform deployment files

FunctionGraph deployment files

ecs-stopped/terraform/function.tf
##########################################################
# Python event function to handle ECS stopped compliance
##########################################################
resource "opentelekomcloud_fgs_function_v2" "MyFunction" {

  name   = format("%s_%s", var.prefix, var.function_name)
  app    = "default"
  
  handler =  var.handler_name

  runtime   = "Python3.10"

  agency = opentelekomcloud_identity_agency_v3.agency.name  

  code_type = "inline"
  func_code     = filebase64(format("${path.module}/../%s", "src/index.py"))
  code_filename = "index.py"

  description      = var.description
  memory_size      = 128
  timeout          = 30
  max_instance_num = 10

  log_group_id   = opentelekomcloud_lts_group_v2.MyLogGroup.id
  log_group_name = opentelekomcloud_lts_group_v2.MyLogGroup.group_name

  log_topic_id   = opentelekomcloud_lts_stream_v2.MyLogStream.id
  log_topic_name = opentelekomcloud_lts_stream_v2.MyLogStream.stream_name

  # set some environment variables
  user_data = jsonencode({
    "RMS_ENDPOINT_URL" : "https://rms.eu-de.otc.t-systems.com"
  })

  tags = {
    "app_group" = var.tag_app_group
  }

}

output "MY_FUNCTION_URN" {
  value = opentelekomcloud_fgs_function_v2.MyFunction.urn
}

output "MY_FUNCTION_VERSION" {
  value = opentelekomcloud_fgs_function_v2.MyFunction.version
}

Config deployment files

ecs-stopped/terraform/rms.tf
#######################################################################
# Policy assignment for FunctionGraph to manage ECS stopped compliance
#######################################################################
resource "opentelekomcloud_rms_policy_assignment_v1" "my_policy_assignment" {
  name        = format("%s-%s", var.prefix, "Stopped_ECS_is_nonCompliant")
  description = "Policy assignment for FunctionGraph"

  custom_policy {
    auth_type = "agency"
    auth_value = {
      agency_name = format("\"%s\"", opentelekomcloud_identity_agency_v3.config_agency.name)
    }
    function_urn = format("%s:%s", opentelekomcloud_fgs_function_v2.MyFunction.urn, opentelekomcloud_fgs_function_v2.MyFunction.version)
  }

  parameters = {
    "ECSstatus": "\"SHUTOFF\""
  }

  period = ""
  
  policy_filter {
    # Specify the region where your ECS instance is located
    region = "eu-de"
    # ID of your ECS instance that you want to check for stopped compliance
    resource_id = "cdb29bdd-1235-4e98-90d3-34bb77450393"
    resource_provider = "ecs"
    resource_type = "cloudservers"
    tag_key = ""
    tag_value = ""
  }
  status = "Enabled"

    
}

Note

  • In policy_filter you need to adapt the region and resource_id to match your ECS instance.

  • For getting values for resource_provider and resource_type see FAQ: Listing Cloud Services.

  • The Parameter ECSstatus configured in parameters = {} is used in the FunctionGraph function for comparison.

    def evaluate_compliance() in index.py
      # ...
      if resource.get("properties").get("status") == parameter.get("ECSstatus").get("value"):
      # ...
    

    For possible values for ECSstatus, see the FAQ: How to get Resource Details. Execute the script for every change in the ECS instance status and check value of resources.properties.status.

Variables

Sample variables file:

ecs-stopped/terraform/variables.tfvars
# Terraform variables for scratch-event sample

# prefix of all resources
prefix        = "python"

# description of the function
description = "Custom rule for ECS stopped state"

# name of the function (will be prefixed)
function_name = "custom-rule-ecs-stopped"

# handler function name defined in your code, e.g. "index.handler"
handler_name = "index.handler"

# resources will be tagged with this app_group tag
tag_app_group = "custom-rule-ecs-stopped"

You might want to adjust the variables to your need.

Makefile

Sample Makefile:

ecs-stopped/Makefile
# Makefile for Terraform deployment

# Terraform backend configuration
BACKEND_CONFIG_BUCKET := "doc-samples-tf-backend"
BACKEND_CONFIG_KEY := "terraform_state/python/custom-rule-ecs-stopped.tf"
BACKEND_CONFIG_REGION := "eu-de"
BACKEND_CONFIG_ENDPOINTS := "endpoints={s3=\"https://obs.eu-de.otc.t-systems.com\"}"

CURRENT_MAKEFILE := $(firstword $(MAKEFILE_LIST))

tf_init:
	terraform -chdir=terraform \
	  init \
	  -backend-config=$(BACKEND_CONFIG_ENDPOINTS) \
	  -backend-config="bucket=$(BACKEND_CONFIG_BUCKET)" \
	  -backend-config="key=$(BACKEND_CONFIG_KEY)" \
	  -backend-config="region=$(BACKEND_CONFIG_REGION)"

tf_plan: 
	if [ ! -f "terraform/.terraform.lock.hcl" ]; then \
		$(MAKE) -f $(CURRENT_MAKEFILE) tf_init; \
	fi
	terraform -chdir=terraform \
	  plan \
		-var-file="variables.tfvars" 

tf_apply:
	if [ ! -f "terraform/.terraform.lock.hcl" ]; then \
		$(MAKE) -f $(CURRENT_MAKEFILE) tf_init; \
	fi
	terraform -chdir=terraform \
	  apply -auto-approve \
	  -var-file="variables.tfvars"

tf_destroy:
	terraform -chdir=terraform \
	  destroy -auto-approve \
		-var-file="variables.tfvars"

.PHONY: tf_init tf_plan tf_apply tf_destroy

You might following variables to your need:

# Terraform backend configuration
BACKEND_CONFIG_BUCKET := "doc-samples-tf-backend"
BACKEND_CONFIG_KEY := "terraform_state/python/custom-rule-ecs-stopped.tf"
BACKEND_CONFIG_REGION := "eu-de"
BACKEND_CONFIG_ENDPOINTS := "endpoints={s3=\"https://obs.eu-de.otc.t-systems.com\"}"

Deploy

To deploy using Terraform run:

make tf_deploy

To destroy the deployed resources using Terraform run:

make tf_destroy