Email / Mailboxes

Bacula Enterprise Microsoft 365 Plugin can protect M365 Mailboxes associated to users. It is possible to utilize advanced selection methods to decide exactly what is backed up (folders included/excluded, users included/excluded), as well as control precisely which messages or attachments to restore and where (original user’s account or another user’s account). The information protected with this service is:

  • Folders (Inbox, Deleted Items, Sent…)

    • Messages (Metadata and contents)

    • Attachments

      • File Attachments

      • Item Attachments

      • Reference Attachments

  • Folder Rules

  • Mailbox Settings

  • Outlook Categories

Public folders are not supported and folder sharing permissions are not protected (see Email Limitations). On the other hand, folders are backed up always from their original accounts:

  • This means if a folder ‘folderA’ is shared from user ‘U1’ to user ‘U2’, if you only backup the entire mailbox of U2, contents of folderA will not be protected. In order to protect folderA you will need to add some additional job protecting U1 and folderA into the backup set or simply add the entire mailbox of U1.

User mailboxes and shared mailboxes are supported, it is only required to setup the correct address in user* parameters (shared mailboxes are treated as users). For groups, please note that they have no real mailbox, therefore it is not possible to backup the emails belonging to a given group. However, protecting emails of any of the users that is member of those groups will result in protecting all the received emails of that group.

Mailbox backup includes the following features:

  • Incremental backup with Delta function:

    • Delta function is applied for each folder individually

  • MIME object backup:

    • Based on the fileset parameter email_mime it is possible to get mime messages as well as the M365 objects. These kind of objects can be useful to have if there is any plan of using the emails outside the M365 service.

    • At restore time, if the restore operation is done via M365 services and not to a local FileSystem, mime_objects are automatically ignored.

  • Multilevel-attachments:

    • M365 supports having messages or event objects as the attachment of a given message object. These attachments can have additional attachments. This situation is what M365 Plugin considers multilevel-attachments. Some particular objects such as Files, when present in this multilevel way, are got embedded in the metadata of the message attached. This implies that the plugin must get them in a single HTTP call and load them entirely in memory. If there is not enough memory in the system, the variable multilevel_attach can be disabled and those objects will be ignored.

    • Multilevel-attachments are backed up at the same level. This means that a message with this structure:

      • Base Message has 1 Message-Attachment, Message-Attachment has 1 more internal Message-Attachment 2, and Message-Attachment 2 has a File attachment

      • When restored, the result will be all Attachments being restored to the same level, not one inside the another as they were originally. For example:

      • Base Message

        • Message-Attachment 1

        • Message-Attachment 2

        • File Attachment

Messages will be formatted in the catalog in order to not include sensitive information and will be included in a path like this:

  • /@m365/tenant.name/users/user@tenant.com/email/foldername/2021-04-19 09.58.52..J-123.10.msg

    • Where the message name is composed as: messageDate..J-JobId.JobIndex.msg

      • messageDate corresponds with the receivedDate

      • JobIndex is an internal index relative to a backlup job execution

      • The extension .msg corresponds to a file containing an email

      • Mime messages will have the extra word ‘mime’ in their extension. For example:

        • /@m365/tenant.name/users/user@tenant.com/email/foldername/2021-04-19 15.54.06..J-2021-04-19_15.54.45_03.10.mime.msg

  • /@m365/tenant.name/users/user@tenant.com/email/mailbox\_settings.mail.set

    • Mailbox Settings

  • /@m365/tenant.name/users/user@tenant.com/email/rules/nameRule.mail.rule

    • Mailbox Rules

  • /@m365/tenantname/users/usermail/outlook\_category_name.olk.cat/

  • Mailbox Categories

Attachments will be stored together with message objects:

  • They include their original name (file name)

  • They have an extension about their type (.file.att, .ref.att or .item.att)

  • The first part of the attachment name is the name of the parent message

Here are a few attachment examples:

/@m365/tenant.name/users/user@tenant.com/email/foldername/2021-04-19 15.54.06..J-123.10.Aliquid.gen.1.file.att
/@m365/tenant.name/users/user@tenant.com/email/foldername/2021-04-19 15.54.06..J-123.10.Dolor.gen.4.ref.att
/@m365/tenant.name/users/user@tenant.com/email/foldername/2021-04-19 15.54.06..J-123.10.Sapien Nostrum Aperiri Unum - t.3.item.att
/@m365/tenant.name/users/user@tenant.com/email/foldername/2021-04-19 15.54.06..J-123.10.Veri.gen.2.file.att
/@m365/tenant.name/users/user@tenant.com/email/foldername/2021-04-19 15.54.06..J-123.10.msgParent message

