Azure B2C Active Directory: Update one property on all User - azure-active-directory

In my current project I'm using Microsofts Azure B2C Actice Directory.
My plan is to update a speciffic property (testClaim) of every single user.
What I'm actually doing ist loading all the users in my AD and updating each of them in an foreach-loop.
var requestBody = new SetTestClaimRequest
{
ClaimName = "testClaim",
Value = "thisIsATestValue"
};
var client = new RestClient("myRes");
var request = new RestRequest(Method.PUT);
request.AddJsonBody(requestBody);
The problem I'm facing is, that the GraphApi begins to block my requests, after just a few, and just answering with the following error:
Error Calling the Graph API:
{
"odata.error": {
"code": "Request_ThrottledTemporarily",
"message": {
"lang": "en",
"value": "Your request is throttled temporarily. Please try after 150 seconds."
},
"requestId": "ccf8a936-490e-4c4a-87aa-125157b2e6dd",
"date": "2020-04-17T12:37:44"
}
}
Is there a way to avoid this without throttling my request?
In my opinion throttling isn't a choice cause it would take multiple hours to update the amount of users im dealing with.

No, there is no way to bypass throttling limits. It may take some hours to process at the accepted rate. Try 1000 ops per minute maximum. Make sure to implement back off logic if you get a HTTP 429.

Related

Journey builder's custom activity: Fetch data extension data in bulk

I am new to Salesforce Marketing Cloud and journey builder.
https://developer.salesforce.com/docs/marketing/marketing-cloud/guide/creating-activities.html
We are building journey builder's custom activity in which it will use a data extension as the source and when the journey builder is invoked, it will fetch a row and send this data to our company's internal endpoint. The team got that part working. We are using the postmonger.js.
I have a couple of questions:
Is there a way to retrieve the data from data extension in bulk so that we can call our company's internal bulk endpoint? Calling the endpoint for each record in the data extension for our use case would not be efficient enough and won't work.
When the journey is invoked and an entry in the data extension is retrieved and that data is sent to our internal endpoint, is there a machanism to mark this entry as already sent such that next time the journey is run, it won't process the entry that's already sent?
Here is a snippet of our customActivity.js in which this is populating one record. (I changed some variable names.). Is there a way to populate multiple records such that when "execute" is called, it is passing a list of payloads as input to our internal endpoint.
function save() {
try {
var TemplateNameValue = $('#TemplateName').val();
var TemplateIDValue = $('#TemplateID').val();
let auth = "{{Contact.Attribute.Authorization.Value}}"
payload['arguments'].execute.inArguments = [{
"vendorTemplateId": TemplateIDValue,
"field1": "{{Contact.Attribute.DD.field1}}",
"eventType": TemplateNameValue,
"field2": "{{Contact.Attribute.DD.field2}}",
"field3": "{{Contact.Attribute.DD.field3}}",
"field4": "{{Contact.Attribute.DD.field4}}",
"field5": "{{Contact.Attribute.DD.field5}}",
"field6": "{{Contact.Attribute.DD.field6}}",
"field7": "{{Contact.Attribute.DD.field7}}",
"messageMetadata" : {}
}];
payload['arguments'].execute.headers = `{"Authorization":"${auth}"}`;
payload['configurationArguments'].stop.headers = `{"Authorization":"default"}`;
payload['configurationArguments'].validate.headers = `{"Authorization":"default"}`;
payload['configurationArguments'].publish.headers = `{"Authorization":"default"}`;
payload['configurationArguments'].save.headers = `{"Authorization":"default"}`;
payload['metaData'].isConfigured = true;
console.log(payload);
connection.trigger('updateActivity', payload);
} catch(err) {
document.getElementById("error").style.display = "block";
document.getElementById("error").innerHtml = err;
}
console.log("Template Name: " + JSON.stringify(TemplateNameValue));
console.log("Template ID: " + JSON.stringify(TemplateIDValue));
}
});
Any advise or idea is highly appreciated!
Thank you.
Grace
Firstly, i implore you to not proceed with the design pattern of fetching data for each subscriber, from Marketing Cloud, that gets sent through the custom activity, for arguments sake i'll list two big issues.
You have no way of limiting the configuration of data extensions columns or column names in SFMC (Salesforce Marketing Cloud). If any malicious user or by human error would delete a column or change a column name your service would stop receiving that value.
Secondly, Marketing Cloud has 2 sets of API limitations, yearly and minute by minute. Depending on your licensing, you could run into the yearly limit.
The problem you have with limitation on minutes (2500 for REST and 2000 for SOAP) is that each usage of the custom activity in journey builder would multiple the amount of invocations per minute. Hitting this limit would cause issues for incremental data flows into SFMC.
I'd also suggest not retrieving any data from Marketing Cloud when a customer gets sent through a custom activity. Users should pick which corresponding rows/data that should be sent to the custom activity in their segmentation.
The eventDefinitionKey can be picked up from postmonger after requestedTriggerEventDefinition in the eventDefinitionModel function. eventDefinitionKey can then be used to programmatically populate SFMC's POST call with data from the Journey Data model, thus allowing marketers to select what data to be sent with the subscriber.
Following is some code to show how it would work in your customActivity.js
connection.on(
'requestedTriggerEventDefinition',
function (eventDefinitionModel) {
var eventKey = eventDefinitionModel['eventDefinitionKey'];
save(eventKey);
}
);
function save(eventKey) {
// subscriberKey fetched directly from Contact model
// columnName is populated from the Journey Data model
var params = {
subscriberKey: '{{Contact.key}}',
columnName: '{{Event.' + eventKey + '.columnName}}',
};
payload['arguments'].execute.inArguments = [params];
}

