Graph API - Security eDiscovery API Export Questions - microsoft-graph-api

We have an application that uses the Security eDiscovery Graph API URLs to create exports based on a Case/Review Set that already exists within the Microsoft Compliance solution. The solution is correctly exporting to the Storage Account container we select, but we do not have a way to determine which folder in the container the export was sent to. See below for example flow/output.
Create Export call (https://learn.microsoft.com/en-us/graph/api/security-ediscoveryreviewset-export?view=graph-rest-beta&tabs=http):
https://graph.microsoft.com/beta/security/cases/ediscoveryCases/{CASE_ID}/reviewSets/{REVIEW_SET}/export
Payload sent:
{
"outputName": "{OUTPUT}",
"description": "",
"exportOptions": "originalFiles",
"exportStructure": "directory",
"azureBlobContainer": "{REDACTED}",
"azureBlobToken": "{REDACTED}"
}
First Response from URL in Location Header:
https://graph.microsoft.com/beta/security/cases/ediscoverycases({CASE_ID})/operations({OPERATION_ID})
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#security/cases/ediscoveryCases({CASE_ID})/operations/$entity",
"#odata.type": "#microsoft.graph.security.ediscoveryExportOperation",
"createdDateTime": "2023-02-16T15:16:12.2704755Z",
"completedDateTime": "0001-01-01T00:00:00Z",
"percentProgress": 0,
"status": "running",
"action": "contentExport",
"id": "86b2309224dd42ccb79271245ea542b6",
"outputName": "{REDACTED}",
"description": "",
"outputFolderId": null,
"azureBlobContainer": null,
"azureBlobToken": null,
"exportOptions": null,
"exportStructure": "none",
"createdBy": {
"application": null,
"user": {
"id": null,
"displayName": null,
"userPrincipalName": "{REDACTED}"
}
}
}
Response when progress hits 100%
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#security/cases/ediscoveryCases({CASE_ID})/operations/$entity",
"#odata.type": "#microsoft.graph.security.ediscoveryExportOperation",
"createdDateTime": "2023-02-16T15:16:12.2704755Z",
"completedDateTime": "2023-02-16T15:28:16.438Z",
"percentProgress": 100,
"status": "succeeded",
"action": "contentExport",
"id": "86b2309224dd42ccb79271245ea542b6",
"outputName": "{REDACTED}",
"description": "",
"outputFolderId": null,
"azureBlobContainer": null,
"azureBlobToken": null,
"exportOptions": null,
"exportStructure": "none",
"createdBy": {
"application": null,
"user": {
"id": null,
"displayName": null,
"userPrincipalName": "{REDACTED}"
}
}
}
Notice that several fields from the Operations call (https://learn.microsoft.com/en-us/graph/api/resources/security-ediscoveryexportoperation?view=graph-rest-beta) are set to null including exportOptions, azureBlobContainer, azureBlobToken, and exportOptions. Then exportStructure is set incorrectly to none.
The exported folder looks like below in the Azure Storage account. That GUID is not surfaced anywhere in the API calls we are getting and makes it very difficult to download the exported files programatically. Is this possible currently?

Related

Retrieve homePhone, mobilePhone and faxPhone from Delve Profile using Graph API

I am trying to retrieve the information in the red box via the graph api resource:
https://graph.microsoft.com/beta/me/profile
Here is the return:
"phones": [
{
"displayName": null,
"type": "business",
"number": "xxxxxxxxxx",
"allowedAudiences": "organization",
"createdDateTime": "2021-06-21T23:36:38.0912984Z",
"inference": null,
"lastModifiedDateTime": "2021-06-21T23:36:38.0912984Z",
"id": "a28e87ba-25xx-45xx-8fxx-5f340b597fxx",
"isSearchable": false,
"createdBy": {
"device": null,
"user": null,
"application": {
"displayName": "AAD",
"id": null
}
},
"lastModifiedBy": {
"device": null,
"user": null,
"application": {
"displayName": "AAD",
"id": null
}
},
"source": {
"type": [
"AAD"
]
}
}
],
It looks to only be returning the info from AAD, not the fields in the Delve profile. Upon further research I found these fields are actually linked to the SharePoint online profile. Is there a way to grab this information using the Graph API?

MS Teams Graph API: Get all teams user(s) are apart of

I want to use the graph api to get the teams a list of users is a member/owner of. I am using this endpoint to pass the list of users, then select the properties of the group that I am interested in:
/beta/users/$filter=id+in+('user-1','user-2','user-15')$expand=transitiveMemberOf($select=id,resourceProvisioningOptions,displayName,mailNickname)&$select=id,resourceProvisioningOptions,displayName
This will return a list of groups the member is apart of, including the Teams the member is apart of (up to 15 users). It will filter the groups to only return id,resourceProvisioningOption,displayName and mailNickname.
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(id,resourceProvisioningOptions,displayName,transitiveMemberOf(id,resourceProvisioningOptions,displayName,mailNickname))",
"value": [
{
"id": "user-1",
"displayName": "Jeff Smith",
"transitiveMemberOf": [
{
"#odata.type": "#microsoft.graph.directoryRole",
"id": "directoryRole-1",
"displayName": null
},
{
"#odata.type": "#microsoft.graph.directoryRole",
"id": "directoryRole-2",
"displayName": null
},
{
"#odata.type": "#microsoft.graph.group",
"id": "team-id-1",
"resourceProvisioningOptions": [
"Team"
],
"displayName": "My Team 1",
"mailNickname": "MyTeam1"
},
{
"#odata.type": "#microsoft.graph.directoryRole",
"id": "directoryRole-3",
"displayName": null
},
{
"#odata.type": "#microsoft.graph.group",
"id": "team-id-2",
"resourceProvisioningOptions": [
"Team"
],
"displayName": "My Team 2",
"mailNickname": "MyTeam2"
}
]
}
]
}
Since the user is apart of Azure directory groups, those are returned, but the resourceProvisioningOptions identifies if the user is on a team. In this case - 2 teams.
Problem
When looking in the Teams App, we see that the user is actually apart of the 10 different teams.
When calling this endpoint: https://graph.microsoft.com/v1.0/users/user-1/joinedTeams we see that the user is apart of 10 teams
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams",
"#odata.count": 10,
"value": [
{
"id": "team-id-1",
"createdDateTime": null,
"displayName": "Team 1",
"description": "Team1",
"internalId": null,
"classification": null,
"specialization": null,
"visibility": null,
"webUrl": null,
"isArchived": false,
"isMembershipLimitedToOwners": null,
"memberSettings": null,
"guestSettings": null,
"messagingSettings": null,
"funSettings": null,
"discoverySettings": null,
"summary": null
},
{
"id": "team-id-2",
"createdDateTime": null,
"displayName": "Team 2",
"description": "Team2",
"internalId": null,
"classification": null,
"specialization": null,
"visibility": null,
"webUrl": null,
"isArchived": false,
"isMembershipLimitedToOwners": null,
"memberSettings": null,
"guestSettings": null,
"messagingSettings": null,
"funSettings": null,
"discoverySettings": null,
"summary": null
},
...
{
"id": "team-id-10",
"createdDateTime": null,
"displayName": "Team 3",
"description": "Team3",
"internalId": null,
"classification": null,
"specialization": null,
"visibility": null,
"webUrl": null,
"isArchived": false,
"isMembershipLimitedToOwners": null,
"memberSettings": null,
"guestSettings": null,
"messagingSettings": null,
"funSettings": null,
"discoverySettings": null,
"summary": null
}
]
}
Questions:
Is there another endpoint that I can use to pass a list of users and get the teams they are owner/member of?
Why is the data different between endpoints
I am trying to avoid calling the /joinedTeams endpoint due to number of users and rate limiting.
You could use the /joinedTeams endpoint, but also put those calls in a batch to MS Graph.
See: https://learn.microsoft.com/en-us/graph/json-batching
This would allow you to send a batch of 20 (i think) requests in one call to the graph making it much faster and less likely to be throttled.

