Automatiser les déploiements Nintex Workflow et Nintex Forms

Automatiser les déploiements Nintex Workflow et Nintex Forms

Lors de la mise en œuvre d’un projet utilisant les technologies Nintex, l’ensemble des déploiements ont été automatisés.
Voici, ci-dessous, un descriptif des commandes utilisées. Chaque commande doit être exécutée dans l’invite de commandes PowerShell SharePoint avec un compte administrateur SharePoint et Nintex.

En prérequis, Nintex Workflows et Nintex Forms doivent être déployés au niveau de la ferme SharePoint ainsi que sur la Web Application ciblée. Le déploiement des solutions Nintex proposé via leur installateur reposant sur PowerShell, l’automatisation de l’installation est possible mais n’est pas décrit dans cet article. L’export des workflows (fichiers NWF) et des formulaires (fichiers XML) se fait via le fonctionnement natif proposé par Nintex.

 

Activer Nintex (Web Application)

La première étape de déploiement de Nintex Workflow est son activation au niveau de la Web Application. Son activation n’est possible que par l’administration centrale de SharePoint. Le code nécessaire pour remplacer l’activation manuelle se fait par Reflection :

  1. function Nintex-InstallWebApp($WebApp)
  2. {
  3. Write-Host ">> Installing Web Application <$WebApp> for Nintex..."
  4.  
  5. try {
  6. [System.Reflection.Assembly]::LoadWithPartialName("Nintex.Workflow.AdministrationPages") >> $null
  7. $manageSafeAction = New-Object Nintex.Workflow.AdministrationPages.ManageSafeActions
  8. $installWebAppMethod = $manageSafeAction.GetType().GetMethod("InstallWebApp",[Reflection.BindingFlags]::NonPublic -bor [Reflection.BindingFlags]::Instance)
  9. $installWebAppMethod.Invoke($manageSafeAction, $WebApp)
  10. }
  11. catch [Exception] {
  12. Write-Host ">> An error occured while installing Web Application <$WebApp> for Nintex. Script execution will continue." -ForegroundColor Yellow
  13. #Handle error display here with $_
  14. }
  15.  
  16. Write-Host ">> Installed Web Application <$WebApp> for Nintex" -ForegroundColor Green
  17. }

L’activation de Nintex Workflow pour une Web Application est :

  1. $WebAppUrl = "http://WebApplicationUrl"
  2. $WebApp = Get-SPWebApplication $WebAppUrl
  3. Nintex-InstallWebApp -WebApp $WebApp

Du côté de Nintex Forms, l’activation se fait par la fonctionnalité au niveau Web Application :

  1. $NintexFormsWebApplicationFeatureID = "0cdf436f-61b0-43d2-b250-4360f0353b63"
  2. Enable-SPFeature $NintexFormsWebApplicationFeatureID -Url "http://WebApplicationUrl"

 

Activer Nintex (Site Collection)

Par la suite, l’action de Nintex Workflow et Nintex Form se fait par activation de fonctionnalités au niveau Site Collection.

Ce code permet d’activer la fonctionnalité NintexFormsSitePrerequisites au niveau Site Collection :

  1. $NintexFormsSitePrerequisitesFeatureID = "716f0ee9-e2b0-41f0-a73c-47ed73f135de"
  2. Enable-SPFeature $NintexFormsSitePrerequisitesFeatureID -Url "http://SiteCollectionUrl"

Ce code permet d’activer la fonctionnalité NintexFormsListSite au niveau Site Collection :

  1. $NintexFormsListSiteFeatureID = "202afc3c-7384-4700-978d-6da3d3cce192"
  2. Enable-SPFeature $NintexFormsListSiteFeatureID -Url "http://SiteCollectionUrl"

Ce code permet d’activer la fonctionnalité NintexWorkflow au niveau Site Collection :

  1. $NintexWorkflowFeatureID = "0561D315-D5DB-4736-929E-26DA142812C5"
  2. Enable-SPFeature $NintexWorkflowFeatureID -Url "http://SiteCollectionUrl"

Ce code permet d’activer la fonctionnalité NintexFormsWorkflowSite au niveau Site Collection :

  1. $NintexFormsWorkflowSiteFeatureID = "ac8addc7-7252-4136-8dcb-9887a277ae2c"
  2. Enable-SPFeature $NintexFormsWorkflowSiteFeatureID -Url "http://SiteCollectionUrl"

Ce code permet d’activer la fonctionnalité NintexWorkflowWebParts au niveau Site Collection :

  1. $NintexWorkflowWebPartsFeatureID = "EB657559-BE37-4b91-A369-1C201183C779"
  2. Enable-SPFeature $NintexWorkflowWebPartsFeatureID -Url "http://SiteCollectionUrl"

 

Activer Nintex (Site)

De la même manière que pour le niveau Site Collection, on peut activer les fonctionnalités au niveau Site :

Ce code permet d’activer la fonctionnalité NintexFormsAdminWeb au niveau Site Collection :

  1. $NintexFormsAdminWebFeatureID = "70f4f7da-4fb4-4e30-ba1a-a733efb2e1ac"
  2. Enable-SPFeature $NintexFormsAdminWebFeatureID -Url "http://SiteCollectionUrl"

Ce code permet d’activer la fonctionnalité NintexWorkflowWeb au niveau Site Collection :

  1. $NintexWorkflowWebFeatureID = "9BF7BF98-5660-498a-9399-BC656A61ED5D"
  2. Enable-SPFeature $NintexWorkflowWebFeatureID -Url "http://SiteCollectionUrl"

 

Déployer un workflow Nintex

Le déploiement de workflow Nintex est rendu possible par l’utilisation de la commande d’administration NWAdmin. La fonction de déploiement de workflow est décrite ci-dessous :

  1. function Nintex-PublishWorkflow(
  2. [string] $WorkflowName,
  3. [string] $WorkflowFile,
  4. [string] $SiteUrl,
  5. [string] $TargetList
  6. )
  7. {
  8. Write-Host ">> Publishing Workflow <$WorkflowName> on Site <$SiteUrl> ..."
  9.  
  10. $fileItem = Get-ChildItem $WorkflowFile
  11. Write-Host ">> Workflow File is set: <$($fileItem.Name)>"
  12.  
  13. if($TargetList) {
  14. Write-Host ">> Target List is set: <$TargetList>"
  15. NWAdmin.exe -o DeployWorkflow -workflowName $WorkflowName -nwfFile $WorkflowFile -siteUrl $SiteUrl -targetList $TargetList -saveIfCannotPublish -overwrite
  16. Write-Host ">> Published Workflow <$WorkflowName> on Site <$SiteUrl> in List <$TargetList>" -ForegroundColor Green
  17. }
  18. else {
  19. NWAdmin.exe -o DeployWorkflow -workflowName $WorkflowName -nwfFile $WorkflowFile -siteUrl $SiteUrl -saveIfCannotPublish -overwrite
  20. Write-Host ">> Published Workflow <$WorkflowName> on Site <$SiteUrl>" -ForegroundColor Green
  21. }
  22. }

Ainsi, le déploiement d’un workflow de niveau site devient :

  1. $WorkflowName = "The name of your workflow"
  2. $WorkflowFile = "File path of the nwf"
  3. $SiteUrl = "http://yourSiteCol/sitename"
  4.  
  5. Nintex-PublishWorkflow -WorkflowName $WorkflowName `
  6. -WorkflowFile $WorkflowFile `
  7. -SiteUrl $SiteUrl

