Last active
February 28, 2023 13:35
-
-
Save dadevel/a821bbfcafd25f951df971d413e69045 to your computer and use it in GitHub Desktop.
Send Email with Azure/M365
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 python3 | |
from argparse import ArgumentParser | |
import json | |
import sys | |
import requests | |
# Retrieve an access token via the device code flow: | |
# roadtx auth --tokenfile ~/.cache/azmail.json --resource https://outlook.office.com --client d3590ed6-52b3-4102-aeff-aad2292ab01c --tenant contoso.com --device-code | |
# Refresh an existing access token: | |
# roadtx auth --tokenfile ~/.cache/azmail.json --resource https://outlook.office.com --client d3590ed6-52b3-4102-aeff-aad2292ab01c --tenant contoso.com --refresh-token "$(jq -r .refreshToken ~/.cache/azmail.json)" | |
# Send an email: | |
# azmail -t ~/.cache/aztoken.json -s 'Test 1' -b 'Hello world!' -r john.doe@contoso.com | |
# docs: | |
# - https://learn.microsoft.com/en-us/graph/api/user-sendmail | |
# - https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/version-2.0/mail-rest-operations#SendMessages | |
# I could not find a public client with the Mail.Send permission on graph.microsoft.com. | |
# Therefore I had to fall back to the deprecated outlook.office.com API for now. | |
# See e.g. https://github.com/secureworks/family-of-client-ids-research/blob/main/scope-map.txt | |
#ENDPOINT = 'https://graph.microsoft.com/v1.0/me/sendMail' | |
ENDPOINT = 'https://outlook.office.com/api/v2.0/me/sendmail' | |
def main() -> None: | |
entrypoint = ArgumentParser() | |
entrypoint.add_argument('-t', '--token', required=True) | |
entrypoint.add_argument('-s', '--subject', required=True) | |
entrypoint.add_argument('-b', '--body', required=True) | |
entrypoint.add_argument('--content-type', choices=('text', 'html'), default='text') | |
entrypoint.add_argument('-r', '--recipient', dest='recipients', action='append', required=True) | |
opts = entrypoint.parse_args() | |
if opts.token.startswith('eY'): | |
token = opts.token | |
else: | |
with open(opts.token) as file: | |
data = json.load(file) | |
token = data['accessToken'] | |
response = requests.post( | |
ENDPOINT, | |
headers={'Authorization': f'Bearer {token}'}, | |
json=dict( | |
Message=dict( | |
Subject=opts.subject, | |
Body=dict( | |
ContentType=opts.content_type, | |
Content=opts.body, | |
), | |
ToRecipients=[dict(emailAddress=dict(address=address)) for address in opts.recipients], | |
), | |
SaveToSentItems=True, | |
), | |
) | |
if response.status_code != 202: | |
data = response.json() | |
print(data['error']['message'], file=sys.stderr) | |
exit(1) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment