Skip to content

Instantly share code, notes, and snippets.

@adeelnasir
Last active August 26, 2021 00:14
Show Gist options
  • Save adeelnasir/6875c89b159245d57ae8619777d83150 to your computer and use it in GitHub Desktop.
Save adeelnasir/6875c89b159245d57ae8619777d83150 to your computer and use it in GitHub Desktop.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.Azure;
using Microsoft.WindowsAzure.Storage.Auth;
using B2CGraph.Library.CodeBits;
using System.Text.RegularExpressions;
using B2CGraph.Library;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace B2CGraphShell
{
public class Program
{
private static string tenant = CloudConfigurationManager.GetSetting("b2c:Tenant");
private static string clientId = CloudConfigurationManager.GetSetting("b2c:ClientId");
private static string clientSecret = CloudConfigurationManager.GetSetting("b2c:ClientSecret");
private static string iefGraphAppId = CloudConfigurationManager.GetSetting("b2c:IEFGraphAppId");
private static string customAttribute1 = CloudConfigurationManager.GetSetting("b2c:common:CustomAttribute1");
private static string customAttribute2 = CloudConfigurationManager.GetSetting("b2c:common:CustomAttribute2");
private static string customAttribute3 = CloudConfigurationManager.GetSetting("b2c:common:CustomAttribute3");
private static string customAttribute3Value = CloudConfigurationManager.GetSetting("b2c:common:CustomAttribute3Value");
private static string storageAccountName = CloudConfigurationManager.GetSetting("azure:StorageAccountName");
private static string storageAccessKey = CloudConfigurationManager.GetSetting("azure:StorageAccessKey");
private static string inputFileName = CloudConfigurationManager.GetSetting("azure:InputFileName");
private static string logFileName = CloudConfigurationManager.GetSetting("azure:LogFileName");
private static string outputFileName = CloudConfigurationManager.GetSetting("azure:OutputFileName");
private static string passwordFileName = CloudConfigurationManager.GetSetting("azure:PasswordFileName");
private static string exportPasswords = CloudConfigurationManager.GetSetting("azure:ExportPasswords");
private static string containerName = CloudConfigurationManager.GetSetting("azure:common:ContainerName");
private static B2CGraphClient client = null;
private static ConsoleColor init = ConsoleColor.White;
static void Main(string[] args)
{
init = Console.ForegroundColor;
if (args.Length <= 0)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Please enter a command as the first argument.");
Console.ForegroundColor = init;
return;
}
else
{
try
{
client = new B2CGraphClient(clientId, clientSecret, tenant);
if (client != null)
{
try
{
switch (args[0].ToUpper())
{
case "IMPORT-USERS":
ImportUsers();
break;
default:
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Invalid command.");
break;
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
var innerException = ex.InnerException;
if (innerException != null)
{
while (innerException != null)
{
Console.WriteLine(innerException.Message);
innerException = innerException.InnerException;
}
}
else
{
Console.WriteLine(ex.Message);
}
}
finally
{
Console.ForegroundColor = init;
}
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
var innerException = ex.InnerException;
if (innerException != null)
{
while (innerException != null)
{
Console.WriteLine(innerException.Message);
innerException = innerException.InnerException;
}
}
else
{
Console.WriteLine(ex.Message);
}
}
}
}
private static dynamic GetExistingUsers(string filter)
{
string result;
result = client.GetAllUsers(filter).Result;
dynamic dynJson = JsonConvert.DeserializeObject(result);
return dynJson;
}
private static async Task<dynamic> GetExistingUsers(string filter, List<string> objectIds)
{
string result;
result = await client.GetAllUsers(filter);
dynamic dynJson = JsonConvert.DeserializeObject(result);
JObject originalObject = JObject.Parse(result);
objectIds.AddRange(originalObject.Descendants()
.OfType<JProperty>()
.Where(p => p.Name == "objectId")
.Select(x => x.Value.ToString())
.ToList());
return dynJson;
}
private static void ImportUsers()
{
Console.WriteLine("Starting to import users");
StringBuilder logString = new StringBuilder();
StringBuilder outputString = new StringBuilder();
StringBuilder passwordString = new StringBuilder();
// Create storage account creds object.
StorageCredentials storageCredentials = new StorageCredentials(storageAccountName, storageAccessKey);
// Create storage account object.
CloudStorageAccount storageAccount = new CloudStorageAccount(storageCredentials, true);
// Create the blob client.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Retrieve reference to the bulkload container.
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
// Retrieve reference to the input blob
CloudBlockBlob inputBlockBlob = container.GetBlockBlobReference(inputFileName);
string[] users;
var timeStamp = DateTime.Now.ToString("ddMMyyyyhhmmss");
using (var memoryStream = new MemoryStream())
{
Console.WriteLine("Downloading the users to memory");
inputBlockBlob.DownloadToStream(memoryStream);
users = Encoding.UTF8.GetString(memoryStream.ToArray()).Split(new[] { "\r\n", "\r", "\n" },
StringSplitOptions.None
);
Console.WriteLine("Downloaded the users to memory");
}
Console.WriteLine(@"Read input file with " + users.Length + " users");
Parallel.ForEach(users,
new ParallelOptions { MaxDegreeOfParallelism = 10 },
(user) =>
{
if (user.Length > 0)
{
string userId = "";
try
{
var userAttributes = user.Split(',');
userId = userAttributes.Length > 0 ? userAttributes[0].Trim() : "";
string email = userAttributes.Length > 1 ? userAttributes[1].Trim() : "";
string phone = userAttributes.Length > 2 ? userAttributes[2].Trim().Replace(" ", "") : "";
var contactIdExtensionProperty = "extension_" + iefGraphAppId + "_" + customAttribute1;
var filter = $"$filter=" + contactIdExtensionProperty + " eq '" + userId + "' and accountEnabled eq false";
dynamic dynJson = GetExistingUsers(filter);
bool userExists = false;
foreach (var existingUser in dynJson.value)
{
userExists = true;
break;
}
if (!userExists)
{
Console.WriteLine(@"Generating password...");
//https://docs.microsoft.com/en-us/azure/active-directory/active-directory-passwords-policy
const int requiredLength = 10;
var allowedCharacters = PasswordCharacters.UppercaseLetters | PasswordCharacters.LowercaseLetters | PasswordCharacters.Numbers;
PasswordCharacters[] requiredCharacters = { PasswordCharacters.UppercaseLetters, PasswordCharacters.LowercaseLetters, PasswordCharacters.Numbers, PasswordCharacters.Punctuations, PasswordCharacters.Punctuations };
//generate a new password
var newPassword = PasswordGenerator.Generate(requiredLength, allowedCharacters, requiredCharacters, ".".ToArray());
var country = phone.StartsWith("+") ? null : "AU";
var jsonObject = new JObject
{
{"accountEnabled", true},
{"creationType", "LocalAccount"},
{"extension_" + iefGraphAppId + "_" + customAttribute1, userId},
{"displayName", email},
{"extension_" + iefGraphAppId + "_"+customAttribute2, phone},
{"extension_" + iefGraphAppId + "_"+customAttribute3, customAttribute3Value},
{"passwordPolicies", "DisablePasswordExpiration"},
{"country", country},
{"passwordProfile", new JObject
{
{"password", newPassword},
{"forceChangePasswordNextLogin", false}
}
},
{"signInNames", new JArray
{
new JObject
{
{"value", email},
{"type", "emailAddress"}
}
}
}
};
string json = JsonConvert.SerializeObject(jsonObject);
object formatted = JsonConvert.DeserializeObject(client.CreateUser(json).Result);
var jsonString = JsonConvert.SerializeObject(formatted, Formatting.Indented);
var objectId = JObject.Parse(jsonString)["objectId"].ToString();
Console.ForegroundColor = ConsoleColor.White;
//append a line to the log string for log file
logString.AppendLine(timeStamp + ": User with Contact ID: " + userId + " imported successfully.");
outputString.AppendLine(userId + "," + objectId);
//if password needs to be exported then create a line of email,password for the password file
if (exportPasswords == "yes")
{
passwordString.AppendLine(email + "," + newPassword);
}
Console.WriteLine(@"User created successfully...");
}
else
{
logString.AppendLine(timeStamp + ": User with Contact ID: " + userId + " already exists");
Console.WriteLine(@"User already exists...");
}
}
catch (Exception ex)
{
var logMsg = "";
var consoleMsg = "";
var innerException = ex.InnerException;
if (innerException != null)
{
while (innerException != null)
{
logMsg = timeStamp + ": Contact ID: " + userId + " isnt imported due to the error:" + innerException.Message;
consoleMsg = "Error while creating user..." + innerException.Message;
innerException = innerException.InnerException;
}
}
else
{
logMsg = timeStamp + ": Contact ID: " + userId + " isnt imported due to the error:" + ex.Message;
consoleMsg = "Error while creating user..." + ex.Message;
}
logString.AppendLine(logMsg);
Console.WriteLine(consoleMsg);
}
}
});
//upload the log file
UploadFile(container, logFileName, logString.ToString(), "log");
//upload the output file
UploadFile(container, outputFileName, outputString.ToString(), "output");
//if password needs to be exported then upload the password file
if (exportPasswords == "yes")
{
UploadFile(container, passwordFileName, passwordString.ToString(), "password");
}
// Delete the input file from input blob storage container as well as working directory.
inputBlockBlob.Delete();
Console.WriteLine(@"Deleted the input file after processing successfully");
}
private static void UploadFile(CloudBlobContainer container, string fileName, string strToWrite, string type)
{
// Retrieve reference to the blob
CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName.Replace("timestamp", DateTime.Now.ToString("ddMMyyyyhhmmss")));
//get bytes from the string
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(strToWrite));
//write to the log folder
blockBlob.UploadFromStream(ms);
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(@"Uploaded the " + type + " file successfully");
}
}
}
@dipakfatania
Copy link

Hi, Thank you for sharing great code to import bulk users. Can you please share the entire project? I am getting some problem to get this assembly B2CGraph.Library

@adeelnasir
Copy link
Author

Hi dipakfatania,

You can remove these lines of code that are referring to the library project. You wont need them.

if (phone.Length > 0)
phone = phone.ValidateMobileNumber();

{"extension_" + iefGraphAppId + "_"+customAttribute4, phone.MaskMobileNumber()},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment