본문 바로가기

Programming/Python

[Python] 웹크롤링으로 AWS Personal health dashboard 이벤트 긁어오기 자동화

반응형

1. 개요

[1] AWS PHD(Personal Health Dashboard)에서는 하드웨어 메인터넌스 및 VPN 싱글 터널 알림, 보안 패치 등에 대한 내용을 알려준다.

 

 

[2] AWS Support Plan은 Basic, Developer, Business, Enterprise, ..로 구성되어 있다.

 

=> 여기서 AWS Health API는 Business 티어 이상부터 사용이 가능하다. 그 말은 즉슨 Basic 티어나 Developer 티어는 AWS Health API를 사용할 권한이 부여되지 않는다는 뜻이다.

 

=> Developer 이하 고객사는 AWS API를 활용하여 프로그래밍 방식으로 PHD를 확인할 수 없다.

 

 

[3] Developer 이하 고객사가 여러개일 경우 AWS Web Console로 직접 들어가서 확인하는데 많은 시간이 소요될 수 있다.

 

=> 웹 크롤링으로 조금이나마 자동화를 구현할 수 있다.

 

 

* 주의 사항

필요하신 분들은 해당 글은 업무에 참고만 하시길 바라며, 각각의 환경 및 네트워크 등을 고려해야 함

2. 원리 및 방법

1. ReadOnly 권한을 부여 받은 Role을 통해 대표 계정에서 각 고객사 계정으로 AssumeRole로 웹 콘솔에 접근

 

A. 역할 전환 클릭

 

B. 계정명, 역할명을 기입한 뒤 역할 전환 클릭

 

 

2. Personal Health Dashboard로 접근하여 어떤 Event가 있는지 확인한 뒤, 이벤트가 발견될 경우 Text 파일에 기입.

3. 이러한 행위를 List로 반복문을 돌음

 

4. 아래는 결과물임


3. 코드 설명

[주의 사항]

 

* 예외 처리를 대충하였음.

* 네트워크에 따라 동작이 잘 되지 않을 가능성 O

 

Logic 설명

1. 각 계정을 Assume Role로 접속할 수 있는 계정으로 Login / 고객사 Account 기입

   => MFA 설정이 되어있지 않아야 가능

 

2. CONSOLE로 접속

 

3. Explicitly Wait으로 특정 Element가 나올때까지 대기한 뒤 username id가 있는 element가 보일 경우 ID, Password를 기입 후 로그인

 

4. 각각 For 문을 돌면서 schedule_changes를 확인

 

5. get_info 함수를 통해 각 계정에 대해 schedule_changes 확인

 

 

# Full script

# Import
from selenium import webdriver
from selenium.webdriver.common.keys import Keys # Keys
from selenium.webdriver.common.action_chains import ActionChains

# DeprecationWarning: executable_path has been deprecated, please pass in a Service object
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# Explicit wait
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from selenium.common.exceptions import NoSuchElementException
from time import sleep

########################################################################################################
########################## AWS 대표 계정 (ID, PASSWORD) 기입 #######################################
########################## MFA 설정이 되어 있지 않아야 합니다.       #######################################
########################################################################################################
AWS_ACCOUNT_ID = "xxxxxxxxxx"
AWS_ACCOUNT_PW = "aaaaaaaaaa"


########################################################################################################
########################## 고객 Account ID 기입 #########################################################
########################################################################################################
ROLE_NAME="xxxx-xxxx-role"
ACCOUNTS = ["xxxxxxxxxxx","xxxxxxxxxxx","xxxxxxxxxxx","xxxxxxxxxxx"]



# 웹 콘솔 링크
XXXXXXX_CONSOLE_URL = "https://xxxxxxx.signin.aws.amazon.com/console"
PHD_DASHBOARD_URL = "https://phd.aws.amazon.com/phd/home?region=ap-northeast-2#/dashboard/scheduled-changes"


def check_exists_by_xpath(xpath):

    try:
        driver.find_element_by_xpath(xpath)
    except NoSuchElementException:
        return False
    return True

def get_info(account_id: int):

    # 2. 역할 전환 페이지 이동 //*[@id="switchrole_firstrun_button"] //*[@id="switchrole_firstrun_button"]
 
    driver.get("https://signin.aws.amazon.com/switchrole")

    # 첫 번째 접속할 경우 id : switchrole_firstrun_button가 존재
    if check_exists_by_xpath('//*[@id="switchrole_firstrun_button"]'):
        try:
            WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'switchrole_firstrun_button')))
            driver.find_element_by_id("switchrole_firstrun_button").click()
        except Exception as e:
            print(f"check exists by xpath 함수에서 Error 발생, {e}")

    
    # 두 번째 리스트 요소부터 위의 id가 없음


    # 3. 역활 전환 페이지에서 '계정', '역할' 기입 후 역할 전환

    # [Explicitly Wait]
    WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'account')))

    driver.find_element_by_id("account").send_keys(account_id)
    driver.find_element_by_id("roleName").send_keys(ROLE_NAME)
    driver.find_element_by_id("input_switchrole_button").click()

    sleep(1)

    # 4. Scheduled changes
    
    driver.get(PHD_DASHBOARD_URL)
    
    sleep(1) 

    WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'awsui-table-container')))

    change_table = driver.find_element_by_class_name("awsui-table-container")

    change_trs = change_table.find_element_by_tag_name("tbody").find_elements_by_tag_name("tr")
    
    file.write(f"고객사 [{account_id}]\n\n")

    for change_tr in change_trs:

        change_tds = change_tr.find_elements_by_tag_name("td")

        for td_idx, change_td in enumerate(change_tds):

            file.write(f"{change_td.text}\n")
        
        file.write(f"\n")
    
    file.write(f"##########################################################\n\n")


try:
    # Selenium Options
    options = webdriver.ChromeOptions()
    options.add_argument('window-size=1920,1080')

    # options.add_argument('headless')    # Headless Option

    # Chrome Driver
    # driver = webdriver.Chrome(".\chromedriver.exe", options=options)
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)

    driver.get(SUPPORT_CONSOLE_URL)

    # [Explicitly Wait]
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, 'username'))
    )

    # 1. ID, Password 기입 및 로그인
    driver.find_element_by_id("username").send_keys(SUPPORT_ACCOUNT_ID)
    driver.find_element_by_id("password").send_keys(SUPPORT_ACCOUNT_PW)

    driver.find_element_by_id("signin_button").send_keys(Keys.ENTER)

    
    WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'nav-usernameMenu')))

    with open("scheduled_changes.txt", "w", encoding="utf-8") as file:
        
        file.write(f"확인 대상 고객사들 : {ACCOUNTS}\n\n")
        
        for account in ACCOUNTS:
            try:
                sleep(2)
                get_info(account)

            except Exception as e:
                print(f"각 Account 당 For loop 돌면서 에러 발생, {e}")    
    
    sleep(5)

except Exception as e:
    print(f"Error 발생하였습니다.\n사유 : {e}")

finally:
    # Chrome Driver quit
    driver.quit()

 

아쉬운점

- Code를 잘 짜지 못하다 보니 예외처리에 대한 부분을 퉁쳐서 처리함

 

- Element가 나오지 않을 경우를 깊게 고려하지 않아 sleep 함수로 처리하였음

 

- other notification에 대한 부분도 같이 출력을 하려 했지만 같이 출력하려할 경우 보이지 않아 다른 Python 파일을 따로 만들었음

 

- Selenium이 은근히 고려해야 할 점이 많은 것 같음

반응형