1 Reply Latest reply on May 14, 2020 3:14 AM by AaronWhittaker

    Verbose logging in Powershel deployed via Product Provisioning

    davidallison Lurker

      Looking for some advice on how to get something more than 'Job Executed Successfully' when deploying a Powershell script in Product Provisioning

       

      I have a script with 3 outcomes and I'd like to get that information from the console. My Powershell skills are rather primitice.  I've tried Write-Output and Write-Host but no luck. Looking at adding logging methods to the script

        • 1. Re: Verbose logging in Powershel deployed via Product Provisioning
          AaronWhittaker Novice

          I would check out the PowerShell App Deploy Toolkit and see if you can take their logging functions to incorporate into your own script, they are quite feature packed.

           

          As for returning the result, that I'm not sure of as I haven't played too much with product provisioning. A very round about kind of way to get the result is for the script to write a reg key somewhere that you pick up as a custom attribute. We have started to play around with them quite a bit here to log things such as AD site.

           

          You need to create an XML and add it to C:\Program Files (x86)\AirWatch\AgentUI\Cache\Profiles. The XML will tell WS1 what reg keys to collect, an example of the XML is below. It wont be real time though as it will only update when the device syncs.

           

          <xml version="1.0">
              <wap-provisioningdoc name="desiredDocName /V_1">
                  <characteristic type="com.windowspc.getregistryinfo.managed">
                      <reg_value value_name="ADSiteDate" key_name="SOFTWARE\HealthCheck" custom_attribute_name="ADSite.LastUpdate" />
                      <reg_value value_name="ADSiteString" key_name="SOFTWARE\HealthCheck" custom_attribute_name="ADSite.Name" />
                  </characteristic>
              </wap-provisioningdoc>
          

           

          The biggest 'gotcha' with the custom attributes is that there are some forbidden characters that aren't allowed in the custom attributes. I got around them by replacing the characters with their ASCII equivalent.

           

          $ASCIIStringArray = @{
              '&#34' = """"
              '&#39' = "'"
              '&#92' = "\"
              '&#47' = "/"
              '&#58' = ":"
              '&#59' = ";"
          }
          
          $ASCIIStringArrayReverse = @{ }
          foreach ($item in $ASCIIStringArray.Keys) {
              $ASCIIStringArrayReverse.Add($ASCIIStringArray[$item], $item)
          }
          
          Function Convert-CustomVariableSafeString {
              <#
          .SYNOPSIS
              Converts the custom variable string into something safe that WorkSpace ONE custom attributes will be able to read
          .PARAMETER OriginalString
              The string that you want to convert
          .PARAMETER ToSafe
              A switch to tell the function to convert the string to something safe for WS1
          .PARAMETER FromSafe
              A switch to tell the function to convert the string from something safe for WS1 into something easier to read e.g. when outputting to a report
          .EXAMPLE
              Convert-CustomVariableSafeString -OriginalString $Value -ToSafe
          .NOTES
          
          #>
              Param (
                  $OriginalString,
                  [switch]$ToSafe,
                  [switch]$FromSafe
              )
          
              Begin {
                  
              }
              Process {
                  if ($ToSafe) {
          
                      If ($OriginalString -match "^[A-Za-z0-9\``\!\@\#\`$\^\(\)\%\-_\+\=\'\,\.]*$") {
                          #string is valid
                          $ReturnString = $OriginalString
                      } else {
                          ForEach ($Mapped in $ASCIIStringArray.Values) {
                              $OriginalString = $OriginalString.Replace($Mapped, $ASCIIStringArrayReverse[$Mapped])
                          }
                          If ($OriginalString -match "[A-Za-z0-9\``\!\@\#\`$\^\(\)\%\-_\+\=\'\,\.]*") {
                              $ReturnString = $OriginalString
                          } else {
                              $longFormatter = "";
                              foreach ($char in $OriginalString) {
                                  if ($char -notmatch "[A-Za-z0-9\``\!\@\#\`$\^\(\)\%\-\_\+\=\'\,\.]") {
                                      $longFormatter += "`%";
                                  } else {
                                      $longFormatter += $char;
                                  }
                              }
                              $ReturnString = $longFormatter
                          }
                      }
                  }
          
                  if ($FromSafe) {
                      $ReturnString = Replace-Ascii -string $OriginalString
                  }
          
                  Write-Output -InputObject $ReturnString
              }
              End {
                  
              }
          }
          
          Function Replace-ASCII {
              <#
          .SYNOPSIS
              Replaces the forbidden characters that WorkSpace ONE cannot read with their ASCII equivalent
          .PARAMETER String
              The string to replace
          .EXAMPLE
              Replace-ASCII -String "23/12/2020 11:51:33"
                  Returns "23/12/2020 11&#5851&#5833"
          .NOTES
          
          #>
              Param (
                  $string
              )
          
              Begin {
              }
              Process {
          
                  foreach ($Character in $ASCIIStringArray.Keys) {
                      $string = $string.Replace($Character, $ASCIIStringArray[$Character] )
                  }
          
                  Write-Output -InputObject $string
              }
              End {
              }
          }
          

           

          Its not the best bit of code ive ever written and im sure it can be cleaned up but it does the job.

           

          I have realised while typing all of this that this is a very complicated way of returning an exit code but figured I spent so long typing it that I should post it anyway and it might be helpful to you at some later point.

           

          I might have a play around in the next few days and see if I can work out the return code thing for product provisioning directly.