SCOrchDev-AzureAutomationIntegration.psm1

#requires -Version 3 -Modules Azure, SCOrchDev-Exception, SCOrchDev-GitIntegration, SCOrchDev-Utility
<#
    .Synopsis
        Takes a ps1 file and publishes it to the current Azure Automation environment.
     
    .Parameter FilePath
        The full path to the script file
 
    .Parameter CurrentCommit
        The current commit to store this version under
 
    .Parameter RepositoryName
        The name of the repository that will be listed as the 'owner' of this
        runbook
#>

Function Publish-AzureAutomationRunbookChange
{
    Param(
        [Parameter(Mandatory = $True)]
        [String] 
        $FilePath,
        
        [Parameter(Mandatory = $True)]
        [String]
        $CurrentCommit,

        [Parameter(Mandatory = $True)]
        [String]
        $RepositoryName,

        [Parameter(Mandatory = $True)]
        [PSCredential]
        $Credential,

        [Parameter(Mandatory = $True)]
        [String]
        $AutomationAccountName,

        [Parameter(Mandatory = $True)]
        [String]
        $SubscriptionName
    )
    
    Write-Verbose -Message "[$FilePath] Starting [Publish-AzureAutomationRunbookChange]"
    $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop

    Try
    {
        Connect-AzureAutomationAccount -Credential $Credential `
                                       -SubscriptionName $SubscriptionName `
                                       -AutomationAccountName $AutomationAccountName

        $WorkflowName = Get-WorkflowNameFromFile -FilePath $FilePath
        
        $ErrorActionPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
        $Runbook = Get-AzureAutomationRunbook -Name $WorkflowName `
                                              -AutomationAccountName $AutomationAccountName
        $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop

        if($Runbook -as [bool])
        {
            Write-Verbose -Message "[$WorkflowName] Update"
            $TagUpdateJSON = New-ChangesetTagLine -TagLine ($Runbook.Tags -join ';') `
                                                  -CurrentCommit $CurrentCommit `
                                                  -RepositoryName $RepositoryName
            $TagUpdate = ConvertFrom-Json -InputObject $TagUpdateJSON
            $TagLine = $TagUpdate.TagLine
            $NewVersion = $TagUpdate.NewVersion
            if($NewVersion)
            {
                $Runbook = Set-AzureAutomationRunbookDefinition -Name $WorkflowName `
                                                                -Path $FilePath `
                                                                -Overwrite `
                                                                -AutomationAccountName $AutomationAccountName
                $TagUpdate = Set-AzureAutomationRunbook -Name $WorkflowName `
                                                        -Tags $TagLine.Split(';') `
                                                        -AutomationAccountName $AutomationAccountName
            }
            else
            {
                Write-Verbose -Message "[$WorkflowName] Already is at commit [$CurrentCommit]"
            }
        }
        else
        {
            Write-Verbose -Message "[$WorkflowName] Initial Import"
            
            $TagLine = "RepositoryName:$RepositoryName;CurrentCommit:$CurrentCommit;"
            $Runbook = New-AzureAutomationRunbook -Path $FilePath `
                                                  -Tags $TagLine.Split(';') `
                                                  -AutomationAccountName $AutomationAccountName
            
            $NewVersion = $True
        }
        if($NewVersion)
        {
            $Null = Publish-AzureAutomationRunbook -Name $WorkflowName `
                                                   -AutomationAccountName $AutomationAccountName
        }
    }
    Catch
    {
        Write-Exception -Stream Warning -Exception $_
    }
    Write-Verbose -Message "[$FilePath] Finished [Publish-AzureAutomationRunbookChange]"
}
<#
.Synopsis
    Takes a json file and publishes all schedules and variables from it into Azure Automation
     
.Parameter FilePath
    The path to the settings file to process
 
.Parameter CurrentCommit
    The current commit to tag the variables and schedules with
 
.Parameter RepositoryName
    The Repository Name that will 'own' the variables and schedules
#>

Function Publish-AzureAutomationSettingsFileChange
{
    Param( 
        [Parameter(Mandatory = $True)]
        [String] 
        $FilePath,
        
        [Parameter(Mandatory = $True)]
        [String]
        $CurrentCommit,

        [Parameter(Mandatory = $True)]
        [String]
        $RepositoryName,

        [Parameter(Mandatory = $True)]
        [PSCredential]
        $Credential,

        [Parameter(Mandatory = $True)]
        [String]
        $AutomationAccountName,

        [Parameter(Mandatory = $True)]
        [String]
        $SubscriptionName
    )
    
    Write-Verbose -Message "[$FilePath] Starting [Publish-AzureAutomationSettingsFileChange]"
    $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop

    Try
    {
        Connect-AzureAutomationAccount -Credential $Credential `
                                       -SubscriptionName $SubscriptionName `
                                       -AutomationAccountName $AutomationAccountName

        $VariablesJSON = Get-GlobalFromFile -FilePath $FilePath -GlobalType Variables
        $Variables = $VariablesJSON | ConvertFrom-JSON | ConvertFrom-PSCustomObject
        foreach($VariableName in $Variables.Keys)
        {
            Try
            {
                Write-Verbose -Message "[$VariableName] Updating"
                $Variable = $Variables."$VariableName"
                $ErrorActionPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
                $AzureAutomationVariable = Get-AzureAutomationVariable -Name $VariableName `
                                                                       -AutomationAccountName $AutomationAccountName
                $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
                if($AzureAutomationVariable -as [bool])
                {
                    Write-Verbose -Message "[$($VariableName)] is an existing Variable"
                    $TagUpdateJSON = New-ChangesetTagLine -TagLine $AzureAutomationVariable.Description`
                                                          -CurrentCommit $CurrentCommit `
                                                          -RepositoryName $RepositoryName
                    $TagUpdate = $TagUpdateJSON | ConvertFrom-Json
                    $VariableDescription = "$($TagUpdate.TagLine)"
                    $NewVersion = $TagUpdate.NewVersion
                    $NewVariable = $False
                }
                else
                {
                    Write-Verbose -Message "[$($VariableName)] is a New Variable"
                    $VariableDescription = "$($Variable.Description)`n`r__RepositoryName:$($RepositoryName);CurrentCommit:$($CurrentCommit);__"
                    $NewVersion = $True
                    $NewVariable = $True
                }
                if($NewVersion)
                {
                    $VariableParameters = @{
                        'Name' = $VariableName ;
                        'Value' = $Variable.Value ;
                        'Encrypted' = $Variable.isEncrypted ;
                        'AutomationAccountName' = $AutomationAccountName
                    }
                    if($NewVariable)
                    {
                        $Null = New-AzureAutomationVariable @VariableParameters `
                                                            -Description $VariableDescription
                    }
                    else
                    {
                        $Null = Set-AzureAutomationVariable @VariableParameters
                        $Null = Set-AzureAutomationVariable -Name $VariableName `
                                                            -Description $VariableDescription `
                                                            -AutomationAccountName $AutomationAccountName
                    }
                }
                else
                {
                    Write-Verbose -Message "[$($VariableName)] Is not a new version. Skipping"
                }
                Write-Verbose -Message "[$($VariableName)] Finished Updating"
            }
            Catch
            {
                $Exception = New-Exception -Type 'VariablePublishFailure' `
                                           -Message 'Failed to publish a variable to Azure Automation' `
                                           -Property @{
                    'ErrorMessage' = Convert-ExceptionToString $_ ;
                    'VariableName' = $VariableName ;
                }
                Write-Warning -Message $Exception -WarningAction Continue
            }
        }
        $SchedulesJSON = Get-GlobalFromFile -FilePath $FilePath -GlobalType Schedules
        $Schedules = $SchedulesJSON | ConvertFrom-JSON | ConvertFrom-PSCustomObject
        foreach($ScheduleName in $Schedules.Keys)
        {
            Write-Verbose -Message "[$ScheduleName] Updating"
            try
            {
                $Schedule = $Schedules."$ScheduleName"
                $ErrorActionPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
                $AzureAutomationSchedule = Get-AzureAutomationSchedule -Name $ScheduleName `
                                                                       -AutomationAccountName $AutomationAccountName
                $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
                if($AzureAutomationSchedule -as [bool])
                {
                    Write-Verbose -Message "[$($ScheduleName)] is an existing Schedule"
                    $TagUpdateJSON = New-ChangesetTagLine -TagLine $Schedule.Description`
                                                          -CurrentCommit $CurrentCommit `
                                                          -RepositoryName $RepositoryName
                    $TagUpdate = ConvertFrom-Json -InputObject $TagUpdateJSON
                    $ScheduleDescription = "$($TagUpdate.TagLine)"
                    $NewVersion = $TagUpdate.NewVersion
                    if($NewVersion)
                    {
                        Write-Verbose -Message "[$($ScheduleName)] is an Updated Schedule. Deleting to re-create"
                        Remove-AzureAutomationSchedule -Name $ScheduleName `
                                                       -Force `
                                                       -AutomationAccountName $AutomationAccountName
                    }
                }
                else
                {
                    Write-Verbose -Message "[$($ScheduleName)] is a New Schedule"
                    $ScheduleDescription = "$($Schedule.Description)`n`r__RepositoryName:$($RepositoryName);CurrentCommit:$($CurrentCommit);__"
                    
                    $NewVersion = $True
                }
                if($NewVersion)
                {
                    $CreateSchedule = New-AzureAutomationSchedule -Name $ScheduleName `
                                                                  -Description $ScheduleDescription `
                                                                  -DayInterval $Schedule.DayInterval `
                                                                  -StartTime $Schedule.NextRun `
                                                                  -ExpiryTime $Schedule.ExpirationTime `
                                                                  -AutomationAccountName $AutomationAccountName
                    if(-not ($CreateSchedule -as [bool]))
                    {
                        Throw-Exception -Type 'ScheduleFailedToCreate' `
                                        -Message 'Failed to create the schedule' `
                                        -Property @{
                            'ScheduleName'     = $ScheduleName
                            'Description'      = $ScheduleDescription
                            'DayInterval'      = $Schedule.DayInterval
                            'StartTime'        = $Schedule.NextRun
                            'ExpiryTime'       = $Schedule.ExpirationTime
                            'AutomationAccountName' = $AutomationAccountName
                        }
                    }
                    try
                    {
                        $Parameters = ConvertFrom-PSCustomObject -InputObject $Schedule.Parameter `
                                                                 -MemberType NoteProperty
                        $Register = Register-AzureAutomationScheduledRunbook -AutomationAccountName $AutomationAccountName `
                                                                             -RunbookName $Schedule.RunbookName `
                                                                             -ScheduleName $ScheduleName `
                                                                             -Parameters $Parameters
                        if(-not($Register -as [bool]))
                        {
                            Throw-Exception -Type 'ScheduleFailedToSet' `
                                            -Message 'Failed to set the schedule on the target runbook' `
                                            -Property @{
                                'ScheduleName' = $ScheduleName ;
                                'RunbookName' = $Schedule.RunbookName ;
                                'Parameters' = $(ConvertTo-Json -InputObject $Parameters) ;
                                'AutomationAccountName' = $AutomationAccountName
                            }
                        }
                    }
                    catch
                    {
                        $ErrorActionPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
                        Remove-AzureAutomationSchedule -Name $ScheduleName `
                                                       -Force `
                                                       -AutomationAccountName $AutomationAccountName
                                                       $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Continue
                        Write-Exception -Exception $_ -Stream Warning
                    }
                }
            }
            catch
            {
                Write-Exception -Exception $_ -Stream Warning
            }
            Write-Verbose -Message "[$($ScheduleName)] Finished Updating"
        }
    }
    Catch
    {
        Write-Exception -Stream Warning -Exception $_
    }
    Write-Verbose -Message "[$FilePath] Finished [Publish-AzureAutomationSettingsFileChange]"
}
<#
.Synopsis
    Checks an Azure Automation environment and removes any global assets tagged
    with the current repository that are no longer found in
    the repository
 
.Parameter RepositoryName
    The name of the repository
#>

Function Remove-AzureAutomationOrphanAsset
{
    Param(
        [Parameter(Mandatory = $True)]
        [String]
        $RepositoryName,

        [Parameter(Mandatory = $True)]
        [PSCredential]
        $Credential,

        [Parameter(Mandatory = $True)]
        [String]
        $AutomationAccountName,

        [Parameter(Mandatory = $True)]
        [String]
        $SubscriptionName,

        [Parameter(Mandatory = $True)]
        [PSCustomObject] 
        $RepositoryInformation
    )

    Write-Verbose -Message 'Starting [Remove-AzureAutomationOrphanAsset]'
    $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
    Try
    {
        Connect-AzureAutomationAccount -Credential $Credential `
                                       -SubscriptionName $SubscriptionName `
                                       -AutomationAccountName $AutomationAccountName

        $AzureAutomationVariables = Get-AzureAutomationVariable -AutomationAccountName $AutomationAccountName
        if($AzureAutomationVariables) 
        {
            $AzureAutomationVariables = Group-AssetsByRepository -InputObject $AzureAutomationVariables 
        }

        $RepositoryAssets = Get-GitRepositoryAssetName -Path "$($RepositoryInformation.Path)\$($RepositoryInformation.GlobalsFolder)"

        if($AzureAutomationVariables."$RepositoryName")
        {
            $VariableDifferences = Compare-Object -ReferenceObject $AzureAutomationVariables."$RepositoryName".Name `
                                                  -DifferenceObject $RepositoryAssets.Variable
            Foreach($Difference in $VariableDifferences)
            {
                Try
                {
                    if($Difference.SideIndicator -eq '<=')
                    {
                        Write-Verbose -Message "[$($Difference.InputObject)] Does not exist in Source Control"
                        Remove-AzureAutomationVariable -Name $Difference.InputObject `
                                                       -AutomationAccountName $AutomationAccountName `
                                                       -Force
                        Write-Verbose -Message "[$($Difference.InputObject)] Removed from Azure Automation"
                    }
                }
                Catch
                {
                    $Exception = New-Exception -Type 'RemoveAAAssetFailure' `
                                                -Message 'Failed to remove an AA Asset' `
                                                -Property @{
                        'ErrorMessage' = (Convert-ExceptionToString $_) ;
                        'AssetName' = $Difference.InputObject ;
                        'AssetType' = 'Variable' ;
                        'AutomationAccountName' = $AutomationAccountName ;
                        'RepositoryName' = $RepositoryName ;
                    }
                    Write-Warning -Message $Exception -WarningAction Continue
                }
            }
        }
        else
        {
            Write-Warning -Message "[$RepositoryName] No Variables found in environment for this repository" `
                          -WarningAction Continue
        }

        $AzureAutomationSchedules = Get-AzureAutomationSchedule -AutomationAccountName $AutomationAccountName
        if($AzureAutomationSchedules) 
        {
            $AzureAutomationSchedules = Group-AssetsByRepository -InputObject $AzureAutomationSchedules 
        }

        if($AzureAutomationSchedules."$RepositoryName")
        {
            $ScheduleDifferences = Compare-Object -ReferenceObject $AzureAutomationSchedules."$RepositoryName".Name `
                                                  -DifferenceObject $RepositoryAssets.Schedule
            Foreach($Difference in $ScheduleDifferences)
            {
                Try
                {
                    if($Difference.SideIndicator -eq '<=')
                    {
                        Write-Verbose -Message "[$($Difference.InputObject)] Does not exist in Source Control"
                        Remove-AzureAutomationSchedule -Name $Difference.InputObject `
                                                       -AutomationAccountName $AutomationAccountName `
                                                       -Force
                        Write-Verbose -Message "[$($Difference.InputObject)] Removed from Azure Automation"
                    }
                }
                Catch
                {
                    $Exception = New-Exception -Type 'RemoveAAAssetFailure' `
                                                -Message 'Failed to remove an AA Asset' `
                                                -Property @{
                        'ErrorMessage' = (Convert-ExceptionToString $_) ;
                        'AssetName' = $Difference.InputObject ;
                        'AssetType' = 'Schedule' ;
                        'AutomationAccountName' = $AutomationAccountName ;
                        'RepositoryName' = $RepositoryName ;
                    }
                    Write-Warning -Message $Exception -WarningAction Continue
                }
            }
        }
        else
        {
            Write-Warning -Message "[$RepositoryName] No Schedules found in environment for this repository" `
                          -WarningAction Continue
        }
    }
    Catch
    {
        $Exception = New-Exception -Type 'RemoveAzureAutomationOrphanAssetWorkflowFailure' `
                                   -Message 'Unexpected error encountered in the Remove-AzureAutomationOrphanAsset workflow' `
                                   -Property @{
            'ErrorMessage' = (Convert-ExceptionToString $_) ;
            'RepositoryName' = $RepositoryName ;
        }
        Write-Exception -Exception $Exception -Stream Warning
    }
    Write-Verbose -Message 'Finished [Remove-AzureAutomationOrphanAsset]'
}

<#
    .Synopsis
        Checks an Azure Automation environment and removes any runbooks tagged
        with the current repository that are no longer found in
        the repository
 
    .Parameter RepositoryName
        The name of the repository
#>

Function Remove-AzureAutomationOrphanRunbook
{
    Param(
        [Parameter(Mandatory = $True)]
        [String]
        $RepositoryName,

        [Parameter(Mandatory = $True)]
        [PSCredential]
        $Credential,

        [Parameter(Mandatory = $True)]
        [String]
        $AutomationAccountName,

        [Parameter(Mandatory = $True)]
        [String]
        $SubscriptionName,

        [Parameter(Mandatory = $True)]
        [PSCustomObject] 
        $RepositoryInformation
    )

    Write-Verbose -Message 'Starting [Remove-AzureAutomationOrphanRunbook]'
    $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
    Try
    {
        Connect-AzureAutomationAccount -Credential $Credential `
                                       -SubscriptionName $SubscriptionName `
                                       -AutomationAccountName $AutomationAccountName

        $AzureAutomationRunbooks = Get-AzureAutomationRunbook -AutomationAccountName $AutomationAccountName
        if($AzureAutomationRunbooks) 
        {
            $AzureAutomationRunbooks = Group-RunbooksByRepository -InputObject $AzureAutomationRunbooks 
        }

        $RepositoryWorkflows = Get-GitRepositoryWorkflowName -Path "$($RepositoryInformation.Path)\$($RepositoryInformation.RunbookFolder)"
        $Differences = Compare-Object -ReferenceObject $AzureAutomationRunbooks.$RepositoryName.Name `
                                      -DifferenceObject $RepositoryWorkflows
    
        Foreach($Difference in $Differences)
        {
            if($Difference.SideIndicator -eq '<=')
            {
                Try
                {
                    Write-Verbose -Message "[$($Difference.InputObject)] Does not exist in Source Control"
                    Remove-AzureAutomationRunbook -Name $Difference.InputObject `
                                                  -AutomationAccountName $AutomationAccountName
                    Write-Verbose -Message "[$($Difference.InputObject)] Removed from Azure Automation"
                }
                Catch
                {
                    $Exception = New-Exception -Type 'RemoveAzureAutomationRunbookFailure' `
                                                -Message 'Failed to remove a Azure Automation Runbook' `
                                                -Property @{
                        'ErrorMessage' = (Convert-ExceptionToString $_) ;
                        'Name' = $Difference.InputObject ;
                        'AutomationAccount' = $AutomationAccountName ;
                    }
                    Write-Warning -Message $Exception -WarningAction Continue
                }
            }
        }
    }
    Catch
    {
        $Exception = New-Exception -Type 'RemoveAzureAutomationOrphanRunbookWorkflowFailure' `
                                   -Message 'Unexpected error encountered in the Remove-AzureAutomationOrphanRunbook workflow' `
                                   -Property @{
            'ErrorMessage' = (Convert-ExceptionToString $_) ;
            'RepositoryName' = $RepositoryName ;
        }
        Write-Exception -Exception $Exception -Stream Warning
    }
    Write-Verbose -Message 'Finished [Remove-AzureAutomationOrphanRunbook]'
}

<#
    .SYNOPSIS
    Returns $true if working in a local development environment, $false otherwise.
#>

function Test-LocalDevelopment
{
    $LocalDevModule = Get-Module -ListAvailable -Name 'LocalDev' -Verbose:$False -ErrorAction 'SilentlyContinue' -WarningAction 'SilentlyContinue'
    if($Null -ne $LocalDevModule -and ($env:LocalAuthoring -ne $False))
    {
        return $True
    }
    return $False
}

<#
.SYNOPSIS
    Gets one or more automation variable values from the given web service endpoint.
 
.DESCRIPTION
    Get-BatchAutomationVariable gets the value of each variable given in $Name.
    If $Prefix is set, "$Prefix-$Name" is looked up in (helps keep the
    list of variables in $Name concise).
 
.PARAMETER Name
    A list of variable values to retrieve.
     
.PARAMETER Prefix
    A prefix to be applied to each variable name when performing the lookup.
    A '-' is added to the end of $Prefix automatically.
#>

Function Get-BatchAutomationVariable
{
    [OutputType([hashtable])]
    Param(
        [Parameter(Mandatory = $True)]
        [String[]]
        $Name,

        [Parameter(Mandatory = $False)]
        [AllowNull()]
        [String]
        $Prefix = $Null
    )
    $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
    $Variables = @{}
    
    ForEach($VarName in $Name)
    {
        If(-not [String]::IsNullOrEmpty($Prefix))
        {
            $_VarName =  "$Prefix-$VarName"
        }
        Else
        {
            $_VarName = $VarName
        }
        $Result = Get-AutomationVariable -Name "$_VarName"
        $Variables[$VarName] = $Result
        Write-Verbose -Message "Variable [$Prefix / $VarName] = [$($Variables[$VarName])]"
    }
    Return ($Variables -as [hashtable])
}
<#
.Synopsis
    Returns a list of the runbook workers in the target hybrid runbook worker deployment.
#>

Function Get-AzureAutomationHybridRunbookWorker
{
    Param(
        [Parameter(Mandatory = $True)]
        [String[]]
        $Name
    )
    
    Return @($env:COMPUTERNAME) -as [array]
}

<#
.Synopsis
    Connects to an Azure Automation Account
#>

Function Connect-AzureAutomationAccount
{
    Param(
        [Parameter(Mandatory = $True)]
        [PSCredential]
        $Credential,

        [Parameter(Mandatory = $True)]
        [string]
        $SubscriptionName,

        [Parameter(Mandatory = $True)]
        [string]
        $AutomationAccountName
    )

    $VBP = $VerbosePreference
    $VerbosePreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
    $AzureAccount = Get-AzureAccount
    if($AzureAccount.Id -ne $Credential.UserName)
    {
        $AzureAccount | ForEach-Object { Remove-AzureAccount -Name $_.Id -Force }
        Add-AzureAccount -Credential $Credential
        
    }
    Select-AzureSubscription -SubscriptionName $SubscriptionName
    $AzureAccountAccessible = (Get-AzureAutomationAccount -Name $AutomationAccountName) -as [bool]
    $VerbosePreference = [System.Management.Automation.ActionPreference]$VBP
    if(-not $AzureAccountAccessible)
    {
        Throw-Exception -Type 'AzureAutomationAccountNotAccessible' `
                        -Message 'Could not access the target Azure Automation Account' `
                        -Property @{
                            'Credential' = $Credential ;
                            'SubscriptionName' = $SubscriptionName ;
                            'AutomationAccountName' = $AutomationAccountName ;
                        }
    }
}

<#
.Synopsis
    Top level function for syncing a target git Repository to Azure Automation
#>

Function Sync-GitRepositoryToAzureAutomation
{
    Param(
        [Parameter(Mandatory = $True)]
        [pscredential]
        $SubscriptionAccessCredential,

        [Parameter(Mandatory = $True)]
        [pscredential]
        $RunbookWorkerAccessCredenial,

        [Parameter(Mandatory = $True)]
        [psobject]
        $RepositoryInformation,
        
        [Parameter(Mandatory = $True)]
        [string]
        $RepositoryInformationJSON,

        [Parameter(Mandatory = $True)]
        [string]
        $AutomationAccountName,
        
        [Parameter(Mandatory = $True)]
        [string]
        $SubscriptionName,

        [Parameter(Mandatory = $True)]
        [string]
        $RepositoryName
    )
    
    Write-Verbose -Message 'Starting [Sync-GitRepositoryToAzureAutomation]'
    $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
    
    Try
    {
        $RunbookWorker = Get-AzureAutomationHybridRunbookWorker -Name $RepositoryInformation.HybridWorkerGroup
        
        # Update the repository on all SMA Workers
        Invoke-Command -ComputerName $RunbookWorker -Credential $RunbookWorkerAccessCredenial -ScriptBlock {
            $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
                    
            $RepositoryInformation = $Using:RepositoryInformation
            Update-GitRepository -RepositoryInformation $RepositoryInformation
        }

        $RepositoryChangeJSON = Find-GitRepositoryChange -RepositoryInformation $RepositoryInformation
        $RepositoryChange = ConvertFrom-Json -InputObject $RepositoryChangeJSON
        if($RepositoryChange.CurrentCommit -as [string] -ne $RepositoryInformation.CurrentCommit -as [string])
        {
            Write-Verbose -Message "Processing [$($RepositoryInformation.CurrentCommit)..$($RepositoryChange.CurrentCommit)]"
            Write-Verbose -Message "RepositoryChange [$RepositoryChangeJSON]"
            $ReturnInformationJSON = Group-RepositoryFile -Files $RepositoryChange.Files `
                                                          -RepositoryInformation $RepositoryInformation
            $ReturnInformation = ConvertFrom-Json -InputObject $ReturnInformationJSON
            Write-Verbose -Message "ReturnInformation [$ReturnInformationJSON]"
            
            Foreach($SettingsFilePath in $ReturnInformation.SettingsFiles)
            {
                Publish-AzureAutomationSettingsFileChange -FilePath $SettingsFilePath `
                                                          -CurrentCommit $RepositoryChange.CurrentCommit `
                                                          -RepositoryName $RepositoryName `
                                                          -Credential $SubscriptionAccessCredential `
                                                          -AutomationAccountName $AutomationAccountName `
                                                          -SubscriptionName $SubscriptionName
            }
            Foreach($RunbookFilePath in $ReturnInformation.ScriptFiles)
            {
                Publish-AzureAutomationRunbookChange -FilePath $RunbookFilePath `
                                                     -CurrentCommit $RepositoryChange.CurrentCommit `
                                                     -RepositoryName $RepositoryName `
                                                     -Credential $SubscriptionAccessCredential `
                                                     -AutomationAccountName $AutomationAccountName `
                                                     -SubscriptionName $SubscriptionName
            }
            
            if($ReturnInformation.CleanRunbooks)
            {
                Remove-AzureAutomationOrphanRunbook -RepositoryName $RepositoryName `
                                                    -SubscriptionName $SubscriptionName `
                                                    -AutomationAccountName $AutomationAccountName `
                                                    -Credential $SubscriptionAccessCredential `
                                                    -RepositoryInformation $RepositoryInformation
            }
            if($ReturnInformation.CleanAssets)
            {
                Remove-AzureAutomationOrphanAsset -RepositoryName $RepositoryName `
                                                  -SubscriptionName $SubscriptionName `
                                                  -AutomationAccountName $AutomationAccountName `
                                                  -Credential $SubscriptionAccessCredential `
                                                  -RepositoryInformation $RepositoryInformation
            }
            if($ReturnInformation.ModuleFiles)
            {
                Try
                {
                    Write-Verbose -Message 'Validating Module Path on Runbook Wokers'
                    $RepositoryModulePath = "$($RepositoryInformation.Path)\$($RepositoryInformation.PowerShellModuleFolder)"
                    Invoke-Command -ComputerName $RunbookWorker -Credential $RunbookWorkerAccessCredenial -ScriptBlock {
                        $RepositoryModulePath = $Using:RepositoryModulePath
                        Try
                        {
                            Add-PSEnvironmentPathLocation -Path $RepositoryModulePath
                        }
                        Catch
                        {
                            $Exception = New-Exception -Type 'PowerShellModulePathValidationError' `
                                               -Message 'Failed to set PSModulePath' `
                                               -Property @{
                                'ErrorMessage' = (Convert-ExceptionToString $_) ;
                                'RepositoryModulePath' = $RepositoryModulePath ;
                                'RunbookWorker' = $env:COMPUTERNAME ;
                            }
                            Write-Warning -Message $Exception -WarningAction Continue
                        }
                    }
                    Write-Verbose -Message 'Finished Validating Module Path on Runbook Wokers'
                }
                Catch
                {
                    Write-Exception -Exception $_ -Stream Warning
                }
            }
            $UpdatedRepositoryInformation = (Set-RepositoryInformationCommitVersion -RepositoryInformation $RepositoryInformationJSON `
                                                                                    -RepositoryName $RepositoryName `
                                                                                    -Commit $RepositoryChange.CurrentCommit) -as [string]

            $null = Set-AutomationVariable -Name 'ContinuousIntegration-RepositoryInformation' `
                                           -Value $UpdatedRepositoryInformation

            Write-Verbose -Message "Finished Processing [$($RepositoryInformation.CurrentCommit)..$($RepositoryChange.CurrentCommit)]"
        }
    }
    Catch
    {
        Write-Exception -Stream Warning -Exception $_
    }

    Write-Verbose -Message 'Completed [Sync-GitRepositoryToAzureAutomation]'
}
Export-ModuleMember -Function * -Verbose:$false