SharePoint: Why won’t you let me delete a column? A case of “Cannot complete this action.”

Today I deployed a custom SharePoint column and list definition to our productive systems and added some extra columns to test some functionality. I then wanted to delete them again. Boom: ASP.net Error page. ULS told me: “Cannot complete this action. Please try again.“. Suffice to say: Trying again did not help.

Fired up the all-might Powershell and tried deleting the column from there:

PS C:\swFiles\DualConsult.SharePoint.SecretsList\Scripts> $field.Delete()
Exception calling "Delete" with "0" argument(s): "Cannot complete this action.
Please try again."
At line:1 char:14
+ $field.Delete <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

Nothing obvious turned up in ye grande search engine so I had a look what was different about this list and other lists. Turns out I had created a custom content type for the list and it was the only content type on the list (the standard “item” content type wasn’t part of the definition). Since I had used this pattern before I didn’t suspect anything wrong, but since I was stuck I went ahead and added the “item” content type.

SharePoint 2010 - List Content Types

SharePoint 2010 - List Content Types

Tried deleting the columns again and voila it worked. Interesting side note: Once I removed the “item” content type again, I could delete the second column I had created without any problems as well. SharePoint, you are strange.

SharePoint PeoplePicker is not displaying any AD Users? or “Why can’t I add user permissions in WSS?”

Today I was assigned an incident where the SharePoint administrators could not add permissions to a WSS site for a specific Active Directory user. The problem was the People Picker was not displaying any users – it just said “User not found” regardless of which substring I entered (and I ensured the substring was present in an existing Active Directory user’s name).

There are a couple of resources on the net for:

  • People Picker only shows a partial list of AD users
  • People Picker only shows a list of AD in specific OUs
  • People Picker has a bad day and just doesn’t like you today

But none applied to or solved my problem, my PP didn’t show any error, it just didn’t find any users.

One clue was that directly after the installation it actually worked and since then “nothing had changed”. Two existing users that were added after installation were still in the PeoplePicker cache, so I confirmed that it must have worked at some time in the past.

In Event Viewer I searched for errors during of shortly after my searched, but nothing obvious appeared. I then filtered for only “errors and warnings” and those that happened after the installation date and (apart from the classic DCOM permissions errors under a 2008 R2 installation) it showed me that about 6 days after installation the server started having NETLOGON errors along the lines of “Computer could not be authorized.” This continued until today. Turns out for some reason the computer account of the server in Active Directory got screwed up and after

  • detaching the server from the domain
  • manually deleting the computer account in Active Direcotry
  • and rejoining the server to the domain

everything worked again as usual.

HTH, Alex

Big>Days and StudentBig>Days 2009 – Session Files

Microsoft Big>Days and Student Big>Days are almost over and I haven’t uploaded my slides and source yet. For the Microsoft Big>Days you can find all our source code and much more at http://www.codeplex.com/bigdays09.

All my session files at the Student Big>Days: “ASP.NET MVC with SharpArchitecture” are available on SkyDrive.

Have fun, thanks for attending. Any questions, feel free to contact me!

Quick Tip: System Center Operation Manager R2 Beta “failed to lower Monad ExecutionPolicy security level…”

Those of you trying out the BETA of SCOM R2 may run into an error during installation telling you to search for “value 3″ in the setup log files.

Doing that may uncover an error along the lines of:

GetRegistyStringValue: RegQueryValueEx failed.. Error Code: 0x80070002.
SetRegistyStringValue: RegOpenKeyEx failed to open registry key. Error Code: 0x80070005.
SetMonadExecutionPolicy: Unable to lower Monad ExecutionPolicy security level..
Action ended 12:38:48: _SetMonadExecutionPolicy.E1C3A829_C8F3_443F_B5FE_0CE01DB47829. Return value 3.

The simple solution is to disable User Account Control.

Actually if you launch PowerShell and try the “Set-ExecutionPolicy RemoteSigned” you will also get an access denied on the registry when UAC is activated. Same cause, same solution.

PS: No, I haven’t turned into a System Engineer, still a dev. Might talk a bit about what I’m doing here soon…

Restoring a backup. Wait, where are my permissions? or “How to replace all permissions in one directory tree with the permissions from another identical tree”

Ok, I’m starting to get fond of these “a slice of reality” or “what am I going to show you” title blog posts…

So once again this piece of code is a bit drastic. It’s neatly written if you ask me and should show you how you can replace the file security settings (ACLs) with some other files security settings (same for directories). The only meaningfull place to do this (off the top of my head) is when you have two identical files and for some reason the one file has the correct permissions and the other doesn’t. But daily developer business is always full of these little request, and it’s like a personal search for the holy grail – you just have to give it a try.