Backup parameters

The list below shows the specific backup parameters that can be set up in order to control the behavior of the email module.

In order to select the email module, the common service parameter must be equals or be containing the value email.

Entities that can include mailboxes are: users.

Option

Required

Default

Values

Example

Description

email_files

No

Strings representing existing mailfolders for the given users or groups separated by ‘,’

Inbox, Sent

Backup only specified mailfolders belonging to the selected users

email_files_exclude

No

Strings representing existing mailfoders for the given users or groups separated by ‘,’

Archive, Personal

Exclude selected mailfolders belonging to the selected users

email_files_regex_include

No

Valid regex

.*Company

Backup matching mailfolders. Please, only provide list parameters (files + files_exclude) or regex ones. But do not try to combine them.

email_files_regex_exclude

No

Valid regex

.*Plan

Exclude matching mailfolders from the selection. Please, only provide list parameters (files + files_exclude) or regex ones. But do not try to combine them. If this is the only parameter found for selection, all elements will be included and this list will be excluded.

email_multilevel_attach

No

Yes

0, no, No, false, FALSE, false, off ; 1, yes, Yes, TRUE, true, on

No

Include multilevel attachments in backup. A multilevel attachment is, for example, a message attached to a message containing more elements attached inside. Under certain circumstances, multilevel attachments could imply higher memory needs, as some of them are embedded in the metadata of the objects to backup, so they need to be caught in a single call and be loaded entirely in memory. This parameter is intended to control the behavior against that situation.

email_exclude_attachments

No

No

0, no, No, false, FALSE, false, off ; 1, yes, Yes, TRUE, true, on

Yes

Exclude any attachment from backup

email_mime

No

No

0, no, No, false, FALSE, false, off ; 1, yes, Yes, TRUE, true, on

Yes

Backup raw MIME file of every email, in addition to the Message object itself

email_settings

No

No

0, no, No, false, FALSE, false, off ; 1, yes, Yes, TRUE, true, on

Yes

Backup mailbox settings of included users

email_rules

No

No

0, no, No, false, FALSE, false, off ; 1, yes, Yes, TRUE, true, on

Yes

Backup mailbox folder rules of included users

email_categories

No

No

0, no, No, false, FALSE, false, off ; 1, yes, Yes, TRUE, true, on

Yes

Backup outlook categories of included users

email_messages_exclude_expr

No

String representing a valid Boolean Javascript expression regarding email message fields

emailSubject.includes(‘private’) && !emailIsRead

Exclude from backup all messages that match the provided expression

email_messages_exclude_index_expr

No

String representing a valid Boolean Javascript expression regarding email message fields

/.*private.com/.test(emailFrom)

Exclude only from indexing (catalog email tables) messages matching the provided expression

email_fields_exclude

No

String representing a list of email message fields

emailTo, emailBodyPreview

Do not store into the backup the provided list of message fields

email_fields_exclude_index

No

String representing a list of email message fields

emailFrom, emailSubject

Do not store into the index (catalog email tables) the provided list of message fields

email_filter_received_from

No

Date in format: yyyy-MM-dd HH:mm:ss

2020-06-01 00:00:00

Request messages for backup starting only from the provided date

Note

In previous versions, instead of email_files* parameters, it was possible to use files_* parameters. They are deprecated parameters now as we recommend to use module specific parameters in order to have better control and more possibilities inside a single fileset. However, jobs using files_* parameters will still work. The following fields: email_messages_exclude_expr, email_messages_exclude_index_expr, email_fields_exclude, email_fields_exclude_index, email_filter_received_from are available from Bacula Enterprise version 14.0.

Restore

The list below shows the subset of restore parameters that can be used to control the behavior of email module restore operations:

  • destination_user, destination_path, send_report, allow_duplicates, debug, foreign_container_generation

Use cases

The following restore scenarios are supported:

  • Restore directories, emails, or attachments to original user or to a different user

    • Restore parameters implied: destination_user

  • Restore directories, emails, or attachments to original path or to a different path

    • Restore parameters implied: destination_path

  • Restore files, directories, or file versions to local file system (general restore where parameter must be set to a path)

  • It is possible to control whether or not duplicate elements are allowed (based on file id):

    • Restore parameters implied: allow_duplicates

Particularities:

  • If no destination_user is set, every message will be restored into its original mailbox

  • If no destination_path is set, every message will be restored into its original path

    • If the selection contains messages from several users:

      • Original user messages will be restored in their original location

      • For other users, a special folder will be created with the email address of each of them, containing the full path and messages of the restored objects, unless the parameter foreign_container_generation is disabled

      • Example:

Restore of emails from 2 different users over a third mailbox without destination_path result in auto-generated Restore_date folder containing those 2 foreign users with the restored folder inside of them

Restore of emails from 2 different users over a third mailbox without destination_path result in auto-generated Restore_date folder containing those 2 foreign users with the restored folder inside of them

  • Restored elements will be duplicated by default, unless allow_duplicates variable is disabled

    • Even when disabling that variable, messages will be checked by id. So if there is an element with the same information but different ID, it will not be considered to be a duplicate

For more details about the behavior of each parameter, please check the general section of restore parameters.

Messages exclude expressions

Bacula Systems is aware about one of many privacy concerns that may arise when tools like this M365 Plugin enables the possibility to backup and restore data coming from different users, so the backup administrator can restore potentially private data at his will. Moreover, emails are usually one of the most critical items in terms of privacy.

One of many strategies this plugin offers in order to deal with that problem is the possibility to exclude messages. This is a very powerfull feature where it is possible to use quite flexible expressions that allow to select a subset of messages and simply exclude them from the backup:

  • email_messages_exclude_expr new fileset parameter

Or only from the index (from the catalog)

  • email_messages_exclude_index_expr new fileset parameter

Not only messages can be excluded but also select only a subset of email fields to be included in the protected information. It is possible to exclude fields from the backup:

  • email_fields_exclude new fileset parameter

Or only from the index (from the catalog):

  • email_fields_exclude_index new fileset parameter

Please, be aware that if the fields are excluded from backup, the restore operation to M365 can fail easily, since for instance emails cannot exist in M365 without From field, Subject field, etc.

All four discussed expressions are based on an internal structure of fields to work with. Below you can see the entire list of fields that you can use:

  • emailTags

  • emailSubject

  • emailFolderName

  • emailFrom

  • emailTo

  • emailCc

  • emailBodyPreview

  • emailImportance

  • emailTime

  • emailIsRead

  • emailIsDraft

Please note that it is very important to write the fields exactly as written above.

These fields can be used in a comma separated list in the ‘email_fields_exclude’ parameter and also ‘email_fields_exclude_index’ parameter.

Then, for ‘email_messages_exclude_expr’ and ‘email_messages_exclude_index_expr’ use them in a valid boolean expression in Javascript language syntax. Some examples are provided below:

Expression to exclude messages where subject includes the word ‘private’
emailSubject.includes('private')
Complex expression to exclude messages that are not read and are Draft or their folder name is named Private
!emailIsRead && (emailIsDraft || emailFolderName == 'Private')
Expression to exclude messages based on the received or sent date
!emailTime < Date.parse('2012-11-01')
Expression to exclude messages using a regex based on emailFrom
/.*private.com/.test(emailFrom)

Note

This feature is available since Bacula Enterprise version 14.0

Expression tester

This expression mechanism can sometimes be uncertain for end users, where they can have doubts about the correct behavior of their prepared expressions. In order to help with that, M365 Plugin presents a query method that allows to test those expressions against a static pre-loaded set of data.

There are two commands available:

  • Show command: It will show the static data in json format, so it is possible to see the contents to adapt the expressions to test

  • Test command: It will apply the expression parameters to the pre-loaded static data

The test command has the following format:

Expression tester Show command
.query client=<your-fd-client> plugin="m365: tenant=<your-tenant-id>" parameter=email-expr-show

The show command has the following fomat

Expression tester Test command
.query client=<your-fd-client> plugin="m365: tenant=<your-tenant-id> email_messages_exclude_expr = \"<your-js-expression>\"" parameter=json|email-expr-test
// Or
.query client=<your-fd-client> plugin="m365: tenant=<your-tenant-id> email_messages_exclude_index_expr = \"<your-js-expression>\"" parameter=json|email-expr-test

Please, not that you need to provide a valid tenantId, even if it’s not really used to process any data.

The test command produces some JSON output with objects with the exact format that is received from Microsoft and, consequently the same format that is stored in backup. Please not the ‘total’ value at the end, where the value of 12 total pre-loaded messages is shown

Expression tester Show command output
.query client=<your-fd-client> plugin="m365: tenant=<your-tenant-id>" parameter=json|email-expr-show
....
    "email-12": {
      "body": {
        "content": "These are the contents in text format of the 12 email of test data. It has the following categories:orange, black, white, purpleYou can try to filter this body using any JS method like /.*12.*/.test(emailBody) or emailBody.includes(12)",
        "contentType": "TEXT"
      },
      "ccRecipients": [
        {
          "emailAddress": {
            "address": "danny@other.com"
          }
        },
        {
          "emailAddress": {
            "address": "lucas@other.com"
          }
        },
        {
          "emailAddress": {
            "address": "terese@other.com"
          }
        }
      ],
      "from": {
        "emailAddress": {
          "address": "elon@other.com"
        }
      },
      "hasAttachments": false,
      "isDraft": false,
      "isRead": false,
      "replyTo": [
        {
          "emailAddress": {
            "address": "elon@other.com"
          }
        }
      ],
      "sentDateTime": {
        "dateTime": {
          "date": {
            "year": 2021,
            "month": 12,
            "day": 5
          },
          "time": {
            "hour": 11,
            "minute": 30,
            "second": 0,
            "nano": 0
          }
        },
        "offset": {
          "totalSeconds": 0
        }
      },
      "subject": "This is private subject 12",
      "toRecipients": [
        {
          "emailAddress": {
            "address": "laura@other.com"
          }
        },
        {
          "emailAddress": {
            "address": "jack@other.com"
          }
        },
        {
          "emailAddress": {
            "address": "john@other.com"
          }
        }
      ],
      "categories": [
        "orange",
        "black",
        "white",
        "purple"
      ]
    }
  },
  {
    "total": "12"
  }

The test command, on its side will produce two different outputs. The first part presents the same format than the show format, and those are the messages that would be included in the backup. The second part presents a different format, so an output like:

Expression tester Test command, index part output
.query client=<your-fd-client> plugin="m365: tenant=<your-tenant-id>" parameter=json|email-expr-show
....
      {
    "meta-email-12": {
      "EmailId": "",
      "EmailOwner": "test@test.com",
      "EmailTenant": "johndoe.onmicrosoft.com",
      "EmailTags": "orange,black,white,purple",
      "EmailSubject": "This is private subject 12",
      "EmailFolderName": "/",
      "EmailFrom": "elon@other.com",
      "EmailTo": "laura@other.com,jack@other.com,john@other.com",
      "EmailCc": "danny@other.com,lucas@other.com,terese@other.com",
      "EmailInternetMessageId": "",
      "EmailBodyPreview": "",
      "EmailImportance": "",
      "EmailConversationId": "",
      "EmailSize": 235,
      "EmailIsRead": 0,
      "EmailIsDraft": 0,
      "EmailHasAttachment": 0,
      "Type": "EMAIL",
      "Version": 1,
      "Plugin": "m365"
    }
  },
  {
    "total-backup": "12"
  },
  {
    "total-index": "12"
  }

That part represents the information that would be indexed in the backup (included into the catalog). You can also see the total entries at the end, that are very useful to quickly compare with the original 12 value and so, knowing if our expression is filtering the expected data or not. Below we provide an example where some filtering is applied to the backup, but also to the index:

Expression tester Test command, index part output
.query client=127.0.0.1-fd plugin="m365: tenant=xxxx-xx-xxxx-xxx-xxxx email_messages_exclude_expr=\"emailFrom == 'elon@other.com'\" email_messages_exclude_index_expr=\"emailSubject.includes('private')\"" parameter=json|email-expr-test
...
     {
    "meta-email-4": {
      "EmailId": "",
      "EmailOwner": "test@test.com",
      "EmailTenant": "johndoe.onmicrosoft.com",
      "EmailTags": "orange,black,white,purple",
      "EmailSubject": "This is orange subject 8",
      "EmailFolderName": "/",
      "EmailFrom": "bob@company.com",
      "EmailTo": "laura@company.com,jack@company.com,john@company.com",
      "EmailCc": "danny@company.com,lucas@company.com,terese@company.com",
      "EmailInternetMessageId": "",
      "EmailBodyPreview": "",
      "EmailImportance": "",
      "EmailConversationId": "",
      "EmailSize": 232,
      "EmailIsRead": 0,
      "EmailIsDraft": 0,
      "EmailHasAttachment": 0,
      "Type": "EMAIL",
      "Version": 1,
      "Plugin": "m365"
    }
  },
  {
    "total-backup": "6"
  },
  {
    "total-index": "4"
  }
In case your expression is not valid, the plugin will also inform about that with the following message:
  • error=Error listing elements. Cause: Predicate test error!! Review your query ….

Fileset examples

Backup Full MailBox of some users, but excluding some folders:

Fileset Example
FileSet {
   Name = fs-m365-drive-adjon-users-notemp
   Include {
      Options { signature = MD5 }
      Plugin = "m365: service=email tenant=57uia43-d107-17a2-a2g2-aa53c10tdahc objectid=56ddf1h9-eb5d-
   42nf-bac7-7b019fd284g5 user=\"adelev@baculaenterprise.onmicrosoft.com,jonis@baculaenterprise.onmicrosoft.com\"
   email_files_exclude=\"*.temporary\""
   }
}

Backup all MailBoxes:

Fileset Example
FileSet {
   Name = fs-m365-email-all
   Include {
      Options { signature = MD5 }
      Plugin = "m365: service=email tenant=57uia43-d107-17a2-a2g2-aa53c10tdahc objectid=56ddf1h9-eb5d-
   42nf-bac7-7b019fd284g5"
   }
}

Backup only the Inbox folder of some users:

Fileset Example
FileSet {
   Name = fs-m365-email-2user-inbox
   Include {
      Options { signature = MD5 }
      Plugin = "m365: service=email tenant=57uia43-d107-17a2-a2g2-aa53c10tdahc objectid=56ddf1h9-eb5d-
   42nf-bac7-7b019fd284g5 user="peter@mycompany.com,john@mycompany.com" email_files=inbox"
   }
}

Backup some users and include MIME messages:

Fileset Example
FileSet {
   Name = fs-m365-email-2user-mime
   Include {
      Options { signature = MD5 }
      Plugin = "m365: service=email tenant=57uia43-d107-17a2-a2g2-aa53c10tdahc objectid=56ddf1h9-eb5d-
   42nf-bac7-7b019fd284g5 user="peter@mycompany.com,miriam@mycompany.com" email_mime=yes"
   }
}

Disable backup of multi-level attachments:

Fileset Example
FileSet {
   Name = fs-m365-email-multilevel-attach
   Include {
      Options { signature = MD5 }
      Plugin = "m365: service=email tenant=57uia43-d107-17a2-a2g2-aa53c10tdahc objectid=56ddf1h9-eb5d-
   42nf-bac7-7b019fd284g5 user="peter@mycompany.com,miriam@mycompany.com" multilevel_attach=no"
   }
}

Well known folders

Microsoft 365 can present the folders information in local languages to the user.

In general, there is no ‘multilanguage’ support, in the sense that folders must be included with their original name. For example, if you create a folder named ‘books’, you cannot expect it to be backed up if you use something like ‘livres’ or ‘libros’ from other languages. You need to use the real name that was used to create such folder.

There is one very important special case though, which is ‘well known folders’. Well known folders are folders like ‘inbox’, ‘outbox’, ‘archive’… A full list can be found here: https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0

That kind of folders can be ‘found’ by the plugin using their ‘well known name’, instead their internal id, as it’s the general case. Therefore, for them it is possible to get the folder using their English well known word even if the user sees the folder with a translated word.

For example, to backup inbox it is needed to use ‘inbox’ even if for some user it is ‘Posteingang’ or ‘boîte de réception’. Microsoft 365 Plugin will recognize this special words and will query the information through them.

To summarize:

  • Well known folders -> Use English word

  • Other user folders -> Use original name