Automating Documentation with GitHub & Markdown – Part 2 – Construct

Automating Documentation with GitHub & Markdown – Part 2 – Construct

In Part 1 I discussed the concept and structure of what I am trying to achieve. In this part I’ll show you how it all works together.

Before we get on with GitHub and document automation, I wanted to be able to create a handful of base template documents from the same set of markdown assets. I don’t want to be having to type in the filenames of each document I want to include each time. To solve this I first created 4 or 5 document flows from the elements I created that target the main different deployment scenarios customers will want. Once I had the order of the elements for each document template, I needed a way of being able to call the structure with ease.

I decided I would create a JSON file that I could write a PS script to use to generate the templates for me. In the JSON file I would create document options, and each option would list out the markdown elements and the order I want them to appear in the final document.

"cloudcollab": [
  {
    "id":"1",
    "file":"DocIntro.md"
  },
  {
    "id":"2",
    "file":"MSTeamsLogicalArchitecture.md"
  },
  {
    "id":"3",
    "file":"MSTeamsSecCom.md"
  },
  {
    "id":"4",
    "file":"MSTeamsGovernance.md"
  },
  {
    "id":"5",
    "file":"MSTeamsLicensing.md"
  },
  {
    "id":"6",
    "file":"MSTeamsMessaging.md"
  },
  {
    "id":"7",
    "file":"MSTeamsCollaboration.md"
  },
  {
    "id":"8",
    "file":"MSTeamsClient.md"
  },
  {
    "id":"9",
    "file":"MSTeamsVDI.md"
  },
  {
    "id":"10",
    "file":"MSTeamsOperations.md"
  },
  {
    "id":"11",
    "file":"MSTeamsReporting.md"
  },
  {
    "id":"12",
    "file":"MSTeamsDisaster.md"
  }
],

In the above JSON structure I would be creating a Teams design document based on Chat and Collaboration only.

Now for storing all this on Github. First off, I don’t want to be sharing all my IP with the public (sorry) so I needed to ensure it is protected. GitHub supports private repositories, so I made one for my files. If you want your team to work on the documentation, you can invite fellow workers as contributors to your private repository. For free I think you can invite up to 3 people. To add more than 3, you need a Github premium account.

One caveat on using private repositories is that when you are authoring a document and referencing an image file from that repository in markdown, it doesn’t work because of the authentication barrier. In order to solve this, I create a separate, public repository to store just my images, and I would reference those instead.

To keep files in sync, it is best to use GitHub Desktop to keep your documentation up to date.

Now, to generate the document from GitHub, I thought this would be easy to use Powershell and Invoke-RestMethod or Invoke-WebRequest to pull the files I wanted directly from the private repository. It turns out that GitHub doesn’t allow you to send your authentication token using either of these methods. Several blogs and trials later I gave up, and thought that I had reached a dead end.

I found a program called git scm which you can install that allows you to use some command line tools to clone the repository. After you install this, edit your PATH variable in your system Environmental Variables to include the following path:

C:\Program Files\Git\Bin

This will allow you to reference git.exe without specifying the path to the executable in any script.

All that is left now is to clone the repository to your local machine, create the YAML front matter file, process the JSON file and build the document.

To clone the repository from PowerShell use the following command

git clone https://github.com/user/repo

You’ll be asked to login to GitHub and it will generate an access key so that all future clones happen without having to enter your username and password.

Now you need to create your YAML file. You can do this in PowerShell by prompting for all the variables, or reference a pre-made YAML template

Once you have got this, if you have used a JSON array to structure your document elements, read this into a PowerShell variable

$file = Get-Content  .\teamsdoc\params.json | ConvertFrom-Json

Now you can create an option menu in PowerShell and press 1,2,3 etc to generate a document

function Show-Menu
{
     param (
           [string]$Title = 'Please choose which type of document to create'
     )
     cls
     Write-Host "================ $Title ================"
     
     Write-Host "1: Press '1' Collab Only."
     Write-Host "2: Press '2' Collab and Meetings."
     Write-Host "3: Press '3' Cloud PSTN Calling."
     Write-Host "4: Press '4' Direct Routing."
     Write-Host "5: Press '5' Hybrid PSTN"
     Write-Host "Q: Press 'Q' to quit."
}

do
{
     Show-Menu
     $input = Read-Host "Please make a selection"
     switch ($input)
     {
           '1' {
                cls
                'You chose Collab Only'
                Generate-DocTemplate -Template "cloudcollab"
           } '2' {
                cls
                'You chose Collab and Meetings'
                Generate-DocTemplate -Template "cloudcollabmeetings"
           } '3' {
                cls
                'You chose Cloud PSTN Calling'
                Generate-DocTemplate -Template "cloudcalling"
           } '4' {
                cls
                'You chose Direct Routing - To be added'
           } '5' {
                cls
                'You chose Hybrid Voice - To be added'
           } 'q' {
                return
           }
     }
    pause

}
until ($input -eq 'q')

Finally, create yourself a little function to take the chosen structure from JSON and add the file elements

Function Generate-DocTemplate{
        
        param (
            [string]$Template = 'cloudcollab'
        )
       
       $doc = $file.$Template

       foreach($element in $doc){
            
            $input = "$($input) $($element.file)"
       }

       $mdfiles = $input.Substring(1)

       Set-Location -Path C:\autodoc\teamsdoc

       $test = Test-Path -Path command.cmd

       If ($test -eq $true){

        Remove-Item -Path command.cmd -Force -confirm:$false

       }

       New-item -ItemType file -name command.cmd

       set-content -path command.cmd -value "cd c:\autodoc\teamsdoc
       pandoc.exe $($mdfiles) --filter pandoc-mustache --toc --standalone --reference-doc $($documentreference) --o LLD.docx"

       .\command.cmd

       # move lld to final folder

       Move-Item -Path LLD.docx -Destination C:\autodoc\final

       Set-Location -Path 'C:\autodoc'

       Rename-Item -Path .\final\LLD.docx -NewName "$($customer) Teams Low Level Design.docx"

       ## clean up directory

       Remove-Item -Path .\teamsdoc -Recurse -Force -Confirm:$false


       Write-Host "Finished creating document, check c:\autodoc\final for the document. You may now press Q to quit..." -ForegroundColor Green
       
}

One tip is that when you create your Pandoc command programmatically PowerShell has trouble parsing it due to the double dash parameters, so the way to get around this is to temporarily create a .cmd file and write the output to this file, then execute it using command prompt. After it is completed, the command file can be deleted.

Full PowerShell Script:

# set directory and create directory structure.

$tdir = Test-Path C:\autodoc -PathType Container

If ($tdir -eq $false){
        
        $mdir = New-Item -Path c:\autodoc -Force -ItemType Diretory
}
$tdirf = Test-Path c:\autodoc\final -PathType Container

If ($tdirf -eq $false){
    $mdirf = New-Item -Path c:\autodoc\final -ItemType Directory -Force
}

# change working directory of script

Set-Location -Path C:\autodoc -PassThru

# clone the git

    git clone https://github.com/user/repo


# get parameters file and load variables for script

$file = Get-Content  .\teamsdoc\params.json | ConvertFrom-Json

ForEach($var in $file.variables){
        
        Set-Variable -Name $var.param -Value $var.value
}

## Or use PS to Set variable values instead of parsing them from JSON
$customer = Read-Host "Please enter customer name"
$date = Get-Date -Format d-mm-y
$plan = Read-Host "Enter O365 Plan e.g. E5"
$version = Read-Host "Enter Document Version"
$residency = Read-Host "Enter Tenant Location"
$audioconferencinglicenses = Read-Host "Enter how many audio conferencing licenses required"
$azureadp1licenses = Read-Host "Enter how many Azure P1 licenses needed"
$meetingroomlicenses = Read-Host "Enter how many meeting room licenses needed"
$commonarealicenses = Read-Host "Enter how many common area phone licenses required"
$domcallingplanlicenses = Read-Host "Enter how many Domestic Calling Plan licenses required"
$intcallingplanlicenses = Read-Host "Enter how many International Calling Plan licenses required"
$phonesystemlicenses = Read-Host "Enter how many phone system licenses required"
$enterpriseuserlicenses = Read-Host "Enter how many E plans are required"
$communicationcredits = Read-Host "How much will be loaded into Communication Credits?"
$virtualuserlicenses = Read-Host "How many virtual user licenses required"
$documentreference = "referencedoc.docx"
## Create YAML file

$yaml = ".\teamsdoc\docmeta.yaml"

$filetest = Test-Path .\teamsdoc\docmeta.yaml

if ($filetest -eq $true){
    
    Remove-Item -Path .\teamsdoc\docmeta.yaml -Force -Confirm:$false
}

New-Item -Path $yaml -ItemType File -Force