Le déploiement d’un workflow de niveau liste devient :

  1. $WorkflowName = "The name of your workflow"
  2. $WorkflowFile = "File path of the nwf"
  3. $SiteUrl = "http://yourSiteCol/sitename"
  4. $ListName = "Name of the list"
  5.  
  6. Nintex-PublishWorkflow -WorkflowName $WorkflowName `
  7. -WorkflowFile $WorkflowFile `
  8. -SiteUrl $SiteUrl `
  9. -TargetList $ListName

Pour aller plus loin, il est possible de sauvegarder des Tokens dans les fichiers exportés, rendant le déploiement possible sur de multiples environnements, par les méthodes suivantes :

  1. function Nintex-PublishWorkflow-TokensReplace(
  2. [string] $WorkflowName,
  3. [string] $WorkflowFile,
  4. [string] $SiteUrl,
  5. [string] $TargetList,
  6. [string] $TargetPath
  7. )
  8. {
  9. Write-Host ">> Publishing Workflow <$WorkflowName> on Site <$SiteUrl> ..."
  10.  
  11. $File = Get-ChildItem $WorkflowFile
  12.  
  13. if(!$(Test-Path $TargetPath)) {
  14. $tempDirectory = New-Item -ItemType Directory -Force -Path $TargetPath
  15. }
  16.  
  17. $TargetPath = Resolve-Path $TargetPath
  18.  
  19. if(!$TargetPath.EndsWith("\")) {
  20. $TargetPath = $TargetPath + "\"
  21. }
  22.  
  23. $TargetFile = $TargetPath + $($File.Name)
  24.  
  25. $stream = Get-ContentToStreamWithTokens-Nintex -FileName $WorkflowFile
  26.  
  27. try {
  28. $fileStream = [System.IO.File]::Create($TargetFile)
  29. [byte[]] $bytes = New-Object Byte[] $stream.Length
  30. $stream.Read($bytes, 0, [int] $stream.Length) >> $null
  31. $fileStream.Write($bytes, 0, $bytes.Length)
  32. }
  33. finally {
  34. $fileStream.Close()
  35. }
  36.  
  37. $targetFileItem = Get-ChildItem $TargetFile
  38. Write-Host ">> Workflow File with Tokens replaced is created: <$($targetFileItem.Name)>"
  39.  
  40. if($TargetList) {
  41. Write-Host ">> Target List is set: <$TargetList>"
  42. NWAdmin.exe -o DeployWorkflow -workflowName $WorkflowName -nwfFile $TargetFile -siteUrl $SiteUrl -targetList $TargetList -saveIfCannotPublish -overwrite
  43. Write-Host ">> Published Workflow <$WorkflowName> on Site <$SiteUrl> in List <$TargetList>" -ForegroundColor Green
  44. }
  45. else {
  46. NWAdmin.exe -o DeployWorkflow -workflowName $WorkflowName -nwfFile $TargetFile -siteUrl $SiteUrl -saveIfCannotPublish -overwrite
  47. Write-Host ">> Published Workflow <$WorkflowName> on Site <$SiteUrl>" -ForegroundColor Green
  48. }
  49. }
  50.  
  51. function Get-ContentToStreamWithTokens-Nintex(
  52. $FileName
  53. )
  54. {
  55. Write-Host ">> Getting content from File <$([System.IO.Path]::GetFileName($FileName))> ..."
  56. $fileContent = [System.IO.File]::ReadAllText($FileName, [System.Text.Encoding]::UTF8)
  57.  
  58. if($Web) {
  59. $siteUrl = "http://YourSharePointUrl"
  60. if(!$siteUrl.EndsWith("/")) { $siteUrl = $siteUrl + "/" }
  61. }
  62.  
  63. $fileContent = $fileContent.Replace("{{SharePointUrl}}", "http://YourSharePointUrl")
  64.  
  65. # Credentials are encrypted by Nintex
  66. $spAdmin = $(Nintex-EncryptCredential -Username "SPAdmin" -Password "***")
  67. $fileContent = $fileContent.Replace("{{SharePointAdmin}}", $spAdmin)
  68.  
  69.  
  70. $encoding = New-Object System.Text.UTF8Encoding($False)
  71. $fileBytes = $encoding.GetBytes($fileContent.ToString())
  72. [System.IO.MemoryStream] $fileStream = New-Object System.IO.MemoryStream(,$fileBytes)
  73.  
  74. return $fileStream
  75. }

 

Déployer un formulaire Nintex

Le déploiement de formulaires Nintex est rendu possible par l’exploitation des services web exposés par Nintex. Le code ci-dessous permet ainsi de déployer un formulaire précédemment exporté :

  1. function Nintex-PublishForm(
  2. [string] $FileName,
  3. [Microsoft.SharePoint.SPWeb] $Web,
  4. [string] $ListRootFolderName
  5. )
  6. {
  7. Write-Host ">> Publishing Nintex Form on List <$ListRootFolderName>..."
  8.  
  9. $formDigest = Get-FormDigest -Web $Web
  10. $addressUrl = [Microsoft.SharePoint.Utilities.SPUtility]::ConcatUrls($Web.Url, "_vti_bin/NintexFormsServices/NfRestService.svc/PublishForm")
  11. $addressUri = New-Object System.Uri($addressUrl)
  12.  
  13. # Create the web request
  14. [System.Net.HttpWebRequest] $request = [System.Net.WebRequest]::Create($addressUri)
  15.  
  16. # Add authentication to request
  17. $request.Credentials = New-Object System.Net.NetworkCredential($NintexAdministratorUsername, $NintexAdministratorPassword, $NintexAdministratorDomain)
  18.  
  19. # Set type to POST
  20. $request.Method = "POST";
  21. $request.ContentType = "application/json; charset=utf-8";
  22. $request.Accept = "application/json, text/javascript, */*; q=0.01"
  23. $request.Headers.Add("X-RequestDigest", $formDigest);
  24. $request.Headers.Add("X-Requested-With", "XMLHttpRequest")
  25.  
  26. $form = Nintex-GetJsonFormFromXml -FileName $FileName
  27.  
  28. # Create the data we want to send
  29. $list = Get-SPListByInternalName -Web $Web -InternalName $ListRootFolderName
  30. $id = "{$($list.ID)}"
  31. $data = "{`"contentTypeId`": `"`", `"listId`": `"$id`", `"form`": $form }"
  32.  
  33. # Create a byte array of the data we want to send
  34. $utf8 = New-Object System.Text.UTF8Encoding
  35. [byte[]] $byteData = $utf8.GetBytes($data.ToString())
  36.  
  37. # Set the content length in the request headers
  38. $request.ContentLength = $byteData.Length;
  39.  
  40. # Write data
  41. try {
  42. $postStream = $request.GetRequestStream()
  43. $postStream.Write($byteData, 0, $byteData.Length);
  44. }
  45. finally {
  46. if($postStream) { $postStream.Dispose() }
  47. }
  48.  
  49. # Get response
  50. try {
  51. [System.Net.HttpWebResponse] $response = [System.Net.HttpWebResponse] $request.GetResponse()
  52.  
  53. # Get the response stream
  54. [System.IO.StreamReader] $reader = New-Object System.IO.StreamReader($response.GetResponseStream())
  55.  
  56. try {
  57. $strResult = $reader.ReadToEnd()
  58. $jsonResult = ConvertFrom-Json $strResult
  59. Write-Host ">> Published Nintex Form on List <$ListRootFolderName>" -ForegroundColor Green
  60. Write-Host ">> Published Version is : $($jsonResult.PublishFormResult.Version)"
  61. }
  62. catch [Exception] {
  63. Write-Host ">> Warning: Unable to get version from Publish Form Result." -ForegroundColor Yellow
  64. Write-Host ">> Form Publishing could have failed." -ForegroundColor Yellow
  65. Write-Host ">> PublishFormResult is : $($strResult)"
  66. }
  67. }
  68. finally {
  69. if($response) { $response.Dispose() }
  70. }
  71. }
  72.  
  73. function Get-FormDigest(
  74. [Microsoft.SharePoint.SPWeb] $Web
  75. )
  76. {
  77. AWrite-Host ">> Getting Form Digest ..."
  78.  
  79. [System.Reflection.Assembly]::LoadWithPartialName("System.IO") >> $null
  80.  
  81. $formDigestRequest = [Microsoft.SharePoint.Utilities.SPUtility]::ConcatUrls($Web.Site.RootWeb.Url, "_api/contextinfo")
  82.  
  83. Write-Host ">> Form Digest url is: $formDigestRequest"
  84.  
  85. $formDigestUri = New-Object System.Uri($formDigestRequest)
  86.  
  87. $credCache = New-Object System.Net.CredentialCache
  88. $credCache.Add($formDigestUri, "NTLM", [System.Net.CredentialCache]::DefaultNetworkCredentials)
  89. $spRequest = [System.Net.HttpWebRequest] [System.Net.HttpWebRequest]::Create($formDigestRequest)
  90. $spRequest.Credentials = $credCache
  91. $spRequest.Method = "POST"
  92. $spRequest.Accept = "application/json;odata=verbose"
  93. $spRequest.ContentLength = 0
  94.  
  95. [System.Net.HttpWebResponse] $endpointResponse = [System.Net.HttpWebResponse] $spRequest.GetResponse()
  96. [System.IO.Stream]$postStream = $endpointResponse.GetResponseStream()
  97. [System.IO.StreamReader] $postReader = New-Object System.IO.StreamReader($postStream)
  98. $results = $postReader.ReadToEnd()
  99.  
  100. $postReader.Close()
  101. $postStream.Close()
  102.  
  103. #Get the FormDigest Value
  104. $startTag = "FormDigestValue"
  105. $endTag = "LibraryVersion"
  106. $startTagIndex = $results.IndexOf($startTag) + 1
  107. $endTagIndex = $results.IndexOf($endTag, $startTagIndex)
  108. [string] $newFormDigest = $null
  109. if (($startTagIndex -ge 0) -and ($endTagIndex -gt $startTagIndex))
  110. {
  111. $newFormDigest = $results.Substring($startTagIndex + $startTag.Length + 2, $endTagIndex - $startTagIndex - $startTag.Length - 5)
  112. }
  113.  
  114. Write-Host ">> Form Digest Set"
  115.  
  116. return $newFormDigest
  117. }
  118.  
  119. ##
  120. # Gets the json form from a Nintex exported file.
  121. # Filename: the filename (file path)
  122. # Returns: the json representation of an exported Nintex file.
  123. function Nintex-GetJsonFormFromXml($FileName)
  124. {
  125. Write-Host ">> Getting Json from Nintex Form Xml..."
  126.  
  127. [System.Reflection.Assembly]::LoadWithPartialName("Nintex.Forms.SharePoint") >> $null
  128. [System.Reflection.Assembly]::LoadWithPartialName("Nintex.Forms") >> $null
  129.  
  130. [byte[]] $fileBytes = Read-FileBytes -FileName $FileName
  131. try
  132. {
  133. $form = [Nintex.Forms.FormsHelper]::XmlToObject([Nintex.Forms.NFUtilities]::ConvertByteArrayToString($fileBytes))
  134. }
  135. catch [Exception]
  136. {
  137. $form = [Nintex.Forms.FormsHelper]::XmlToObject([Nintex.Forms.NFUtilities]::ConvertByteArrayToString($fileBytes, [System.Text.Encoding]::UTF8))
  138. }
  139.  
  140. $form.LiveSettings.Url = ""
  141. $form.LiveSettings.ShortUrl = ""
  142. $form.RefreshLayoutDisplayNames()
  143. $form.Id = [guid]::NewGuid()
  144.  
  145. $json = [Nintex.Forms.FormsHelper]::ObjectToJson($form)
  146.  
  147. Write-Host ">> Json from Nintex Form Xml is set"
  148.  
  149. return $json
  150. }
  151.  
  152. ##
  153. # Reads the file bytes.
  154. # Filename: the filename (file path)
  155. # Returns: the file bytes.
  156. function Read-FileBytes($Filename)
  157. {
  158. try {
  159. [system.io.stream] $stream = [system.io.File]::OpenRead($Filename)
  160. try {
  161. [byte[]] $filebytes = New-Object byte[] $stream.length
  162. [void] $stream.Read($filebytes, 0, $stream.Length)
  163.  
  164. return $filebytes
  165. }
  166. finally {
  167. $stream.Close()
  168. }
  169. }
  170. catch {
  171. Write-Error "Error reading file $fileItem - $_"
  172. return
  173. }
  174. }
  175.  
  176. ##
  177. # Gets the SharePoint List by its internal name.
  178. # Internal name refers to the List Root Folder name.
  179. # Web: the SPWeb
  180. # InternalName: the SharePoint List internal name
  181. # Returns: the SharePoint list or $null if not found.
  182. function Get-SPListByInternalName($Web, $InternalName)
  183. {
  184. #RootFolder == internal listname, loop through and find (null will be returned if its not found)
  185. $list = $Web.Lists | Where { $_.RootFolder.Name.ToLowerInvariant() -eq $InternalName.ToLowerInvariant()}
  186.  
  187. return $list
  188. }
  189.  
  190. $WebUrl = "http://SiteCollectionUrl/SiteName"
  191. $Web = Get-SPWeb -Site $WebUrl
  192. $FileName = "PathToXmlFile"
  193. $ListRootFolderName = "ListRootFolderName"
  194. $NintexAdministratorUsername ="NintexAdmin"
  195. $NintexAdministratorPassword ="NintexPassword"
  196. $NintexAdministratorDomain ="Domain"
  197.  
  198. Nintex-PublishForm -Web $Web `
  199. -FileName $FileName `
  200. -ListRootFolderName $ListRootFolderName

 

Déployer des constantes

Le code suivant permet de déployer des constantes associées à un workflow :

  1. $TargetFile = "path of the xml"
  2. $SiteUrl = "http://yourSiteCol/sitename"
  3. NWAdmin.exe -o ImportWorkflowConstants -siteUrl $SiteUrl -inputFile $TargetFile -handleExisting Overwrite -includeSite

 

iNext Consulting SA

Chemin des Aulx 14 - Bât14 CTN

CH-1228 Plan-les-Ouates


+41 22 794 71 36

contact(at)inext-consulting.ch