Sync a Local directory with S3

Sync a Local directory with S3

import os
import sys
import boto3
import hashlib
from datetime import datetime
from botocore.exceptions import ClientError

boto3.setup_default_session(profile_name='default')

if len(sys.argv) < 3:
    print("Not enough arguments.")
    print("Usage: python3 py-sync.py [SOURCE_DIRECTORY] [DESTINATION_BUCKET_NAME]")
    exit()

# Init objects
s3_client = boto3.client('s3')

SOURCE_DIR = sys.argv[1]
DESTINATION_BUCKET = sys.argv[2]

def check_file_exists(bucket, key):
    try:
        s3_client.head_object(Bucket=bucket, Key=key)
    except ClientError as e:
        return int(e.response['Error']['Code']) != 404
    return True

def md5(fname):
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

print("Filename-Local", end=', ')
print("Filename-S3", end=', ')
print("File-Status", end=', ')
print("Action")

print("--------------", end=', ')
print("-----------", end=', ')
print("-----------", end=', ')
print("------")

for subdir, dirs, files in os.walk(SOURCE_DIR):
    for file in files:
        file_path_full = subdir + os.sep + file
        file_path_relative = file_path_full.replace(SOURCE_DIR + os.sep, '')
        file_key = file_path_relative.replace('\\', '/')

        print(file_path_full, end=', ')
        print('s3://' + DESTINATION_BUCKET + '/' + file_key, end=', ')

        if check_file_exists(DESTINATION_BUCKET, file_key) == False: # File doesnt exists, upload it
            s3_client.upload_file(file_path_full, DESTINATION_BUCKET, file_key)
            print("New", end=', ')
            print("Uploading")

        else:
            response = s3_client.head_object(Bucket=DESTINATION_BUCKET, Key=file_key)
            md5_s3 = response['ResponseMetadata']['HTTPHeaders'].get('etag')
            md5_s3 = md5_s3.replace('\"', '')
            md5_local = (md5(file_path_full))

            if md5_local != md5_s3:
                s3_client.upload_file(file_path_full, DESTINATION_BUCKET, file_key)
                print("Modified", end=', ')
                print("Uploading")

            else:
                print("No-Change", end=', ')
                print("Skipping")

Extend Linux host volume on AWS

1. First extend from AWS Console/CLI
2. Check File system:
    $ sudo file -s /dev/xvdf

# For ext3/ext4

$ df -h (Check what OS is seeing the size of volume/partition)
$ (optional for partition) sudo growpart /dev/xvdf 1 
$ lsblk
$ sudo resize2fs /dev/xvdf (sudo resize2fs /dev/xvdf1 for partition)

See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html

Fix hostname in RHEL7

I found that even though I change the hostname on a RHEL host (AWS EC2 Instance), it changed after a reboot. Here is a fix:

$ sudo hostnamectl set-hostname --static abc.ot.cloud.example.com.au
$ sudo vi /etc/cloud/cloud.cfg

......Add to end of file
preserve_hostname: true

Add a new Volume to EC2 Host (Linux)

# From AWS Console, create a {200G} VOlume in correct AZ and attach to the running instance

Mount point example: /dev/xvdf

# Connect to Instance using ssh

# Run command to list block devices

$ lsblk

# Check if any filesystem exists on the device

# If it says 'data' then it is unformatted

$ sudo file -s /dev/xvdf

# Create a file system

$ sudo mkfs -t ext4 /dev/xvdf

# Mount the file system

$ sudo mount /dev/xvdf /u01

# Add to fstab so it persists on next reboot

# Device UUID is required (first command)

$ sudo file -s /dev/xvdf

$ cp /etc/fstab /etc/fstab.bak

$ vi /etc/fstab

UUID=524df55a-5d38-4380-9d53-e95856d3c0b1       /u01   ext4    defaults,nofail        0       2




 Example:

 UUID=a3828273-2053-41f9-97cc-f12e23436d16       /u01   ext3    defaults,nofail        0       2

Open (Listen On) port via PowerShell

To check network connectivity, you can open a port via PowerShell quickly and check if someone can connect to it:

    $endpoint = new-object System.Net.IPEndPoint ([system.net.ipaddress]::any, 8080)    
    $listener = new-object System.Net.Sockets.TcpListener $endpoint
    $listener.server.ReceiveTimeout = 3000
    $listener.start()    
    try {
    Write-Host "Listening on port $port, press CTRL+C to cancel"
    While ($true){
        if (!$listener.Pending())
        {
            Start-Sleep -Seconds 1; 
            continue; 
        }
        $client = $listener.AcceptTcpClient()
        $client.client.RemoteEndPoint | Add-Member -NotePropertyName DateTime -NotePropertyValue (get-date) -PassThru
        $client.close()
        }
    }
    catch {
        Write-Error $_          
    }
    finally{
            $listener.stop()
            Write-host "Listener Closed Safely"
    }

Fix Timezone

WINDOWS

Check Timezone

> [System.TimeZone]::CurrentTimeZone.StandardName

Fix Timezone

> C:\windows\system32\tzutil /s "AUS Eastern Standard Time"

LINUX

$ timedatectl set-timezone Australia/Canberra

or

$ sudo ln -sf /usr/share/zoneinfo/America/Los_Angeles /etc/localtime

Run an SSM Command against a set of EC2 Instances

A quick script today to run an SSM Command against a set of EC2 Instances. You can selectively target instance based on a TAG.

import boto3
import os
from time import sleep

lambda_func_name = os.getenv("AWS_LAMBDA_FUNCTION_NAME", "")

if lambda_func_name == "":  # We are not running in AWS
    boto3.setup_default_session(profile_name='<profile_name>')

ec2_client = boto3.client('ec2', region_name='ap-southeast-2')
ssm_client = boto3.client('ssm', region_name='ap-southeast-2')  # use region code in which you are working

sleep_duration = 2

def get_instances(os):

    instances = []

    # Check running or stopped instances
    response = ec2_client.describe_instances(
        Filters=[
            {
                'Name': 'instance-state-name',
                'Values': ['running']
            }
        ])

    # Iterate over instance(s)
    for r in response['Reservations']:
        for inst in r['Instances']:
            inst_id = inst['InstanceId']
            tags = inst['Tags']

            ins_tag = ""

            for tag in tags:
                if "OSFamily" in tag['Key']:
                    ins_tag = (tag['Value'])
                    break
                else:
                    ins_tag = "NA"

            if ins_tag == os:
                instances.append(inst_id)

    return instances

def ssm_run_command(instance_id, cmd, os_family):

    document = "AWS-RunPowerShellScript"

    if (os_family == "Linux"):
        document = "AWS-RunShellScript"

    response = ssm_client.send_command(
        InstanceIds=[
            instance_id  # use instance id on which you want to execute, even multiple is allowed
        ],
        DocumentName=document,
        Parameters={
            'commands': [
                cmd
            ]
        },
    )

    #print(response)

    sleep(sleep_duration)  # Seconds to wait for command to execute

    command_id = response['Command']['CommandId']
    output = ssm_client.get_command_invocation(CommandId = command_id, InstanceId = instance_id)

    return output['StandardOutputContent']

def check_time_zone_windows():

    os_family = "Windows"

    instances = get_instances(os_family)
    command = "[System.TimeZone]::CurrentTimeZone.StandardName"

    instances_with_wrong_tz = []

    for instance in instances:
        print("Checking instance: " + instance)
        time_zone = ssm_run_command(instance, command, os_family)
        #print(time_zone)

        if ( time_zone[0:3] != "AUS"):
            instances_with_wrong_tz.append(instance)

    result_str = ""

    if (len(instances_with_wrong_tz) > 0):

        result_str = "Following " + os_family + " instances have wrong Timezone:\n\n"

        for instance in instances_with_wrong_tz:
            result_str = result_str + instance + "\n\n"

    return result_str

def check_time_zone_linux():
    os_family = "Linux"

    instances = get_instances(os_family)
    command = 'date +"%Z"'

    instances_with_wrong_tz = []

    for instance in instances:
        print("Checking instance: " + instance)
        time_zone = ssm_run_command(instance, command, os_family)
        #print(time_zone)

        if (time_zone[0:2] != "AE"):
            instances_with_wrong_tz.append(instance)

    result_str = ""

    if (len(instances_with_wrong_tz) > 0):

        result_str = "Following " + os_family + " instances have wrong Timezone:\n\n"

        for instance in instances_with_wrong_tz:
            result_str = result_str + instance + "\n\n"

    return result_str

def lambda_handler(event, context):

    result_lin = check_time_zone_linux()
    result_win = check_time_zone_windows()

    result = result_lin + "\n\n" + result_win

    print(result)

if __name__ == "__main__":
    lambda_handler(0, 0)

Find missing Tags on EC2 Instances

Find missing Tags on EC2 Instances

# This script look for missing tags on EC2 instances
# Initialize 5 Environment variables tag1...tag5
# Usual tags to check can be as follows
#  - cpm backup
#  - monitor_site24x7
#  - Project
#  - Environment
#  - Owner

import boto3
import logging
import os

lambda_func_name = os.getenv("AWS_LAMBDA_FUNCTION_NAME", "")

if lambda_func_name == "":  # We are not running in AWS
    boto3.setup_default_session(profile_name='iconwater')

# setup simple logging for INFO
logger = logging.getLogger()
logger.setLevel(logging.INFO)

# define the connection
ec2 = boto3.resource('ec2')

def send_alert(alert_data):
    topic_arn = os.getenv("TopicARN", "")

    if topic_arn == "":
        print("send_alert: Missing topic ARN. Returning without sending alert.")
        return

    subject = os.getenv('CustomerID', '') + " - Missing EC2 Instances Tag Check"
    message = "Missing EC2 Instances Tag Check Results: \n\n" + alert_data

    print("send_alert: *** Sending alert ***")
    print("send_alert: Message: {0}".format(message))

    client = boto3.client('sns')
    response = client.publish(TargetArn=topic_arn,
                              Message=message,
                              Subject=subject)

def find_instances_with_missing_tags(tag_to_check):
    result_str = ""

    client = boto3.client('ec2')

    client = boto3.client('ec2', region_name='ap-southeast-2')
    # Check running or stopped instances
    response = client.describe_instances(
        Filters=[
            {
                'Name': 'instance-state-name',
                'Values': ['running', 'stopped']
            }
        ])
    # Iterate over instance(s)
    for r in response['Reservations']:
        for inst in r['Instances']:
            inst_id = inst['InstanceId']
            tags = inst['Tags']
            # Check the Name tag
            for tag in tags:
                if 'Name' in tag['Key']:
                    ins_name = (tag['Value'])
                    break
                else:
                    ins_name = "{No-Name}"

            for tag in tags:
                if tag_to_check in tag['Key']:
                    ins_tag = (tag['Value'])
                    break
                else:
                    ins_tag = "NA"

            if ins_tag == "NA":
                s = "Tag '{}' missing for instance {} ({})\n\n".format(tag_to_check, ins_name, inst['InstanceId'])
                # print (s)
                result_str = result_str + s
                # else:
                #    print("Tag '{}' present for instance {} ({})".format(tag_to_check, ins_name, inst['InstanceId']))

    return result_str

def lambda_handler(event, context):
    tag1 = os.getenv("tag1", "")
    tag2 = os.getenv("tag2", "")
    tag3 = os.getenv("tag3", "")
    tag4 = os.getenv("tag4", "")
    tag5 = os.getenv("tag5", "")

    s = ""

    if tag1 != "": s = s + find_instances_with_missing_tags(tag1)
    if tag2 != "": s = s + find_instances_with_missing_tags(tag2)
    if tag3 != "": s = s + find_instances_with_missing_tags(tag3)
    if tag4 != "": s = s + find_instances_with_missing_tags(tag4)
    if tag5 != "": s = s + find_instances_with_missing_tags(tag5)

    if s != "":
        print(s)
        send_alert(s)

    return 0

if __name__ == "__main__":
    lambda_handler(0, 0)