Beta Graph API eDiscovery Review Set Apply Tag appear to be tagging just the Group/Section tag and not the child tag

I'm am developing a power automate flow using the Beta Advanced eDiscovery Graph API which I do the following:
Create a Tag structure for an Advanced eDiscovery Case. This consists of a Section/Group (Parent) Tag named 'Privilege' the 4 child Tags. (see code below) All functions are working w/o error and appear in the eDisovery Case.
Using the Beta Graph API I create a Review Set Query. (see code below) This too is working w/o errors and appears in the eDiscovery Case Review.
Lastly, Using the Beta Graph API I apply one of the child tags associated to the review query from step 2. Again this works w/o error.
The issue is that when I open the Review in Advanced eDiscovery I see the messages that match the review query matches on with Tags, however when I view the message the child tag does not get highlighted.
If I apply a tag manually then again view the message the Tag is highlighted (selected).
Also, when in the Review Set page tag Filter only lists the Parent Tag not the child tag which I specified in the applytag Graph Apl call.
Following are Graph API calls, URI, Body, and Outputs:
Make the Group Tag
https://graph.microsoft.com/beta/compliance/ediscovery/cases/9bb82c7c-bba3-4ad2-a584-4ec30a33544b/tags
Body
{
"displayName": "Privilege",
"childSelectability": "Many"
}
Output
{
"#odata.context": "graph.microsoft.com/beta/$metadata#compliance/ediscovery/cases('9bb82c7c-bba3-4ad2-a584-4ec30a33544b')/tags/$entity",
"displayName": "Privilege",
"description": null,
"lastModifiedDateTime": "2021-11-06T21:13:59.6518126Z",
"childSelectability": "Many",
**"id": "4ab4daccb8c14ef789be7d2580bab245",**
"createdBy": {
"user": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"displayName": null,
"userPrincipalName": "xxxxxxxxxxxxx.onmicrosoft.com"
}
}
}
Make 4 child options
1.
POST //graph.microsoft.com/beta/compliance/ediscovery/cases/9bb82c7c-bba3-4ad2-a584-4ec30a33544b/tags
Body
{
"displayName": "Attorney Work Product",
"childSelectability": "Many",
"parent#odata.bind": "//graph.microsoft.com/beta/compliance/ediscovery/cases/9bb82c7c-bba3-4ad2-a584-4ec30a33544b/tags/**4ab4daccb8c14ef789be7d2580bab245**"
}
Outputs
Status Code:201
{
"#odata.context": "://graph.microsoft.com/beta/$metadata#compliance/ediscovery/cases('9bb82c7c-bba3-4ad2-a584-4ec30a33544b')/tags/$entity",
"displayName": "Attorney Work Product",
"description": null,
"lastModifiedDateTime": "2021-11-06T21:14:00.2924266Z",
"childSelectability": "Many",
"id": "6646aa1cde37408dad6be43723dd0503",
"createdBy": {
"user": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"displayName": null,
"userPrincipalName": "xxxxxxxxxxxxxxxxxxxxxx.onmicrosoft.com"
}
}
}
2.POST
//graph.microsoft.com/beta/compliance/ediscovery/cases/9bb82c7c-bba3-4ad2-a584-4ec30a33544b/tags
Body
{
"displayName": "Attorney Communication",
"childSelectability": "Many",
"parent#odata.bind": "https://graph.microsoft.com/beta/compliance/ediscovery/cases/9bb82c7c-bba3-4ad2-a584-4ec30a33544b/tags/4ab4daccb8c14ef789be7d2580bab245"
}
Outputs
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#compliance/ediscovery/cases('9bb82c7c-bba3-4ad2-a584-4ec30a33544b')/tags/$entity",
"displayName": "Attorney Communication",
"description": null,
"lastModifiedDateTime": "2021-11-06T21:14:01.1385531Z",
"childSelectability": "Many",
"id": "03a1f53540874e7085dc57bdb6e71b29",
"createdBy": {
"user": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"displayName": null,
"userPrincipalName": "xxxxxxxxxxxxxxxx.onmicrosoft.com"
}
}
}
3. POST
//graph.microsoft.com/beta/compliance/ediscovery/cases/9bb82c7c-bba3-4ad2-a584-4ec30a33544b/tags
Body
{
"displayName": "Potentially Privileged",
"childSelectability": "Many",
"parent#odata.bind": "https://graph.microsoft.com/beta/compliance/ediscovery/cases/9bb82c7c-bba3-4ad2-a584-4ec30a33544b/tags/4ab4daccb8c14ef789be7d2580bab245"
}
Outputs
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#compliance/ediscovery/cases('9bb82c7c-bba3-4ad2-a584-4ec30a33544b')/tags/$entity",
"displayName": "Potentially Privileged",
"description": null,
"lastModifiedDateTime": "2021-11-06T21:14:01.5424902Z",
"childSelectability": "Many",
"id": "dee7b721d042423a870f9fd695cff573",
"createdBy": {
"user": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"displayName": null,
"userPrincipalName": "xxxxxxxxxxxxxxxxxxxxxx.onmicrosoft.com"
}
}
}
4. Post
://graph.microsoft.com/beta/compliance/ediscovery/cases/9bb82c7c-bba3-4ad2-a584-4ec30a33544b/tags
body
{
"displayName": "Privileged",
"childSelectability": "Many",
"parent#odata.bind": "https://graph.microsoft.com/beta/compliance/ediscovery/cases/9bb82c7c-bba3-4ad2-a584-4ec30a33544b/tags/4ab4daccb8c14ef789be7d2580bab245"
}
Outputs
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#compliance/ediscovery/cases('9bb82c7c-bba3-4ad2-a584-4ec30a33544b')/tags/$entity",
"displayName": "Privileged",
"description": null,
"lastModifiedDateTime": "2021-11-06T21:14:02.0233516Z",
"childSelectability": "Many",
"id": "2bb05d142ab7455eb16a820a54f147fb",
"createdBy": {
"user": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"displayName": null,
"userPrincipalName": "xxxxxxxxxxxxxxxxxx.onmicrosoft.com"
}
}
}
enter image description here
Make Review Set Query
//graph.microsoft.com/beta/compliance/ediscovery/cases/f193df40-551d-451c-9ef8-135df2475fc2/reviewSets/921d10fa-9a16-44e5-8240-d9f9cb7d1b5f/queries
Body
{
"displayName": "Potentially Privileged",
"query": "(SenderAuthor:\"adeleV#pageman.onmicrosoft.com\" OR Recipients:\"adeleV#xxxxxxxxxxxx.onmicrosoft.com\")"
}
Outputs
Status code 201
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#compliance/ediscovery/cases('f193df40-551d-451c-9ef8-135df2475fc2')/reviewSets('921d10fa-9a16-44e5-8240-d9f9cb7d1b5f')/queries/$entity",
"displayName": "Potentially Privileged",
"query": "((SenderAuthor:\"adeleV#xxxxxxxxxxx.onmicrosoft.com\" OR Recipients:\"adeleV#xxxxxxxxx.onmicrosoft.com\"))",
"lastModifiedDateTime": "2021-11-11T21:37:38.4273704Z",
"id": "1c2670a8-7542-4168-9873-b62b3d097cc5",
"createdDateTime": "2021-11-11T21:37:38.4273704Z",
"lastModifiedBy": {
"user": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"displayName": null,
"userPrincipalName": "xxxxxxxxxxxxxxxxxxx.onmicrosoft.com"
}
},
"createdBy": {
"user": {
"id": "xxxxxxxxxxxxxxxxxxxxxx",
"displayName": null,
"userPrincipalName": "xxxxxxxxxxxxxxx.onmicrosoft.com"
}
}
}
Apply Tag
//graph.microsoft.com/beta/compliance/ediscovery/cases/f193df40-551d-451c-9ef8-135df2475fc2/reviewSets/921d10fa-9a16-44e5-8240-d9f9cb7d1b5f/queries/1c2670a8-7542-4168-9873-b62b3d097cc5/applyTags
Body
{
"tagsToAdd": [
{
"id": "e4518b76afec491b8d845f87179791f3"
}
]
}
Outputs
Status Code 202
Using the location I track the progress until 100% complete
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#compliance/ediscovery/cases('f193df40-551d-451c-9ef8-135df2475fc2')/operations/$entity",
"#odata.type": "#microsoft.graph.ediscovery.tagOperation",
"createdDateTime": "2021-11-11T21:37:41.4396791Z",
"completedDateTime": "2021-11-11T21:40:42.919Z",
"percentProgress": 100,
"status": "succeeded",
"action": "applyTags",
"id": "b1755016878c49ed875ce9246c30614d",
"createdBy": {
"user": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"displayName": null,
"userPrincipalName": "xxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
enter image description here
enter image description here
enter image description here

Retrieve Shifts Unpaid Breaks using Graph API

When you create a Shift via Microsoft Teams, there is a field for an Unpaid Break in minutes, as shown here:
Shifts UI
When this shift is retrieved via Microsoft Graph API, the Unpaid Break field is nowhere to be found.
{
"#odata.etag": "\"350013f7-0000-0400-0000-60d4e66a0000\"",
"id": "SHFT_273d6cb6-8a97-4e56-9012-41902a8904d6",
"createdDateTime": "2021-06-24T20:09:14.58Z",
"lastModifiedDateTime": "2021-06-24T20:09:14.58Z",
"schedulingGroupId": "TAG_28cc22dd-65df-4f3f-86aa-f5fcff11da80",
"userId": "fd4be5ff-ceeb-4f6c-8331-4324573d37f8",
"draftShift": null,
"lastModifiedBy": {
"application": null,
"device": null,
"user": {
"id": "fd4be5ff-ceeb-4f6c-8331-4324573d37f8",
"displayName": "",
"userIdentityType": "aadUser"
}
},
"sharedShift": {
"displayName": "Sleep Shift",
"notes": null,
"startDateTime": "2021-06-15T12:00:00Z",
"endDateTime": "2021-06-15T21:00:00Z",
"theme": "blue",
"activities": []
}
}
I've tried the /teams/{teamId}/schedule/shifts and /teams/{teamId}/schedule/timesOff endpoints.
How do you retrieve the Unpaid Break value via Microsoft Graph? Is there a different endpoint I should be using?
Currently, there is no way to get 'Unpaid Break field' in response using Graph API. This will only be available in UI.

Remove the certain properties of objects from the json document response Swagger for REST api

Hi I am using swagger for documentation of my RESTful web service. wanted to know is there any way to remove the certain properties of objects from the json document response? I mean there are lots of properties that swagger gives for my method param objects and response model (e.g. notes, defaultValue, allowableValue, internalDescription etc.) that are not required for me and are null due to that the response is not much readable
For method params:
"parameters": [
{
"name": "someName1",
"description": null,
"notes": null,
"paramType": "path",
"defaultValue": null,
"allowableValues": null,
"required": true,
"allowMultiple": false,
"paramAccess": null,
"internalDescription": null,
"wrapperName": null,
"dataType": "string",
"valueTypeInternal": null
},
{
"name": "someName2",
"description": null,
"notes": null,
"paramType": "query",
"defaultValue": null,
"allowableValues": null,
"required": true,
"allowMultiple": false,
"paramAccess": null,
"internalDescription": null,
"wrapperName": null,
"dataType": "string",
"valueTypeInternal": null
}
],
-=============================================================================
For response model classes
"SomeResponseClass": {
"required": false,
"name": null,
"id": "SomeResponseClass",
"properties": {
"instanceVariable1": {
"required": false,
"name": null,
"id": null,
"properties": null,
"allowableValues": null,
"description": null,
"notes": null,
"access": null,
"default": null,
"additionalProperties": null,
"items": null,
"uniqueItems": false,
"type": "Date"
},
"instanceVariable2": {
"required": false,
"name": null,
"id": null,
"properties": null,
"allowableValues": null,
"description": null,
"notes": null,
"access": null,
"default": null,
"additionalProperties": null,
"items": null,
"uniqueItems": false,
"type": "double"
}
}
your JSON mapper is not configured to ignore null properties. You can easily address this as follows:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JacksonJsonProvider extends JacksonJaxbJsonProvider {
private static ObjectMapper commonMapper = null;
public JacksonJsonProvider() {
if(commonMapper == null){
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
commonMapper = mapper;
}
super.setMapper(commonMapper);
}
}
Add this mapper to your scanning properties in the web.xml and the nulls will be gone.

Resources