├── ConvertWorkflowFW.csproj ├── Program.cs ├── README.md ├── SS.png └── SS1.png /ConvertWorkflowFW.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4442A724-1D87-43ED-9D94-C5D878AC729A} 8 | Exe 9 | ConvertWorkflowFW 10 | ConvertWorkflowFW 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\AngleSharp.0.9.9\lib\net45\AngleSharp.dll 38 | 39 | 40 | ..\packages\Microsoft.Azure.ActiveDirectory.GraphClient.2.1.0\lib\portable-net4+sl5+win+wpa+wp8\Microsoft.Azure.ActiveDirectory.GraphClient.dll 41 | 42 | 43 | ..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll 44 | 45 | 46 | ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll 47 | 48 | 49 | ..\packages\Microsoft.Data.Edm.5.8.4\lib\net40\Microsoft.Data.Edm.dll 50 | 51 | 52 | ..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll 53 | 54 | 55 | ..\packages\Microsoft.Data.Services.Client.5.8.4\lib\net40\Microsoft.Data.Services.Client.dll 56 | 57 | 58 | ..\packages\Microsoft.Graph.1.9.0\lib\net45\Microsoft.Graph.dll 59 | 60 | 61 | ..\packages\Microsoft.Graph.Core.1.9.0\lib\net45\Microsoft.Graph.Core.dll 62 | 63 | 64 | ..\packages\Microsoft.IdentityModel.7.0.0\lib\net35\microsoft.identitymodel.dll 65 | 66 | 67 | ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.19.8\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll 68 | 69 | 70 | ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.19.8\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll 71 | 72 | 73 | ..\packages\Microsoft.IdentityModel.JsonWebTokens.5.2.4\lib\net451\Microsoft.IdentityModel.JsonWebTokens.dll 74 | 75 | 76 | ..\packages\Microsoft.IdentityModel.Logging.5.2.4\lib\net451\Microsoft.IdentityModel.Logging.dll 77 | 78 | 79 | ..\packages\Microsoft.IdentityModel.Tokens.5.2.4\lib\net451\Microsoft.IdentityModel.Tokens.dll 80 | 81 | 82 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.Office.Client.Policy.dll 83 | 84 | 85 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.Office.Client.TranslationServices.dll 86 | 87 | 88 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.Office.SharePoint.Tools.dll 89 | 90 | 91 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.Online.SharePoint.Client.Tenant.dll 92 | 93 | 94 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.ProjectServer.Client.dll 95 | 96 | 97 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.dll 98 | 99 | 100 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.DocumentManagement.dll 101 | 102 | 103 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.Publishing.dll 104 | 105 | 106 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.Runtime.dll 107 | 108 | 109 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.Runtime.Windows.dll 110 | 111 | 112 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.Search.dll 113 | 114 | 115 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.Search.Applications.dll 116 | 117 | 118 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.Taxonomy.dll 119 | 120 | 121 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.UserProfiles.dll 122 | 123 | 124 | ..\packages\Microsoft.SharePointOnline.CSOM.16.1.20317.12000\lib\net45\Microsoft.SharePoint.Client.WorkflowServices.dll 125 | 126 | 127 | ..\packages\WindowsAzure.Storage.7.0.0\lib\net40\Microsoft.WindowsAzure.Storage.dll 128 | 129 | 130 | ..\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll 131 | 132 | 133 | ..\packages\SharePointPnPCoreOnline.3.24.2008.1\lib\net461\OfficeDevPnP.Core.dll 134 | 135 | 136 | ..\packages\SharePointPnP.IdentityModel.Extensions.1.2.4\lib\net45\SharePointPnP.IdentityModel.Extensions.dll 137 | 138 | 139 | 140 | ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | ..\packages\System.IdentityModel.Tokens.Jwt.5.2.4\lib\net451\System.IdentityModel.Tokens.Jwt.dll 149 | 150 | 151 | ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll 152 | True 153 | True 154 | 155 | 156 | ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll 157 | 158 | 159 | ..\packages\System.Net.Http.4.3.1\lib\net46\System.Net.Http.dll 160 | True 161 | True 162 | 163 | 164 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 165 | 166 | 167 | 168 | ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll 169 | 170 | 171 | ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll 172 | True 173 | True 174 | 175 | 176 | ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll 177 | 178 | 179 | 180 | ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll 181 | True 182 | True 183 | 184 | 185 | ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll 186 | True 187 | True 188 | 189 | 190 | ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll 191 | True 192 | True 193 | 194 | 195 | ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll 196 | True 197 | True 198 | 199 | 200 | 201 | ..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll 202 | 203 | 204 | ..\packages\System.Text.Encodings.Web.4.7.1\lib\net461\System.Text.Encodings.Web.dll 205 | 206 | 207 | ..\packages\System.Text.Json.4.7.2\lib\net461\System.Text.Json.dll 208 | 209 | 210 | ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll 211 | 212 | 213 | ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll 214 | 215 | 216 | 217 | 218 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading.Tasks; 4 | using System.Net.Http; 5 | using System.Xml; 6 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 7 | using System.Net.Http.Headers; 8 | using System.Text.Json; 9 | 10 | namespace ConvertWorkflowFW 11 | { 12 | class Program 13 | { 14 | /***************** 15 | * TODO: Replace user, password, and SharePoint Online URL. 16 | */ 17 | private static readonly HttpClient client = new HttpClient(); 18 | private static readonly string user = "username@domain.onmicrosoft.com"; 19 | private static readonly string password = "password"; 20 | private static readonly string url = "https://domain.sharepoint.com"; 21 | static async Task Main(string[] args) 22 | { 23 | await GetLegacyWorkflow(); //Legacy Authentication must be enabled within tenant(https://techcommunity.microsoft.com/t5/microsoft-sharepoint-blog/sharepoint-online-authentication-in-powershell-for-csom-when/ba-p/510114) 24 | await CreateNewWorkflow(); 25 | } 26 | 27 | /***************** 28 | * TODO: Enable Legacy Authentication 29 | * Replace workflow site collection URL, workflow name 30 | */ 31 | private static async Task GetLegacyWorkflow() 32 | { 33 | string workflowSiteCollectionURL = "/sites/SiteCollectionName"; 34 | string workflowName = "TestWorkflow"; 35 | /***************** 36 | * Get Security Token for required FedAuth and rtFa cookies. 37 | */ 38 | XmlDocument doc = new XmlDocument(); 39 | doc.LoadXml("" + 42 | "" + 43 | "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue" + 44 | "" + 45 | "http://www.w3.org/2005/08/addressing/anonymous" + 46 | "" + 47 | "https://login.microsoftonline.com/extSTS.srf" + 48 | "" + 50 | "" + 51 | "" + user + "" + 52 | "" + password + "" + 53 | "" + 54 | "" + 55 | "" + 56 | "" + 57 | "" + 58 | "" + 59 | "" + 60 | "" + url + workflowSiteCollectionURL + "" + 61 | "" + 62 | "" + 63 | "http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey" + 64 | "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" + 65 | "urn:oasis:names:tc:SAML:1.0:assertion" + 66 | "" + 67 | "" + 68 | ""); 69 | 70 | HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://login.microsoftonline.com/extSTS.srf"); 71 | request.Content = new StringContent(doc.InnerXml, Encoding.UTF8, "application/xml"); 72 | HttpResponseMessage response; 73 | response = await client.SendAsync(request); 74 | var responseString = await response.Content.ReadAsStringAsync(); 75 | 76 | doc = new XmlDocument(); 77 | doc.LoadXml(responseString); 78 | XmlNodeList nodeList = doc.GetElementsByTagName("wsse:BinarySecurityToken"); 79 | string token = string.Empty; 80 | foreach (XmlNode node in nodeList) 81 | { 82 | token = node.InnerText; 83 | } 84 | /***************** 85 | * Send Security Token to get FedAuth and rtFa cookies. 86 | */ 87 | request = new HttpRequestMessage(HttpMethod.Post, url + "/_forms/default.aspx?wa=wsignin1.0"); 88 | request.Headers.Add("Accept", "application/json; odata=verbose"); 89 | request.Content = new StringContent(token); 90 | response = await client.SendAsync(request); 91 | 92 | /***************** 93 | * Since we are using the same HttpClient object, the FedAuth and rtFa cookies are now contained in it. 94 | * Get the .xoml file for the relevant workflow. 95 | */ 96 | request = new HttpRequestMessage(HttpMethod.Post, url + workflowSiteCollectionURL + "/_vti_bin/_vti_aut/author.dll"); 97 | request.Headers.Add("X-Vermeer-Content-Type", "application/x-www-form-urlencoded"); 98 | request.Content = new StringContent("method=get+document:15.0.0.4455&service_name=" + 99 | workflowSiteCollectionURL + 100 | "&document_name=Workflows/" + workflowName + "/" + 101 | workflowName + 102 | ".xoml&old_theme_html=false&force=true&get_option=none&doc_version=&timeout=0&expandWebPartPages=true", 103 | Encoding.UTF8, "application/x-www-form-urlencoded"); 104 | 105 | response = await client.SendAsync(request); 106 | responseString = await response.Content.ReadAsStringAsync(); 107 | 108 | /***************** 109 | * Since we are using the same HttpClient object, the FedAuth and rtFa cookies are now contained in it. 110 | * Get the .xoml.wfconfig.xml file for the relevant workflow. 111 | */ 112 | request = new HttpRequestMessage(HttpMethod.Post, url + workflowSiteCollectionURL + "/_vti_bin/_vti_aut/author.dll"); 113 | request.Headers.Add("X-Vermeer-Content-Type", "application/x-www-form-urlencoded"); 114 | request.Content = new StringContent("method=get+document:15.0.0.4455&service_name=" + 115 | workflowSiteCollectionURL + 116 | "&document_name=Workflows/" + workflowName + "/" + 117 | workflowName + 118 | ".xoml.wfconfig.xml&old_theme_html=false&force=true&get_option=none&doc_version=&timeout=0&expandWebPartPages=true", 119 | Encoding.UTF8, "application/x-www-form-urlencoded"); 120 | 121 | response = await client.SendAsync(request); 122 | responseString = await response.Content.ReadAsStringAsync(); 123 | } 124 | 125 | /***************** 126 | * TODO: Create app registration for CDS authentication(https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/walkthrough-register-app-azure-active-directory#create-an-application-registration), 127 | * Replace clientID with app registration Client Id, 128 | * Replace Power Automate Web API URL(https://docs.microsoft.com/en-us/power-automate/web-api#compose-http-requests), 129 | * Replace clientData with content from relevant Power Automate flow(https://docs.microsoft.com/en-us/power-automate/web-api#list-flows) 130 | */ 131 | private static async Task CreateNewWorkflow() 132 | { 133 | string resource = "https://org00000000.crm.dynamics.com"; 134 | string authorityURI = "https://login.microsoftonline.com/common"; 135 | string clientID = "000000000-0000-0000-0000-000000000"; 136 | Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authorityURI, false); 137 | UserPasswordCredential credentials = new UserPasswordCredential(user, password); 138 | AuthenticationResult authResult = await authContext.AcquireTokenAsync(resource, clientID, credentials); 139 | string accessToken = authResult.AccessToken; 140 | //Replace with clientData from preferred Power Automate flow. 141 | string clientData = "{\"properties\":{\"connectionReferences\":{\"shared_commondataservice\":{\"connectionName\":\"shared-commondataser-00000000-0000-0000-0000-000000000004\",\"source\":\"Invoker\",\"id\":\"/providers/Microsoft.Power Apps/apis/shared_commondataservice\"}},\"definition\":{\"$schema\": \"https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#\",\"contentVersion\": \"1.0.0.0\",\"parameters\": {\"$connections\": {\"defaultValue\": {},\"type\": \"Object\"},\"$authentication\": {\"defaultValue\": {},\"type\": \"SecureObject\"}},\"triggers\": {\"Recurrence\": {\"recurrence\": {\"frequency\": \"Minute\",\"interval\": 1},\"type\": \"Recurrence\"}},\"actions\": {\"List_records\": {\"runAfter\": {},\"metadata\": {\"flowSystemMetadata\": {\"swaggerOperationId\": \"GetItems_V2\"}},\"type\": \"ApiConnection\",\"inputs\": {\"host\": {\"api\": {\"runtimeUrl\": \"https://firstrelease-001.azure-apim.net/apim/commondataservice\"},\"connection\": {\"name\": \"@parameters('$connections')['shared_commondataservice']['connectionId']\"}},\"method\": \"get\",\"path\": \"/v2/datasets/@{encodeURIComponent(encodeURIComponent('default.cds'))}/tables/@{encodeURIComponent(encodeURIComponent('accounts'))}/items\",\"queries\": {\"$top\": 1},\"authentication\": \"@parameters('$authentication')\"}}},\"outputs\": {}}},\"schemaVersion\":\"1.0.0.0\"}"; 142 | WorkFlow workflow = new WorkFlow 143 | { 144 | category = 5, 145 | statecode = 0, 146 | name = "Sample Flow", 147 | type = 1, 148 | description = "Sample flow to test programmatic creation.", 149 | primaryentity = "none", 150 | clientdata = clientData 151 | }; 152 | client.DefaultRequestHeaders.Accept.Add( 153 | new MediaTypeWithQualityHeaderValue("application/json")); 154 | HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, resource + "/api/data/v9.1/workflows"); 155 | request.Content = new StringContent(JsonSerializer.Serialize(workflow), Encoding.UTF8, "application/json"); 156 | request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); 157 | HttpResponseMessage response = client.SendAsync(request).Result; 158 | if (!response.IsSuccessStatusCode) 159 | { 160 | Console.WriteLine("Error creating workflow: " + response.StatusCode + " - " + response.ReasonPhrase); 161 | } 162 | } 163 | } 164 | public class WorkFlow 165 | { 166 | public int category { get; set; } 167 | public int statecode { get; set; } 168 | public string name { get; set; } 169 | public int type { get; set; } 170 | public string description { get; set; } 171 | public string primaryentity { get; set; } 172 | public string clientdata { get; set; } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SharePointWorkflowConverter 2 | A proof-of-concept C# console application for retrieving legacy 2010 & 2013 custom SharePoint workflow definition files, and creating new Power Automate flows programmatically. 3 | 4 | ### General Notes: 5 | 1. This project is a proof-of-concept to demonstrate that it's *possible* to programmatically retrieve/parse SharePoint legacy workflow definition files and create new Power Automate flows. As for specific use cases and implementation, this is a launch pad for you to build those out. 6 | 1. Only compatible with SharePoint Online. 7 | 2. Tested with a tenant [global admin](https://docs.microsoft.com/en-us/microsoft-365/admin/add-users/about-admin-roles?view=o365-worldwide#commonly-used-microsoft-365-admin-center-roles) account. 8 | 3. Uses the 4.7.2 [.NET Framework](https://docs.microsoft.com/en-us/dotnet/standard/choosing-core-framework-server). 9 | 4. The code for retrieving legacy workflows and the code for creating a new Power Automate flow are contained in 2 separate respective functions: `GetLegacyWorkflow()` and `CreateNewWorkflow()`. If you don't need one or the other for your purposes, simply comment them out. 10 | ### Notes about retrieving legacy 2010/2013 workflows: 11 | 1. [Legacy authentication](https://techcommunity.microsoft.com/t5/microsoft-sharepoint-blog/sharepoint-online-authentication-in-powershell-for-csom-when/ba-p/510114) must be enabled in the tenant for this code to work. 12 | 2. All custom list/library workflows created within SharePoint Designer will have `*.xoml` and `*.xoml.wfconfig.xml` files. Each file contains pertinent information describing the workflow, such as list/library associations, actions, etc. You can manually view these files in [SharePoint Designer 2013](https://www.microsoft.com/en-us/download/details.aspx?id=35491) by clicking *All Files* from the left-hand navigation and selecting > *Workflows*. The files can be programmatically retrieved using this project and parsed to map to equivalent Power Automate connections, actions, etc. 13 | ![SharePoint Designer Screenshot](/SS.png?raw=true) 14 | 3. Declarative `*.xoml` and `*.xoml.wfconfig.xml` files do not appear to exist for workflows created from [OOB SharePoint 2010 or 2013 workflow templates](https://support.microsoft.com/en-us/office/overview-of-workflows-included-with-sharepoint-d74fcceb-3a64-40fb-9904-cc33ca49da56). *(If you can find them - I'll buy you a beverage of your choice!)* 15 | ### Notes about creating Power Automate flows: 16 | 1. This project leverages the [Power Automate Management Web API](https://docs.microsoft.com/en-us/power-automate/web-api) to create a new Power Automate flow. 17 | 2. Per Microsoft's [documentation](https://docs.microsoft.com/en-us/power-automate/web-api), flows shown in the *My Flows* tab are not supported by these APIs. Instead you will find flows created via the API in your *[Solutions](https://flow.microsoft.com/en-us/blog/solutions-in-microsoft-flow/) > Default Solution* tab. 18 | *(I have opened a [GitHub issue](https://github.com/MicrosoftDocs/power-automate-docs/issues/323) inquiring about how to programmatically create Power Automate flows within solutions other than Default.)* 19 | 3. The meat and potatoes of the flow payload is in the `clientdata` property. To help determine appropriate content for this section, I recommend manually creating the desired Power Automate flow using the UI. Then, you can [leverage the API](https://docs.microsoft.com/en-us/power-automate/web-api#list-flows) to get the `clientdata` content for your manual flow and copy/paste it into the payload for your new flow. 20 | *(Also a great strategy for programmatically bulk-creating Power Automate flows in an environment!)* 21 | 4. Once you successfully create a new Power Automate flow via the API, you will need to turn it on. Then, do a Ctrl+F5 refresh to confirm your connections appear as expected. 22 | ![Power Automate Screenshot](/SS1.png?raw=true) 23 | 24 | ### Happy Automating! :-) 25 | -------------------------------------------------------------------------------- /SS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dev-Mom/SharePointWorkflowConverter/b13d2a83fb657eb0d52dc9d8488911093405d51f/SS.png -------------------------------------------------------------------------------- /SS1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dev-Mom/SharePointWorkflowConverter/b13d2a83fb657eb0d52dc9d8488911093405d51f/SS1.png --------------------------------------------------------------------------------