Using Powershell To Access Gmail API

4 minute read

Description:

Use the following techniques to access Gmail API using Powershell. Most of guide was from following Linda here.

To Resolve:

  1. Go to https://console.developers.google.com and click to create OAuth Client ID credentials. Make sure to select “OTHER” as the application type, not Web Application as you might think. Name is something like “gmail”. Click to download the credentials in JSON format.

  2. Enable the Gmail API and look for something called SCOPES for it. For gmail API, this is found here. The full reference guide can be found here. We are going to use “https://mail.google.com/”

  3. Now we need to use a browser to get a one time authorization code. We simply use our ClientID, Secret, and Scope from above and build the following URL:
    https://accounts.google.com/o/oauth2/auth?client\_id={CLIENT ID}&redirect\_uri=urn:ietf:wg:oauth:2.0:oob&scope={SCOPE}&response_type=code

NOTE: Replace {Client ID} and {Scope} including the brackets, with your actual ClientID and Scope

  1. Now we will run the following script to get a “refresh token”:

    $ClientID = "{CLIENT ID}";
    $Secret = "{SECRET}";
    $RedirectURI = "urn:ietf:wg:oauth:2.0:oob";
    $AuthorizationCode = '{Code from web browser link above}';
    
    $TokenParams = @{
    client_id=$clientId;
    client_secret=$secret;
    code=$AuthorizationCode;
    grant_type='authorization_code';
    redirect_uri=$redirectURI
    }
    
    $Token = Invoke-WebRequest -Uri "https://accounts.google.com/o/oauth2/token" -Method POST -Body $tokenParams | ConvertFrom-Json
    
    # Save this
    $Token.refresh_token
    
    
    
    • It will then give you a screen you may have seen in the past like an app wants to access/manage your emails. Click allow. Also, if you get to this point and you get a couple 400:Bad Requests or 401’s, just go back and redo steps 1-3. I had issues with my second account I did while writing this up, but I eventually got the $Token.refresh_token to populate.
  2. Use the refresh token to get new access token. The access token is used to access an api by sending the access_token parm with any request. Access tokens are only valid for about an hour after that you will need to request a new one using your refresh_token:

    $ClientID = "{CLIENT ID}";
    $Secret = "{SECRET}";
    $RedirectURI = "urn:ietf:wg:oauth:2.0:oob";
    $RefreshToken = "{Refresh token from above}";
    
    $RefreshTokenParams = @{
    client_id=$clientId;
    client_secret=$secret;
    refresh_token=$refreshToken;
    grant_type='refresh_token';
    }
    
    $RefreshedToken = Invoke-WebRequest -Uri "https://accounts.google.com/o/oauth2/token" -Method POST -Body $refreshTokenParams | ConvertFrom-Json
    
    $AccessToken = $refreshedToken.access_token
    
    • The link I read above said that you have to request a Refresh Token every hour or so, but I’ve been using a hard coded refresh token all week while testing this using the snippet above and have not had to generate anything new. In other words, I’m not sure that this has to change so YMMV. I have not had to do steps 1-4 except once. Here is what I put in the top of my scripts:**
    $ClientID = '90922440056456462-nu1ntdasfdasdfomk8afdasdfjd21slasdfasodfvj9grrger9dff3h413vgc.apps.googleusercontent.com'; #Not real, just an example that I actually hard code it.
    $Secret = 'Gdl8JSyoDXH9basasfdfsdfhhasdfdfadfMcwasdfadfsQqLeFPJC'; #Not real, just an example that I actually hard code it.
    $RedirectURI = 'urn:ietf:wg:oauth:2.0:oob';
    $RefreshToken = '1/LDYZZKTc8yVdfnITe9Ts7gByasdlfk;jasl;dkfjas;df3ZtvuworF4lgvUdhbHg'; #Not real, just an example that I actually hard code it.
    
    $RefreshTokenParams = @{
    client_id=$clientId;
    client_secret=$secret;
    refresh_token=$refreshToken;
    grant_type='refresh_token';
    }
    
    $RefreshedToken = Invoke-WebRequest -Uri "https://accounts.google.com/o/oauth2/token" -Method POST -Body $refreshTokenParams | ConvertFrom-Json
    
    $AccessToken = $refreshedToken.access_token
    
    
    
  3. Now that we have our access token, we can do just about anything the API references will allow us to do. For example:

    #Initialize a counter variable
    $Results = 0
    
    # Get all emails
    $Request = Invoke-WebRequest -Uri "https://www.googleapis.com/gmail/v1/users/me/messages?access_token=$accesstoken" -Method Get | ConvertFrom-Json
    $messages = $($Request.messages)
    Write-output "Found $($messages.count) messages to go through"
    
    #Go through each email one by one
    ForEach ($message in $messages)
    {
    $a = $($message.id)
    # This is how to access an individual email
    $b = Invoke-WebRequest -Uri ("https://www.googleapis.com/gmail/v1/users/me/messages/$a" + "?access_token=$accesstoken") -Method Get | ConvertFrom-Json
    
    # Example: Find emails that contain 2886 somewhere in the subject
    Write-Output "Message Subject : $($b.snippet)"
    Write-Output "Seeing if it matches a string request"
    
    If ($($b.snippet) -match "2886")
    {
    Write-Output "Matched"
    # This grabs the body of the email. It will be scrambled in a "like-base64 format"
    $c = $b.payload.parts.body.data[0]
    
    # Now we clean it up and convert it to ASCII
    $d = $c.Replace('-','+').Replace('_','/')
    [String]$e = [System.Text.Encoding]::Ascii.GetString([System.Convert]::FromBase64String($d))
    
    #At this point, we are done, we have our email. Feel free to skip the next part. This was me parsing the email for a certain "amount" that I will then cast to [single]
    $f = $e -Replace '\s',''
    $g = $f.IndexOf('$')
    $h = $f.Substring($g,10)
    $i = $h -match '\$(.*)[V]'
    [single]$j = $matches[1]
    $matches = $null
    Write-Output "Amount `$$j`n=============="
    #Adding to counter variable
    [single]$Results += $j
    }
    Else
    {
    Write-output "didn't match"
    }
    }
    Write-Output "Total `$$Results"
    
  4. That is all for now, but in summary, once you have the $AccessToken, you can access Gmail API pretty easily:

    All emails:
    Invoke-WebRequest -Uri "https://www.googleapis.com/gmail/v1/users/me/messages?access_token=$accesstoken" -Method Get

    Individual Email (Where $a = $($message.id) (see above)):
    Invoke-WebRequest -Uri ("https://www.googleapis.com/gmail/v1/users/me/messages/$a" + "?access_token=$accesstoken") -Method Get

    Trash an email (Where $a = $($message.id) (see above)):
    Invoke-WebRequest -Uri ("https://www.googleapis.com/gmail/v1/users/me/messages/$a/trash" + "?access_token=$accesstoken") -Method Post

    Found this on StackExchange. If you just want to get the most recent emails header, you can use this function:

    Function Get-SubjectLine
    {
    #Acquires most recent message ID using access token.
    $messageIDjson = Invoke-WebRequest -Uri "https://www.googleapis.com/gmail/v1/users/me/messages?access_token=$accessToken" -Method Get | ConvertFrom-Json;
    #Converts JSON message and thread ids into string.
    $messageID = ($messageIDjson | Out-String);
    #Seperates string on first message ID, places messageID into $result.
    $start = $messageID.indexOf("=") + 1;
    $end = $messageID.indexOf(";", $start);
    $length = $end - $start;
    $result = $messageID.substring($start, $length);
    #Acquires most recent message, using ID stored in result and access token.
    $messages = Invoke-WebRequest -Uri ("https://www.googleapis.com/gmail/v1/users/me/messages/$result" + "?access_token=$accessToken") -Method Get | ConvertFrom-Json;
    
    return $messages.snippet;
    }
    

    I’m sure there are millions of other things you can do once you are at this step. Enjoy!