Created
March 7, 2018 05:11
-
-
Save josephsindel/cf156e1a084f049ce4b6a8bd8652b33d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# pylint: disable=invalid-name | |
# pylint: disable=bare-except | |
# pylint: disable=missing-docstring | |
# pylint: disable=line-too-long | |
# pylint: disable=redefined-outer-name | |
# pylint: disable=protected-access | |
import logging | |
import gzip | |
import urllib2 | |
import json | |
import base64 | |
import ssl | |
import os | |
import re | |
import boto3 | |
from winrm.protocol import Protocol | |
from ldap3 import Server, Connection, ALL, NTLM | |
logger = logging.getLogger() | |
logger.setLevel(logging.INFO) | |
def get_env_var(env_var, value=None): | |
ssm = boto3.client('ssm') | |
response = None | |
envvar = os.getenv(env_var, None) | |
if envvar is None: | |
envvar = value | |
if envvar != value: | |
if re.match("SSM~.*", envvar) is not None: | |
with_decryption = False | |
envvar = envvar.split("SSM~")[1] | |
if re.match(".*~true", envvar) is not None: | |
with_decryption = True | |
envvar = envvar.split("~true")[0] | |
try: | |
response = ssm.get_parameters(Names=[envvar], WithDecryption=with_decryption) | |
except: | |
logger.error("could not lookup %s", envvar) | |
if response is not None: | |
return response['Parameters'][0]['Value'] | |
return envvar | |
DOMAINS = get_env_var("DOMAINS") | |
SERVERS = get_env_var("SERVERS") | |
LDAP_DOMAINS = get_env_var("LDAP_DOMAINS") | |
DNS_USERNAME = get_env_var("DNS_USERNAME") | |
DNS_PASSWORD = get_env_var("DNS_PASSWORD") | |
LDAP_USERNAME = get_env_var("LDAP_USERNAME") | |
LDAP_PASSWORD = get_env_var("LDAP_PASSWORD") | |
JIRA_USERNAME = get_env_var("JIRA_USERNAME") | |
JIRA_PASSWORD = get_env_var("JIRA_PASSWORD") | |
JIRA_API_ADDRESS = get_env_var("JIRA_API_ADDRESS") | |
OOM_API_KEY = get_env_var("OOM_API_KEY") | |
OOM_API_ADDRESS = get_env_var("OOM_API_ADDRESS") | |
WINRM_PROTOCOL = get_env_var("WINRM_PROTOCOL") | |
WINRM_PORT = get_env_var("WINRM_PORT") | |
def handle(event, context): | |
s3 = boto3.client('s3') | |
logger.debug("event is %s, context is %s", event, context) | |
trailevents = list() | |
for record in event['Records']: | |
key = record['s3']['object']['key'] | |
bucket = record['s3']['bucket']['name'] | |
s3.download_file(bucket, key, "/tmp/event.json.gz") | |
with gzip.open("/tmp/event.json.gz", 'rb') as f: | |
filecontent = f.read() | |
trailevents.append(json.loads(filecontent)) | |
logger.info("retrieved file s3://%s/%s", bucket, key) | |
instanceIds = list() | |
for trailevent in trailevents: | |
for var in trailevent['Records']: | |
if var['eventName'] == "TerminateInstances": | |
instanceIds.append({var['awsRegion'], var['responseElements']['instancesSet']['items'][0]['instanceId']}) | |
if instanceIds != []: | |
for k, v in instanceIds: | |
instanceId = v | |
region = k | |
aws_details = gather_instance_details(instanceId, region) | |
logger.debug("got instance details %s", json.dumps(aws_details)) | |
delete_oom_record(instanceId) | |
delete_ldap_record(aws_details["hostname"]) | |
delete_dns_record(aws_details["hostname"]) | |
create_jira_ticket(aws_details) | |
else: | |
logger.info("no terminate-instance events found") | |
def which_server(hname): | |
domains = DOMAINS.split(",") | |
servers = SERVERS.split(",") | |
ldap_domains = LDAP_DOMAINS.split() | |
domain = ".".join(hname.split('.')[1:]).lower() | |
if domain in domains: | |
logger.info("matched domain %s", domain) | |
return servers[domains.index(domain)], domain, ldap_domains[domains.index(domain)] | |
logger.error("failed to match domain %s", domain) | |
return None, domain, None | |
def delete_dns_record(hostname): | |
server, domain, ldap_domain = which_server(hostname) | |
if server is not None: | |
p = Protocol(endpoint=WINRM_PROTOCOL + "://" + server + ":" + WINRM_PORT + "/wsman", | |
transport='credssp', username=DNS_USERNAME, | |
password=DNS_PASSWORD, server_cert_validation='ignore') | |
shell_id = p.open_shell() | |
command_id = p.run_command(shell_id, 'dnscmd', ['localhost', "/RecordDelete", domain, str(hostname.split(".")[0]), 'A', '/f']) | |
std_out, std_err, status_code = p.get_command_output(shell_id, command_id) | |
p.cleanup_command(shell_id, command_id) | |
p.close_shell(shell_id) | |
if std_err is not None: | |
logger.info("Successfully delete DNS A/PTR record for %s in domain %s", hostname, ldap_domain) | |
logger.debug(std_out) | |
else: | |
logger.error("failed to delete DNS records for %s", hostname) | |
logger.debug(std_err) | |
else: | |
logger.error("ldap server not found") | |
def delete_ldap_record(hostname): | |
server, domain, ldap_domain = which_server(hostname) | |
if server is not None: | |
ldap = Server(server, get_info=ALL) | |
conn = Connection(ldap, user=LDAP_USERNAME, password=LDAP_PASSWORD, authentication=NTLM) | |
conn.bind() | |
host = hostname.split('.')[0] | |
conn.search(ldap_domain, '(&(objectclass=computer)(cn=' + host + '))') | |
if len(conn.entries) == 1: | |
dn = str(conn.entries[0]) | |
conn.delete(dn.split()[1]) | |
logger.info("deleted ldap record %s in domain %s", dn.split()[1], domain) | |
elif len(conn.entries) > 1: | |
logger.error("more then one ldap result") | |
else: | |
logger.error("ldapsearch returned no results") | |
def gather_instance_details(instanceId, region): | |
ec2 = boto3.client("ec2", region_name=region) | |
instance_info = ec2.describe_instances(InstanceIds=[instanceId]) | |
OomHeaders = {'Content-type':"application/json", 'Authorization2':OOM_API_KEY} | |
request = urllib2.Request(OOM_API_ADDRESS + "?filter=barcode%20eq%20\'" + instanceId + "\'", headers=OomHeaders) | |
try: | |
response = urllib2.urlopen(request) | |
content = json.loads(response.read()) | |
except: | |
content = None | |
try: | |
oom_hostname = content[0]["name"] | |
except: | |
oom_hostname = None | |
logger.info("%s not found in ec2 instance_info", instanceId) | |
for tag in instance_info["Reservations"][0]["Instances"][0]["Tags"]: | |
if tag["Key"] == "Name": | |
ec2_hostname = tag["Value"] | |
ami = instance_info["Reservations"][0]["Instances"][0]["ImageId"] | |
az = instance_info["Reservations"][0]["Instances"][0]["Placement"]["AvailabilityZone"] | |
instance_type = instance_info["Reservations"][0]["Instances"][0]["InstanceType"] | |
if oom_hostname == ec2_hostname: | |
logger.info("oom hostname and ec2 hostname match - %s", ec2_hostname) | |
hostname = ec2_hostname | |
elif oom_hostname is not None and ec2_hostname is None: | |
logger.info("obtained hostname from oom - %s", oom_hostname) | |
hostname = oom_hostname | |
elif ec2_hostname is not None: | |
logger.info("obtained hostname from ec2 - %s", ec2_hostname) | |
hostname = ec2_hostname | |
else: | |
logger.info("unable to determine hostname") | |
hostname = instanceId | |
return {"hostname": hostname, | |
"amiId": ami, | |
"az": az, | |
"instanceId": instanceId, | |
"instanceType": instance_type} | |
def delete_oom_record(instanceId): | |
OomHeaders = {'Content-type':"application/json", 'Authorization2':OOM_API_KEY} | |
request = urllib2.Request(OOM_API_ADDRESS + "?filter=barcode%20eq%20\'" + instanceId + "\'", headers=OomHeaders) | |
try: | |
response = urllib2.urlopen(request) | |
content = json.loads(response.read()) | |
except: | |
content = None | |
try: | |
equipment_id = content[0]["equipment_id"] | |
except: | |
equipment_id = None | |
logger.error("%s not found in oom", instanceId) | |
if equipment_id is not None: | |
opener = urllib2.build_opener(urllib2.HTTPHandler) | |
request = urllib2.Request(OOM_API_ADDRESS + "/" + equipment_id, headers=OomHeaders) | |
request.get_method = lambda: 'DELETE' | |
opener.open(request) | |
logger.info("Deleted OOM record for %s", instanceId) | |
def create_jira_ticket(aws_details, host_details=None): | |
JiraDescription = "||FieldName||Value||\n" | |
JiraDescription += "|AMI Id|" + aws_details['amiId'] + "|\n" | |
JiraDescription += "|Availability Zone|"+ aws_details['az'] + "|\n" | |
JiraDescription += "|Instance-Id|"+ aws_details['instanceId'] + "|\n" | |
JiraDescription += "|Instance-type|"+ aws_details['instanceType'] + "|\n" | |
JiraDescription += "|HostName|" + aws_details['hostname'] + "|\n" | |
if host_details is not None: | |
logger.debug("for later") | |
JiraTicketCreateBody = {'fields':{'project':{'id': '11107'}, | |
'summary': "AWS Server Decommission " + aws_details['hostname'], | |
'description': JiraDescription, | |
'labels': [aws_details['instanceId'], aws_details['hostname']], | |
'issuetype': { | |
'id': '10815'}}} | |
JiraCredentials = JIRA_USERNAME + ':' + JIRA_PASSWORD | |
JiraHeaders = {'Content-type':'application/json', "Authorization": "Basic " + base64.b64encode(JiraCredentials)} | |
request = urllib2.Request(JIRA_API_ADDRESS, headers=JiraHeaders, data=json.dumps(JiraTicketCreateBody)) | |
response = urllib2.urlopen(request, context=ssl._create_unverified_context()) | |
response = response.read() | |
TicketId = json.loads(response)['key'] | |
JiraTransitionBody = {'transition':{'id':11}} | |
request = urllib2.Request(JIRA_API_ADDRESS + '/' + TicketId + '/transitions?expand=transitions.fields', headers=JiraHeaders, data=json.dumps(JiraTransitionBody)) | |
response = urllib2.urlopen(request, context=ssl._create_unverified_context()) | |
response.read() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment