AWS Kosten sparen mit SSM Automations
July 10, 2020Ein Vorteil bei der Nutzung der Cloud ist, dass Entwickler:innen bei Bedarf schnell Testumgebungen erstellen können um neue Dinge auszuprobieren. Diese Testumgebungen können jedoch auch schnell wieder vergessen werden und somit zu vermeidbaren Kosten führen. Mit AWS SSM Automations gibt es eine Möglichkeit, Ressourcen automatisch zu verwalten, zum Beispiel um sie zu stoppen oder ganz zu löschen. Womöglich sollen Entwickler:innen aber auch einen Weg haben, falls sie bestimmte Ressourcen langfristig nutzen wollen. Dies lässt sich mit Tagging auch unkompliziert realisieren.
In diesem Beispiel wird mit unserem IaC-Tool der Wahl Cloudformation eine Lösung gezeigt, die alle EC2 instanzen ohne einen bestimmten Tag-Value zu einer bestimmten Uhrzeit stoppt.
Um sicherzugehen, dass alle Ressourcen einen entsprechenden Tag haben, wird zusätzlich automatisch per Config::ConfigRule
und Config::RemediationConfiguration
der Tag vergeben sollte er nicht vorhanden sein.
Das komplette Beispiel ist unter https://github.com/superluminar-io/cf-ssm-example zu finden.
Einen gewünschten Zustand von Ressourcen konfigurieren
Die SSM::Association
Ressource wird genutzt, um einen gewünschten Zustand von ausgewählten EC2 bzw. On-Premise Instanzen zu beschreiben, der entweder per Schedule oder bei bestimmten Ereignissen automatisiert hergestellt wird.
Hierfür stellt AWS eine Menge von SSM::Document
s bereit, die häufig verwendete Eigenschaften (z.B. eine EC2 Instanz ist gestoppt) beschreiben und eine Automatisierung anbieten, um diese Eigenschaften zu erreichen (also die Instanz stoppen falls nötig).
Alternativ können auch eigene Documents erstellt werden, um Eigenschaften zu automatisieren die AWS nicht selber anbietet.
Für unseren ersten Anwendungsfall gibt es jedoch das bereitgestellte AWS-StopEC2Instance
Document.
Zusätzlich wird noch eine IAM Rolle benötigt, die bei der Ausführung vom SSM genutzt wird:
AutomationExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: ssm.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyDocument:
Statement:
- Sid: StopEC2Instances
Action:
- ec2:StopInstances
- ec2:DescribeInstanceStatus
Effect: Allow
Resource: "*"
PolicyName: ssm-policy
InstancesToStopAssociation:
Type: AWS::SSM::Association
Properties:
Name: AWS-StopEC2Instance
ScheduleExpression: !Sub "cron(0 ${ExecutionHour} ? * * *)"
ApplyOnlyAtCronInterval: true
Parameters:
AutomationAssumeRole:
- !GetAtt AutomationExecutionRole.Arn
Targets:
- Key: !Sub "tag:${TagName}"
Values:
- !Ref TagValue
AutomationTargetParameterName: InstanceId
Zu sehen ist, dass alle Instanzen mit dem Tag TagName=TagValue
als Target
ausgewählt werden und ihre InstanceId
an das SSM::Document
gegeben wird.
Per ScheduleExpression
wird angegeben, dass der Status täglich zu einer definierten Uhrzeit sichergestellt wird.
ApplyOnlyAtCronInterval
ist nötig, damit die Automatisierung tatsächlich nur zu der angegebenen Uhrzeit ausgeführt wird
und nicht z.B. wenn es zu einer Aktualisierung der SSM::Association
kommt.
Default Tagging sicherstellen
Eine SSM::Association
hat jedoch keine Möglichkeit Instanzen auswählen, die einen gewissen Tag nicht haben.
Hierfür kann AWS Config genutzt werden, um automatisch default Werte für nicht vergebene Tags zu vergeben.
Hierfür wird eine Config::ConfigRule
und Config::RemediationConfiguration
erstellt, außerdem ein SSM:Document
,
da es in diesem Fall kein von AWS bereitgestelltes gibt.
Das SSM::Document
beschreibt, wie eine Instanz mit dem default Wert für den Tag versehen wird:
TagInstancesDocument:
Type: AWS::SSM::Document
Properties:
DocumentType: Automation
Content:
schemaVersion: "0.3"
assumeRole: "{{ AutomationAssumeRole }}"
parameters:
InstanceId:
type: String
AutomationAssumeRole:
type: String
mainSteps:
- name: TagEC2Instance
action: aws:createTags
inputs:
ResourceType: EC2
ResourceIds:
- "{{ InstanceId }}"
Tags:
- Key: !Ref TagName
Value: !Ref TagValue
Die referenzierte IAM Rolle, welche eine von AWS verwaltete Policy verwendet, die alle Rechte beinhaltet die zur Ausführung der Automatisierung nötig sind:
AutomationServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ssm.amazonaws.com
- ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole
Path: "/"
RoleName: AutomationServiceRole
Das erstellte SSM::Document
kann nun verwendet werden, um ungetaggte Instanzen zu taggen:
RequireManagedTag:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: RequireDeleteTag
Description: !Sub "Checks whether the '${TagName}' tag is set for EC2 Instances."
Source:
Owner: AWS
SourceIdentifier: REQUIRED_TAGS
Scope:
ComplianceResourceTypes:
- AWS::EC2::Instance
InputParameters:
tag1Key: !Ref TagName
TagUntaggedInstances:
Type: AWS::Config::RemediationConfiguration
Properties:
Automatic: true
ConfigRuleName: !Ref RequireManagedTag
MaximumAutomaticAttempts: 5
RetryAttemptSeconds: 50
Parameters:
AutomationAssumeRole:
StaticValue:
Values:
- !GetAtt AutomationServiceRole.Arn
InstanceId:
ResourceValue:
Value: RESOURCE_ID
TargetId: !Ref TagInstancesDocument
TargetType: "SSM_DOCUMENT"
Fazit
Insgesamt ist nun sichergestellt, dass alle ungetaggten Instanzen mit einem default Wert für den Tag versehen werden. Instanzen die diesen default Wert haben werden zu einer ausgewählten Uhrzeit gestoppt. Will ein Entwickler:innen sichergehen, dass seine Instanzen nicht gestoppt werden, kann er den Wert für den Tag einfach ändern.
Diese Konfiguration könnte zu Beispiel als Teil der AWS Landingzone in Accounts deployed werden um sie für alle Accounts auszurollen.
Da SSM::Association
s nur EC2 und On-Premise instanzen unterstützt, kann dieses Setup nicht direkt genutzt werden um
z.B. RDS Instanzen zu stoppen.
Allerdings können SSM::Documents
auch unabhängig von SSM::Association
s ausgeführt werden.
Somit kann z.B. eine Lösung mit einer geschedulten Lambda genutzt werden, um den generellen Ansatz auf andere Ressourcentypen anzuwenden.