diff --git a/aws/policy/data-services.yaml b/aws/policy/data-services.yaml index 8c4164d4..ccb12d94 100644 --- a/aws/policy/data-services.yaml +++ b/aws/policy/data-services.yaml @@ -17,6 +17,9 @@ Statement: - glue:GetConnections - rds:DescribeDB* - rds:List* + - es:Describe* + - es:Get* + - es:List* Resource: "*" - Sid: AllowGlobalResourceRestrictedActionsWhichIncurNoFees Effect: Allow @@ -127,6 +130,12 @@ Statement: - rds:CreateDBCluster - elasticache:CreateCacheCluster - redshift:CreateCluster + - es:AddTags + - es:CreateDomain + - es:DeleteDomain + - es:RemoveTags + - es:UpdateDomainConfig + - es:UpgradeDomain Resource: - 'arn:aws:rds:{{ aws_region }}:{{ aws_account_id }}:cluster:*' - 'arn:aws:elasticache:{{ aws_region }}:{{ aws_account_id }}:cluster:*' @@ -134,7 +143,8 @@ Statement: - 'arn:aws:elasticache:{{ aws_region }}:{{ aws_account_id }}:parametergroup:*' - 'arn:aws:elasticache:{{ aws_region }}:{{ aws_account_id }}:securitygroup:*' - 'arn:aws:redshift:{{ aws_region }}:{{ aws_account_id }}:cluster:*' - # This allows AWS Services to autmatically create their Default Service Linked Roles + - 'arn:aws:es:{{ aws_region }}:{{ aws_account_id }}:domain:*' + # This allows AWS Services to automatically create their Default Service Linked Roles # These have fixed policies and can only be assumed by the service itself. - Sid: AllowServiceLinkedRoleCreation Effect: Allow diff --git a/aws/terminator/data_services.py b/aws/terminator/data_services.py index 11a58a56..e48b91d2 100644 --- a/aws/terminator/data_services.py +++ b/aws/terminator/data_services.py @@ -1,5 +1,7 @@ import datetime +import botocore.exceptions + from . import DbTerminator, Terminator, get_tag_dict_from_tag_list @@ -297,3 +299,46 @@ def age_limit(self): def terminate(self): self.client.delete_cluster(ClusterArn=self.id) + + +class OpenSearch(Terminator): + + @staticmethod + def create(credentials): + def get_available_clusters(client): + domains = [] + for domain in client.list_domain_names()['DomainNames']: + try: + domain_status = client.describe_domain(DomainName=domain['DomainName'])['DomainStatus'] + if not domain_status['Deleted']: + # 'Deleted' is true if a delete request has been received for the domain + # but resource cleanup is still in progress. + domain_config = client.describe_domain_config(DomainName=domain['DomainName']) + domain_status["CreationDate"] = domain_config['DomainConfig']['Status']['CreationDate'] + domains.append(domain_status) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError): # pylint: disable=duplicate-except + # Unlikely, but possible. The domain may have been deleted after invoking + # list_domain_names(). + pass + return domains + + return Terminator._create(credentials, OpenSearch, 'opensearch', get_available_clusters) + + @property + def id(self): + return self.instance['DomainId'] + + @property + def name(self): + return self.instance['DomainName'] + + @property + def created_time(self): + return self.instance['CreationDate'] + + @property + def age_limit(self): + return datetime.timedelta(minutes=60) + + def terminate(self): + self.client.delete_domain(DomainName=self.name)