Updating Skills property of User through MS Graph API fails

I'm trying to update the "Skills" property of a user (note: NOT using the Me endpoint). This returns an exception. I'm using the .NET SDK GraphServiceClient to do so, using the latest 3.6.0 version.
To Reproduce
Steps to reproduce the behavior, use the following sample code:
var user = new User()
{
Skills = skills
};
await graphClient.Users[userId].Request().UpdateAsync(user);
Expected behavior
I'd expect this code to send a POST request to the Graph API endpoint and that request should update the Skills property of the given used, provided that the authenticated service has been granted the User.ReadWrite.All permission in Azure AD (which it has).
Instead, the following error is being returned by the Graph endpoint:
"error": {
"code": "-1, Microsoft.SharePoint.Client.InvalidClientQueryException",
"message": "A type named 'Microsoft.SharePoint.user' could not be resolved by the model. When a model is available, each type name must resolve to a valid type.",
"innerError": {
"request-id": "1948a4ec-60fd-4212-873f-3d34f62f5601",
"date": "2020-05-25T09:35:33"
}
}
}
Not sure why this would not work. I followed the samples although those do not specifically mention updating the Skills property, you'd expect that to work equally for all properties. When I omit the property from the updated User object, I do not get an error in return (but obviously there's nothing updated in that case).
Till it is fixed in v1 as mentioned by baywet you can also use the following as work around for your scenario.
Graph client by default adds odata.type as microsoft.graph.user and sharepoint doesn't expect that value. You could change the oDataType to "https://graph.microsoft.com/v1.0/$metadata#users/$entity" or "Microsoft.SharePoint.user" or null, so that sharepoint can understand the type.
You can set the oDatatype by
var user = new User()
{
Skills = skills
};
user.ODataType = "https://graph.microsoft.com/v1.0/$metadata#users/$entity";
await graphClient.Users[userId].Request().UpdateAsync(user);
This is an ongoing issue that is being addressed in the service. The beta version should already be fixed and the v1 is in progress.
As a workaround you can either use the beta version or craft the request manually without an odata type.

Graph API (Http Status : 412 Precondition Failed - ErrorIrresolvableConflict)

