Twitter Bot - VMware Product Lifecycle Matrix

Last year I wanted to learn Python, and I didn’t really know what serverless (ie: AWS Lambda) was, so I looked for a project where I could learn.

TL;DR Serverless is just somewhere to run your code from 🤣

Something that I find hard to keep track of, is when VMware products are coming up to their end of supports dates. Sometimes the dates change. I’m not going to check for updates each week, but I am on Twitter a lot. *Bam* I should make a Twitter bot that tweets when VMware products are approaching end of support dates.

Here’s how I did it.

Disclaimer: I’m still learning python, so if you have better ways to do things, let me know.

Twitter API

Sign up for a Twitter developer account. Then follow the docs to obtain you own Twitter API keys.

Discovering headers

Looking at lifecycle.vmware.com, it does some back and forth before showing the product lifecycle matrix. The nice thing is that it send it to our browser in JSON.

Firing up Burp Suite to view headers, I turned on intercept, and opened the browser built into Burp Suite. (Burp Suite comes with Kali)

Burp

Then browse to https://lifecycle.vmware.com.

You can forward all the requests, and eventually see a POST to auth.esp.vmware.com. Wow, look at all those other non-related requests!

Burp

Look at the body of the request.

{
  "grant_type":"client_credentials",
  "client_id":"plm-prod-auth",
  "client_secret":"84e0f320-5c9d-4ced-a99b-3cc0a7ad64a9"
  }

These details were sent to us in a response from our GET https://lifecycle.vmware.com/main.js, but the values are always the same, so for now we’ll hardcode those values. The response shows a possible typo clientSeret: "84e0f320-5c9d-4ced-a99b-3cc0a7ad64a9" (clientSecret?).

You can see the response in the screenshot showing access_token is what we’ll need for our next request.

{
  "access_token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXNwLnZtd2FyZS5jb20iLCJzdWIiOiJwbG0tcHJvZC1hdXRoIiwicHJvdmlkZXIiOiJjbGllbnQiLCJ1c2VybmFtZSI6InBsbS1wcm9kLWF1dGgiLCJjbGllbnRfdHlwZSI6ImVzcC1wcm9kLTE1MC1zb21xdiIsImlhdCI6MTYyOTg3MTYyMCwiZXhwIjoxNjQ1NDIzNjIwfQ.ECGvBeca6Blp24n2cVUgnnLGxTXtwCEIQFeBGpmMseORbMCy-Kccn8shz6tKmn5FG9VlqBj0MMFu-Fy7LrebX87DNcW7yknVdnaM8xOJxFP0Rkx3y1qmTBIkpyvl_KBG1ZhlvZucpfb20CD-_GChQ0EDTLo5fDBVC9kZZpeDznyz7Pidas77Bmv-AKA-PRT3taY1Ym8q7O5AXzoVqnNDxPajSxqoRdFJzWQWDadjeEiZs4uLNrsEhzUUS87oQoDilql9iZcPturGX7GQQBJVDhjOIeTPPauKfMlP5aePcArZfKYWFfYSiswSLYwlArHduoeEp1q6Qx81HaDdA2qyyQ",
  "access_type":"bearer",
  "expires_in":15552000
}

Now we can do GET https://plm.esp.vmware.com/api/v1/release_stream/lifecycle_matrix/?to=08/25/2021 and set X-Auth-Key

Burp

And the response is a 560k JSON file. Booya!

You can test this using Postman, or start some Python coding to do it.

Python - GET JSON - Check dates - Tweet

Last year I used the Python module Twython, but it felt a bit heavy for my needs, and I wanted something simple. I found TwitterAPI, and it was exactly what I was after.

Using the above information, I ended up with:

Updated code at https://github.com/daunce/twitter-bot-vmware-product-lifecycle-matrix/blob/main/lambda_function.py

import requests
import json
from datetime import datetime, timedelta, date
from TwitterAPI import TwitterAPI


def GetProductLifecycleMatrixJSON():
    url = 'https://auth.esp.vmware.com/api/auth/v1/tokens'
    custom_header = {
        "Content-Type": "application/json"
    }

    json_body = {
        "grant_type":"client_credentials",
        "client_id":"plm-prod-auth",
        "client_secret":"84e0f320-5c9d-4ced-a99b-3cc0a7ad64a9"
    }

    r = requests.post(url,headers = custom_header, json = json_body)
    data = (r.text).split("\"")

    url = 'https://plm.esp.vmware.com/api/v1/release_stream/lifecycle_matrix/?to=08/19/2021'

    custom_header = {
        "Accept":"application/json",
        "X-Auth-Key":data[3]
    }

    r2 = requests.get(url,headers = custom_header)

    return r2.text


def sendOutput(product, reason, days):
    tag = "#ProductLifecycleMatrix"
    tagUrl = "https://lifecycle.vmware.com"
    if days == 0:
        tweet_text = f'{product} reaches {reason} TODAY. {tagUrl} {tag}'
    else:    
        tweet_text = f'{product} reaches {reason} in {days} days. {tagUrl} {tag}'

    print(tweet_text)

    consumer_key="<your_api_key>"
    consumer_secret="<your_api_secret>"
    access_token_key="<your_access_token_key>"
    access_token_secret="<your_access_token_secret>"
    api = TwitterAPI(consumer_key, 
                    consumer_secret,
                    access_token_key,
                    access_token_secret)

    # Comment out below to prevent from tweeting while testing
    r = api.request('statuses/update', {'status': tweet_text})
    print('SUCCESS' if r.status_code == 200 else 'PROBLEM: ' + r.text)


def significantDate(plmrecord):

    today = date.today()

    # How many days in advanced to check.
    daysWarning = [0, 30, 90, 180, 365]     # Set this on when to trigger alerts. Days before event.
    futureDate = [0] * len(daysWarning)     # Prepare array to store futureDates

    # Set future days to check. 
    for i in range(len(daysWarning)):
        futureDate[i] = today + timedelta(days = daysWarning[i])

    for i in range(len(futureDate)):
        # Check End of support dates
        if datetime.strptime(plmrecord["end_support_date"], "%Y-%m-%d").date() == futureDate[i]:
            sendOutput(plmrecord["name"],"end of general support", daysWarning[i])
    
        # Check End of technical guidance
        if plmrecord["end_tech_guidance_date"] != None: # Some records have null
            if datetime.strptime(plmrecord["end_tech_guidance_date"], "%Y-%m-%d").date() == futureDate[i]:
                sendOutput(plmrecord["name"],"end of technical guidance", daysWarning[i])
         
plmdata  = json.loads(GetProductLifecycleMatrixJSON())

for i in range(len(plmdata["supported"])):
    significantDate(plmdata["supported"][i])

print("Processed ", i, "records")

Testing this locally it seemed to work. I could schedule this to run from my workstation, but here’s where serverless comes in.

AWS Lamba

To run the code in AWS Lambda I had to make a few minor changes.

I renamed my code lambda_function.py, and moved the main code inside a function called def main(event, context):

Package up your python code & TwitterAPI following Deploy Python Lambda functions with .zip file archives.

Open up the AWS Lamba console, and choose “Create function”

Set a function name, and choose Python 3.9 for Runtime.

Create AWS Lambda function

Back at the Code menu within the Lambda AWS console, upload the zip file you just created.

If you click Test, it will run the code. Be careful if you are tweeting from the code. Twitter won’t let you tweet the same thing twice within a certain timeframe.

Click Deploy to publish those changes.

Click + Add trigger, and choose EventBridge (CloudWatch Events).

Set the name, and schedule expression. Check out Cron cheatsheet for help.

Add trigger

If you’ve run it manually using the Test option, or the scheduled time has past, you can click Monitor / Logs / View logs in CLoudWatch and see output of any print() statements.

Now I need to think of other projects to fill the void.

Andrew Dauncey
Andrew Dauncey
Senior Consultant at VMware PSO

Every day I’m shuffling

Previous