$content = "customer: $($customer)
supplier: $($supplier)
date:  '$($date)'
plan: $($plan)
version: '$($version)'
residency: $($residency)
audioconferencinglicenses: '$($audioconferencinglicenses)'
azureadp1licenses: '$($azureadp1licenses)'
meetingroomlicenses: '$($meetingroomlicenses)'
commonarealicenses: '$($commonarealicenses)'
domcallingplanlicenses: '$($domcallingplanlicenses)'
intcallingplanlicenses: '$($intcallingplanlicenses)'
phonesystemlicenses: '$($phonesystemlicenses)'
enterpriseuserlicenses: '$($enterpriseuserlicenses)'
communicationcredits: '$($communicationcredits)'
requiredvirtualuserlicenses: '$($virtualuserlicenses)'
virtualuserlicenses: '10'"

Set-Content -Path $yaml -Value $content

## Create document template

Function Generate-DocTemplate{
        
        param (
            [string]$Template = 'cloudcollab'
        )
       
       $doc = $file.$Template

       foreach($element in $doc){
            
            $input = "$($input) $($element.file)"
       }

       $mdfiles = $input.Substring(1)

       Set-Location -Path C:\autodoc\teamsdoc

       $test = Test-Path -Path command.cmd

       If ($test -eq $true){

        Remove-Item -Path command.cmd -Force -confirm:$false

       }

       New-item -ItemType file -name command.cmd

       set-content -path command.cmd -value "cd c:\autodoc\teamsdoc
       pandoc.exe $($mdfiles) --filter pandoc-mustache --toc --standalone --reference-doc $($documentreference) --o LLD.docx"

       .\command.cmd

       # move lld to final folder

       Move-Item -Path LLD.docx -Destination C:\autodoc\final

       Set-Location -Path 'C:\autodoc'

       Rename-Item -Path .\final\LLD.docx -NewName "$($customer) Teams Low Level Design.docx"

       ## clean up directory

       Remove-Item -Path .\teamsdoc -Recurse -Force -Confirm:$false


       Write-Host "Finished creating document, check c:\autodoc\final for the document. You may now press Q to quit..." -ForegroundColor Green
       
}
## set option menu

function Show-Menu
{
     param (
           [string]$Title = 'Please choose which type of document to create'
     )
     cls
     Write-Host "================ $Title ================"
     
     Write-Host "1: Press '1' Collab Only."
     Write-Host "2: Press '2' Collab and Meetings."
     Write-Host "3: Press '3' Cloud PSTN Calling."
     Write-Host "4: Press '4' Direct Routing."
     Write-Host "5: Press '5' Hybrid PSTN"
     Write-Host "Q: Press 'Q' to quit."
}

do
{
     Show-Menu
     $input = Read-Host "Please make a selection"
     switch ($input)
     {
           '1' {
                cls
                'You chose Collab Only'
                Generate-DocTemplate -Template "cloudcollab"
           } '2' {
                cls
                'You chose Collab and Meetings'
                Generate-DocTemplate -Template "cloudcollabmeetings"
           } '3' {
                cls
                'You chose Cloud PSTN Calling'
                Generate-DocTemplate -Template "cloudcalling"
           } '4' {
                cls
                'You chose Direct Routing - To be added'
           } '5' {
                cls
                'You chose Hybrid Voice - To be added'
           } 'q' {
                return
           }
     }
    pause

}
until ($input -eq 'q')

Example JSON file params.json – add your own params you used in your document.