So if you ever want to copy the exact permission structure from one directory tree to another (for example you used some copy tool that doesn’t copy the permissions) then you can use this little tool to do just that.

A word of warning (as always). Have a backup of your permissions (most probably you will, because you need to copy them from somewhere) and of course run this tool at your own risk. The binary is available here and is compiled from the source you see below. Have fun and tell me if you find bugs or if it’s useful.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Security.AccessControl; using System.Diagnostics; using System.Security.Principal; namespace AlexDuggleby.Tools.MirrorNTFSPermissions { class Program { #region Properties ///

/// The root we are copying from /// private static DirectoryInfo m_diFromRoot; /// /// The root we are copying to /// private static DirectoryInfo m_diToRoot; /// /// The log file to write to /// private static FileInfo m_fiLog; #endregion #region Main Operation static void Main(string[] args) { // Check args and output usage if required if (args.Length != 3) { OutputUsage(); System.Environment.Exit(-1); return; } if (PopulateAndCheckDirectoriesExist(args) && ConfirmUserAction()) { try { Trace.Listeners.Add(new ConsoleTraceListener()); Trace.Listeners.Add(new TextWriterTraceListener(m_fiLog.FullName)); WriteTraceHeader(); Console.Clear(); CopyPermissions(m_diFromRoot, m_diToRoot, true); WriteTraceFooter(); } catch (ApplicationException aex) { OutputError("Application", aex); } catch (Exception ex) { OutputError("Critical", ex); } } } private static void CopyDirectorySecurity(DirectoryInfo diFrom, DirectoryInfo diTo, bool skipRootNameCheck, out bool continueInThisDirectory) { continueInThisDirectory = true; try { // Name comparison if (diFrom.Name != diTo.Name && !skipRootNameCheck) throw new ApplicationException(string.Format("During processing the directory names changed! '{0}'-'{1}'", diFrom.Name, diTo.Name)); // Set my own permissions DirectorySecurity _dsFrom = Directory.GetAccessControl(diFrom.FullName, AccessControlSections.All); DirectorySecurity _dsTo = new DirectorySecurity(); _dsTo.SetSecurityDescriptorSddlForm(_dsFrom.GetSecurityDescriptorSddlForm(AccessControlSections.All)); Directory.SetAccessControl(diTo.FullName, _dsTo); // Tell the user what we are doing Trace.WriteLine(string.Format("Copying directory security from {0} to {1}", diFrom.FullName, diTo.FullName)); } catch (PathTooLongException _plex) { Trace.WriteLine(string.Format("[WARN] Path too long do continue with this directory '{0}'", diFrom.FullName)); continueInThisDirectory = false; // Abort because all files and subdirs will be affected } catch (Exception _ex) // General catch here, because we would like the batch to continue { Trace.WriteLine(string.Format("[ERROR] Error while doing current directory '{0}': {1}", diFrom.FullName, _ex.ToString())); // We will continue here, since error type is undefined. continueInThisDirectory = true; } } #endregion #region File/Directory I/O and Security private static void CopyPermissions(DirectoryInfo diFrom, DirectoryInfo diTo, bool skipRootNameCheck) { bool _continueInThisDirectory; CopyDirectorySecurity(diFrom, diTo, skipRootNameCheck, out _continueInThisDirectory); if (_continueInThisDirectory) { // Now set files foreach (FileInfo _fiTo in diTo.GetFiles()) { CopyFileSecurity(diFrom, _fiTo); } // Get all the -to- subdirs ... foreach (DirectoryInfo _toSubDir in diTo.GetDirectories()) { // ... and check if they also exist in the from directory DirectoryInfo _fromSubDir = diFrom.GetDirectories(_toSubDir.Name, SearchOption.TopDirectoryOnly).FirstOrDefault(d => d.Name == _toSubDir.Name); if (_fromSubDir != null) { // Recursive CopyPermissions(_fromSubDir, _toSubDir, false); } } } } private static void CopyFileSecurity(DirectoryInfo diFrom, FileInfo fiTo) { // Turn the paths into relative paths based on the root dirs we started from string _relativeFromPath = fiTo.Directory.FullName.ToLower() .Replace(m_diToRoot.FullName.ToLower(), ""); string _relativeToPath = diFrom.FullName.ToLower() .Replace(m_diFromRoot.FullName.ToLower(), ""); // now compare those relative paths, just to be sure if (_relativeFromPath != _relativeToPath) throw new ApplicationException(string.Format( "Subdirectories went out of sync during processing! '{0}'-'{1}'", _relativeFromPath, _relativeToPath)); FileInfo _fiFrom = new FileInfo(Path.Combine(diFrom.FullName, fiTo.Name)); if (_fiFrom.Exists) // if it doesn't exist in the from we will ignore... { // Copy the file permissions FileSecurity _fsFrom = File.GetAccessControl(_fiFrom.FullName); // Sometimes there are now access rules on the item, so only the owner can access it if (File.GetAccessControl(fiTo.FullName).GetAccessRules(true, true, typeof(NTAccount)).Count == 0) { // Tell the user what we are doing Trace.WriteLine(string.Format("File can only be accessed by owner: {0}", fiTo.FullName)); } else { try { // Copy the file permissions FileSecurity _fsTo = new FileSecurity(); _fsTo.SetSecurityDescriptorSddlForm(_fsFrom.GetSecurityDescriptorSddlForm(AccessControlSections.All)); File.SetAccessControl(fiTo.FullName, _fsTo); // Tell the user what we are doing Trace.WriteLine(string.Format("Copying file security from {0} to {1}", _fiFrom.FullName, fiTo.FullName)); } catch (Exception _ex) { // Tell the user what went wrong Trace.WriteLine(string.Format("[ERROR] Error while copying file security from {0} to {1}: {2}", _fiFrom.FullName, fiTo.FullName, _ex.ToString())); } } } else { Trace.WriteLine(string.Format("File did not exist in original tree: {0}", fiTo.FullName)); } } #endregion #region User I/O and Tracing private static void WriteTraceHeader() { Trace.WriteLine("*****************************************"); Trace.WriteLine("Mirror NTFS Permissions"); Trace.WriteLine("Version 1.0 - 27.05.2008"); Trace.WriteLine("Alex Duggleby - http://alexduggleby.com"); Trace.WriteLine("*****************************************"); Trace.WriteLine("== New run @ " + DateTime.Now.ToString() + ""); Trace.WriteLine("== From : " + m_diFromRoot.FullName + ""); Trace.WriteLine("== To : " + m_diToRoot.FullName + ""); Trace.WriteLine("========================================="); } private static void WriteTraceFooter() { Console.ForegroundColor = ConsoleColor.Green; Trace.WriteLine("========================================="); Trace.WriteLine("Finished!"); Trace.WriteLine("========================================="); Console.ForegroundColor = ConsoleColor.White; Trace.Flush(); } private static void OutputError(string errType, Exception ex) { Console.ForegroundColor = ConsoleColor.Red; string _s = string.Format("***********************{1}{2} error: {0}", ex.ToString(), Environment.NewLine, errType); Trace.WriteLine(_s); Console.ForegroundColor = ConsoleColor.White; OutputPressKeyToExit(); Trace.Flush(); } private static void OutputPressKeyToExit() { Console.WriteLine(); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("{0}Press any key to exit", Environment.NewLine); Console.ReadKey(); } private static void OutputUsage() { Console.WriteLine("Mirror NTFS Permissions"); Console.WriteLine("by Alex Duggleby - http://alexduggleby.com"); Console.WriteLine(); Console.WriteLine("Usage: application.exe "); Console.WriteLine("Replaces all permissions on all subdirectory and files in path2 with those from path1 if they exist there."); Console.WriteLine("The is used to write the appplications trace output."); Console.WriteLine(); } private static bool ConfirmUserAction() { Console.WriteLine("Do you wish to copy all permissions form '{0}' to '{1}'? [y/n]", m_diFromRoot.FullName, m_diToRoot.FullName); if (Console.ReadKey().KeyChar.ToString().ToLower() != "y") { Console.WriteLine("User aborted!"); OutputPressKeyToExit(); System.Environment.Exit(-1); return false; } return true; } #endregion #region Prechecks private static bool PopulateAndCheckDirectoriesExist(string[] args) { string _dirFrom = args[0]; string _dirTo = args[1]; string _logFile = args[2]; if (_dirFrom.EndsWith("\\")) _dirFrom = _dirFrom.Substring(0, _dirFrom.Length - 1); if (_dirTo.EndsWith("\\")) _dirTo = _dirTo.Substring(0, _dirTo.Length - 1); m_diFromRoot = new DirectoryInfo(_dirFrom); m_diToRoot = new DirectoryInfo(_dirTo); m_fiLog = new FileInfo(_logFile); if (!m_fiLog.Exists) { try { m_fiLog.Create().Close(); } catch (Exception _ex) { Console.WriteLine("Could not create log file: " + _ex.ToString()); } m_fiLog.Refresh(); } if (!m_diFromRoot.Exists) Console.WriteLine("Directory '{0}' does not exist", m_diFromRoot.FullName); if (!m_diToRoot.Exists) Console.WriteLine("Directory '{0}' does not exist", m_diToRoot.FullName); if (!m_fiLog.Exists) Console.WriteLine("File '{0}' does not exist", m_fiLog.FullName); return m_diFromRoot.Exists && m_diToRoot.Exists && m_fiLog.Exists; } #endregion } }