In many cases Managed Metadata fields are used to attach metadata to documents/items and provisioning these fields without using Full-Trust-Code is possible using CSOM and CAML (either from an app or from a remote tool). This solution works for both SharePoint 2013 on-premise and O365.
I’ve created the method “AddTaxonomyField” to provision the TaxonomyField because a Taxonomy field exists of two fields, whereof one hidden and the two are connected to each other. The method returns the TaxonomyField that is just created so you can continue configuring it.
For this code to work you need to reference the Microsoft.SharePoint.Client, Microsoft.SharePoint.Client.Runtime and Microsoft.SharePoint.Client.Taxonomy.
internal TaxonomyField AddTaxonomyField(string displayName, string internalFieldName, string groupName, bool required, bool multi, bool showNewForm, bool showEditForm, bool showDisplayForm)
{
Guid noteFieldId = Guid.NewGuid();
Guid taxFieldId = Guid.NewGuid();
string noteField = string.Format(@"
<Field ID=""{0}""
Name=""{1}TaxHTField0""
StaticName=""{1}TaxHTField0""
DisplayName=""{2}_0""
Type=""Note""
Required=""FALSE""
Hidden=""TRUE""
ShowInViewForms=""FALSE""
CanToggleHidden=""TRUE""
Overwrite=""TRUE""
SourceID=""http://schemas.microsoft.com/sharepoint/v3""/>",
noteFieldId,
internalFieldName,
displayName,
taxFieldId,
groupName,
required.ToString().ToUpper(),
multi.ToString().ToUpper(),
showEditForm.ToString().ToUpper(),
showNewForm.ToString().ToUpper(),
showDisplayForm.ToString().ToUpper());
string taxField = string.Format(@"
<Field ID=""{3}""
Name=""{1}""
StaticName=""{1}""
Group=""{4}""
DisplayName=""{2}""
Type=""TaxonomyFieldType""
ShowField=""Term1033""
Required =""{5}""
Mult=""{6}""
Overwrite=""TRUE""
ShowInDisplayForm=""{9}""
ShowInEditForm=""{7}""
ShowInNewForm=""{8}""
ShowInFileDlg=""TRUE""
SourceID=""http://schemas.microsoft.com/sharepoint/v3"">
<Default></Default>
<Customization>
<ArrayOfProperty>
<Property>
<Name>IsPathRendered</Name>
<Value xmlns:q7=""http://www.w3.org/2001/XMLSchema"" p4:type=""q7:boolean"" xmlns:p4=""http://www.w3.org/2001/XMLSchema-instance"">
false
</Value>
</Property>
<Property>
<Name>TextField</Name>
<Value xmlns:q6=""http://www.w3.org/2001/XMLSchema"" p4:type=""q6:string"" xmlns:p4=""http://www.w3.org/2001/XMLSchema-instance"">
{0}
</Value>
</Property>
</ArrayOfProperty>
</Customization>
</Field>",
noteFieldId,
internalFieldName,
displayName,
taxFieldId,
groupName,
required.ToString().ToUpper(),
multi.ToString().ToUpper(),
showEditForm.ToString().ToUpper(),
showNewForm.ToString().ToUpper(),
showDisplayForm.ToString().ToUpper());
Field noteSPField = Context.Web.Fields.AddFieldAsXml(noteField, true, AddFieldOptions.AddFieldInternalNameHint);
Context.Load(noteSPField);
Field taxSPfield = Context.Web.Fields.AddFieldAsXml(taxField, true, AddFieldOptions.AddFieldInternalNameHint);
Context.ExecuteQuery();
return Context.CastTo<TaxonomyField>(taxSPfield);
}
After creating the TaxonomyField you can update it’s settings (e.g. connect it to the Managed Metadata Service). Make sure to update the termstore related ID’s with your own (or retrieve them dynamically):
// Create the Taxonomy Field
TaxonomyField taxField = AddTaxonomyField("My Field", "MyField", "My Columns", true, false, true, true, true);
// Update these ID's with your own Managed Metadata ID's
Guid anchorId = new Guid("{12E721A6-576B-4842-9DE2-CFAB24DBBC16}");
Guid termStoreId = new Guid("{9362E986-1061-4BA1-988F-2698BEBD1D63}");
Guid termSetId = new Guid("{40729487-AC5B-40DF-87AB-DF172487DA41}");
taxField.AnchorId = anchorId;
// If this is set to true terms that are not validated will be created
taxField.CreateValuesInEditForm = false;
// If this is set to true the user will be given the option to add new terms
taxField.Open = false;
// Id of the term store
taxField.SspId = termStoreId;
// If this is set to a URL the items will link to that URL on display
taxField.TargetTemplate = string.Empty;
// This assumes you have a group and term set created. Normally you would pick the specific termset you want
taxField.TermSetId = termSetId;
taxField.UserCreated = false;
taxField.Update();
Context.ExecuteQuery();