From 4c9e2513fed4c2b41c831091ccb36ba3314c0a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Granfeldt?= Date: Thu, 13 Sep 2018 22:45:08 +0200 Subject: [PATCH] v5.5.3.1309 * _New feature_ - added support for $Schema parameter. Gets passed to Import and Export script * Optimized tracing and fixed minor bugs in tracing for errors and warnings --- .../MA.Export.cs | 19 +- .../MA.Impersonation.cs | 23 - .../MA.Import.cs | 1005 +++++++++-------- .../MA.Parameters.cs | 5 - .../MA.Password.cs | 11 - .../MA.PowerShell.cs | 8 +- .../MA.Schema.cs | 2 - Granfeldt.PowerShell.ManagementAgent/MA.cs | 14 +- .../Properties/AssemblyInfo.cs | 4 +- .../Tracer.cs | 116 +- 10 files changed, 582 insertions(+), 625 deletions(-) diff --git a/Granfeldt.PowerShell.ManagementAgent/MA.Export.cs b/Granfeldt.PowerShell.ManagementAgent/MA.Export.cs index 523fa3a..5f52dcc 100644 --- a/Granfeldt.PowerShell.ManagementAgent/MA.Export.cs +++ b/Granfeldt.PowerShell.ManagementAgent/MA.Export.cs @@ -1,4 +1,7 @@ -using Microsoft.MetadirectoryServices; +// july 5, 2018, soren granfeldt +// - added schema psobject as parameter to export script + +using Microsoft.MetadirectoryServices; using System; using System.Collections; using System.Collections.Generic; @@ -30,17 +33,15 @@ int IMAExtensible2CallExport.ExportMaxPageSize } void IMAExtensible2CallExport.OpenExportConnection(System.Collections.ObjectModel.KeyedCollection configParameters, Schema types, OpenExportConnectionRunStep exportRunStep) { - Tracer.IndentLevel = 0; Tracer.Enter("openexportconnection"); - Tracer.Indent(); try { InitializeConfigParameters(configParameters); OpenRunspace(); - schema = types; + InitializeSchemaVariables(types); - exportType = exportRunStep.ExportType; + exportType = exportRunStep.ExportType; Tracer.TraceInformation("export-type '{0}'", exportType); exportBatchSize = exportRunStep.BatchSize; Tracer.TraceInformation("export-batch-size '{0}'", exportBatchSize); @@ -52,14 +53,12 @@ void IMAExtensible2CallExport.OpenExportConnection(System.Collections.ObjectMode } finally { - Tracer.Unindent(); Tracer.Exit("openexportconnection"); } } PutExportEntriesResults IMAExtensible2CallExport.PutExportEntries(IList csentries) { Tracer.Enter("putexportentries"); - Tracer.Indent(); PutExportEntriesResults exportEntries = new PutExportEntriesResults(); PSDataCollection exportPipeline = new PSDataCollection(); try @@ -69,8 +68,9 @@ PutExportEntriesResults IMAExtensible2CallExport.PutExportEntries(IList importResults; - List csentryqueue = new List(); - - public int ImportDefaultPageSize - { - get { return 100; } - } - public int ImportMaxPageSize - { - get { return 10000; } - } - - Schema schema; - - public OpenImportConnectionResults OpenImportConnection(System.Collections.ObjectModel.KeyedCollection configParameters, Schema types, OpenImportConnectionRunStep openImportRunStep) - { - Tracer.IndentLevel = 0; - Tracer.Enter("openimportconnection"); - Tracer.Indent(); - try - { - Tracer.TraceInformation("getting-schema"); - Tracer.Indent(); - try - { - foreach (SchemaType type in types.Types) - { - foreach (SchemaAttribute attr in type.AnchorAttributes) - { - Tracer.TraceInformation("{0}-anchor-attribute {1} [{2}]", type.Name, attr.Name, attr.DataType); - objectTypeAnchorAttributeNames.Add(type.Name, attr.Name); - } - foreach (SchemaAttribute attr in type.Attributes) - { - Tracer.TraceInformation("{0}-attribute {1} [{2}]", type.Name, attr.Name, attr.DataType); - } - } - schema = types; - } - catch (Exception ex) - { - Tracer.TraceError("getting-schema", ex); - } - finally - { - Tracer.Unindent(); - Tracer.TraceInformation("got-schema"); - } - - InitializeConfigParameters(configParameters); - - //SetupImpersonationToken(); - - OpenRunspace(); - - Tracer.TraceInformation("resetting-pipeline-results-and-counters"); - importResults = new List(); - pageToken = ""; - - OpenImportConnectionResults oicr = new OpenImportConnectionResults(); - ImportRunStepPageSize = openImportRunStep.PageSize; - Tracer.TraceInformation("openimportrunstep-pagesize '{0}'", ImportRunStepPageSize); - - oicr.CustomData = openImportRunStep.ImportType == OperationType.Full ? "" : openImportRunStep.CustomData; - Tracer.TraceInformation("openimportrunstep-customdata '{0}'", oicr.CustomData); - - importOperationType = openImportRunStep.ImportType; - Tracer.TraceInformation("openimportrunstep-importtype '{0}'", importOperationType); - - return oicr; - } - catch (Exception ex) - { - Tracer.TraceError("openimportconnection", ex); - throw; - } - finally - { - Tracer.Unindent(); - Tracer.Exit("openimportconnection"); - } - } - public GetImportEntriesResults GetImportEntries(GetImportEntriesRunStep importRunStep) - { - Tracer.Enter("getimportentries"); - Tracer.Indent(); - try - { - #region call import script - - // if results is null, then this is the first time that we're called, - // so call script and get pipeline object and custom data - Tracer.TraceInformation("more-to-import '{0}'", MoreToImport); - - if (MoreToImport) - { - MoreToImport = false; // make sure we set more-to-import to false; could be overwritten further down if pagedimports is true, though - - // on first call, we set customdata to value from last successful run - returnedCustomData = importRunStep.CustomData; - - Command cmd = new Command(Path.GetFullPath(ImportScript)); - cmd.Parameters.Add(new CommandParameter("User", Username)); - cmd.Parameters.Add(new CommandParameter("Password", Password)); - cmd.Parameters.Add(new CommandParameter("Credentials", GetSecureCredentials())); - cmd.Parameters.Add(new CommandParameter("OperationType", importOperationType.ToString())); - cmd.Parameters.Add(new CommandParameter("UsePagedImport", UsePagedImport)); - cmd.Parameters.Add(new CommandParameter("PageSize", ImportRunStepPageSize)); - - Tracer.TraceInformation("setting-custom-data '{0}'", importRunStep.CustomData); - powershell.Runspace.SessionStateProxy.SetVariable("RunStepCustomData", importRunStep.CustomData); - Tracer.TraceInformation("setting-page-token '{0}'", pageToken); - powershell.Runspace.SessionStateProxy.SetVariable("PageToken", pageToken); - - importResults = InvokePowerShellScript(cmd, null).ToList(); - - returnedCustomData = powershell.Runspace.SessionStateProxy.GetVariable("RunStepCustomData"); - pageToken = powershell.Runspace.SessionStateProxy.GetVariable("PageToken"); - - Tracer.TraceInformation("page-token-returned '{0}'", pageToken == null ? "(null)" : pageToken); - Tracer.TraceInformation("custom-data returned '{0}'", returnedCustomData); - Tracer.TraceInformation("number-of-object(s)-in-pipeline {0:n0}", importResults.Count); - - if (UsePagedImport) - { - Tracer.TraceError("paged-import-not-supported-currently"); - object moreToImportObject = powershell.Runspace.SessionStateProxy.GetVariable("MoreToImport"); - if (moreToImportObject == null) - { - Tracer.TraceError("For paged imports, the global variable 'MoreToImport' must be set to 'true' or 'false'"); - } - else - { - Tracer.TraceInformation("MoreToImport-value-returned '{0}'", moreToImportObject); - if (bool.TryParse(moreToImportObject == null ? bool.FalseString : moreToImportObject.ToString(), out MoreToImport)) - { - Tracer.TraceInformation("paged-import-setting-MoreToImport-to '{0}'", MoreToImport); - } - else - { - Tracer.TraceError("Value returned in MoreToImport must be a boolean with value of 'true' or 'false'"); - } - } - } - else - { - MoreToImport = false; - Tracer.TraceInformation("non-paged-import-setting-MoreToImport-to '{0}'", MoreToImport); - } - - } - #endregion - - #region parse returned objects - if (importResults != null && importResults.Count > 0) - { - List importResultsBatch = importResults.Take(ImportRunStepPageSize).ToList(); - if (importResults.Count > ImportRunStepPageSize) - { - importResults.RemoveRange(0, importResultsBatch.Count); - } - else - { - importResults.Clear(); - } - Tracer.TraceInformation("converting-objects-to-csentrychange {0:n0}", importResultsBatch.Count); - foreach (PSObject obj in importResultsBatch) - { - HashSet attrs = new HashSet(); - - Tracer.TraceInformation("start-connector-space-object"); - Tracer.Indent(); - try - { - CSEntryChange csobject = CSEntryChange.Create(); - - if (obj.BaseObject.GetType() != typeof(System.Collections.Hashtable)) - { - Tracer.TraceWarning("invalid-object-in-pipeline '{0}'", obj.BaseObject.GetType()); - continue; - } - - object AnchorValue = null; - string AnchorAttributeName = null; - string objectDN = null; - string objectClass = ""; // should be string to prevent null exceptions - string changeType = null; - string ErrorName = null; - string ErrorDetail = null; - MAImportError ImportErrorType = MAImportError.Success; // assume no error - Hashtable hashTable = (Hashtable)obj.BaseObject; - - #region get control values - Tracer.TraceInformation("start-getting-control-values"); - Tracer.Indent(); - foreach (string key in hashTable.Keys) - { - if (key.Equals(Constants.ControlValues.ObjectClass, StringComparison.OrdinalIgnoreCase) || key.Equals(Constants.ControlValues.ObjectClassEx, StringComparison.OrdinalIgnoreCase)) - { - objectClass = (string)hashTable[key]; - Tracer.TraceInformation("got-objectclass {0}, {1}", objectClass, key); - continue; - } - if (key.Equals(Constants.ControlValues.DN, StringComparison.OrdinalIgnoreCase)) - { - objectDN = (string)hashTable[key]; - Tracer.TraceInformation("got-dn {0}, {1}", objectDN, key); - continue; - } - if (key.Equals(Constants.ControlValues.ChangeType, StringComparison.OrdinalIgnoreCase) || key.Equals(Constants.ControlValues.ChangeTypeEx, StringComparison.OrdinalIgnoreCase)) - { - changeType = (string)hashTable[key]; - Tracer.TraceInformation("got-changetype {0}, {1}", changeType, key); - continue; - } - if (key.Equals(Constants.ControlValues.ErrorName, StringComparison.OrdinalIgnoreCase)) - { - ErrorName = (string)hashTable[key]; - Tracer.TraceInformation("got-errorname {0}, {1}", ErrorName, key); - continue; - } - if (key.Equals(Constants.ControlValues.ErrorDetail, StringComparison.OrdinalIgnoreCase)) - { - ErrorDetail = (string)hashTable[key]; - Tracer.TraceInformation("got-errordetail {0}, {1}", ErrorDetail, key); - continue; - } - } - - if (string.IsNullOrEmpty(objectClass)) - { - Tracer.TraceError("missing-objectclass"); - ImportErrorType = MAImportError.ImportErrorCustomContinueRun; - ErrorName = "missing-objectclass-value"; - ErrorDetail = "No value provided for objectclass attribute"; - } - else - { - AnchorAttributeName = objectTypeAnchorAttributeNames[objectClass] == null ? "" : (string)objectTypeAnchorAttributeNames[objectClass]; - if (string.IsNullOrEmpty(AnchorAttributeName)) - { - ImportErrorType = MAImportError.ImportErrorInvalidAttributeValue; - ErrorName = "invalid-objecttype"; - ErrorDetail = "Objecttype not defined in schema"; - } - - foreach (string key in hashTable.Keys) - { - if (key.Equals(AnchorAttributeName, StringComparison.OrdinalIgnoreCase)) - { - AnchorValue = hashTable[key]; - Tracer.TraceInformation("got-anchor {0}, {1}", AnchorValue, key); - break; - } - } - } - Tracer.Unindent(); - Tracer.TraceInformation("end-getting-control-values"); - - if (AnchorValue == null) - { - Tracer.TraceError("missing-anchor"); - ImportErrorType = MAImportError.ImportErrorCustomContinueRun; - ErrorName = "missing-anchor-value"; - ErrorDetail = "No value provided for anchor attribute"; - } - - if (AnchorValue != null && string.IsNullOrEmpty(objectDN)) - { - Tracer.TraceInformation("setting-anchor-as-dn {0}", AnchorValue); - objectDN = AnchorValue.ToString(); - } - - if (!string.IsNullOrEmpty(ErrorName)) - { - ImportErrorType = MAImportError.ImportErrorCustomContinueRun; - if (string.IsNullOrEmpty(ErrorDetail)) - { - ErrorDetail = "No error details provided"; - } - } - #endregion control values - - #region return invalid object - if (ImportErrorType != MAImportError.Success) - { - Tracer.TraceInformation("returning-invalid-object"); - if (AnchorValue != null) - { - csobject.AnchorAttributes.Add(AnchorAttribute.Create(AnchorAttributeName, AnchorValue)); - } - csobject.ObjectModificationType = ObjectModificationType.Add; - if (!string.IsNullOrEmpty(objectClass)) - { - try - { - csobject.ObjectType = objectClass; - } - catch (NoSuchObjectTypeException otEx) - { - Tracer.TraceError("no-such-object '{0}'", otEx); - } - } - if (!string.IsNullOrEmpty(objectClass)) - { - csobject.DN = objectDN; - } - Tracer.TraceError("invalid-object dn: {0}, type: {1}, name: {2}, details: {3} ", objectDN, ImportErrorType, ErrorName, ErrorDetail); - csobject.ErrorCodeImport = ImportErrorType; - csobject.ErrorName = ErrorName; - csobject.ErrorDetail = ErrorDetail; - csentryqueue.Add(csobject); - continue; - } - #endregion - - #region return deleted object - // we must set ObjectModificationType before any other attributes; otherwise it will default to 'Add' - if (!string.IsNullOrEmpty(changeType) && changeType.Equals("delete", StringComparison.OrdinalIgnoreCase)) - { - Tracer.TraceInformation("returning-deleted-object"); - Tracer.TraceInformation("change-type {0}", changeType); - csobject.ObjectModificationType = ObjectModificationType.Delete; - csobject.ObjectType = objectClass; - csobject.DN = objectDN; - - // we need to get the object anchor value for the deletion - csobject.AnchorAttributes.Add(AnchorAttribute.Create(AnchorAttributeName, AnchorValue)); - csentryqueue.Add(csobject); - continue; - } - #endregion - - #region returned live object - Tracer.TraceInformation("returning-valid-object"); - csobject.ObjectModificationType = ObjectModificationType.Add; - csobject.ObjectType = objectClass; - csobject.DN = objectDN; - csobject.AnchorAttributes.Add(AnchorAttribute.Create(AnchorAttributeName, AnchorValue)); - foreach (string key in hashTable.Keys) - { - try - { - if (Regex.IsMatch(key, string.Format(@"^(objectClass|\[objectclass\]|changeType|\[changetype\]|\[DN\]|\[ErrorName\]|\[ErrorDetail\]|{0})$", AnchorAttributeName), RegexOptions.Compiled | RegexOptions.IgnoreCase)) - { - Tracer.TraceInformation("skip-control-value {0}", key); - continue; - } - if (hashTable[key] == null) - { - Tracer.TraceInformation("skip-null-value-for '{0}'", key); - continue; - } - SchemaAttribute sa = schema.Types[objectClass].Attributes[key]; - Tracer.TraceInformation("attribute: {0} (type {1}, {2}): '{3}'", key, sa.DataType, sa.IsMultiValued ? "multi-value" : "single-value", hashTable[key]); - if (sa.IsMultiValued) - { - //Tracer.TraceInformation("add-multivalue '{0}' [{1}]", key, hashTable[key].GetType()); - List mvs = new List(); - if (hashTable[key].ToString().EndsWith("[]")) - { - mvs.AddRange((object[])hashTable[key]); - } - else - { - mvs.Add(hashTable[key]); - } - csobject.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(key, mvs)); - } - else - { - csobject.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(key, hashTable[key])); - } - - } - catch (KeyNotFoundException keyexception) - { - Tracer.TraceError("attribute-is-not-defined-for '{0}' / '{1}' ({2})", key, objectClass, keyexception.ToString()); - } - } - #endregion - - if (csobject.ErrorCodeImport != MAImportError.Success) - { - Tracer.TraceError("defective-csentrychange id: {0}, dn: {1}, errorcode: {2}, error: {3}, details: {4}", csobject.Identifier, csobject.DN, csobject.ErrorCodeImport, csobject.ErrorName, csobject.ErrorDetail); - } - Tracer.TraceInformation("returning-csentry dn: {0}, id: {1}", csobject.DN, csobject.Identifier); - csentryqueue.Add(csobject); - } - catch (Exception ex) - { - Tracer.TraceError("creating-csentrychange", ex); - } - finally - { - Tracer.Unindent(); - Tracer.TraceInformation("end-connector-space-object"); - } - } - // clearing results for next loop - importResultsBatch.Clear(); - } - #endregion - - #region dequeue csentries - - GetImportEntriesResults importReturnInfo = null; - - Tracer.TraceInformation("total-import-object(s)-left {0:n0}", importResults.Count); - Tracer.TraceInformation("total-connector-space-object(s)-left {0:n0}", csentryqueue.Count); - - List batch = csentryqueue.Take(ImportRunStepPageSize).ToList(); - if (csentryqueue.Count > ImportRunStepPageSize) - { - csentryqueue.RemoveRange(0, batch.Count); - } - else - { - csentryqueue.Clear(); - } - importReturnInfo = new GetImportEntriesResults(); - importReturnInfo.MoreToImport = MoreToImport || importResults.Count > 0 || (csentryqueue.Count > 0); - importReturnInfo.CustomData = returnedCustomData == null ? "" : returnedCustomData.ToString(); - importReturnInfo.CSEntries = batch; - - Tracer.TraceInformation("should-return-for-more {0}", importReturnInfo.MoreToImport); - Tracer.TraceInformation("custom-data '{0}'", importReturnInfo.CustomData); - Tracer.TraceInformation("connector-space-object(s)-returned {0:n0}", importReturnInfo.CSEntries.Count); - - return importReturnInfo; - - #endregion - } - catch (Exception ex) - { - Tracer.TraceError("getimportentries", ex); - throw; - } - finally - { - Tracer.Unindent(); - Tracer.Exit("getimportentries"); - } - } - public CloseImportConnectionResults CloseImportConnection(CloseImportConnectionRunStep importRunStep) - { - Tracer.Enter("closeimportconnectionresults"); - Tracer.Indent(); - try - { - CloseRunspace(); - - //RevertImpersonation(); - - CloseImportConnectionResults cicr = new CloseImportConnectionResults(); - Tracer.TraceInformation("custom-data {0}", importRunStep.CustomData); - Tracer.TraceInformation("close-reason {0}", importRunStep.Reason); - if (importRunStep.Reason == CloseReason.Normal) - { - cicr.CustomData = importRunStep.CustomData; - } - Dispose(); - return cicr; - } - catch (Exception ex) - { - Tracer.TraceError("closeimportconnection", ex); - throw; - } - finally - { - Tracer.Unindent(); - Tracer.Exit("closeimportconnectionresults"); - } - } - - } + public partial class PowerShellManagementAgent : IDisposable, IMAExtensible2GetCapabilities, IMAExtensible2GetSchema, IMAExtensible2GetParameters, IMAExtensible2CallImport, IMAExtensible2CallExport, IMAExtensible2Password + { + Hashtable objectTypeAnchorAttributeNames = new Hashtable(); + + OperationType importOperationType; + int ImportRunStepPageSize; + bool MoreToImport = true; + object returnedCustomData = ""; + object pageToken; + bool UsePagedImport = false; + string ImportScript = null; + + List importResults; + List csentryqueue = new List(); + + public int ImportDefaultPageSize + { + get { return 100; } + } + public int ImportMaxPageSize + { + get { return 10000; } + } + + Schema schema; + PSObject schemaPSObject; + + public PSObject InitializeSchemaVariables(Schema Schema) + { + if (Schema == null) return null; + schema = Schema; + + schemaPSObject = new PSObject(); + foreach (SchemaType type in schema.Types) + { + PSObject typeObj = new PSObject(); + typeObj.Members.Add(new PSNoteProperty("ObjectType", type.Name)); + typeObj.Members.Add(new PSNoteProperty("PossibleDNComponentsForProvisioning", type.PossibleDNComponentsForProvisioning)); + PSObject attrObj = new PSObject(); + foreach (SchemaAttribute attr in type.AnchorAttributes) + { + Tracer.TraceInformation("{0}-anchor-attribute {1} [{2}]", type.Name, attr.Name, attr.DataType); + attrObj.Members.Add(new PSNoteProperty(attr.Name, attr)); + } + typeObj.Members.Add(new PSNoteProperty("Anchors", attrObj)); + + attrObj = new PSObject(); + foreach (SchemaAttribute attr in type.Attributes) + { + Tracer.TraceInformation("{0}-attribute {1} [{2}]", type.Name, attr.Name, attr.DataType); + attrObj.Members.Add(new PSNoteProperty(attr.Name, attr)); + } + typeObj.Members.Add(new PSNoteProperty("Attributes", attrObj)); + + // add to general schema object + schemaPSObject.Members.Add(new PSNoteProperty(type.Name, typeObj)); + } + return null; + } + + public OpenImportConnectionResults OpenImportConnection(System.Collections.ObjectModel.KeyedCollection configParameters, Schema types, OpenImportConnectionRunStep openImportRunStep) + { + Tracer.Enter("openimportconnection"); + try + { + Tracer.TraceInformation("getting-schema"); + try + { + foreach (SchemaType type in types.Types) + { + foreach (SchemaAttribute attr in type.AnchorAttributes) + { + //Tracer.TraceInformation("{0}-anchor-attribute {1} [{2}]", type.Name, attr.Name, attr.DataType); + objectTypeAnchorAttributeNames.Add(type.Name, attr.Name); + } + foreach (SchemaAttribute attr in type.Attributes) + { + //Tracer.TraceInformation("{0}-attribute {1} [{2}]", type.Name, attr.Name, attr.DataType); + } + } + InitializeSchemaVariables(types); + } + catch (Exception ex) + { + Tracer.TraceError("getting-schema", ex); + } + finally + { + Tracer.TraceInformation("got-schema"); + } + + InitializeConfigParameters(configParameters); + + //SetupImpersonationToken(); + + OpenRunspace(); + + Tracer.TraceInformation("resetting-pipeline-results-and-counters"); + importResults = new List(); + pageToken = ""; + + OpenImportConnectionResults oicr = new OpenImportConnectionResults(); + ImportRunStepPageSize = openImportRunStep.PageSize; + Tracer.TraceInformation("openimportrunstep-pagesize '{0}'", ImportRunStepPageSize); + + oicr.CustomData = openImportRunStep.ImportType == OperationType.Full ? "" : openImportRunStep.CustomData; + Tracer.TraceInformation("openimportrunstep-customdata '{0}'", oicr.CustomData); + + importOperationType = openImportRunStep.ImportType; + Tracer.TraceInformation("openimportrunstep-importtype '{0}'", importOperationType); + + return oicr; + } + catch (Exception ex) + { + Tracer.TraceError("openimportconnection", ex); + throw; + } + finally + { + Tracer.Exit("openimportconnection"); + } + } + public GetImportEntriesResults GetImportEntries(GetImportEntriesRunStep importRunStep) + { + Tracer.Enter("getimportentries"); + try + { + #region call import script + + // if results is null, then this is the first time that we're called, + // so call script and get pipeline object and custom data + Tracer.TraceInformation("more-to-import '{0}'", MoreToImport); + + if (MoreToImport) + { + MoreToImport = false; // make sure we set more-to-import to false; could be overwritten further down if pagedimports is true, though + + // on first call, we set customdata to value from last successful run + returnedCustomData = importRunStep.CustomData; + + Command cmd = new Command(Path.GetFullPath(ImportScript)); + cmd.Parameters.Add(new CommandParameter("User", Username)); + cmd.Parameters.Add(new CommandParameter("Password", Password)); + cmd.Parameters.Add(new CommandParameter("Credentials", GetSecureCredentials())); + cmd.Parameters.Add(new CommandParameter("OperationType", importOperationType.ToString())); + cmd.Parameters.Add(new CommandParameter("UsePagedImport", UsePagedImport)); + cmd.Parameters.Add(new CommandParameter("PageSize", ImportRunStepPageSize)); + cmd.Parameters.Add(new CommandParameter("Schema", schemaPSObject)); + + Tracer.TraceInformation("setting-custom-data '{0}'", importRunStep.CustomData); + powershell.Runspace.SessionStateProxy.SetVariable("RunStepCustomData", importRunStep.CustomData); + Tracer.TraceInformation("setting-page-token '{0}'", pageToken); + powershell.Runspace.SessionStateProxy.SetVariable("PageToken", pageToken); + + importResults = InvokePowerShellScript(cmd, null).ToList(); + + returnedCustomData = powershell.Runspace.SessionStateProxy.GetVariable("RunStepCustomData"); + pageToken = powershell.Runspace.SessionStateProxy.GetVariable("PageToken"); + + Tracer.TraceInformation("page-token-returned '{0}'", pageToken == null ? "(null)" : pageToken); + Tracer.TraceInformation("custom-data returned '{0}'", returnedCustomData); + Tracer.TraceInformation("number-of-object(s)-in-pipeline {0:n0}", importResults.Count); + + if (UsePagedImport) + { + // Tracer.TraceError("paged-import-not-supported-currently"); + object moreToImportObject = powershell.Runspace.SessionStateProxy.GetVariable("MoreToImport"); + if (moreToImportObject == null) + { + Tracer.TraceError("For paged imports, the global variable 'MoreToImport' must be set to 'true' or 'false'"); + } + else + { + Tracer.TraceInformation("MoreToImport-value-returned '{0}'", moreToImportObject); + if (bool.TryParse(moreToImportObject == null ? bool.FalseString : moreToImportObject.ToString(), out MoreToImport)) + { + Tracer.TraceInformation("paged-import-setting-MoreToImport-to '{0}'", MoreToImport); + } + else + { + Tracer.TraceError("Value returned in MoreToImport must be a boolean with value of 'true' or 'false'"); + } + } + } + else + { + MoreToImport = false; + Tracer.TraceInformation("non-paged-import-setting-MoreToImport-to '{0}'", MoreToImport); + } + + } + #endregion + + #region parse returned objects + if (importResults != null && importResults.Count > 0) + { + List importResultsBatch = importResults.Take(ImportRunStepPageSize).ToList(); + if (importResults.Count > ImportRunStepPageSize) + { + importResults.RemoveRange(0, importResultsBatch.Count); + } + else + { + importResults.Clear(); + } + Tracer.TraceInformation("converting-objects-to-csentrychange {0:n0}", importResultsBatch.Count); + foreach (PSObject obj in importResultsBatch) + { + HashSet attrs = new HashSet(); + + Tracer.TraceInformation("start-connector-space-object"); + try + { + CSEntryChange csobject = CSEntryChange.Create(); + + if (obj.BaseObject.GetType() != typeof(System.Collections.Hashtable)) + { + Tracer.TraceWarning("invalid-object-in-pipeline '{0}'", 1, obj.BaseObject.GetType()); + continue; + } + + object AnchorValue = null; + string AnchorAttributeName = null; + string objectDN = null; + string objectClass = ""; // should be string to prevent null exceptions + string changeType = null; + string ErrorName = null; + string ErrorDetail = null; + MAImportError ImportErrorType = MAImportError.Success; // assume no error + Hashtable hashTable = (Hashtable)obj.BaseObject; + + #region get control values + Tracer.TraceInformation("start-getting-control-values"); + foreach (string key in hashTable.Keys) + { + if (key.Equals(Constants.ControlValues.ObjectClass, StringComparison.OrdinalIgnoreCase) || key.Equals(Constants.ControlValues.ObjectClassEx, StringComparison.OrdinalIgnoreCase)) + { + objectClass = (string)hashTable[key]; + Tracer.TraceInformation("got-objectclass {0}, {1}", objectClass, key); + continue; + } + if (key.Equals(Constants.ControlValues.DN, StringComparison.OrdinalIgnoreCase)) + { + objectDN = (string)hashTable[key]; + Tracer.TraceInformation("got-dn {0}, {1}", objectDN, key); + continue; + } + if (key.Equals(Constants.ControlValues.ChangeType, StringComparison.OrdinalIgnoreCase) || key.Equals(Constants.ControlValues.ChangeTypeEx, StringComparison.OrdinalIgnoreCase)) + { + changeType = (string)hashTable[key]; + Tracer.TraceInformation("got-changetype {0}, {1}", changeType, key); + continue; + } + if (key.Equals(Constants.ControlValues.ErrorName, StringComparison.OrdinalIgnoreCase)) + { + ErrorName = (string)hashTable[key]; + Tracer.TraceInformation("got-errorname {0}, {1}", ErrorName, key); + continue; + } + if (key.Equals(Constants.ControlValues.ErrorDetail, StringComparison.OrdinalIgnoreCase)) + { + ErrorDetail = (string)hashTable[key]; + Tracer.TraceInformation("got-errordetail {0}, {1}", ErrorDetail, key); + continue; + } + } + + if (string.IsNullOrEmpty(objectClass)) + { + Tracer.TraceError("missing-objectclass"); + ImportErrorType = MAImportError.ImportErrorCustomContinueRun; + ErrorName = "missing-objectclass-value"; + ErrorDetail = "No value provided for objectclass attribute"; + } + else + { + AnchorAttributeName = objectTypeAnchorAttributeNames[objectClass] == null ? "" : (string)objectTypeAnchorAttributeNames[objectClass]; + if (string.IsNullOrEmpty(AnchorAttributeName)) + { + ImportErrorType = MAImportError.ImportErrorInvalidAttributeValue; + ErrorName = "invalid-objecttype"; + ErrorDetail = "Objecttype not defined in schema"; + } + + foreach (string key in hashTable.Keys) + { + if (key.Equals(AnchorAttributeName, StringComparison.OrdinalIgnoreCase)) + { + AnchorValue = hashTable[key]; + Tracer.TraceInformation("got-anchor {0}, {1}", AnchorValue, key); + break; + } + } + } + Tracer.TraceInformation("end-getting-control-values"); + + if (AnchorValue == null) + { + Tracer.TraceError("missing-anchor"); + ImportErrorType = MAImportError.ImportErrorCustomContinueRun; + ErrorName = "missing-anchor-value"; + ErrorDetail = "No value provided for anchor attribute"; + } + + if (AnchorValue != null && string.IsNullOrEmpty(objectDN)) + { + Tracer.TraceInformation("setting-anchor-as-dn {0}", AnchorValue); + objectDN = AnchorValue.ToString(); + } + + if (!string.IsNullOrEmpty(ErrorName)) + { + ImportErrorType = MAImportError.ImportErrorCustomContinueRun; + if (string.IsNullOrEmpty(ErrorDetail)) + { + ErrorDetail = "No error details provided"; + } + } + #endregion control values + + #region return invalid object + if (ImportErrorType != MAImportError.Success) + { + Tracer.TraceInformation("returning-invalid-object"); + if (AnchorValue != null) + { + csobject.AnchorAttributes.Add(AnchorAttribute.Create(AnchorAttributeName, AnchorValue)); + } + csobject.ObjectModificationType = ObjectModificationType.Add; + if (!string.IsNullOrEmpty(objectClass)) + { + try + { + csobject.ObjectType = objectClass; + } + catch (NoSuchObjectTypeException otEx) + { + Tracer.TraceError("no-such-object '{0}'", otEx); + } + } + if (!string.IsNullOrEmpty(objectClass)) + { + csobject.DN = objectDN; + } + Tracer.TraceError("invalid-object dn: {0}, type: {1}, name: {2}, details: {3} ", objectDN, ImportErrorType, ErrorName, ErrorDetail); + csobject.ErrorCodeImport = ImportErrorType; + csobject.ErrorName = ErrorName; + csobject.ErrorDetail = ErrorDetail; + csentryqueue.Add(csobject); + continue; + } + #endregion + + #region return deleted object + // we must set ObjectModificationType before any other attributes; otherwise it will default to 'Add' + if (!string.IsNullOrEmpty(changeType) && changeType.Equals("delete", StringComparison.OrdinalIgnoreCase)) + { + Tracer.TraceInformation("returning-deleted-object"); + Tracer.TraceInformation("change-type {0}", changeType); + csobject.ObjectModificationType = ObjectModificationType.Delete; + csobject.ObjectType = objectClass; + csobject.DN = objectDN; + + // we need to get the object anchor value for the deletion + csobject.AnchorAttributes.Add(AnchorAttribute.Create(AnchorAttributeName, AnchorValue)); + csentryqueue.Add(csobject); + continue; + } + #endregion + + #region returned live object + Tracer.TraceInformation("returning-valid-object"); + csobject.ObjectModificationType = ObjectModificationType.Add; + csobject.ObjectType = objectClass; + csobject.DN = objectDN; + csobject.AnchorAttributes.Add(AnchorAttribute.Create(AnchorAttributeName, AnchorValue)); + foreach (string key in hashTable.Keys) + { + try + { + if (Regex.IsMatch(key, string.Format(@"^(objectClass|\[objectclass\]|changeType|\[changetype\]|\[DN\]|\[ErrorName\]|\[ErrorDetail\]|{0})$", AnchorAttributeName), RegexOptions.Compiled | RegexOptions.IgnoreCase)) + { + Tracer.TraceInformation("skip-control-value {0}", key); + continue; + } + if (hashTable[key] == null) + { + Tracer.TraceInformation("skip-null-value-for '{0}'", key); + continue; + } + SchemaAttribute sa = schema.Types[objectClass].Attributes[key]; + Tracer.TraceInformation("attribute: {0} (type {1}, {2}): '{3}'", key, sa.DataType, sa.IsMultiValued ? "multi-value" : "single-value", hashTable[key]); + if (sa.IsMultiValued) + { + //Tracer.TraceInformation("add-multivalue '{0}' [{1}]", key, hashTable[key].GetType()); + List mvs = new List(); + if (hashTable[key].ToString().EndsWith("[]")) + { + mvs.AddRange((object[])hashTable[key]); + } + else + { + mvs.Add(hashTable[key]); + } + csobject.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(key, mvs)); + } + else + { + csobject.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(key, hashTable[key])); + } + + } + catch (KeyNotFoundException keyexception) + { + Tracer.TraceError("attribute-is-not-defined-for '{0}' / '{1}' ({2})", key, objectClass, keyexception.ToString()); + } + } + #endregion + + if (csobject.ErrorCodeImport != MAImportError.Success) + { + Tracer.TraceError("defective-csentrychange id: {0}, dn: {1}, errorcode: {2}, error: {3}, details: {4}", csobject.Identifier, csobject.DN, csobject.ErrorCodeImport, csobject.ErrorName, csobject.ErrorDetail); + } + Tracer.TraceInformation("returning-csentry dn: {0}, id: {1}", csobject.DN, csobject.Identifier); + csentryqueue.Add(csobject); + } + catch (Exception ex) + { + Tracer.TraceError("creating-csentrychange", ex); + } + finally + { + Tracer.TraceInformation("end-connector-space-object"); + } + } + // clearing results for next loop + importResultsBatch.Clear(); + } + #endregion + + #region dequeue csentries + + GetImportEntriesResults importReturnInfo = null; + + Tracer.TraceInformation("total-import-object(s)-left {0:n0}", importResults.Count); + Tracer.TraceInformation("total-connector-space-object(s)-left {0:n0}", csentryqueue.Count); + + List batch = csentryqueue.Take(ImportRunStepPageSize).ToList(); + if (csentryqueue.Count > ImportRunStepPageSize) + { + csentryqueue.RemoveRange(0, batch.Count); + } + else + { + csentryqueue.Clear(); + } + importReturnInfo = new GetImportEntriesResults(); + importReturnInfo.MoreToImport = MoreToImport || importResults.Count > 0 || (csentryqueue.Count > 0); + importReturnInfo.CustomData = returnedCustomData == null ? "" : returnedCustomData.ToString(); + importReturnInfo.CSEntries = batch; + + Tracer.TraceInformation("should-return-for-more {0}", importReturnInfo.MoreToImport); + Tracer.TraceInformation("custom-data '{0}'", importReturnInfo.CustomData); + Tracer.TraceInformation("connector-space-object(s)-returned {0:n0}", importReturnInfo.CSEntries.Count); + + return importReturnInfo; + + #endregion + } + catch (Exception ex) + { + Tracer.TraceError("getimportentries", ex); + throw; + } + finally + { + Tracer.Exit("getimportentries"); + } + } + public CloseImportConnectionResults CloseImportConnection(CloseImportConnectionRunStep importRunStep) + { + Tracer.Enter("closeimportconnectionresults"); + try + { + CloseRunspace(); + + CloseImportConnectionResults cicr = new CloseImportConnectionResults(); + Tracer.TraceInformation("custom-data {0}", importRunStep.CustomData); + Tracer.TraceInformation("close-reason {0}", importRunStep.Reason); + if (importRunStep.Reason == CloseReason.Normal) + { + cicr.CustomData = importRunStep.CustomData; + } + Dispose(); + return cicr; + } + catch (Exception ex) + { + Tracer.TraceError("closeimportconnection", ex); + throw; + } + finally + { + Tracer.Exit("closeimportconnectionresults"); + } + } + + } } diff --git a/Granfeldt.PowerShell.ManagementAgent/MA.Parameters.cs b/Granfeldt.PowerShell.ManagementAgent/MA.Parameters.cs index 3af1251..62ba4f3 100644 --- a/Granfeldt.PowerShell.ManagementAgent/MA.Parameters.cs +++ b/Granfeldt.PowerShell.ManagementAgent/MA.Parameters.cs @@ -10,7 +10,6 @@ public partial class PowerShellManagementAgent : IDisposable, IMAExtensible2GetC IList IMAExtensible2GetParameters.GetConfigParameters(System.Collections.ObjectModel.KeyedCollection configParameters, ConfigParameterPage page) { Tracer.Enter("getconfigparameters"); - Tracer.Indent(); try { List configParametersDefinitions = new List(); @@ -67,7 +66,6 @@ ParameterValidationResult IMAExtensible2GetParameters.ValidateConfigParameters(S try { Tracer.Enter("validateconfigparameters"); - Tracer.Indent(); if (page == ConfigParameterPage.Connectivity) { string schemaScriptFilename = Path.GetFullPath(configParameters[Constants.Parameters.SchemaScript].Value); @@ -102,7 +100,6 @@ ParameterValidationResult IMAExtensible2GetParameters.ValidateConfigParameters(S } finally { - Tracer.Unindent(); Tracer.Exit("validateconfigparameters"); } return new ParameterValidationResult(ParameterValidationResultCode.Success, "", ""); @@ -110,7 +107,6 @@ ParameterValidationResult IMAExtensible2GetParameters.ValidateConfigParameters(S public void InitializeConfigParameters(System.Collections.ObjectModel.KeyedCollection configParameters) { Tracer.Enter("initializeconfigparameters"); - Tracer.Indent(); try { if (configParameters != null) @@ -145,7 +141,6 @@ public void InitializeConfigParameters(System.Collections.ObjectModel.KeyedColle } finally { - Tracer.Unindent(); Tracer.Exit("initializeconfigparameters"); } } diff --git a/Granfeldt.PowerShell.ManagementAgent/MA.Password.cs b/Granfeldt.PowerShell.ManagementAgent/MA.Password.cs index 3eaa4be..c09245f 100644 --- a/Granfeldt.PowerShell.ManagementAgent/MA.Password.cs +++ b/Granfeldt.PowerShell.ManagementAgent/MA.Password.cs @@ -24,9 +24,7 @@ ConnectionSecurityLevel IMAExtensible2Password.GetConnectionSecurityLevel() } void IMAExtensible2Password.OpenPasswordConnection(KeyedCollection configParameters, Partition partition) { - Tracer.IndentLevel = 0; Tracer.Enter("openpasswordconnection"); - Tracer.Indent(); try { InitializeConfigParameters(configParameters); @@ -40,14 +38,12 @@ void IMAExtensible2Password.OpenPasswordConnection(KeyedCollection passwordPipeline = new PSDataCollection(); try { @@ -95,14 +89,12 @@ void CallPasswordScript(PasswordOperation Action, CSEntry csentry, SecureString finally { passwordPipeline = null; - Tracer.Unindent(); Tracer.TraceInformation("callpasswordscript"); } } void IMAExtensible2Password.SetPassword(CSEntry csentry, SecureString newPassword, PasswordOptions options) { Tracer.Enter("setpassword"); - Tracer.Indent(); try { CallPasswordScript(PasswordOperation.Set, csentry, new SecureString(), newPassword, options); @@ -114,14 +106,12 @@ void IMAExtensible2Password.SetPassword(CSEntry csentry, SecureString newPasswor } finally { - Tracer.Unindent(); Tracer.Exit("setpassword"); } } void IMAExtensible2Password.ClosePasswordConnection() { Tracer.Enter("closepasswordconnection"); - Tracer.Indent(); try { CloseRunspace(); @@ -134,7 +124,6 @@ void IMAExtensible2Password.ClosePasswordConnection() } finally { - Tracer.Unindent(); Tracer.Exit("closepasswordconnection"); } } diff --git a/Granfeldt.PowerShell.ManagementAgent/MA.PowerShell.cs b/Granfeldt.PowerShell.ManagementAgent/MA.PowerShell.cs index 1c960d1..5d72406 100644 --- a/Granfeldt.PowerShell.ManagementAgent/MA.PowerShell.cs +++ b/Granfeldt.PowerShell.ManagementAgent/MA.PowerShell.cs @@ -28,8 +28,8 @@ static void Verbose_DataAdded(object sender, DataAddedEventArgs e) } static void Warning_DataAdded(object sender, DataAddedEventArgs e) { - Tracer.TraceWarning("warning {0}", ((PSDataCollection)sender)[e.Index].ToString()); - } + Tracer.TraceWarning("warning {0}", 1, ((PSDataCollection)sender)[e.Index]); + } static void Debug_DataAdded(object sender, DataAddedEventArgs e) { Tracer.TraceInformation("debug {0}", ((PSDataCollection)sender)[e.Index].ToString()); @@ -116,7 +116,6 @@ Collection InvokePowerShellScript(Command command, PSDataCollection configParameters) { Tracer.Enter("getschema"); - Tracer.Indent(); try { Schema schema = Schema.Create(); @@ -134,7 +133,6 @@ Schema IMAExtensible2GetSchema.GetSchema(KeyedCollection