Retrieving attachments from Exchange mailbox using python

Story:

I got a request from a client who constantly gets CVs and have to download them for the hiring managers to review them and wanted to get some way an automated mechanism of downloading all those emails.

Googling lead me to the exchangelib project which is a great python source for such a purpose. I built my local lab of the following servers to test it

  1. AD 2016 moh10ly.local
  2. Exchange 2016 = exch01.moh10ly.local
  3. AAD = Another 2016 server to test from

I built my local Certification authority and made sure that all servers has the CA installed to avoid any issues on python.

If you are going to use this on your production environment you can basically install Python anywhere even on your own computer and it should work if EWS is exposed and Autodiscover is configured propely and have a valid and trusted 3rd party Certificate.

However, If you would like to schedule this to work on a daily basis and let it download attachments from mailbox then you’ll need a server or at least a computer to rely on that it would be on when the scheduled task works.

Prerequisites :

Windows Server 2016:

  • Download and install latest version of Python 3.10
  • From CMD run the following cmd
    • Pip install exchangelib
    • Pip install exchangelib[kerberos]
    • Pip install exchangelib[sspi]
  • Download and install MIT Kerberos MSI from https://web.mit.edu/KERBEROS/dist 64bit version
  • If you’re doing this on a local Lab without a trusted 3rd certificate you’ll need to make sure to export your certificatation Authority Certificate in CER format, copy the cert and add it to the end of Python root PEM.

Testing Scenario

  • I created two mailboxes on my local Exchange server lab
  • I have sent myself an email to info@moh10ly.com with 3 attachments in it as you can see.
  • Setup a server to use to download all attachments from the mailbox.

Prepare your Python Script

  • Import Packages in order to use them.

from exchangelib import DELEGATE, IMPERSONATION, Account, Credentials, EWSDateTime, EWSTimeZone, Configuration, NTLM, GSSAPI, CalendarItem, Message, Mailbox, Attendee, Q, ExtendedProperty, FileAttachment, ItemAttachment, HTMLBody, Build, Version, FolderCollection
  • Prepare Credentials of the user you would like to retrieve the attachments from.
credentials = Credentials(username='moh10ly\info', password='Bc12345$')
  • Enter your Exchange server configuration. Since autodiscover didn’t work for me as I don’t have a public certifiate so I went ahead and placed the server configuration.
ews_url = 'https://mail.moh10ly.com/EWS/exchange.asmx'
ews_auth_type = 'NTLM'
primary_smtp_address = 'info@moh10ly.com'
config = Configuration(service_endpoint=ews_url, credentials=credentials, auth_type=ews_auth_type)
  • Place the account type and configuration
account = Account(
primary_smtp_address=primary_smtp_address,
config=config, autodiscover=False,
access_type=DELEGATE)
  • Configure the local path of where you want to save attachments to on the server where this code is going to be launched from. in my code example I have created a folder called “Temp” on the C root drive and that’s what I will use.
  • You can pickup a different local path by changing the /temp path in the line “local_path = os.path.join(‘/temp‘, attachment.name)”
import os.path
from exchangelib import Account, FileAttachment, ItemAttachment, Message

some_folder = account.inbox 
for item in some_folder.all():
    for attachment in item.attachments:
        if isinstance(attachment, FileAttachment):
            local_path = os.path.join('/temp', attachment.name)
            with open(local_path, 'wb') as f:
                f.write(attachment.content)

This by now should be working fine and you should see that it is saving all your mailbox attachments to the folder that you have configured in the path section of the code.

Complete code to run

Working config


from exchangelib import DELEGATE, IMPERSONATION, Account, Credentials, EWSDateTime, EWSTimeZone, Configuration, NTLM, GSSAPI, CalendarItem, Message, Mailbox, Attendee, Q, ExtendedProperty, FileAttachment, ItemAttachment, HTMLBody, Build, Version, FolderCollection


credentials = Credentials(username='moh10ly\info', password='Bc12345$')

ews_url = 'https://mail.moh10ly.com/EWS/exchange.asmx'
ews_auth_type = 'NTLM'
primary_smtp_address = 'info@moh10ly.com'
config = Configuration(service_endpoint=ews_url, credentials=credentials, auth_type=ews_auth_type)


account = Account(
primary_smtp_address=primary_smtp_address,
config=config, autodiscover=False,
access_type=DELEGATE)

import os.path
from exchangelib import Account, FileAttachment, ItemAttachment, Message

some_folder = account.inbox 
for item in some_folder.all():
    for attachment in item.attachments:
        if isinstance(attachment, FileAttachment):
            local_path = os.path.join('/temp', attachment.name)
            with open(local_path, 'wb') as f:
                f.write(attachment.content)


-----------------

#To download all attachments in the inbox:

for item in account.inbox.all():
    for attachment in item.attachments:
        if isinstance(attachment, FileAttachment):
            local_path = os.path.join('/sky', attachment.name)
            with open(local_path, 'wb') as f, attachment.fp as fp:
                buffer = fp.read(1024)
                while buffer:
                    f.write(buffer)
                    buffer = fp.read(1024)
            print('Saved attachment to', local_path)

Hope this have helped you

References:

https://towardsdatascience.com/download-email-attachment-from-microsoft-exchange-web-services-automatically-9e20770f90ea

Check under attachments:

https://ecederstrand.github.io/exchangelib/

https://pypi.org/project/exchangelib/

Troubleshoot cert issue

https://stackoverflow.com/questions/51925384/unable-to-get-local-issuer-certificate-when-using-requests-in-python

With graph

https://techcommunity.microsoft.com/t5/identity-authentication/what-oauth-permissions-needed-for-exchangelib/m-p/2858179