private void GetExternalUsers(Office365Tenant tenant) { GetExternalUsersResults results = null; if (!string.IsNullOrEmpty(SiteUrl)) { results = tenant.GetExternalUsersForSite(SiteUrl, Position, PageSize, Filter, SortOrder); } else { results = tenant.GetExternalUsers(Position, PageSize, Filter, SortOrder); } ClientContext.Load(results, r => r.TotalUserCount, r => r.UserCollectionPosition, r => r.ExternalUserCollection.Include(u => u.DisplayName, u => u.InvitedAs, u => u.UniqueId, u => u.AcceptedAs, u => u.WhenCreated, u => u.InvitedBy)); ClientContext.ExecuteQueryRetry(); foreach (var externalUser in results.ExternalUserCollection) { if (!ShowOnlyUsersWithAcceptingAccountNotMatchInvitedAccount) { WriteObject(externalUser); } else if (!string.Equals(externalUser.InvitedAs, externalUser.AcceptedAs, System.StringComparison.OrdinalIgnoreCase)) { WriteObject(externalUser); } } }
public ActionResult CreateDefaultOrigins() { try { IList <string> publicCdnOrigins = new List <string>(); using (var clientContext = GetClientContext()) { var tenant = new Office365Tenant(clientContext); tenant.CreateTenantCdnDefaultOrigins(SPOTenantCdnType.Public); publicCdnOrigins = tenant.GetTenantCdnOrigins(SPOTenantCdnType.Public); clientContext.ExecuteQuery(); } return(Json(publicCdnOrigins)); } catch (Exception ex) { Response.StatusCode = (int)HttpStatusCode.InternalServerError; return(Json(ex.Message)); } }
public ActionResult SetCDN(bool value) { bool CDNEnabled = false; try { using (var clientContext = GetClientContext()) { var tenant = new Office365Tenant(clientContext); tenant.SetTenantCdnEnabled(SPOTenantCdnType.Public, value); var publicCDNEnabled = tenant.GetTenantCdnEnabled(SPOTenantCdnType.Public); clientContext.ExecuteQuery(); CDNEnabled = publicCDNEnabled.Value; return(Json(CDNEnabled)); } } catch (Exception ex) { Response.StatusCode = (int)HttpStatusCode.InternalServerError; return(Json(ex.Message)); } }
public ActionResult SetFiletypes(List <string> filetypes) { try { string cdnFileTypes; using (var clientContext = GetClientContext()) { var tenant = new Office365Tenant(clientContext); var newFileTypes = string.Join(",", filetypes); tenant.SetTenantCdnPolicy(SPOTenantCdnType.Public, SPOTenantCdnPolicyType.IncludeFileExtensions, newFileTypes); var publicCDNPolicies = tenant.GetTenantCdnPolicies(SPOTenantCdnType.Public); clientContext.ExecuteQuery(); cdnFileTypes = publicCDNPolicies.Where(s => s.StartsWith(SPOTenantCdnPolicyType.IncludeFileExtensions.ToString())).First(); } return(Json(ConvertToList(cdnFileTypes))); } catch (Exception ex) { Response.StatusCode = (int)HttpStatusCode.InternalServerError; return(Json(ex.Message)); } }
/// <summary> /// Demonstrates how to check status for specific submission or for all submissions /// </summary> /// <param name="ctx"></param> /// <param name="workItemId"></param> private static void CheckStatusOfRequestedProcess(ClientContext ctx, Guid workItemId) { /// /// CHECK STATUS of the property job with GUID - notice that there's additional logs in the folder as well /// // Check status of specific request based on received GUID Office365Tenant tenant = new Office365Tenant(ctx); var job = tenant.GetImportProfilePropertyJob(workItemId); ctx.Load(job); ctx.ExecuteQuery(); Console.Write("\n--\n"); Console.WriteLine(string.Format("ID: {0} - Request status: {1} - Error status: {2}", job.JobId, job.State.ToString(), job.Error.ToString())); Console.Write("\n--\n"); /// /// Get list of all jobs in the tenant /// var jobs = tenant.GetImportProfilePropertyJobs(); ctx.Load(jobs); ctx.ExecuteQuery(); foreach (var item in jobs) { Console.WriteLine(string.Format("ID: {0} - Request status: {1} - Error status: {2}", item.JobId, item.State.ToString(), item.Error.ToString())); } }
// from https://msdn.microsoft.com/en-us/library/office/microsoft.online.sharepoint.tenantmanagement.aspx public override EventResult <ClientResult <Guid> > Execute() { EventResult <ClientResult <Guid> > result = new EventResult <ClientResult <Guid> >(); string filePath = CreatetImportValueFile(); Office365Tenant tenant = new Office365Tenant(Param.Util.ProjContext); IDictionary <string, string> dic = new Dictionary <string, string>(); dic.Add("BirthPlace", "brthPlace"); dic.Add("GraduateDate", "grdDate"); string sourceDataIdProperty = "IdName"; string sourceUri = UploadJsonFile(Param, filePath); // 先行リリーステナントでないとつかえない?https://github.com/SharePoint/PnP/issues/1400 Param.Util.ProjContext.Load(tenant); Param.Util.ProjContext.ExecuteQuery(); result.Result = tenant.QueueImportProfileProperties(ImportProfilePropertiesUserIdType.Email, sourceDataIdProperty, dic, sourceUri); Param.Util.ProjContext.ExecuteQuery(); if (result.Result.Value.Equals(Guid.Empty)) { MessageBox.Show("先行リリースではないので使えないみたいです。"); } System.IO.File.Delete(filePath); return(result); }
protected override void ExecuteCmdlet() { var o365 = new Office365Tenant(ClientContext); if (ParameterSpecified(nameof(JobId))) { var job = o365.GetImportProfilePropertyJob(JobId.Id); ClientContext.Load(job); ClientContext.ExecuteQueryRetry(); GetErrorInfo(job); WriteObject(job); } else { ImportProfilePropertiesJobStatusCollection jobs = o365.GetImportProfilePropertyJobs(); ClientContext.Load(jobs); ClientContext.ExecuteQueryRetry(); foreach (var job in jobs) { GetErrorInfo(job); } WriteObject(jobs); } }
protected override void ExecuteCmdlet() { var office365Tenant = new Office365Tenant(ClientContext); ClientContext.Load(office365Tenant); ClientContext.ExecuteQueryRetry(); GetExternalUsers(office365Tenant); }
/// <summary> /// Returns a list all external users for a given site that have at least the viewpages permission /// </summary> /// <param name="web">Tenant administration web</param> /// <param name="siteUrl">Url of the site fetch the external users for</param> /// <returns>A list of <see cref="OfficeDevPnP.Core.Entities.ExternalUserEntity"/> objects</returns> public static List <ExternalUserEntity> GetExternalUsersForSiteTenant(this Web web, Uri siteUrl) { if (siteUrl == null) { throw new ArgumentNullException("siteUrl"); } Tenant tenantAdmin = new Tenant(web.Context); Office365Tenant tenant = new Office365Tenant(web.Context); Site site = tenantAdmin.GetSiteByUrl(siteUrl.OriginalString); web = site.RootWeb; List <ExternalUserEntity> externalUsers = new List <ExternalUserEntity>(); int pageSize = 50; int position = 0; GetExternalUsersResults results = null; while (true) { results = tenant.GetExternalUsersForSite(siteUrl.OriginalString, position, pageSize, string.Empty, SortOrder.Ascending); web.Context.Load(results, r => r.UserCollectionPosition, r => r.TotalUserCount, r => r.ExternalUserCollection); web.Context.ExecuteQuery(); foreach (var externalUser in results.ExternalUserCollection) { User user = web.SiteUsers.GetByEmail(externalUser.AcceptedAs); web.Context.Load(user); web.Context.ExecuteQuery(); var permission = web.GetUserEffectivePermissions(user.LoginName); web.Context.ExecuteQuery(); var doesUserHavePermission = permission.Value.Has(PermissionKind.ViewPages); if (doesUserHavePermission) { externalUsers.Add(new ExternalUserEntity() { DisplayName = externalUser.DisplayName, AcceptedAs = externalUser.AcceptedAs, InvitedAs = externalUser.InvitedAs, InvitedBy = externalUser.InvitedBy, UniqueId = externalUser.UniqueId, WhenCreated = externalUser.WhenCreated }); } } position = results.UserCollectionPosition; if (position == -1 || position == results.TotalUserCount) { break; } } return(externalUsers); }
protected override void ExecuteCmdlet() { if (string.IsNullOrWhiteSpace(IdProperty)) { throw new InvalidEnumArgumentException(@"IdProperty cannot be empty"); } switch (ParameterSetName) { case ParameterSet_UPLOADFILE: if (string.IsNullOrWhiteSpace(Path)) { throw new InvalidEnumArgumentException(@"Path cannot be empty"); } var webCtx = ClientContext.Clone(PnPConnection.Current.Url); var web = webCtx.Web; var webServerRelativeUrl = web.EnsureProperty(w => w.ServerRelativeUrl); if (!Folder.ToLower().StartsWith(webServerRelativeUrl)) { Folder = UrlUtility.Combine(webServerRelativeUrl, Folder); } if (!web.DoesFolderExists(Folder)) { throw new InvalidOperationException($"Folder {Folder} does not exist"); } var folder = web.GetFolderByServerRelativeUrl(Folder); var fileName = System.IO.Path.GetFileName(Path); File file = folder.UploadFile(fileName, Path, true); Url = new Uri(webCtx.Url).GetLeftPart(UriPartial.Authority) + file.ServerRelativeUrl; break; case ParameterSet_URL: if (string.IsNullOrWhiteSpace(Url)) { throw new InvalidEnumArgumentException(@"Url cannot be empty"); } break; } var o365 = new Office365Tenant(ClientContext); var propDictionary = UserProfilePropertyMapping.Cast <DictionaryEntry>().ToDictionary(kvp => (string)kvp.Key, kvp => (string)kvp.Value); var id = o365.QueueImportProfileProperties(IdType, IdProperty, propDictionary, Url); ClientContext.ExecuteQueryRetry(); var job = o365.GetImportProfilePropertyJob(id.Value); ClientContext.Load(job); ClientContext.ExecuteQueryRetry(); WriteObject(job); }
protected override void ExecuteCmdlet() { if (!string.IsNullOrEmpty(Url)) { var siteProperties = Tenant.GetSitePropertiesByUrl(Url, Detailed); ClientContext.Load(siteProperties); ClientContext.ExecuteQueryRetry(); Model.SPOSite site = null; if (ParameterSpecified(nameof(DisableSharingForNonOwnersStatus))) { var office365Tenant = new Office365Tenant(ClientContext); var clientResult = office365Tenant.IsSharingDisabledForNonOwnersOfSite(Url); ClientContext.ExecuteQuery(); site = new Model.SPOSite(siteProperties, clientResult.Value); } else { site = new Model.SPOSite(siteProperties, null); } WriteObject(site, true); } else { SPOSitePropertiesEnumerableFilter filter = new SPOSitePropertiesEnumerableFilter() { IncludePersonalSite = IncludeOneDriveSites.IsPresent ? PersonalSiteFilter.Include : PersonalSiteFilter.UseServerDefault, IncludeDetail = Detailed, #pragma warning disable CS0618 // Type or member is obsolete Template = Template, #pragma warning restore CS0618 // Type or member is obsolete Filter = Filter, }; SPOSitePropertiesEnumerable sitesList = null; var sites = new List <SiteProperties>(); do { sitesList = Tenant.GetSitePropertiesFromSharePointByFilters(filter); Tenant.Context.Load(sitesList); Tenant.Context.ExecuteQueryRetry(); sites.AddRange(sitesList.ToList()); filter.StartIndex = sitesList.NextStartIndexFromSharePoint; } while (!string.IsNullOrWhiteSpace(sitesList.NextStartIndexFromSharePoint)); if (Template != null) { WriteObject(sites.Where(t => t.Template == Template).OrderBy(x => x.Url), true); } else { WriteObject(sites.OrderBy(x => x.Url), true); } } }
/// <summary> /// Query the Tenant UPS based on Site Collection /// </summary> /// <param name="siteUrl"></param> /// <param name="invitedAs"></param> /// <returns></returns> public static List <SPExternalUserEntity> CheckExternalUserForSite(this ClientContext adminContext, ITraceLogger logger, string siteUrl, string invitedAs = "") { if (siteUrl == null) { throw new ArgumentNullException("siteUrl"); } var externalUsers = new List <SPExternalUserEntity>(); int pageSize = 50; int position = 0; GetExternalUsersResults results = null; var officeTenantContext = new Office365Tenant(adminContext); while (true) { logger.LogInformation($"Checking External User with {invitedAs} at start {position} and page size {pageSize}"); results = officeTenantContext.GetExternalUsersForSite(siteUrl, position, pageSize, invitedAs, SortOrder.Ascending); adminContext.Load(results, r => r.UserCollectionPosition, r => r.TotalUserCount, r => r.ExternalUserCollection); adminContext.ExecuteQueryRetry(); foreach (ExternalUser externalUser in results.ExternalUserCollection) { externalUsers.Add(new SPExternalUserEntity() { AcceptedAs = externalUser.AcceptedAs, DisplayName = externalUser.DisplayName, InvitedAs = externalUser.InvitedAs, InvitedBy = externalUser.InvitedBy, UniqueId = externalUser.UniqueId, UserId = externalUser.UserId, WhenCreated = externalUser.WhenCreated }); } position = results.UserCollectionPosition; if (position == -1 || position == results.TotalUserCount) { break; } } return(externalUsers); }
protected override void ExecuteCmdlet() { if (ShouldProcess($"Sign out user {User} from all devices")) { var office365Tenant = new Office365Tenant(ClientContext); var result = office365Tenant.RevokeAllUserSessions(User); ClientContext.Load(result); ClientContext.ExecuteQueryRetry(); switch (result.State) { case SPOUserSessionRevocationState.FeatureDisabled: { WriteWarning("This cmdlet will be available in the future, but is not ready for use in your organization yet."); break; } case SPOUserSessionRevocationState.Failure: { WriteWarning($"Sorry, something went wrong and we could not sign out {User} from any device."); break; } case SPOUserSessionRevocationState.InstantaneousSuccess: { WriteWarning($"We succesfully signed out {User} from all devices."); break; } case SPOUserSessionRevocationState.NonInstantaneousSuccess: { WriteWarning($"It can take up to an hour to sign out {User} from all devices."); break; } case SPOUserSessionRevocationState.UserNotFound: { WriteWarning($"We could not find the user {User}. Check for typos and try again."); break; } default: throw new PSInvalidOperationException(); } } }
protected override void ExecuteCmdlet() { var context = ClientContext; var site = ClientContext.Site; var siteUrl = ClientContext.Url; if (ParameterSpecified(nameof(Identity))) { context = ClientContext.Clone(Identity.Url); site = context.Site; siteUrl = context.Url; } Office365Tenant office365Tenant = new Office365Tenant(context); context.Load(office365Tenant); office365Tenant.DisableSharingForNonOwnersOfSite(siteUrl); context.ExecuteQueryRetry(); }
/// <summary> /// Returns a list all external users in your tenant /// </summary> /// <param name="web">Tenant administration web</param> /// <returns>A list of <see cref="OfficeDevPnP.Core.Entities.ExternalUserEntity"/> objects</returns> public static List <ExternalUserEntity> GetExternalUsersTenant(this Web web) { Tenant tenantAdmin = new Tenant(web.Context); Office365Tenant tenant = new Office365Tenant(web.Context); List <ExternalUserEntity> externalUsers = new List <ExternalUserEntity>(); int pageSize = 50; int position = 0; GetExternalUsersResults results = null; while (true) { results = tenant.GetExternalUsers(position, pageSize, string.Empty, SortOrder.Ascending); web.Context.Load(results, r => r.UserCollectionPosition, r => r.TotalUserCount, r => r.ExternalUserCollection); web.Context.ExecuteQuery(); foreach (var externalUser in results.ExternalUserCollection) { externalUsers.Add(new ExternalUserEntity() { DisplayName = externalUser.DisplayName, AcceptedAs = externalUser.AcceptedAs, InvitedAs = externalUser.InvitedAs, InvitedBy = externalUser.InvitedBy, UniqueId = externalUser.UniqueId, WhenCreated = externalUser.WhenCreated }); } position = results.UserCollectionPosition; if (position == -1 || position == results.TotalUserCount) { break; } } return(externalUsers); }
protected override void ExecuteCmdlet() { var office365Tenant = new Office365Tenant(ClientContext); var results = office365Tenant.RemoveExternalUsers(UniqueIDs); if (this.ShouldProcess(nameof(UniqueIDs), "Remove External Users")) { var resultObject = new PSObject(); ClientContext.Load(results); ClientContext.ExecuteQueryRetry(); if (results.RemoveSucceeded.Length > 0) { resultObject.Properties.Add(new PSNoteProperty("Succeeded", results.RemoveSucceeded)); } if (results.RemoveFailed.Length > 0) { resultObject.Properties.Add(new PSNoteProperty("Failed", results.RemoveFailed)); } WriteObject(resultObject); } }
protected override void ExecuteCmdlet() { var context = ClientContext; var site = ClientContext.Site; var siteUrl = ClientContext.Url; if (ParameterSpecified(nameof(Identity))) { context = ClientContext.Clone(Identity.Url); site = context.Site; siteUrl = context.Url; } Office365Tenant office365Tenant = new Office365Tenant(context); context.Load(office365Tenant); var isSharingDisabledForNonOwnersOfSite = office365Tenant.IsSharingDisabledForNonOwnersOfSite(siteUrl); context.ExecuteQueryRetry(); // Inverting the outcome here on purpose as the wording of the cmdlet indicates that a true means sharing for owners and members is allowed and false would mean only sharing for owners would be allowed WriteObject(!isSharingDisabledForNonOwnersOfSite.Value); }
public ActionResult GetCDNSettings() { var cdnManagerModel = new CDNManagerModel(); using (var clientContext = GetClientContext()) { clientContext.Load(clientContext.Web, w => w.Url); var tenant = new Office365Tenant(clientContext); var publicCDNEnabled = tenant.GetTenantCdnEnabled(SPOTenantCdnType.Public); var publicCdnOrigins = tenant.GetTenantCdnOrigins(SPOTenantCdnType.Public); var publicCDNPolicies = tenant.GetTenantCdnPolicies(SPOTenantCdnType.Public); clientContext.ExecuteQuery(); cdnManagerModel.PublicCDNEnabled = publicCDNEnabled.Value; cdnManagerModel.Origins = publicCdnOrigins; //Origins will need refactor. It is just a string now, not an object var fileTypes = publicCDNPolicies.Where(s => s.StartsWith(SPOTenantCdnPolicyType.IncludeFileExtensions.ToString())).First(); cdnManagerModel.Filetypes = ConvertToList(fileTypes); cdnManagerModel.SPOSiteUrl = clientContext.Web.Url; } return(Json(cdnManagerModel, JsonRequestBehavior.AllowGet)); }
public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestMessage req, TraceWriter log) { log.Info("Triggering Azure Function to process the JSON"); // parse query parameter string fileName = req.GetQueryNameValuePairs() .FirstOrDefault(q => string.Compare(q.Key, "fileName", true) == 0) .Value; string tenantAdminUrl = "https://m365x131504-admin.sharepoint.com"; // User name and pwd to login to the tenant string userName = ""; string pwd = ""; string fileUrl = "https://m365x131504.sharepoint.com/sites/PowerApps/Shared%20Documents/" + fileName; ClientResult <Guid> workItemId; string status = string.Empty; // Get access to source tenant with tenant permissions using (var ctx = new ClientContext(tenantAdminUrl)) { //Provide count and pwd for connecting to the source var passWord = new SecureString(); foreach (char c in pwd.ToCharArray()) { passWord.AppendChar(c); } ctx.Credentials = new SharePointOnlineCredentials(userName, passWord); // Only to check connection and permission, could be removed ctx.Load(ctx.Web); ctx.ExecuteQuery(); string title = ctx.Web.Title; // Let's get started on the actual code!!! Office365Tenant tenant = new Office365Tenant(ctx); ctx.Load(tenant); ctx.ExecuteQuery(); /// /// /// /// /// /// /// /// /// /// DO import based on file whcih is already uploaded to tenant /// /// /// /// /// /// /// /// /// // Type of user identifier ["PrincipleName", "EmailAddress", "CloudId"] // in the User Profile Service. // In this case we use email as the identifier at the UPA storage ImportProfilePropertiesUserIdType userIdType = ImportProfilePropertiesUserIdType.Email; // Name of user identifier property in the JSON var userLookupKey = "IdName"; var propertyMap = new System.Collections.Generic.Dictionary <string, string>(); // First one is the file, second is the target at User Profile Service // Notice that we have here 2 custom properties in UPA called 'City' and 'Office' propertyMap.Add("MyCustomProperty", "MyCustomProperty"); propertyMap.Add("MyCustomProperty1", "MyCustomProperty1"); // Returns a GUID, which can be used to see the status of the execution and end results workItemId = tenant.QueueImportProfileProperties( userIdType, userLookupKey, propertyMap, fileUrl ); ctx.ExecuteQuery(); var job = tenant.GetImportProfilePropertyJob(workItemId.Value); ctx.Load(job); ctx.ExecuteQuery(); status = string.Format("ID: {0} - Request status: {1} - Error status: {2}", job.JobId, job.State.ToString(), job.Error.ToString()); } return(workItemId == null ? req.CreateResponse(HttpStatusCode.BadRequest, "Some Error Occurred") : req.CreateResponse(HttpStatusCode.OK, status)); }
static void Main(string[] args) { #region Variables: string adminURL = "https://pcfromdc-admin.sharepoint.com"; // SPO Admin Site URL string siteURL = "https://pcfromdc.sharepoint.com/sites/spc18"; // Site where we upload JSON file string importFileURL = "https://pcfromdc.sharepoint.com/sites/spc18/upaSync/upaOutput-WebJob.txt"; string docLibName = "UPA Sync"; // Document Library Name for upload string spoUserName = ConfigurationManager.AppSettings["spoUserName"]; string spoPassword = ConfigurationManager.AppSettings["spoPassword"]; #endregion #region Query SQL and Build JSON'ish String for upload to O365 SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(); builder.DataSource = "pcdemo.database.windows.net"; builder.UserID = ConfigurationManager.AppSettings["dataBaseUserName"]; builder.Password = ConfigurationManager.AppSettings["dataBasePW"]; builder.InitialCatalog = "pcDemo_Personnel"; #endregion #region Start to build jsonOutput string StringBuilder jsonSB = new StringBuilder(); jsonSB.AppendLine("{"); jsonSB.AppendLine("\"value\":"); jsonSB.AppendLine("["); #endregion #region Get info from Azure SQL Table using (SqlConnection connection = new SqlConnection(builder.ConnectionString)) { connection.Open(); StringBuilder sb = new StringBuilder(); sb.Append("SELECT TOP(10) mail, city "); sb.Append("FROM pcDemo_SystemUsers "); sb.Append("Where city is not null "); sb.Append("and mail like '*****@*****.**' "); sb.Append("or mail like '%pcdemo.net'"); String sql = sb.ToString(); using (SqlCommand command = new SqlCommand(sql, connection)) { using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { jsonSB.AppendLine("{"); jsonSB.AppendLine("\"IdName\": \"" + reader.GetString(0) + "\","); jsonSB.AppendLine("\"Property1\": \"" + reader.GetString(1) + "\""); jsonSB.AppendLine("},"); } } } } Console.WriteLine("SQL query completed..."); #endregion #region finish json'ish string and convert to stream // Clean up jsonSB and remove last comma string jasonClean = jsonSB.ToString(); jasonClean = (jasonClean.Trim()).TrimEnd(','); // Add jasonClean back into StringBuilder StringBuilder jsonEnd = new StringBuilder(jasonClean); jsonEnd.AppendLine(""); jsonEnd.AppendLine("]"); jsonEnd.AppendLine("}"); string jsonOutput = jsonEnd.ToString(); Console.WriteLine("JSON build completed..."); // Convert String to Stream byte[] byteArray = Encoding.UTF8.GetBytes(jsonOutput); MemoryStream stream = new MemoryStream(byteArray); Console.WriteLine("JSON converted..."); #endregion #region Upload JSON file to SPO using (var clientContext = new ClientContext(siteURL)) { // set username and password var passWord = new SecureString(); foreach (char c in spoPassword.ToCharArray()) { passWord.AppendChar(c); } clientContext.Credentials = new SharePointOnlineCredentials(spoUserName, passWord); Web web = clientContext.Web; FileCreationInformation newFile = new FileCreationInformation(); newFile.Overwrite = true; newFile.ContentStream = stream; newFile.Url = importFileURL; List docLibrary = web.Lists.GetByTitle(docLibName); docLibrary.RootFolder.Files.Add(newFile); clientContext.Load(docLibrary); clientContext.ExecuteQuery(); } Console.WriteLine("File Uploaded..."); #endregion #region Bulk Upload API using (var clientContext = new ClientContext(adminURL)) { // set username and password var passWord = new SecureString(); foreach (char c in spoPassword.ToCharArray()) { passWord.AppendChar(c); } clientContext.Credentials = new SharePointOnlineCredentials(spoUserName, passWord); // Get Tenant Context Office365Tenant tenant = new Office365Tenant(clientContext); clientContext.Load(tenant); clientContext.ExecuteQuery(); // Only to check connection and permission, could be removed clientContext.Load(clientContext.Web); clientContext.ExecuteQuery(); string title = clientContext.Web.Title; Console.WriteLine("Logged into " + title + "..."); clientContext.Load(clientContext.Web); ImportProfilePropertiesUserIdType userIdType = ImportProfilePropertiesUserIdType.Email; var userLookupKey = "IdName"; var propertyMap = new System.Collections.Generic.Dictionary <string, string>(); propertyMap.Add("Property1", "City"); // propertyMap.Add("Property2", "Office"); var workItemId = tenant.QueueImportProfileProperties(userIdType, userLookupKey, propertyMap, importFileURL); clientContext.ExecuteQuery(); } Console.WriteLine("UPA Bulk Update Completed..."); #endregion }
/// <summary> /// Returns a list all external users for a given site that have at least the viewpages permission /// </summary> /// <param name="web">Tenant administration web</param> /// <param name="siteUrl">Url of the site fetch the external users for</param> /// <returns>A list of <see cref="OfficeDevPnP.Core.Entities.ExternalUserEntity"/> objects</returns> public static List<ExternalUserEntity> GetExternalUsersForSiteTenant(this Web web, Uri siteUrl) { if (siteUrl == null) throw new ArgumentNullException("siteUrl"); Tenant tenantAdmin = new Tenant(web.Context); Office365Tenant tenant = new Office365Tenant(web.Context); Site site = tenantAdmin.GetSiteByUrl(siteUrl.OriginalString); web = site.RootWeb; List<ExternalUserEntity> externalUsers = new List<ExternalUserEntity>(); int pageSize = 50; int position = 0; GetExternalUsersResults results = null; while (true) { results = tenant.GetExternalUsersForSite(siteUrl.OriginalString, position, pageSize, string.Empty, SortOrder.Ascending); web.Context.Load(results, r => r.UserCollectionPosition, r => r.TotalUserCount, r => r.ExternalUserCollection); web.Context.ExecuteQuery(); foreach (var externalUser in results.ExternalUserCollection) { User user = web.SiteUsers.GetByEmail(externalUser.AcceptedAs); web.Context.Load(user); web.Context.ExecuteQuery(); var permission = web.GetUserEffectivePermissions(user.LoginName); web.Context.ExecuteQuery(); var doesUserHavePermission = permission.Value.Has(PermissionKind.ViewPages); if (doesUserHavePermission) { externalUsers.Add(new ExternalUserEntity() { DisplayName = externalUser.DisplayName, AcceptedAs = externalUser.AcceptedAs, InvitedAs = externalUser.InvitedAs, InvitedBy = externalUser.InvitedBy, UniqueId = externalUser.UniqueId, WhenCreated = externalUser.WhenCreated }); } } position = results.UserCollectionPosition; if (position == -1 || position == results.TotalUserCount) { break; } } return externalUsers; }
/// <summary> /// Returns a list all external users in your tenant /// </summary> /// <param name="web">Tenant administration web</param> /// <returns>A list of <see cref="OfficeDevPnP.Core.Entities.ExternalUserEntity"/> objects</returns> public static List<ExternalUserEntity> GetExternalUsersTenant(this Web web) { Tenant tenantAdmin = new Tenant(web.Context); Office365Tenant tenant = new Office365Tenant(web.Context); List<ExternalUserEntity> externalUsers = new List<ExternalUserEntity>(); int pageSize = 50; int position = 0; GetExternalUsersResults results = null; while (true) { results = tenant.GetExternalUsers(position, pageSize, string.Empty, SortOrder.Ascending); web.Context.Load(results, r => r.UserCollectionPosition, r => r.TotalUserCount, r => r.ExternalUserCollection); web.Context.ExecuteQuery(); foreach (var externalUser in results.ExternalUserCollection) { externalUsers.Add(new ExternalUserEntity() { DisplayName = externalUser.DisplayName, AcceptedAs = externalUser.AcceptedAs, InvitedAs = externalUser.InvitedAs, InvitedBy = externalUser.InvitedBy, UniqueId = externalUser.UniqueId, WhenCreated = externalUser.WhenCreated }); } position = results.UserCollectionPosition; if (position == -1 || position == results.TotalUserCount) { break; } } return externalUsers; }
static void Main(string[] args) { ConsoleColor defaultForeground = Console.ForegroundColor; // Something like: https://contoso-admin.sharepoint.com string tenantAdminUrl = GetInput("Enter the admin URL of your tenant", false, defaultForeground); // User name and pwd to login to the tenant string userName = GetInput("Enter your user name", false, defaultForeground); string pwd = GetInput("Enter your password", true, defaultForeground); // File URL to the profile value like: https://contoso.sharepoint.com/Shared%20Documents/sample.txt string fileUrl = GetInput("Enter the URL to the file located in your tenant", false, defaultForeground); // Get access to source tenant with tenant permissions using (var ctx = new ClientContext(tenantAdminUrl)) { //Provide count and pwd for connecting to the source var passWord = new SecureString(); foreach (char c in pwd.ToCharArray()) { passWord.AppendChar(c); } ctx.Credentials = new SharePointOnlineCredentials(userName, passWord); // Only to check connection and permission, could be removed ctx.Load(ctx.Web); ctx.ExecuteQuery(); string title = ctx.Web.Title; // Let's get started on the actual code!!! Office365Tenant tenant = new Office365Tenant(ctx); ctx.Load(tenant); ctx.ExecuteQuery(); /// /// /// /// /// /// /// /// /// /// DO import based on file whcih is already uploaded to tenant /// /// /// /// /// /// /// /// /// // Type of user identifier ["PrincipleName", "EmailAddress", "PrincipalName"] // in the User Profile Service. // In this case we use email as the identifier at the UPA storage ImportProfilePropertiesUserIdType userIdType = ImportProfilePropertiesUserIdType.Email; // Name of user identifier property in the JSON var userLookupKey = "IdName"; var propertyMap = new System.Collections.Generic.Dictionary <string, string>(); // First one is the file, second is the target at User Profile Service // Notice that we have here 2 custom properties in UPA called 'City' and 'OfficeCode' propertyMap.Add("Title", "Title"); propertyMap.Add("City", "City"); propertyMap.Add("Office", "OfficeCode"); // Returns a GUID, which can be used to see the status of the execution and end results var workItemId = tenant.QueueImportProfileProperties( userIdType, userLookupKey, propertyMap, fileUrl ); ctx.ExecuteQuery(); /// /// /// /// /// /// /// /// /// /// // CALL CHECK STATUS in OWN method with the received GUID /// /// /// /// /// /// /// /// /// /// CheckStatusOfRequestedProcess(ctx, workItemId.Value); // Just to pause and indicate that it's all done Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("\n-----------------------------------------"); Console.WriteLine("We are all done. Press enter to continue."); Console.ReadLine(); } }
static void Main(string[] args) { //get governance variables such as warning duration and cutoff duration int warningDuration = Convert.ToInt32(ConfigurationManager.AppSettings["WarningDuration"]); int cutoffDuration = Convert.ToInt32(ConfigurationManager.AppSettings["CutoffDuration"]); string tenantName = ConfigurationManager.AppSettings["TenantName"]; string tenantUpnDomain = ConfigurationManager.AppSettings["TenantUpnDomain"]; Uri tenantAdminUri = new Uri(String.Format("https://{0}-admin.sharepoint.com", tenantName)); string webUrl = ""; #if DEBUG webUrl = "http://localhost:25440/"; #else webUrl = "https://sposharing.azurewebsites.net/"; #endif foreach (var siteUrl in sites) { //initialize a process date for this site and clean up to match SQL percision DateTime processDate = DateTime.Now; string stringTicks = processDate.Ticks.ToString(); int adjustmentTicks = Convert.ToInt32(stringTicks.Substring(stringTicks.Length - 5)); processDate = processDate.Subtract(TimeSpan.FromTicks(adjustmentTicks)); //use O365 Tenant Administration to get all the external sharing details for this site List <ExternalShareDetails> shares = new List <ExternalShareDetails>(); string adminRealm = TokenHelper.GetRealmFromTargetUrl(tenantAdminUri); var adminToken = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, tenantAdminUri.Authority, adminRealm).AccessToken; using (var clientContext = TokenHelper.GetClientContextWithAccessToken(tenantAdminUri.ToString(), adminToken)) { //load the tenant var tenant = new Office365Tenant(clientContext); clientContext.Load(tenant); clientContext.ExecuteQuery(); //initalize varables to going through the paged results int position = 0; bool hasMore = true; while (hasMore) { //get external users 50 at a time (this is the limit and why we are paging) var externalUsers = tenant.GetExternalUsersForSite(siteUrl, position, 50, String.Empty, SortOrder.Descending); clientContext.Load(externalUsers, i => i.TotalUserCount); clientContext.Load(externalUsers, i => i.ExternalUserCollection); clientContext.ExecuteQuery(); //convert each external user to our own entity foreach (var extUser in externalUsers.ExternalUserCollection) { position++; shares.Add(new ExternalShareDetails() { AcceptedAs = extUser.AcceptedAs.ToLower(), DisplayName = extUser.DisplayName, InvitedAs = extUser.InvitedAs.ToLower(), InvitedBy = (String.IsNullOrEmpty(extUser.InvitedBy)) ? null : extUser.InvitedBy.ToLower(), UserId = extUser.UserId, WhenCreated = extUser.WhenCreated }); } //determine if we have more pages to process hasMore = (externalUsers.TotalUserCount > position); } } //get an AppOnly accessToken and clientContext for the site collection Uri siteUri = new Uri(siteUrl); string realm = TokenHelper.GetRealmFromTargetUrl(siteUri); string accessToken = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, siteUri.Authority, realm).AccessToken; using (var clientContext = TokenHelper.GetClientContextWithAccessToken(siteUri.ToString(), accessToken)) { //first we need to load the site to determine if external sharing is enabled (Site.ShareByEmailEnabled) var site = clientContext.Site; var siteOwner = clientContext.Site.Owner; clientContext.Load(site); clientContext.Load(siteOwner); //include the site owner in case the share "InvitedBy" is null...we will send them email instead clientContext.ExecuteQuery(); //validate that the site has sharing turned on if (site.ShareByEmailEnabled) { //process all of the shares foreach (var externalShare in shares) { //look for an existing record in the database using (ExternalSharingDataEntities entities = new ExternalSharingDataEntities()) { var shareRecord = entities.ExternalShares.FirstOrDefault(i => i.LoginName.Equals(externalShare.AcceptedAs)); if (shareRecord != null) { //Update LastProcessedDate column of the record with the processDate shareRecord.LastProcessedDate = processDate; entities.SaveChanges(); } else { //get the original share date var details = getREST(accessToken, String.Format("{0}/_api/Web/SiteUserInfoList/Items({1})/FieldValuesAsText", siteUrl, externalShare.UserId)); externalShare.WhenCreated = Convert.ToDateTime(details.Descendants(ns + "Created").FirstOrDefault().Value); shareRecord = new ExternalShare() { UniqueIdentifier = Guid.NewGuid(), SiteCollectionUrl = siteUrl.ToLower(), LoginName = externalShare.AcceptedAs, UserId = externalShare.UserId, InvitedBy = (String.IsNullOrEmpty(externalShare.InvitedBy)) ? siteOwner.Email : externalShare.InvitedBy, OriginalSharedDate = externalShare.WhenCreated, LastProcessedDate = processDate }; entities.ExternalShares.Add(shareRecord); entities.SaveChanges(); } //check if the record falls inside the warnings double daysActive = processDate.Subtract(shareRecord.OriginalSharedDate).TotalDays; if (shareRecord.RefreshSharedDate != null) { daysActive = processDate.Subtract((DateTime)shareRecord.RefreshSharedDate).TotalDays; } //check for cutoff if (daysActive > cutoffDuration) { //remove the SPUser from the site clientContext.Web.SiteUsers.RemoveById(externalShare.UserId); clientContext.ExecuteQuery(); //delete the record entities.ExternalShares.Remove(shareRecord); entities.SaveChanges(); } else if (daysActive > warningDuration) { int expiresIn = Convert.ToInt32(cutoffDuration - daysActive); //send email to InvitedBy (which will be site collection owner when null) EmailProperties email = new EmailProperties(); email.To = new List <String>() { shareRecord.InvitedBy }; email.Subject = String.Format("Action Required: External sharing with {0} about to expire", externalShare.AcceptedAs); email.Body = String.Format("<html><body><p>You are receiving this message because you are the site administrator of <a href='{0}'>{0}</a> OR you shared it with {1}. The external access for this user is set to expire in {2} days. Use the link below to view additional details and perform actions to revoke OR extend access for another {3} days. If you do not act on this notice, the external access for this user to terminate in {2} days.</p><ul><li><a href='{4}Details/{5}'>View Details</a></li><li><a href='{4}Extend/{5}'>Extend {3} Days</a></li><li><a href='{4}Revoke/{5}'>Revoke Access</a></li></ul></body></html>", siteUrl, externalShare.AcceptedAs, expiresIn.ToString(), cutoffDuration.ToString(), webUrl, shareRecord.UniqueIdentifier); Utility.SendEmail(clientContext, email); clientContext.ExecuteQuery(); } } } } } //delete all database records for this site that have an older processDate...these should represent external users deleted by manually using (ExternalSharingDataEntities entities = new ExternalSharingDataEntities()) { var cleanUpRecords = entities.ExternalShares.Where(i => i.SiteCollectionUrl.Equals(siteUrl.ToLower()) && i.LastProcessedDate < processDate); foreach (var record in cleanUpRecords) { entities.ExternalShares.Remove(record); entities.SaveChanges(); } } } }
protected override void ExecuteCmdlet() { var context = ClientContext; var site = ClientContext.Site; var siteUrl = ClientContext.Url; var executeQueryRequired = false; if (!string.IsNullOrEmpty(Identity)) { context = ClientContext.Clone(Identity); site = context.Site; siteUrl = context.Url; } if (MyInvocation.BoundParameters.ContainsKey("Classification")) { site.Classification = Classification; executeQueryRequired = true; } if (MyInvocation.BoundParameters.ContainsKey("LogoFilePath")) { var webTemplate = ClientContext.Web.EnsureProperty(w => w.WebTemplate); if (webTemplate == "GROUP") { if (!System.IO.Path.IsPathRooted(LogoFilePath)) { LogoFilePath = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, LogoFilePath); } if (System.IO.File.Exists(LogoFilePath)) { var bytes = System.IO.File.ReadAllBytes(LogoFilePath); var mimeType = System.Web.MimeMapping.GetMimeMapping(LogoFilePath); var result = OfficeDevPnP.Core.Sites.SiteCollection.SetGroupImage(context, bytes, mimeType).GetAwaiter().GetResult(); } else { throw new System.Exception("Logo file does not exist"); } } else { throw new System.Exception("Not an Office365 group enabled site."); } } if (executeQueryRequired) { context.ExecuteQueryRetry(); } if (IsTenantProperty()) { var tenantAdminUrl = UrlUtilities.GetTenantAdministrationUrl(context.Url); context = context.Clone(tenantAdminUrl); executeQueryRequired = false; Func <TenantOperationMessage, bool> timeoutFunction = TimeoutFunction; Tenant tenant = new Tenant(context); var siteProperties = tenant.GetSitePropertiesByUrl(siteUrl, false); if (LockState.HasValue) { tenant.SetSiteLockState(siteUrl, LockState.Value, Wait, Wait ? timeoutFunction : null); WriteWarning("You changed the lockstate of this site. This change is not guaranteed to be effective immediately. Please wait a few minutes for this to take effect."); } if (Owners != null && Owners.Count > 0) { var admins = new List <UserEntity>(); foreach (var owner in Owners) { var userEntity = new UserEntity { LoginName = owner }; admins.Add(userEntity); } tenant.AddAdministrators(admins, new Uri(siteUrl)); } if (Sharing.HasValue) { siteProperties.SharingCapability = Sharing.Value; executeQueryRequired = true; } if (StorageMaximumLevel.HasValue) { siteProperties.StorageMaximumLevel = StorageMaximumLevel.Value; executeQueryRequired = true; } if (StorageWarningLevel.HasValue) { siteProperties.StorageWarningLevel = StorageWarningLevel.Value; executeQueryRequired = true; } #pragma warning disable CS0618 // Type or member is obsolete if (UserCodeWarningLevel.HasValue) { siteProperties.UserCodeWarningLevel = UserCodeWarningLevel.Value; executeQueryRequired = true; } if (UserCodeMaximumLevel.HasValue) { siteProperties.UserCodeMaximumLevel = UserCodeMaximumLevel.Value; executeQueryRequired = true; } #pragma warning restore CS0618 // Type or member is obsolete if (AllowSelfServiceUpgrade.HasValue) { siteProperties.AllowSelfServiceUpgrade = AllowSelfServiceUpgrade.Value; executeQueryRequired = true; } if (NoScriptSite.IsPresent) { siteProperties.DenyAddAndCustomizePages = (NoScriptSite == true ? DenyAddAndCustomizePagesStatus.Enabled : DenyAddAndCustomizePagesStatus.Disabled); executeQueryRequired = true; } if (CommentsOnSitePagesDisabled.IsPresent) { siteProperties.CommentsOnSitePagesDisabled = CommentsOnSitePagesDisabled; executeQueryRequired = true; } if (DefaultLinkPermission.HasValue) { siteProperties.DefaultLinkPermission = DefaultLinkPermission.Value; executeQueryRequired = true; } if (DefaultSharingLinkType.HasValue) { siteProperties.DefaultSharingLinkType = DefaultSharingLinkType.Value; executeQueryRequired = true; } if (DisableAppViews.HasValue) { siteProperties.DisableAppViews = DisableAppViews.Value; executeQueryRequired = true; } if (DisableCompanyWideSharingLinks.HasValue) { siteProperties.DisableCompanyWideSharingLinks = DisableCompanyWideSharingLinks.Value; executeQueryRequired = true; } if (DisableFlows.IsPresent) { siteProperties.DisableFlows = DisableFlows ? FlowsPolicy.Disabled : FlowsPolicy.NotDisabled; executeQueryRequired = true; } if (LocaleId.HasValue) { siteProperties.Lcid = LocaleId.Value; executeQueryRequired = true; } if (!string.IsNullOrEmpty(NewUrl)) { siteProperties.NewUrl = NewUrl; executeQueryRequired = true; } if (RestrictedToGeo.HasValue) { siteProperties.RestrictedToRegion = RestrictedToGeo.Value; executeQueryRequired = true; } if (SocialBarOnSitePagesDisabled.IsPresent) { siteProperties.SocialBarOnSitePagesDisabled = SocialBarOnSitePagesDisabled; executeQueryRequired = true; } if (executeQueryRequired) { siteProperties.Update(); tenant.Context.ExecuteQueryRetry(); } if (DisableSharingForNonOwners.IsPresent) { Office365Tenant office365Tenant = new Office365Tenant(context); context.Load(office365Tenant); context.ExecuteQueryRetry(); office365Tenant.DisableSharingForNonOwnersOfSite(siteUrl); context.ExecuteQuery(); } } }
/// <summary> /// Syncs from Azure Active Directory to SharePoint Online user profiles /// </summary> /// <param name="clientContext">A ClientContext which can be used to interact with SharePoint Online</param> /// <param name="users">Azure AD User objects that need to be synced</param> /// <param name="userProfilePropertyMappings">Hashtable with the mapping from the Azure Active Directory property (the value) to the SharePoint Online User Profile Property (the key)</param> /// <param name="sharePointFolder">Location in the currently connected to site where to upload the JSON file to with instructions to update the user profiles</param> /// <param name="onlyCreateAndUploadMappingsFile">Boolean indicating if only the mappings file should be created and uploaded to SharePoint Online (true) or if the import job on that file should also be invoked (false)</param> public static async Task <ImportProfilePropertiesJobInfo> SyncFromAzureActiveDirectory(ClientContext clientContext, IEnumerable <PnP.PowerShell.Commands.Model.AzureAD.User> users, Hashtable userProfilePropertyMappings, string sharePointFolder, bool onlyCreateAndUploadMappingsFile = false) { var webServerRelativeUrl = clientContext.Web.EnsureProperty(w => w.ServerRelativeUrl); if (!sharePointFolder.ToLower().StartsWith(webServerRelativeUrl)) { sharePointFolder = UrlUtility.Combine(webServerRelativeUrl, sharePointFolder); } if (!clientContext.Web.DoesFolderExists(sharePointFolder)) { throw new InvalidOperationException($"Folder {sharePointFolder} to upload the user profile update file to does not exist on SharePoint Online in the site {clientContext.Url}"); } var folder = clientContext.Web.GetFolderByServerRelativeUrl(sharePointFolder); var bulkUpdateBuilder = new StringBuilder(); var userUpdateBuilder = new StringBuilder(); foreach (var user in users) { foreach (DictionaryEntry userProfilePropertyMapping in userProfilePropertyMappings) { if (userProfilePropertyMapping.Key != null && userProfilePropertyMapping.Value != null) { // Check if the property is a property directly on the user object var aadUserProperty = user.GetType().GetProperty(userProfilePropertyMapping.Value.ToString(), BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); if (aadUserProperty != null) { // Construct an entry with the SharePoint Online User Profile property name and the value it should be set to coming from a property on the User object if (aadUserProperty.PropertyType == typeof(IEnumerable <string>)) { // AAD User property is an array, join all entries with a comma and add the combined string to the mapping output userUpdateBuilder.AppendFormat(@"""{0}"":""{1}"",", userProfilePropertyMapping.Key, string.Join(",", ((IEnumerable)aadUserProperty.GetValue(user)).Cast <string>().ToArray())); } else { // AAD User property is a string, add its value to the mapping output userUpdateBuilder.AppendFormat(@"""{0}"":""{1}"",", userProfilePropertyMapping.Key, aadUserProperty.GetValue(user)); } } else if (user.AdditionalProperties != null && user.AdditionalProperties.TryGetValue(userProfilePropertyMapping.Value.ToString(), out object userProfilePropertyMappingValue)) { // Construct an entry with the SharePoint Online User Profile property name and the value it should be set to coming from a property on the AdditionalProperties dictionary on the User object userUpdateBuilder.AppendFormat(@"""{0}"":""{1}"",", userProfilePropertyMapping.Key, userProfilePropertyMappingValue != null ? userProfilePropertyMappingValue.ToString() : string.Empty); } } } if (userUpdateBuilder.Length > 0) { bulkUpdateBuilder.Append(@"{""IdName"":"""); bulkUpdateBuilder.Append(user.UserPrincipalName); bulkUpdateBuilder.Append(@""","); bulkUpdateBuilder.Append(userUpdateBuilder.ToString().TrimEnd(',')); bulkUpdateBuilder.Append("},"); userUpdateBuilder.Clear(); } } // Check if there's anything to update if (bulkUpdateBuilder.Length == 0) { return(null); } // Construct the entire JSON message with the user profiles and properties to update var json = @"{ ""value"": [" + bulkUpdateBuilder.ToString().TrimEnd(',') + "] }"; // Define the filename to save the file under on SharePoint Online var fileName = $"userprofilesyncdata-{DateTime.Now.ToString("yyyyMMddHHmmss")}-{Guid.NewGuid().ToString().Replace("-", "")}.json"; // Upload the JSON to SharePoint Online File file = null; using (var stream = new System.IO.MemoryStream()) { using (var writer = new System.IO.StreamWriter(stream)) { await writer.WriteAsync(json); writer.Flush(); stream.Position = 0; file = folder.UploadFile(fileName, stream, true); } } // Check if we should kick off the process to import the file if (onlyCreateAndUploadMappingsFile) { return(null); } // Instruct SharePoint Online to process the JSON file var o365 = new Office365Tenant(clientContext); var propDictionary = userProfilePropertyMappings.Cast <DictionaryEntry>().ToDictionary(kvp => (string)kvp.Key, kvp => (string)kvp.Key); var url = new Uri(clientContext.Url).GetLeftPart(UriPartial.Authority) + file.ServerRelativeUrl; var id = o365.QueueImportProfileProperties(ImportProfilePropertiesUserIdType.PrincipalName, "IdName", propDictionary, url); clientContext.ExecuteQueryRetry(); // Retrieve the import json details var job = o365.GetImportProfilePropertyJob(id.Value); clientContext.Load(job); clientContext.ExecuteQueryRetry(); return(job); }
private void SetSiteProperties(Func <TenantOperationMessage, bool> timeoutFunction) { var props = GetSiteProperties(Identity.Url); var updateRequired = false; if (ParameterSpecified(nameof(Title))) { props.Title = Title; updateRequired = true; } if (ParameterSpecified(nameof(DenyAddAndCustomizePages))) { props.DenyAddAndCustomizePages = DenyAddAndCustomizePages ? DenyAddAndCustomizePagesStatus.Enabled : DenyAddAndCustomizePagesStatus.Disabled; updateRequired = true; } if (ParameterSpecified(nameof(LocaleId))) { props.Lcid = LocaleId; updateRequired = true; } if (ParameterSpecified(nameof(AllowSelfServiceUpgrade))) { props.AllowSelfServiceUpgrade = AllowSelfServiceUpgrade; updateRequired = true; } if (ParameterSpecified(nameof(SharingAllowedDomainList))) { props.SharingAllowedDomainList = SharingAllowedDomainList; updateRequired = true; } if (ParameterSpecified(nameof(SharingBlockedDomainList))) { props.SharingBlockedDomainList = SharingBlockedDomainList; updateRequired = true; } if (ParameterSpecified(nameof(SharingDomainRestrictionMode))) { props.SharingDomainRestrictionMode = SharingDomainRestrictionMode; updateRequired = true; } if (ParameterSpecified(nameof(StorageQuota))) { props.StorageMaximumLevel = StorageQuota; updateRequired = true; } if (ParameterSpecified(nameof(StorageQuotaWarningLevel))) { props.StorageWarningLevel = StorageQuotaWarningLevel; updateRequired = true; } if (ParameterSpecified(nameof(StorageQuotaReset))) { props.StorageMaximumLevel = 0; updateRequired = true; } if (ParameterSpecified(nameof(ResourceQuota))) { props.UserCodeMaximumLevel = ResourceQuota; updateRequired = true; } if (ParameterSpecified(nameof(ResourceQuotaWarningLevel))) { props.UserCodeWarningLevel = ResourceQuotaWarningLevel; updateRequired = true; } if (ParameterSpecified(nameof(SharingCapability))) { props.SharingCapability = SharingCapability; updateRequired = true; } if (ParameterSpecified(nameof(DefaultLinkPermission))) { props.DefaultLinkPermission = DefaultLinkPermission; updateRequired = true; } if (ParameterSpecified(nameof(ShowPeoplePickerSuggestionsForGuestUsers))) { Tenant.EnsureProperty(t => t.ShowPeoplePickerSuggestionsForGuestUsers); if (!Tenant.ShowPeoplePickerSuggestionsForGuestUsers) { WriteWarning("ShowPeoplePickerSuggestionsForGuests users has been disabled for this tenant. See Set-PnPTenant"); } props.ShowPeoplePickerSuggestionsForGuestUsers = ShowPeoplePickerSuggestionsForGuestUsers; updateRequired = true; } if (ParameterSpecified(nameof(DefaultSharingLinkType))) { props.DefaultSharingLinkType = DefaultSharingLinkType; updateRequired = true; } if (ParameterSpecified(nameof(DefaultLinkToExistingAccess))) { props.DefaultLinkToExistingAccess = DefaultLinkToExistingAccess.Value; updateRequired = true; } if (ParameterSpecified(nameof(DefaultLinkToExistingAccessReset))) { props.DefaultLinkToExistingAccessReset = true; updateRequired = true; } #pragma warning disable CS0618 if (ParameterSpecified(nameof(BlockDownloadOfNonViewableFiles)) || ParameterSpecified(nameof(AllowDownloadingNonWebViewableFiles))) { var value = ParameterSpecified(nameof(BlockDownloadLinksFileTypes)) ? !BlockDownloadOfNonViewableFiles : AllowDownloadingNonWebViewableFiles; if (ConditionalAccessPolicy == SPOConditionalAccessPolicyType.AllowLimitedAccess) { props.AllowDownloadingNonWebViewableFiles = value; updateRequired = true; if (!value) { WriteWarning("Users will not be able to download files that cannot be viewed on the web. To allow download of files that cannot be viewed on the web run the cmdlet again and set AllowDownloadingNonWebViewableFiles to true."); } } else { if (ShouldContinue("To set AllowDownloadingNonWebViewableFiles parameter you need to set the -ConditionalAccessPolicy parameter to AllowLimitedAccess. We can set the Conditional Access Policy of this site to AllowLimitedAccess. Would you like to continue?", string.Empty)) { ConditionalAccessPolicy = SPOConditionalAccessPolicyType.AllowLimitedAccess; props.ConditionalAccessPolicy = SPOConditionalAccessPolicyType.AllowLimitedAccess; props.AllowDownloadingNonWebViewableFiles = value; if (!value) { WriteWarning("Users will not be able to download files that cannot be viewed on the web. To allow download of files that cannot be viewed on the web run the cmdlet again and set AllowDownloadingNonWebViewableFiles to true."); } } } } #pragma warning restore CS0618 if (ParameterSpecified(nameof(CommentsOnSitePagesDisabled))) { props.CommentsOnSitePagesDisabled = CommentsOnSitePagesDisabled; updateRequired = true; } if (ParameterSpecified(nameof(DisableAppViews))) { props.DisableAppViews = DisableAppViews; updateRequired = true; } if (ParameterSpecified(nameof(DisableCompanyWideSharingLinks))) { props.DisableCompanyWideSharingLinks = DisableCompanyWideSharingLinks; updateRequired = true; } if (ParameterSpecified(nameof(DisableFlows))) { props.DisableFlows = DisableFlows; updateRequired = true; } if (ParameterSpecified(nameof(EnablePWA))) { props.PWAEnabled = EnablePWA ? PWAEnabledStatus.Enabled : PWAEnabledStatus.Disabled; updateRequired = true; } if (ParameterSpecified(nameof(OverrideTenantAnonymousLinkExpirationPolicy))) { props.OverrideTenantAnonymousLinkExpirationPolicy = OverrideTenantAnonymousLinkExpirationPolicy.ToBool(); updateRequired = true; } if (ParameterSpecified(nameof(AnonymousLinkExpirationInDays)) && AnonymousLinkExpirationInDays.HasValue) { props.AnonymousLinkExpirationInDays = AnonymousLinkExpirationInDays.Value; updateRequired = true; } if (ParameterSpecified(nameof(DisableSharingForNonOwners))) { var office365Tenant = new Office365Tenant(ClientContext); ClientContext.Load(office365Tenant); ClientContext.ExecuteQueryRetry(); office365Tenant.DisableSharingForNonOwnersOfSite(Identity.Url); } if (ParameterSpecified(nameof(ConditionalAccessPolicy)) && ConditionalAccessPolicy == SPOConditionalAccessPolicyType.ProtectionLevel) { if (IsRootSite(Identity.Url)) { throw new PSInvalidOperationException("You cannot set the conditional access policy 'ProtectionLevel' on the root site."); } if (string.IsNullOrEmpty(ProtectionLevelName)) { props.AuthContextStrength = null; } else { props.AuthContextStrength = ProtectionLevelName; } updateRequired = true; } else { if (ParameterSpecified(nameof(ProtectionLevelName))) { throw new PSArgumentException("ConditionalAccessPolicy has to be set too when using this parameter."); } if (ParameterSpecified(nameof(ConditionalAccessPolicy))) { props.AuthContextStrength = null; updateRequired = true; } } if (ClientContext.ServerVersion >= new Version(16, 0, 8715, 1200)) // ServerSupportsIpLabelId2 { if (ParameterSpecified(nameof(SensitivityLabel))) { props.SensitivityLabel2 = SensitivityLabel; updateRequired = true; } if (ParameterSpecified(nameof(RemoveLabel))) { props.SensitivityLabel2 = null; updateRequired = true; } } else { WriteWarning("Server does not support setting sensitity label"); } if (ParameterSpecified(nameof(LimitedAccessFileType))) { if (ConditionalAccessPolicy == SPOConditionalAccessPolicyType.AllowLimitedAccess) { props.LimitedAccessFileType = LimitedAccessFileType; updateRequired = true; } else if (ShouldContinue("To set LimitedAccessFileType you need to set the -ConditionalAccessPolicy parameter to AllowLimitedAccess. We can set the Conditional Access Policy of this site to AllowLimitedAccess. Would you like to continue?", string.Empty)) { ConditionalAccessPolicy = SPOConditionalAccessPolicyType.AllowLimitedAccess; props.ConditionalAccessPolicy = SPOConditionalAccessPolicyType.AllowLimitedAccess; props.LimitedAccessFileType = LimitedAccessFileType; updateRequired = true; } } if (ParameterSpecified(nameof(AllowEditing))) { if (ConditionalAccessPolicy == SPOConditionalAccessPolicyType.AllowLimitedAccess) { props.AllowEditing = AllowEditing; } else if (ShouldContinue("To set AllowEditing you need to set the -ConditionalAccessPolicy parameter to AllowLimitedAccess. We can set the Conditional Access Policy of this site to AllowLimitedAccess. Would you like to continue?", string.Empty)) { ConditionalAccessPolicy = SPOConditionalAccessPolicyType.AllowLimitedAccess; props.ConditionalAccessPolicy = SPOConditionalAccessPolicyType.AllowLimitedAccess; props.AllowEditing = AllowEditing; } } if (ParameterSpecified(nameof(RestrictedToGeo))) { props.RestrictedToRegion = RestrictedToGeo; updateRequired = true; } if (ParameterSpecified(nameof(ExternalUserExpirationInDays))) { props.ExternalUserExpirationInDays = ExternalUserExpirationInDays; updateRequired = true; } if (ParameterSpecified(nameof(OverrideTenantExternalUserExpirationPolicy))) { props.OverrideTenantExternalUserExpirationPolicy = OverrideTenantExternalUserExpirationPolicy; updateRequired = true; } if (ParameterSpecified(nameof(AddInformationSegment)) && AddInformationSegment.Length > 0) { props.IBSegmentsToAdd = AddInformationSegment; updateRequired = true; } if (ParameterSpecified(nameof(RemoveInformationSegment)) && RemoveInformationSegment.Length > 0) { props.IBSegmentsToRemove = RemoveInformationSegment; updateRequired = true; } if (ParameterSpecified(nameof(BlockDownloadLinksFileType))) { props.BlockDownloadLinksFileType = BlockDownloadLinksFileType; updateRequired = true; } if (ParameterSpecified(nameof(OverrideBlockUserInfoVisibility))) { props.OverrideBlockUserInfoVisibility = OverrideBlockUserInfoVisibility; updateRequired = true; } if (ParameterSpecified(nameof(HubSiteId))) { var hubsiteProperties = Tenant.GetHubSitePropertiesById(HubSiteId); ClientContext.Load(hubsiteProperties); ClientContext.ExecuteQueryRetry(); if (hubsiteProperties == null || string.IsNullOrEmpty(hubsiteProperties.SiteUrl)) { throw new PSArgumentException("Hubsite not found with the ID specified"); } if (hubsiteProperties.ID != Guid.Empty) { Tenant.ConnectSiteToHubSiteById(Identity.Url, hubsiteProperties.ID); } else { Tenant.ConnectSiteToHubSite(Identity.Url, hubsiteProperties.SiteUrl); } ClientContext.ExecuteQueryRetry(); } if (updateRequired) { var op = props.Update(); ClientContext.Load(op, i => i.IsComplete, i => i.PollingInterval); ClientContext.ExecuteQueryRetry(); if (Wait) { WaitForIsComplete(ClientContext, op, timeoutFunction, TenantOperationMessage.SettingSiteProperties); } } if (Owners != null && Owners.Count > 0) { var admins = new List <UserEntity>(); foreach (var owner in Owners) { var userEntity = new UserEntity { LoginName = owner }; admins.Add(userEntity); } Tenant.AddAdministrators(admins, new Uri(Identity.Url)); } }
protected override void ExecuteCmdlet() { ClientContext.ExecuteQueryRetry(); if (ParameterSpecified(nameof(Identity))) { var siteProperties = Tenant.GetSitePropertiesByUrl(Identity.Url, Detailed); ClientContext.Load(siteProperties); ClientContext.ExecuteQueryRetry(); Model.SPOSite site = null; if (ParameterSpecified(nameof(DisableSharingForNonOwnersStatus))) { var office365Tenant = new Office365Tenant(ClientContext); var clientResult = office365Tenant.IsSharingDisabledForNonOwnersOfSite(Identity.Url); ClientContext.ExecuteQuery(); site = new Model.SPOSite(siteProperties, clientResult.Value); } else { site = new Model.SPOSite(siteProperties, null); } WriteObject(site, true); } else { SPOSitePropertiesEnumerableFilter filter = new SPOSitePropertiesEnumerableFilter() { IncludePersonalSite = IncludeOneDriveSites.IsPresent ? PersonalSiteFilter.Include : PersonalSiteFilter.UseServerDefault, IncludeDetail = Detailed, #pragma warning disable CS0618 // Type or member is obsolete Template = Template, #pragma warning restore CS0618 // Type or member is obsolete Filter = Filter, }; if (ClientContext.ServerVersion >= new Version(16, 0, 7708, 1200)) { if (ParameterSpecified(nameof(GroupIdDefined))) { filter.GroupIdDefined = GroupIdDefined.Value == true ? 1 : 2; } } else if (ParameterSpecified(nameof(GroupIdDefined))) { throw new PSArgumentException("Filtering by Group Id is not yet available for this tenant."); } SPOSitePropertiesEnumerable sitesList = null; var sites = new List <SiteProperties>(); do { sitesList = Tenant.GetSitePropertiesFromSharePointByFilters(filter); Tenant.Context.Load(sitesList); Tenant.Context.ExecuteQueryRetry(); sites.AddRange(sitesList.ToList()); filter.StartIndex = sitesList.NextStartIndexFromSharePoint; } while (!string.IsNullOrWhiteSpace(sitesList.NextStartIndexFromSharePoint)); if (Template != null) { WriteObject(sites.Where(t => t.Template == Template).OrderBy(x => x.Url).Select(s => new Model.SPOSite(s, null)), true); } else { WriteObject(sites.OrderBy(x => x.Url).Select(s => new Model.SPOSite(s, null)), true); } } }
protected override void ExecuteCmdlet() { var context = ClientContext; var site = ClientContext.Site; var siteUrl = ClientContext.Url; var executeQueryRequired = false; if (!string.IsNullOrEmpty(Identity)) { context = ClientContext.Clone(Identity); site = context.Site; siteUrl = context.Url; } if (ParameterSpecified(nameof(Classification))) { site.Classification = Classification; executeQueryRequired = true; } if (ParameterSpecified(nameof(LogoFilePath))) { site.EnsureProperty(s => s.GroupId); if (site.GroupId != Guid.Empty) { if (!System.IO.Path.IsPathRooted(LogoFilePath)) { LogoFilePath = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, LogoFilePath); } if (System.IO.File.Exists(LogoFilePath)) { var bytes = System.IO.File.ReadAllBytes(LogoFilePath); var mimeType = ""; if (LogoFilePath.EndsWith("gif", StringComparison.InvariantCultureIgnoreCase)) { mimeType = "image/gif"; } if (LogoFilePath.EndsWith("jpg", StringComparison.InvariantCultureIgnoreCase)) { mimeType = "image/jpeg"; } if (LogoFilePath.EndsWith("png", StringComparison.InvariantCultureIgnoreCase)) { mimeType = "image/png"; } var result = PnP.Framework.Sites.SiteCollection.SetGroupImageAsync(context, bytes, mimeType).GetAwaiter().GetResult(); } else { throw new Exception("Logo file does not exist"); } } else { throw new Exception("Not an Office365 group enabled site."); } } if (executeQueryRequired) { context.ExecuteQueryRetry(); } if (IsTenantProperty()) { var tenantAdminUrl = UrlUtilities.GetTenantAdministrationUrl(context.Url); context = context.Clone(tenantAdminUrl); executeQueryRequired = false; Func <TenantOperationMessage, bool> timeoutFunction = TimeoutFunction; Tenant tenant = new Tenant(context); var siteProperties = tenant.GetSitePropertiesByUrl(siteUrl, false); if (ParameterSpecified(nameof(OverrideTenantAnonymousLinkExpirationPolicy))) { siteProperties.OverrideTenantAnonymousLinkExpirationPolicy = OverrideTenantAnonymousLinkExpirationPolicy.ToBool(); executeQueryRequired = true; } if (ParameterSpecified(nameof(AnonymousLinkExpirationInDays)) && AnonymousLinkExpirationInDays.HasValue) { siteProperties.AnonymousLinkExpirationInDays = AnonymousLinkExpirationInDays.Value; executeQueryRequired = true; } if (LockState.HasValue) { tenant.SetSiteLockState(siteUrl, LockState.Value, Wait, Wait ? timeoutFunction : null); WriteWarning("You changed the lockstate of this site. This change is not guaranteed to be effective immediately. Please wait a few minutes for this to take effect."); } if (Owners != null && Owners.Count > 0) { var admins = new List <UserEntity>(); foreach (var owner in Owners) { var userEntity = new UserEntity { LoginName = owner }; admins.Add(userEntity); } tenant.AddAdministrators(admins, new Uri(siteUrl)); } if (Sharing.HasValue) { siteProperties.SharingCapability = Sharing.Value; executeQueryRequired = true; } if (StorageMaximumLevel.HasValue) { siteProperties.StorageMaximumLevel = StorageMaximumLevel.Value; executeQueryRequired = true; } if (StorageWarningLevel.HasValue) { siteProperties.StorageWarningLevel = StorageWarningLevel.Value; executeQueryRequired = true; } if (AllowSelfServiceUpgrade.HasValue) { siteProperties.AllowSelfServiceUpgrade = AllowSelfServiceUpgrade.Value; executeQueryRequired = true; } if (NoScriptSite.HasValue) { siteProperties.DenyAddAndCustomizePages = (NoScriptSite == true ? DenyAddAndCustomizePagesStatus.Enabled : DenyAddAndCustomizePagesStatus.Disabled); executeQueryRequired = true; } if (CommentsOnSitePagesDisabled.HasValue) { siteProperties.CommentsOnSitePagesDisabled = CommentsOnSitePagesDisabled.Value; executeQueryRequired = true; } if (DefaultLinkPermission.HasValue) { siteProperties.DefaultLinkPermission = DefaultLinkPermission.Value; executeQueryRequired = true; } if (DefaultSharingLinkType.HasValue) { siteProperties.DefaultSharingLinkType = DefaultSharingLinkType.Value; executeQueryRequired = true; } if (DisableAppViews.HasValue) { siteProperties.DisableAppViews = DisableAppViews.Value; executeQueryRequired = true; } if (DisableCompanyWideSharingLinks.HasValue) { siteProperties.DisableCompanyWideSharingLinks = DisableCompanyWideSharingLinks.Value; executeQueryRequired = true; } if (DisableFlows.HasValue) { siteProperties.DisableFlows = DisableFlows.Value ? FlowsPolicy.Disabled : FlowsPolicy.NotDisabled; executeQueryRequired = true; } if (LocaleId.HasValue) { siteProperties.Lcid = LocaleId.Value; executeQueryRequired = true; } if (RestrictedToGeo.HasValue) { siteProperties.RestrictedToRegion = RestrictedToGeo.Value; executeQueryRequired = true; } if (SocialBarOnSitePagesDisabled.HasValue) { siteProperties.SocialBarOnSitePagesDisabled = SocialBarOnSitePagesDisabled.Value; executeQueryRequired = true; } if (executeQueryRequired) { siteProperties.Update(); tenant.Context.ExecuteQueryRetry(); } if (DisableSharingForNonOwners.IsPresent) { Office365Tenant office365Tenant = new Office365Tenant(context); context.Load(office365Tenant); context.ExecuteQueryRetry(); office365Tenant.DisableSharingForNonOwnersOfSite(siteUrl); context.ExecuteQueryRetry(); } } }
static void Main(string[] args) { ConsoleColor defaultForeground = Console.ForegroundColor; // Something like: https://contoso-admin.sharepoint.com string tenantAdminUrl = GetInput("Enter the admin URL of your tenant", false, defaultForeground); // User name and pwd to login to the tenant string userName = GetInput("Enter your user name", false, defaultForeground); string pwd = GetInput("Enter your password", true, defaultForeground); // File URL to the profile value like: https://contoso.sharepoint.com/Shared%20Documents/sample.txt string fileUrl = GetInput("Enter the URL to the file located in your tenant", false, defaultForeground); // Get access to source tenant with tenant permissions using (var ctx = new ClientContext(tenantAdminUrl)) { //Provide count and pwd for connecting to the source var passWord = new SecureString(); foreach (char c in pwd.ToCharArray()) passWord.AppendChar(c); ctx.Credentials = new SharePointOnlineCredentials(userName, passWord); // Only to check connection and permission, could be removed ctx.Load(ctx.Web); ctx.ExecuteQuery(); string title = ctx.Web.Title; // Let's get started on the actual code!!! Office365Tenant tenant = new Office365Tenant(ctx); ctx.Load(tenant); ctx.ExecuteQuery(); /// /// /// /// /// /// /// /// /// /// DO import based on file whcih is already uploaded to tenant /// /// /// /// /// /// /// /// /// // Type of user identifier ["PrincipleName", "EmailAddress", "CloudId"] // in the User Profile Service. // In this case we use email as the identifier at the UPA storage ImportProfilePropertiesUserIdType userIdType = ImportProfilePropertiesUserIdType.Email; // Name of user identifier property in the JSON var userLookupKey = "IdName"; var propertyMap = new System.Collections.Generic.Dictionary<string, string>(); // First one is the file, second is the target at User Profile Service // Notice that we have here 2 custom properties in UPA called 'City' and 'Office' propertyMap.Add("Property1", "City"); propertyMap.Add("Property2", "Office"); // Returns a GUID, which can be used to see the status of the execution and end results var workItemId = tenant.QueueImportProfileProperties( userIdType, userLookupKey, propertyMap, fileUrl ); ctx.ExecuteQuery(); /// /// /// /// /// /// /// /// /// /// // CALL CHECK STATUS in OWN method with the received GUID /// /// /// /// /// /// /// /// /// /// CheckStatusOfRequestedProcess(ctx, workItemId.Value); // Just to pause and indicate that it's all done Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("\n-----------------------------------------"); Console.WriteLine("We are all done. Press enter to continue."); Console.ReadLine(); } }
/// <summary> /// Demonstrates how to check status for specific submission or for all submissions /// </summary> /// <param name="ctx"></param> /// <param name="workItemId"></param> private static void CheckStatusOfRequestedProcess(ClientContext ctx, Guid workItemId) { /// /// CHECK STATUS of the property job with GUID - notice that there's additional logs in the folder as well /// // Check status of specific request based on received GUID Office365Tenant tenant = new Office365Tenant(ctx); var job = tenant.GetImportProfilePropertyJob(workItemId); ctx.Load(job); ctx.ExecuteQuery(); Console.Write("\n--\n"); Console.WriteLine(string.Format("ID: {0} - Request status: {1} - Error status: {2}", job.JobId, job.State.ToString(), job.Error.ToString())); Console.Write("\n--\n"); /// /// Get list of all jobs in the tenant /// var jobs = tenant.GetImportProfilePropertyJobs(); ctx.Load(jobs); ctx.ExecuteQuery(); foreach (var item in jobs) { Console.WriteLine(string.Format("ID: {0} - Request status: {1} - Error status: {2}", item.JobId, item.State.ToString(), item.Error.ToString())); } }
static void Main(string[] args) { //get governance variables such as warning duration and cutoff duration int warningDuration = Convert.ToInt32(ConfigurationManager.AppSettings["WarningDuration"]); int cutoffDuration = Convert.ToInt32(ConfigurationManager.AppSettings["CutoffDuration"]); string tenantName = ConfigurationManager.AppSettings["TenantName"]; string tenantUpnDomain = ConfigurationManager.AppSettings["TenantUpnDomain"]; Uri tenantAdminUri = new Uri(String.Format("https://{0}-admin.sharepoint.com", tenantName)); string webUrl = ""; #if DEBUG webUrl = "http://localhost:25440/"; #else webUrl = "https://sposharing.azurewebsites.net/"; #endif foreach (var siteUrl in sites) { //initialize a process date for this site and clean up to match SQL percision DateTime processDate = DateTime.Now; string stringTicks = processDate.Ticks.ToString(); int adjustmentTicks = Convert.ToInt32(stringTicks.Substring(stringTicks.Length - 5)); processDate = processDate.Subtract(TimeSpan.FromTicks(adjustmentTicks)); //use O365 Tenant Administration to get all the external sharing details for this site List<ExternalShareDetails> shares = new List<ExternalShareDetails>(); string adminRealm = TokenHelper.GetRealmFromTargetUrl(tenantAdminUri); var adminToken = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, tenantAdminUri.Authority, adminRealm).AccessToken; using (var clientContext = TokenHelper.GetClientContextWithAccessToken(tenantAdminUri.ToString(), adminToken)) { //load the tenant var tenant = new Office365Tenant(clientContext); clientContext.Load(tenant); clientContext.ExecuteQuery(); //initalize varables to going through the paged results int position = 0; bool hasMore = true; while (hasMore) { //get external users 50 at a time (this is the limit and why we are paging) var externalUsers = tenant.GetExternalUsersForSite(siteUrl, position, 50, String.Empty, SortOrder.Descending); clientContext.Load(externalUsers, i => i.TotalUserCount); clientContext.Load(externalUsers, i => i.ExternalUserCollection); clientContext.ExecuteQuery(); //convert each external user to our own entity foreach (var extUser in externalUsers.ExternalUserCollection) { position++; shares.Add(new ExternalShareDetails() { AcceptedAs = extUser.AcceptedAs.ToLower(), DisplayName = extUser.DisplayName, InvitedAs = extUser.InvitedAs.ToLower(), InvitedBy = (String.IsNullOrEmpty(extUser.InvitedBy)) ? null : extUser.InvitedBy.ToLower(), UserId = extUser.UserId, WhenCreated = extUser.WhenCreated }); } //determine if we have more pages to process hasMore = (externalUsers.TotalUserCount > position); } } //get an AppOnly accessToken and clientContext for the site collection Uri siteUri = new Uri(siteUrl); string realm = TokenHelper.GetRealmFromTargetUrl(siteUri); string accessToken = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, siteUri.Authority, realm).AccessToken; using (var clientContext = TokenHelper.GetClientContextWithAccessToken(siteUri.ToString(), accessToken)) { //first we need to load the site to determine if external sharing is enabled (Site.ShareByEmailEnabled) var site = clientContext.Site; var siteOwner = clientContext.Site.Owner; clientContext.Load(site); clientContext.Load(siteOwner); //include the site owner in case the share "InvitedBy" is null...we will send them email instead clientContext.ExecuteQuery(); //validate that the site has sharing turned on if (site.ShareByEmailEnabled) { //process all of the shares foreach (var externalShare in shares) { //look for an existing record in the database using (ExternalSharingDataEntities entities = new ExternalSharingDataEntities()) { var shareRecord = entities.ExternalShares.FirstOrDefault(i => i.LoginName.Equals(externalShare.AcceptedAs)); if (shareRecord != null) { //Update LastProcessedDate column of the record with the processDate shareRecord.LastProcessedDate = processDate; entities.SaveChanges(); } else { //get the original share date var details = getREST(accessToken, String.Format("{0}/_api/Web/SiteUserInfoList/Items({1})/FieldValuesAsText", siteUrl, externalShare.UserId)); externalShare.WhenCreated = Convert.ToDateTime(details.Descendants(ns + "Created").FirstOrDefault().Value); shareRecord = new ExternalShare() { UniqueIdentifier = Guid.NewGuid(), SiteCollectionUrl = siteUrl.ToLower(), LoginName = externalShare.AcceptedAs, UserId = externalShare.UserId, InvitedBy = (String.IsNullOrEmpty(externalShare.InvitedBy)) ? siteOwner.Email : externalShare.InvitedBy, OriginalSharedDate = externalShare.WhenCreated, LastProcessedDate = processDate }; entities.ExternalShares.Add(shareRecord); entities.SaveChanges(); } //check if the record falls inside the warnings double daysActive = processDate.Subtract(shareRecord.OriginalSharedDate).TotalDays; if (shareRecord.RefreshSharedDate != null) daysActive = processDate.Subtract((DateTime)shareRecord.RefreshSharedDate).TotalDays; //check for cutoff if (daysActive > cutoffDuration) { //remove the SPUser from the site clientContext.Web.SiteUsers.RemoveById(externalShare.UserId); clientContext.ExecuteQuery(); //delete the record entities.ExternalShares.Remove(shareRecord); entities.SaveChanges(); } else if (daysActive > warningDuration) { int expiresIn = Convert.ToInt32(cutoffDuration - daysActive); //send email to InvitedBy (which will be site collection owner when null) EmailProperties email = new EmailProperties(); email.To = new List<String>() { shareRecord.InvitedBy }; email.Subject = String.Format("Action Required: External sharing with {0} about to expire", externalShare.AcceptedAs); email.Body = String.Format("<html><body><p>You are receiving this message because you are the site administrator of <a href='{0}'>{0}</a> OR you shared it with {1}. The external access for this user is set to expire in {2} days. Use the link below to view additional details and perform actions to revoke OR extend access for another {3} days. If you do not act on this notice, the external access for this user to terminate in {2} days.</p><ul><li><a href='{4}Details/{5}'>View Details</a></li><li><a href='{4}Extend/{5}'>Extend {3} Days</a></li><li><a href='{4}Revoke/{5}'>Revoke Access</a></li></ul></body></html>", siteUrl, externalShare.AcceptedAs, expiresIn.ToString(), cutoffDuration.ToString(), webUrl, shareRecord.UniqueIdentifier); Utility.SendEmail(clientContext, email); clientContext.ExecuteQuery(); } } } } } //delete all database records for this site that have an older processDate...these should represent external users deleted by manually using (ExternalSharingDataEntities entities = new ExternalSharingDataEntities()) { var cleanUpRecords = entities.ExternalShares.Where(i => i.SiteCollectionUrl.Equals(siteUrl.ToLower()) && i.LastProcessedDate < processDate); foreach (var record in cleanUpRecords) { entities.ExternalShares.Remove(record); entities.SaveChanges(); } } } }