{
"variables":[
    {"param":"customer","value":""},
    {"param":"plan","value":""},
    {"param":"supplier","value":""},
    {"param":"version","value":""},
    {"param":"date","value":""},
    {"param":"enterpriseuserlicenses","value":""},
    {"param":"phonesystemlicenses","value":""},
    {"param":"domcallingplanlicenses","value":""},
    {"param":"intcallingplanlicenses","value":""},
    {"param":"audioconferencinglicenses","value":""},
    {"param":"azureadp1licenses","value":""},
    {"param":"virtualuserlicenses","value":""},
    {"param":"requiredvirtualuserlicenses","value":""},
    {"param":"meetingroomlicenses","value":""},
    {"param":"communicationcredits","value":""},
    {"param":"commonarealicenses","value":""},
    {"param":"residency","value":""}
],
"cloudcollab": [
  {
    "id":"1",
    "file":"DocIntro.md"
  },
  {
    "id":"2",
    "file":"MSTeamsLogicalArchitecture.md"
  },
  {
    "id":"3",
    "file":"MSTeamsSecCom.md"
  },
  {
    "id":"4",
    "file":"MSTeamsGovernance.md"
  },
  {
    "id":"5",
    "file":"MSTeamsLicensing.md"
  },
  {
    "id":"6",
    "file":"MSTeamsMessaging.md"
  },
  {
    "id":"7",
    "file":"MSTeamsCollaboration.md"
  },
  {
    "id":"8",
    "file":"MSTeamsClient.md"
  },
  {
    "id":"9",
    "file":"MSTeamsVDI.md"
  },
  {
    "id":"10",
    "file":"MSTeamsOperations.md"
  },
  {
    "id":"11",
    "file":"MSTeamsReporting.md"
  },
  {
    "id":"12",
    "file":"MSTeamsDisaster.md"
  }
],
"cloudcollabmeetings":[
  {
    "id":"1",
    "file":"DocIntro.md"
  },
  {
    "id":"2",
    "file":"MSTeamsLogicalArchitecture.md"
  },
  {
    "id":"3",
    "file":"MSTeamsSecCom.md"
  },
  {
    "id":"4",
    "file":"MSTeamsGovernance.md"
  },
  {
    "id":"5",
    "file":"MSTeamsLicensing.md"
  },
  {
    "id":"6",
    "file":"MSTeamsMessaging.md"
  },
  {
    "id":"7",
    "file":"MSTeamsMeetings.md"
  },
  {
    "id":"8",
    "file":"MSTeamsLiveEvents.md"
  },
  {
    "id":"9",
    "file":"MSTeamsAudioConferencing.md"
  },
  {
    "id":"10",
    "file":"MSTeamsCollaboration.md"
  },
  {
    "id":"11",
    "file":"MSTeamsClient.md"
  },
  {
    "id":"12",
    "file":"MSTeamsVDI.md"
  },
  {
    "id":"13",
    "file":"MSTeamsNetwork.md"
  },
  {
    "id":"14",
    "file":"MSTeamsPeripherals.md"
  },
  {
    "id":"15",
    "file":"MSTeamsVideoInterop.md"
  },
  {
    "id":"16",
    "file":"MSTeamsOperations.md"
  },
  {
    "id":"17",
    "file":"MSTeamsReporting.md"
  },
  {
    "id":"18",
    "file":"MSTeamsDisaster.md"
  }
],
"cloudcalling":[
  {
    "id":"1",
    "file":"DocIntro.md"
  },
  {
    "id":"2",
    "file":"MSTeamsLogicalArchitecture.md"
  },
  {
    "id":"3",
    "file":"MSTeamsSecCom.md"
  },
  {
    "id":"4",
    "file":"MSTeamsGovernance.md"
  },
  {
    "id":"5",
    "file":"MSTeamsLicensing.md"
  },
  {
    "id":"6",
    "file":"MSTeamsMessaging.md"
  },
  {
    "id":"7",
    "file":"MSTeamsMeetings.md"
  },
  {
    "id":"8",
    "file":"MSTeamsLiveEvents.md"
  },
  {
    "id":"9",
    "file":"MSTeamsAudioConferencing.md"
  },
  {
    "id":"10",
    "file":"MSTeamsCollaboration.md"
  },
  {
    "id":"11",
    "file":"MSTeamsClient.md"
  },
  {
    "id":"12",
    "file":"MSTeamsVDI.md"
  },
  {
    "id":"13",
    "file":"MSTeamsNetwork.md"
  },
  {
    "id":"14",
    "file":"MSTeamsPeripherals.md"
  },
  {
    "id":"15",
    "file":"MSTeamsVideoInterop.md"
  },
  {
    "id":"16",
    "file":"MSTeamsPhoneSystem.md"
  },
  {
    "id":"17",
    "file":"MSTeamsCallingPlans.md"
  },
  {
    "id":"16",
    "file":"MSTeamsOperations.md"
  },
  {
    "id":"17",
    "file":"MSTeamsReporting.md"
  },
  {
    "id":"18",
    "file":"MSTeamsDisaster.md"
  }
]
}

Your mileage will vary and this is not something you can simply copy and paste and get it to work as your variables and requirements will be different to mine. The aim of this was to show you the way and give you the main tooling to help you create your own solution.

For me now, I just need to run my Powershell script and I have 5 document types I can choose from that will give me 80% of what I need within 1 minute.

Taking this further, the input file could be a data capture form using Microsoft Forms and process a Microsoft Flow to generate the document based in the form inputs. This is my next phase on this venture.

Processing…
Success! You're on the list.
Advertisements

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: