This runbook syncs all runbooks from a Visual Studio online (VSO) Git repository to an existing Azure Automation account starting with dependent (child) runbooks and followed by parent runbooks.  This runbook will recursively go through all sub directories from the root runbook path, starting with the most inner child folder, and publishes all child runbooks followed by its parent's runbooks.  

This runbook has a dependency on Connect-Azure, which you can download from  The Azure-Connect runbook must be published for this runbook to run correctly.


Before running this runbook, make sure the following components are available:

Runbook Contents

The runbook's content are displayed below:

PowerShell Workflow
workflow Sync-VsoGitRunbook 
    param ( 
       [string] $VSOCredentialName, 
       [string] $VSOAccount, 
       [string] $VSOProject, 
       [string] $VSORepository, 
       [string] $VSORunbookFolderPath, 
       [string] $AutomationAccount, 
       [string] $AzureConnectionName, 
       [string] $VSOBranch = "master" 
    $psExtension = ".ps1" 
    $apiVersion = "1.0-preview" 
    #Download the Connect-Azure runbook from 
    #Import and Publish the Connect-Azure runbook first      
    Connect-Azure -AzureConnectionName $AzureConnectionName 
    #Getting Credentail asset for VSO alternate authentication credentail 
    $VSOCred = Get-AutomationPSCredential -Name $VSOCredentialName 
    if ($VSOCred -eq $null) 
        throw "Could not retrieve '$VSOCredentialName' credential asset. Check that you created this asset in the Automation service." 
    $VSOAuthUserName = $VSOCred.UserName 
    $VSOAuthPassword = $VSOCred.GetNetworkCredential().Password 
    #Creating authorization header using  
    $basicAuth = ("{0}:{1}" -f $VSOAuthUserName,$VSOAuthPassword) 
    $basicAuth = [System.Text.Encoding]::UTF8.GetBytes($basicAuth) 
    $basicAuth = [System.Convert]::ToBase64String($basicAuth) 
    $headers = @{Authorization=("Basic {0}" -f $basicAuth)} 
    #ex. "" 
    $VSOURL = "https://" + $VSOAccount + "" +  
            $VSOProject + "/repositories/" + $VSORepository + "/items?scopepath=" + $VSORunbookFolderPath +   
            "&recursionlevel=full&includecontentmetadata=true&versionType=branch&version=" + $VSOBranch +   
            "&api-version=" + $apiVersion 
    Write-Verbose("Connecting to VSO using URL: $VSOURL") 
    $results = Invoke-RestMethod -Uri $VSOURL -Method Get -Headers $headers 
    #grab folders only 
    $folderObj = @() 
    foreach ($item in $results.value) 
        if ($item.gitObjectType -eq "tree") 
            $folderObj += $item 
    #recursively go through most inner child folders first, then their parents, parents parents, etc. 
    for ($i = $folderObj.count - 1; $i -ge 0; $i--) 
        Write-Verbose("Processing files in $folderObj[$i]")         
        $folderURL = "https://" + $VSOAccount + "" +  
                $VSOProject + "/repositories/" + $VSORepository + "/items?scopepath=" + $folderObj[$i].path +   
                "&recursionLevel=OneLevel&includecontentmetadata=true&versionType=branch&version=" +  
                $VSOBranch + "&api-version=" + $apiVersion 
        $results = Invoke-RestMethod -Uri $folderURL -Method Get -Headers $headers 
        foreach ($item in $results.value) 
            if (($item.gitObjectType -eq "blob") -and ($item.path -match $psExtension)) 
                $pathsplit = $item.path.Split("/") 
                $filename = $pathsplit[$pathsplit.Count - 1] 
                $tempPath = Join-Path -Path $env:SystemDrive -ChildPath "temp" 
                $outFile = Join-Path -Path $tempPath -ChildPath $filename 
                Invoke-RestMethod -Uri $item.url -Method Get -Headers $headers -OutFile $outFile 
                    #Select the Azure Subscription 
                    Select-AzureSubscription -SubscriptionName $Using:AzureConnectionName 
                    #Get the runbook name 
                    $fname = $Using:filename 
                    $tempPathSplit = $fname.Split(".") 
                    $runbookName = $tempPathSplit[0] 
                    #Import ps1 files into Automation, create one if doesn't exist 
                    Write-Verbose("Importing runbook $runbookName into Automation Account") 
                    $rb = Get-AzureAutomationRunbook -AutomationAccountName $Using:AutomationAccount -Name $runbookName -ErrorAction "SilentlyContinue"   
                    if ($rb -eq $null) 
                        Write-Verbose("Runbook $runbookName doesn't exist, creating it") 
                        New-AzureAutomationRunbook -AutomationAccountName $Using:AutomationAccount -Name $runbookName 
                    #Update the runbook, overwrite if existing 
                    Write-Verbose("Updating $runbookName with content from VSO-Git repository") 
                    Set-AzureAutomationRunbookDefinition -AutomationAccountName $Using:AutomationAccount -Name $runbookName -Path $Using:outFile -Overwrite 
                    #Publish the updated runbook 
                    Write-Verbose("Publishing $runbookName")                                     
                    Publish-AzureAutomationRunbook -AutomationAccountName $Using:AutomationAccount -Name $runbookName