diff --git a/.gitignore b/.gitignore
index 22208a1e..9c81b50a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -289,4 +289,17 @@ __pycache__/
*.odx.cs
*.xsd.cs
-BuildTools/Lib/xRMCIFramework/9.0.0/
\ No newline at end of file
+BuildTools/Lib/xRMCIFramework/9.0.0/
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Xrm.Framework.CI.PowerShell.Cmdlets.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Xrm.Framework.CI.Common.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/System.Management.Automation.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Newtonsoft.Json.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Xrm.Tooling.Connector.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Xrm.Sdk.Workflow.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Xrm.Sdk.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Xrm.Sdk.Deployment.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Rest.ClientRuntime.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Management.Infrastructure.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.IdentityModel.Clients.ActiveDirectory.dll
+/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Crm.Sdk.Proxy.dll
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common.IntegrationTests/ConnectionConfigManagerTest.cs b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common.IntegrationTests/ConnectionConfigManagerTest.cs
new file mode 100644
index 00000000..46b2b855
--- /dev/null
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common.IntegrationTests/ConnectionConfigManagerTest.cs
@@ -0,0 +1,56 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Xrm.Framework.CI.Common.IntegrationTests.Logging;
+
+namespace Xrm.Framework.CI.Common.IntegrationTests
+{
+ [TestClass]
+ public class ConnectionConfigManagerTest
+ {
+ public TestContext TestContext
+ {
+ get;
+ set;
+ }
+
+ [TestMethod]
+ public void TestConnection()
+ {
+ string config = $"{TestContext.TestLogsDir}\\connections.json";
+
+ TestLogger logger = new TestLogger();
+ XrmEncryptionManager encryption = new XrmEncryptionManager(logger);
+ XrmConnectionConfigManager manager = new XrmConnectionConfigManager(logger, encryption, config);
+
+ string con1 = "AuthType=Office365;Username=user1@name.com;Password=passwork;Url=https://name1.crmregion.dynamics.com";
+ string key1 = "crm1";
+
+ string con2 = "AuthType=Office365;Username=user2@name.com;Password=passwork;Url=https://name2.crmregion.dynamics.com";
+ string key2 = "crm2";
+
+ Assert.AreEqual(manager.GetConnections().Count, 0);
+
+ manager.SetConnection(key1, con1);
+
+ Assert.AreEqual(con1, manager.GetConnection(key1));
+
+ manager.SetConnection(key2, con2);
+
+ Assert.AreEqual(con2, manager.GetConnection(key2));
+ Assert.AreEqual(manager.GetConnections().Count, 2);
+ Assert.AreEqual(manager.GetConnections()[0], key1);
+ Assert.AreEqual(manager.GetConnections()[1], key2);
+
+ manager.RemoveConnection(key1);
+
+ Assert.AreEqual(manager.GetConnections().Count, 1);
+ Assert.AreEqual(null, manager.GetConnection(key1));
+ Assert.AreEqual(con2, manager.GetConnection(key2));
+
+ manager.RemoveConnection(key2);
+
+ Assert.AreEqual(null, manager.GetConnection(key2));
+ Assert.AreEqual(manager.GetConnections().Count, 0);
+ }
+ }
+}
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common.IntegrationTests/Xrm.Framework.CI.Common.IntegrationTests.csproj b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common.IntegrationTests/Xrm.Framework.CI.Common.IntegrationTests.csproj
index 75061b69..1a14a27e 100644
--- a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common.IntegrationTests/Xrm.Framework.CI.Common.IntegrationTests.csproj
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common.IntegrationTests/Xrm.Framework.CI.Common.IntegrationTests.csproj
@@ -93,6 +93,7 @@
+
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/Xrm.Framework.CI.Common.csproj b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/Xrm.Framework.CI.Common.csproj
index 8cbe39b9..ab5c13cf 100644
--- a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/Xrm.Framework.CI.Common.csproj
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/Xrm.Framework.CI.Common.csproj
@@ -98,6 +98,8 @@
+
+
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/XrmConnectionConfigManager.cs b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/XrmConnectionConfigManager.cs
new file mode 100644
index 00000000..7d6a8b29
--- /dev/null
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/XrmConnectionConfigManager.cs
@@ -0,0 +1,197 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xrm.Framework.CI.Common.Logging;
+
+namespace Xrm.Framework.CI.Common
+{
+ public class XrmConnectionConfigManager
+ {
+ #region Properties
+
+ protected ILogger Logger
+ {
+ get;
+ set;
+ }
+
+ protected IXrmEncryption Encryption
+ {
+ get;
+ set;
+ }
+
+ public string ConfigPath
+ {
+ get;
+ private set;
+ }
+
+ internal XrmConnectionInfoList ConnectionList
+ {
+ get;
+ set;
+ }
+
+ #endregion
+
+ #region Constructors
+
+ public XrmConnectionConfigManager(ILogger logger, IXrmEncryption encryption, string configPath)
+ {
+ Logger = logger;
+ Encryption = encryption;
+
+ if (!String.IsNullOrEmpty(configPath))
+ {
+ ConfigPath = configPath;
+ }
+ else
+ {
+ ConfigPath = GetTempConfig();
+ }
+ Init();
+ }
+
+ #endregion
+
+ #region Methods
+
+ public void SetConnection(string key, string connectionString)
+ {
+ string encryptedValue = Encryption.Encrypt(connectionString);
+
+ XrmConnectionInfo info = FindConnection(key);
+
+ if (info != null)
+ {
+ info.Value = encryptedValue;
+ }
+ else
+ {
+ ConnectionList.Connections.Add(new XrmConnectionInfo
+ {
+ Key = key,
+ Value = encryptedValue
+ });
+ }
+
+ SaveList();
+ }
+
+ public void RemoveConnection(string key)
+ {
+ XrmConnectionInfo info = FindConnection(key);
+
+ if (info != null)
+ {
+ ConnectionList.Connections.Remove(info);
+ SaveList();
+ }
+ else
+ {
+ throw new Exception($"No connection found with key: {key}");
+ }
+ }
+
+ public string GetConnection(string key)
+ {
+ XrmConnectionInfo found = FindConnection(key);
+
+ if (found != null)
+ {
+ return Encryption.Decrypt(found.Value);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public List GetConnections()
+ {
+ return (from c in ConnectionList.Connections
+ select c.Key).ToList();
+
+ }
+
+ private string GetTempConfig()
+ {
+ string tempFolder = Path.GetTempPath();
+ string configFolder = Path.Combine(tempFolder, "xRMCIFramework");
+ if (!Directory.Exists(configFolder))
+ {
+ Directory.CreateDirectory(configFolder);
+ }
+ string configFile = "connections.json";
+
+ return Path.Combine(configFolder, configFile);
+ }
+
+ private void Init()
+ {
+ if (!File.Exists(ConfigPath))
+ {
+ ConnectionList = new XrmConnectionInfoList();
+ SaveList();
+ }
+ else
+ {
+ LoadList();
+ }
+ }
+
+ private XrmConnectionInfo FindConnection(string key)
+ {
+ var c = from cons in ConnectionList.Connections
+ where cons.Key == key
+ select cons;
+
+ List found = c.ToList();
+
+ if (found.Count > 1)
+ {
+ throw new Exception($"More than one connection found with key: {key}");
+ }
+ else if (found.Count == 1)
+ {
+ return found[0];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private void SaveList()
+ {
+ Serializers.SaveJson(ConfigPath, ConnectionList);
+ }
+
+ private void LoadList()
+ {
+ ConnectionList = Serializers.ParseJson(ConfigPath);
+ }
+
+ #endregion
+ }
+
+ public class XrmConnectionInfo
+ {
+ public string Key { get; set; }
+ public string Value { get; set; }
+ }
+
+ internal class XrmConnectionInfoList
+ {
+ public List Connections { get; set; }
+
+ public XrmConnectionInfoList()
+ {
+ Connections = new List();
+ }
+ }
+}
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/XrmEncryptionManager.cs b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/XrmEncryptionManager.cs
new file mode 100644
index 00000000..6612e937
--- /dev/null
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.Common/XrmEncryptionManager.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using Xrm.Framework.CI.Common.Logging;
+
+namespace Xrm.Framework.CI.Common
+{
+ public interface IXrmEncryption
+ {
+ string Encrypt(string secret);
+ string Decrypt(string encryptedSecret);
+ }
+
+ public class XrmEncryptionManager : IXrmEncryption
+ {
+ #region Properties
+
+ protected ILogger Logger
+ {
+ get;
+ set;
+ }
+
+ #endregion
+
+ #region Constructors
+
+ public XrmEncryptionManager(ILogger logger )
+ {
+ Logger = logger;
+ }
+
+ #endregion
+
+ #region Methods
+
+ public string Encrypt(string secret)
+ {
+ Logger.LogVerbose("Ecrypting secret");
+
+ if (string.IsNullOrEmpty(secret))
+ {
+ throw new Exception("secret can't be empty");
+ }
+
+ byte[] bytes = Encoding.Default.GetBytes(secret);
+
+ byte[] encryptedBytes = ProtectedData.Protect(bytes, (byte[])null, DataProtectionScope.CurrentUser);
+
+ string encryptedSecret = BitConverter.ToString(encryptedBytes).Replace("-", "");
+
+ Logger.LogVerbose("Secret encrypted");
+
+ return encryptedSecret;
+ }
+
+ public string Decrypt(string encryptedSecret)
+ {
+ Logger.LogVerbose("Decrypting secret");
+
+ byte[] encryptedBytes = StringToByteArray(encryptedSecret);
+
+ byte[] bytes = ProtectedData.Unprotect(encryptedBytes, (byte[])null, DataProtectionScope.CurrentUser);
+
+ string secret = Encoding.Default.GetString(bytes);
+
+ Logger.LogVerbose("Decryptings secret");
+
+ return secret;
+ }
+
+ private static byte[] StringToByteArray(string hex)
+ {
+ return Enumerable.Range(0, hex.Length)
+ .Where(x => x % 2 == 0)
+ .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
+ .ToArray();
+ }
+
+ #endregion
+ }
+}
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/GetXrmConnectionCommand.cs b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/GetXrmConnectionCommand.cs
new file mode 100644
index 00000000..0e49e084
--- /dev/null
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/GetXrmConnectionCommand.cs
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using System.Management.Automation;
+using Microsoft.Crm.Sdk.Messages;
+using Xrm.Framework.CI.Common;
+
+namespace Xrm.Framework.CI.PowerShell.Cmdlets
+{
+ ///
+ /// Saves an encrypted connection string in config
+ /// This cmdlet can be used to test your connectivity to CRM by calling
+ /// WhoAmIRequest and returning a WhoAmIResponse object.
+ ///
+ ///
+ ///
+ /// C:\PS>Export-XrmSolution -ConnectionString "" -EntityName "account"
+ /// Exports the "" managed solution to "" location
+ ///
+ [Cmdlet(VerbsCommon.Get, "XrmConnection")]
+ [OutputType(typeof(string))]
+ public class GetXrmConnectionCommand : CommandBase
+ {
+ #region Parameters
+
+ ///
+ /// The key for the connection string
+ ///
+ [Parameter(Mandatory = true)]
+ public string Key { get; set; }
+
+ ///
+ /// The absolute path to the json config file containing the connection. If not supplied connections.json will be created in user temp folder
+ ///
+ [Parameter(Mandatory = false)]
+ public string ConfigFilePath { get; set; }
+
+ #endregion
+
+ #region Process Record
+
+ protected override void ProcessRecord()
+ {
+ base.ProcessRecord();
+
+ Logger.LogInformation("Retrieving connection string with key: {0}", Key);
+
+ XrmEncryptionManager encryption = new XrmEncryptionManager(Logger);
+ XrmConnectionConfigManager manager = new XrmConnectionConfigManager(Logger, encryption, ConfigFilePath);
+
+ base.WriteObject(manager.GetConnection(Key));
+
+ Logger.LogInformation("Retrieved connection string with key: {0} from {1}", Key, manager.ConfigPath);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/GetXrmConnectionsCommand.cs b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/GetXrmConnectionsCommand.cs
new file mode 100644
index 00000000..20b2c6d6
--- /dev/null
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/GetXrmConnectionsCommand.cs
@@ -0,0 +1,52 @@
+using System.Collections.Generic;
+using System.Management.Automation;
+using Microsoft.Crm.Sdk.Messages;
+using Xrm.Framework.CI.Common;
+
+namespace Xrm.Framework.CI.PowerShell.Cmdlets
+{
+ ///
+ /// Saves an encrypted connection string in config
+ /// This cmdlet can be used to test your connectivity to CRM by calling
+ /// WhoAmIRequest and returning a WhoAmIResponse object.
+ ///
+ ///
+ ///
+ /// C:\PS>Export-XrmSolution -ConnectionString "" -EntityName "account"
+ /// Exports the "" managed solution to "" location
+ ///
+ [Cmdlet(VerbsCommon.Get, "XrmConnections")]
+ [OutputType(typeof(string[]))]
+ public class GetXrmConnectionsCommand : CommandBase
+ {
+ #region Parameters
+
+ ///
+ /// The absolute path to the json config file containing the connection. If not supplied connections.json will be created in user temp folder
+ ///
+ [Parameter(Mandatory = false)]
+ public string ConfigFilePath { get; set; }
+
+ #endregion
+
+ #region Process Record
+
+ protected override void ProcessRecord()
+ {
+ base.ProcessRecord();
+
+ Logger.LogInformation("Retrieving connections");
+
+ XrmEncryptionManager encryption = new XrmEncryptionManager(Logger);
+ XrmConnectionConfigManager manager = new XrmConnectionConfigManager(Logger, encryption, ConfigFilePath);
+
+ List connecitons = manager.GetConnections();
+
+ base.WriteObject(manager.GetConnections().ToArray());
+
+ Logger.LogInformation("Retrieved {0} connection(s)from {1}", connecitons.Count, manager.ConfigPath);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/RemoveXrmConnectionCommand.cs b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/RemoveXrmConnectionCommand.cs
new file mode 100644
index 00000000..00e2e634
--- /dev/null
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/RemoveXrmConnectionCommand.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Management.Automation;
+using Microsoft.Crm.Sdk.Messages;
+using Xrm.Framework.CI.Common;
+
+namespace Xrm.Framework.CI.PowerShell.Cmdlets
+{
+ ///
+ /// Saves an encrypted connection string in config
+ /// This cmdlet can be used to test your connectivity to CRM by calling
+ /// WhoAmIRequest and returning a WhoAmIResponse object.
+ ///
+ ///
+ ///
+ /// C:\PS>Export-XrmSolution -ConnectionString "" -EntityName "account"
+ /// Exports the "" managed solution to "" location
+ ///
+ [Cmdlet(VerbsCommon.Remove, "XrmConnection")]
+ public class RemoveXrmConnectionCommand : CommandBase
+ {
+ #region Parameters
+
+ ///
+ /// The key for the connection string
+ ///
+ [Parameter(Mandatory = true)]
+ public string Key { get; set; }
+
+ ///
+ /// The absolute path to the json config file containing the connection. If not supplied connections.json will be created in user temp folder
+ ///
+ [Parameter(Mandatory = false)]
+ public string ConfigFilePath { get; set; }
+
+ #endregion
+
+ #region Process Record
+
+ protected override void ProcessRecord()
+ {
+ base.ProcessRecord();
+
+ Logger.LogInformation("Removing connection string with key: {0}", Key);
+
+ XrmEncryptionManager encryption = new XrmEncryptionManager(Logger);
+ XrmConnectionConfigManager manager = new XrmConnectionConfigManager(Logger, encryption, ConfigFilePath);
+
+ manager.RemoveConnection(Key);
+
+ Logger.LogInformation("Removing connection string with key: {0} from {1}", Key, manager.ConfigPath);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/SetXrmConnectionCommand.cs b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/SetXrmConnectionCommand.cs
new file mode 100644
index 00000000..3afb40c9
--- /dev/null
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/SetXrmConnectionCommand.cs
@@ -0,0 +1,61 @@
+using System.Collections.Generic;
+using System.Management.Automation;
+using Microsoft.Crm.Sdk.Messages;
+using Xrm.Framework.CI.Common;
+
+namespace Xrm.Framework.CI.PowerShell.Cmdlets
+{
+ ///
+ /// Saves an encrypted connection string in config
+ /// This cmdlet can be used to test your connectivity to CRM by calling
+ /// WhoAmIRequest and returning a WhoAmIResponse object.
+ ///
+ ///
+ ///
+ /// C:\PS>Export-XrmSolution -ConnectionString "" -EntityName "account"
+ /// Exports the "" managed solution to "" location
+ ///
+ [Cmdlet(VerbsCommon.Set, "XrmConnection")]
+ public class SetXrmConnectionCommand : CommandBase
+ {
+ #region Parameters
+
+ ///
+ /// The key for the connection string
+ ///
+ [Parameter(Mandatory = true)]
+ public string Key { get; set; }
+
+ ///
+ /// The connection string to store
+ ///
+ [Parameter(Mandatory = true)]
+ public string ConnectionString { get; set; }
+
+ ///
+ /// The absolute path to the json config file containing the connection. If not supplied connections.json will be created in user temp folder
+ ///
+ [Parameter(Mandatory = false)]
+ public string ConfigFilePath { get; set; }
+
+ #endregion
+
+ #region Process Record
+
+ protected override void ProcessRecord()
+ {
+ base.ProcessRecord();
+
+ Logger.LogInformation("Saving connection string with key: {0}", Key);
+
+ XrmEncryptionManager encryption = new XrmEncryptionManager(Logger);
+ XrmConnectionConfigManager manager = new XrmConnectionConfigManager(Logger, encryption, ConfigFilePath);
+
+ manager.SetConnection(Key, ConnectionString);
+
+ Logger.LogInformation("Saved connection string with key: {0} to {1}", Key, manager.ConfigPath);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/Xrm.Framework.CI.PowerShell.Cmdlets.csproj b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/Xrm.Framework.CI.PowerShell.Cmdlets.csproj
index d0f7ee4d..f412f919 100644
--- a/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/Xrm.Framework.CI.PowerShell.Cmdlets.csproj
+++ b/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Cmdlets/Xrm.Framework.CI.PowerShell.Cmdlets.csproj
@@ -103,6 +103,10 @@
+
+
+
+
@@ -230,12 +234,11 @@
-
-
+ Copy /Y $(TargetDir)\*.dll $(SolutionDir)\Xrm.Framework.CI.PowerShell.Scripts\
-
+