I am using Microsoft Graph to create Calendar Events using Application Credentials where the organizer's email id will be used:
https://graph.microsoft.com/v1.0/users/<organizer_email_id>/calendar/events
Before the Create Event, I am issuing a PATCH to update the organizer's displayName, givenName and surname:
PATCH https://graph.microsoft.com/v1.0/users/{id}
I see the Event gets created, but it is sending the mail with old name and throwing the following error:
HTTP Status code : 412 Precondition Failed.
{
"error": {
"code": "ErrorIrresolvableConflict",
"message": "The send or update operation could not be performed because the change key passed in the request does not match the current change key for the item.",
"innerError": {
"request-id": "a36e60a4-0a18-4574-9f7f-75f6c1cce8b4",
"date": "2020-01-05T14:22:54"
}
}
}
It looks like the Event is reaching before the patch request is commited. I don't want to put any delay between two calls but the only option is before creating the event, fire a get request to confirm that name changed. Is there any other workaround or Microsoft needs to fix the bug if there is any?
It seems to be an old known issue of Exchange.
Usually such issues need to be confirmed by Microsoft engineers.
I believe the most effective way now is to contact the support team and attach your request-id for investigation.

How to update the users birthday

I want to update the birthday of a user using the patch request.
Updating other properties works as expected but the moment the birthday property is included, the following error returned:
The request is currently not supported on the targeted entity set
I already tried to update the user to be sure the permissions are fine.
Application permissions are used.
This PATCH request to /V1.0/users/{id} works:
{
"givenName": "Fridas"
}
Passing this request body however:
{
"givenName":"Fridas",
"birthday" : "2014-01-01T00:00:00Z
}
throws an error
{
"error":
{
"code":"BadRequest",
"message":"The request is currently not supported on the targeted entity set",
"innerError":
{
"request-id":"5f0d36d1-0bff-437b-9dc8-5579a7ec6e72",
"date":"2019-08-13T15:27:40"
}
}
}
When I update the birthday separately, I get a 500 error. Print screens below. Updating the user id works fine, birthday does not.
Same user id is used in the request.
I'm not sure why this happens, but a workaround, albeit an annoying one, is to update birthday separately from other attributes.
E.g.
PATCH https://graph.microsoft.com/v1.0/users/userid
{
"birthday" : "2014-01-01T00:00:00Z"
}
Here is a screenshot from MS Graph Explorer:
In fact, this is a limitation in the current system.
User is a composite type. Under the covers some properties in user are mastered by different services, and we currently don't support updates across multiple services.
"birthday" is not mastered by Azure AD. So we can't update it with other properties mastered by Azure AD in the same call.
It is strongly recommended that you update this property separately. I can update it from my side. So you need a backend engineer to track this request for you.
This seems to affect more than Birthday.
Skills[] and Responsibilities[] are also returning 500 Internal Server Error when using PATCH request via REST API with:
{"skills": ["TESTING", "ANOTHER SKILL"]}
Same happens via the GraphServiceClient - except the result is:
Failed to call the Web Api: InternalServerError
Content: {
"error": {"code": "-1, Microsoft.Office.Server.Directory.DirectoryObjectUnauthorizedAccessException",
"message": "Attempted to perform an unauthorized operation.",
"innerError": {
"request-id": "1c2ccc54-0a0c-468f-a18c-6bdfbad4077d",
"date": "2019-08-28T13:23:55"
}}}
These requests work on the Graph Explorer page, but not via calls to the API.

Azure Search service set up

Hello I am trying to create a new azure search service with S2 or S3 tier.
However, as I try to create, I am getting validation error:
Any idea how to resolve this?
below are raw error details:
{
"telemetryId": "127e12a7-2de5-420d-8ce3-181a5e663337",
"bladeInstanceId": "Blade_f10d818ac9854758a2308220f06e7274_5_0",
"galleryItemId": "Microsoft.Search",
"createBlade": "CreateBladeV3",
"code": "InvalidTemplateDeployment",
"message": "The template deployment 'Microsoft.Search' is not valid according to the validation procedure. The tracking id is 'd2a97835-fd96-4b15-95f9-416e19086f31'. See inner errors for details. Please see https://aka.ms/arm-deploy for usage details.",
"details": [
{
"code": "ServiceQuotaExceeded",
"message": "Operation would exceed standard3 service quota"
}
]
}
It looks to me like either the subscription you are using doesn't allow for paid services or you are trying to create an index in the service via import that is too large. However, S2 and S3 allow quite large indexes, so my guess is the former.

Resources