RestoreReplay.ps1

<#PSScriptInfo
 
.COMPANYNAME Microsoft
 
.VERSION 1.0.1
 
.GUID 5163768d-5359-48f8-8bc5-02b93f251115
 
.AUTHOR Microsoft-Sdn-DevTeam
 
.SYNOPSIS
Restore NetworkController by replaying the data from a copy of SDNAPI IMOS database or a NetworkController backup.
 
.DESCRIPTION
Extract the resource JSONs from SDNAPI IMOS or a NetworkController backup and reply it. If the replay is from an IMOS
DB(BackupType Manual) and AesKey used is not available, all the credential resources should be manually re-added -
which includes credentials resources and virtual gateway networkconnection credentials. If the BackupType is
NetworkController, then the AesKey is extracted from the backup.
 
Input directory structure should not be modified. Following is an example of the IMOS DB directory structure. In this
case BackupDirectoryOrZipFile will be "C:\R_131705518767194909"
 
Directory: C:\R_131705518767194909
 
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 6/1/2018 10:47 AM 8192 edb.chk
-a---- 6/1/2018 10:44 AM 10485760 edb.log
-a---- 6/1/2018 10:43 AM 10485760 edb00000006.log
-a---- 6/1/2018 10:44 AM 10485760 edb00000007.log
-a---- 6/1/2018 10:35 AM 10485760 edbres00001.jrs
-a---- 6/1/2018 10:35 AM 10485760 edbres00002.jrs
-a---- 6/1/2018 10:43 AM 10485760 edbtmp.log
-a---- 6/1/2018 10:44 AM 16793600 ImosStore
-a---- 6/1/2018 10:36 AM 0 ImosStore.pat
 
Following is an example of NetworkController backup directory structure. In this case BackupDirectoryOrZipFile will be
"C:\NetworkControllerGeneratedBackup"
 
Directory: C:\NetworkControllerGeneratedBackup
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 5/31/2018 4:41 PM ApiService
d----- 5/31/2018 4:41 PM NamedPropertyStore
-a---- 5/8/2018 11:40 AM 11 VersionId
 
#>


<#
 
.PARAMETER NCRestEndPoint
A DNS name or IP address sepcifying the NetworkController rest endpoint.
.PARAMETER BackupDirectoryOrZipFile
Directory where a copy of the SDNAPI IMOS database or a zip file containing the backup is present. User credentials
used to run the script should have write permissions to this directory.
.PARAMETER AesKey
AES key that was used with the NetworkController where the backup was taken. If the OperationType is NetworkController,
this parameter is not needed and the AES key is extracted from the NamedProperty backup.
.PARAMETER SkipResetReplica
Skip the reset of existing IMOS db. Use the option to retry the replay if some of the resources failed to provision.
.PARAMETER IgnoreFailure
Ignore the failure to a put a resource and continue with other resources. Ignoring a failure may result in cascading
failures for other resources.
.PARAMETER Credential
Specifies a user credential that has permission to perform this action.
.PARAMETER EseUtilPath
Path where the eseutil.exe is present. Default is script_path\ReplicatedStoreViewer
.PARAMETER SDNImosStoreExplorerPath
Path where the SDNImosStoreExplorer.exe is present. Default is script_path
.PARAMETER OperationType
Type of operation to perform [Default ExtractAndReplay].
ExtractOnly - Extract the resources to BackupDirectory\Jsons.
ReplayOnly - Replay the resources from BackupDirectory\Jsons.
ExtractAndReplay - Extract the resources and replay it.
Put - A plain put of all the resources under <BackupDirectoryOrZipFile>\Jsons.
.PARAMETER BackupType
Type of backup.
Manual - A manually copied SDNAPI IMOS dump. The directory should have all the files copied from the service fabric
SDNAPI replica IMOS folder.
NetworkController - A backup created by PutNetworkControllerBackup operation or zip file created by Autobackup.
.PARAMETER GetAesKey
Fetch the current AESKey from networkcontroller.
.PARAMETER MultisiteRestoreOperation
Type of multisite restore operation to perform.
ReplayPrimarySite - Used to indicate that the replay should happen on primary site. Restores all resources.
ReplaySecondaryPrerequisiteLocalResourcesOnly - Replays only local resources that global resources are depdendent on.
May be used when NC site needs to sync global resources using multisite. Resets references in local resources to global resources.
ReplaySecondaryLocalResourcesOnly - Replays only local resources. May be used after global resources have been synced using multisite.
Skips reset of replicas when this switch is turned on.
.PARAMETER Force
Force run the operation. Specifiying the force parameter has the following implications. 1. If the
BackupDirectoryOrZipFile parameter is a zip file, and a directory is already present with the extracted contents it is
overwritten. 2. Do not wait for NetworkController to become healthy before starting the restore.
.EXAMPLE
./RestoreReplay.ps1 -NCRestEndPoint NC.contoso.com -BackupDirectory C:\R_131705518767194909 -BackupType Manual -AesKey "<AesKeyPlaceholder>"
Perform restore from a SDNAPI Imos dump present under c:\R_131705518767194909
.EXAMPLE
./RestoreReplay.ps1 -NCRestEndPoint NC.contoso.com -BackupDirectory C:\AutoBackup_2018-05-30T14-35-52.zip -BackupType NetworkController -OperationType ExtractOnly -Force
Extract the resources from an autoback zip file generated by networkcontroller. Because -Force parameter is specified
if "C:\AutoBackup_2018-05-30T14-35-52" is present, it will be overwritten with contents of "AutoBackup_2018-05-30T14-35-52.zip"
 
.EXAMPLE
./RestoreReplay.ps1 -NCRestEndPoint NC.contoso.com -BackupDirectory C:\AutoBackup_2018-05-30T14-35-52 -BackupType NetworkController -OperationType ReplayOnly -Verbose
Replay the resource from C:\AutoBackup_2018-05-30T14-35-52. A detailed log is also written to the console.
 
.EXAMPLE
# On site 1:
./RestoreReplay.ps1 -NCRestEndPoint RestServer-S3 -BackupDirectory C:\rest\bcdr3 -BackupType NetworkController -MultisiteRestoreOperation ReplayPrimarySite
 
# On site 2:
./RestoreReplay.ps1 -NCRestEndPoint RestServer-S4 -BackupDirectory C:\rest\bcdr3 -BackupType NetworkController -OperationType ExtractOnly
./RestoreReplay.ps1 -NCRestEndPoint RestServer-S4 -BackupDirectory C:\rest\bcdr3 -BackupType NetworkController -OperationType ReplayOnly -MultisiteRestoreOperation ReplaySecondaryPrerequisiteLocalResourcesOnly
 
# (sync sites using multisite)
 
# On site 2:
./RestoreReplay.ps1 -NCRestEndPoint RestServer-S4 -BackupDirectory C:\rest\bcdr3 -BackupType NetworkController -OperationType ReplayOnly -MultisiteRestoreOperation ReplaySecondaryLocalResourcesOnly
 
Replay resources in multisite scenario.
 
.NOTES
 
.LINK
 
#>


[CmdletBinding(DefaultParameterSetName="Default",SupportsShouldProcess=$True, ConfirmImpact="High")]
param(
    [Parameter(position=0,mandatory=$false,ParameterSetName="Default")]
    [ValidateNotNullOrEmpty()]
    [string]$NCRestEndPoint,
    [Parameter(position=1,mandatory=$true,ParameterSetName="Default")]
    [ValidateNotNullOrEmpty()]
    [string]$BackupDirectoryOrZipFile,
    [Parameter(position=2,mandatory=$false,ParameterSetName="Default")]
    [ValidateNotNullOrEmpty()]
    [string]$AesKey,
    [Parameter(position=3,mandatory=$false,ParameterSetName="Default")]
    [switch]$SkipResetReplica,
    [Parameter(position=4,mandatory=$false,ParameterSetName="Default")]
    [switch]$IgnoreFailure,
    [ValidateNotNull()]
    [Parameter(position=5,mandatory=$false,ParameterSetName="Default")]
    [PSCredential] $Credential = [System.Management.Automation.PSCredential]::Empty,
    [Parameter(position=6,mandatory=$false,ParameterSetName="Default")]
    [ValidateNotNullOrEmpty()]
    [string] $EseUtilPath,
    [Parameter(position=7,mandatory=$false,ParameterSetName="Default")]
    [ValidateNotNullOrEmpty()]
    [string] $SDNImosStoreExplorerPath,
    [Parameter(position=8,mandatory=$false,ParameterSetName="Default")]
    [ValidateSet("ExtractOnly","ReplayOnly","ExtractAndReplay","Put")] 
    [string] $OperationType = "ExtractAndReplay",
    [Parameter(position=9,mandatory=$false,ParameterSetName="Default")]
    [ValidateSet("Manual","NetworkController")] 
    [string] $BackupType,
    [Parameter(position=10,mandatory=$false,ParameterSetName="Default")]
    [ValidateSet("ReplayPrimarySite","ReplaySecondaryLocalResourcesOnly","ReplaySecondaryPrerequisiteLocalResourcesOnly")] 
    [string] $MultisiteRestoreOperation,
    [Parameter(position=0,mandatory=$true,ParameterSetName="GetAesKey")]
    [switch] $GetAesKey,
    [Parameter(position=11,mandatory=$false,ParameterSetName="Default")]
    [switch] $Force
)

#Globals
$script:E_PropertyNotFound = -2147017783

$script:NCApplicationUri = "fabric:/NetworkController"
$script:SDNApiServiceUri = "fabric:/NetworkController/ApiService"
$script:ncNameSpace = "root\Microsoft\Windows\NetworkController\Server"

$script:succeededProvState = "Succeeded"
$script:updatingProvState = "Updating"
$script:failedProvState = "Failed"

$script:NCHealthWaitTimeoutInSec = 20 * 60
$script:provisioningTimeoutInSec = 15 * 60

$script:ForceOperation = $false

$script:ResultMessages = @()
$script:WarningMessages = @()
$script:ErrorMessages = @()

$script:LogLevels = @{
    Verbose = "VERBOSE"
    Info = "INFO"
    Warn = "WARN"
    Error = "ERROR"
    Fatal = "FATAL"
}

$script:BackupTypes = @{
    Manual = "Manual"
    NetworkController = "NetworkController"
}

$script:OperationTypes = @{
    ExtractOnly = "ExtractOnly"
    ReplayOnly = "ReplayOnly"
    ExtractAndReplay = "ExtractAndReplay"
    Put = "Put"
}

$script:LogLevel = $script:LogLevels["Warn"]

$script:GlobalBaseTypes = @(
    'virtualnetworkmanager',
    'virtualswitchmanager',
    'iDnsServer',
    'securityTags',
    'accessControlLists',
    'routetables',
    'serviceInsertions',
    'virtualnetworks',
    'networkinterfaces')

# Local base types that global base types are dependent on
$script:PrerequisiteLocalBaseTypes = @(
    'logicalnetworks',
    'macPools')

function FixPwdPath
{
    param(
        [string]$Path
    )

    if([String]::IsNullOrEmpty($Path))
    {
        return $Path
    }
    $newPath = $Path.ToLower()
    if($newPath.StartsWith(".\"))
    {
        $newPath = $((Get-Location).Path) + $newPath.TrimStart(".")
    }
    return $newPath
}

function Initialize
{
    $script:Initialzed = $false

    $script:scriptDir =  Split-Path $script:MyInvocation.MyCommand.Path

    $script:LogFile = Join-Path -Path $script:scriptDir -ChildPath "DebugLog.txt"

    Write-Log "Initializing..."

    $script:BackupDirectory = FixPwdPath -Path $BackupDirectoryOrZipFile

    $script:AesCryptoKey = $AesKey

    if($IgnoreFailure.IsPresent)
    {
        $script:IgnorePutFailure = $true
    }

    if($script:BackupDirectory.EndsWith(".zip"))
    {
        $backupZipFile = $script:BackupDirectory.Split("\") | Select-Object -Last 1
        $backupPath = $script:BackupDirectory
        $destination = $script:BackupDirectory.TrimEnd($backupZipFile)
        $script:BackupDirectory = $script:BackupDirectory.TrimEnd(".zip")

        if(-not ($($OperationType -eq $script:OperationTypes["ReplayOnly"]) -or $($OperationType -eq $script:OperationTypes["Put"])))
        {
            Add-Type -assembly "system.io.compression.filesystem"
            if($(Test-Path $(Join-Path -Path $script:BackupDirectory -ChildPath "*")))
            {
                if($script:ForceOperation)
                {
                    Remove-Item $script:BackupDirectory -Force -ErrorAction Stop -Recurse
                }
                else
                {
                    throw "Zip file provided, but the destination for extracting the zip $($script:BackupDirectory) is not empty. Delete the directory or rerun with -Force, aborting restore"
                }
            }
            
            [io.compression.zipfile]::ExtractToDirectory($BackUpPath, $destination)
        }     
    }

    if($BackupType -eq $($script:BackupTypes["NetworkController"])) 
    {
        if($OperationType -ne "ExtractOnly" -and [String]::IsNullOrEmpty($script:AesCryptoKey))
        {
            #If backup type is NetworkController, try to extract AESKey
            $NamedPropertyXml = Join-Path -Path $script:BackupDirectory -ChildPath "NamedPropertyStore"
            $NamedPropertyXml = Join-Path -Path $NamedPropertyXml -ChildPath "NamedPropertyStore.xml"
            if($(Test-Path $NamedPropertyXml) -eq $true)
            {
                [xml]$contentXml = Get-Content $NamedPropertyXml
                $script:AesCryptoKey = $contentXml.NamedPropertyStore.AESKey
                Write-VerboseLog "Extracted AESKey"
            }
        }
    }

    if ($OperationType -eq "ExtractOnly" -or $OperationType -eq "ExtractAndReplay")
    {
        $tempDir = [System.IO.Path]::GetTempPath()
        if (!(Test-Path -Path $tempDir))
        {
            mkdir $tempDir
        }

        $imosDir = $script:BackupDirectory
        if ($BackupType -eq $($script:BackupTypes["NetworkController"]))
        {
            #Fix up ImosDirectory
            $imosDir = Join-Path -Path $imosDir -ChildPath "ApiService"
            $imosDir = Join-Path -Path $imosDir -ChildPath "new"
        }

        Copy-item -Path $imosDir -Destination $tempDir -Recurse -Force
        $script:ImosDirectory = Join-Path -Path $tempDir -ChildPath (Get-Item -Path $imosDir).BaseName
        $script:ImosFile = Join-Path -Path $script:ImosDirectory -ChildPath "ImosStore"
        $script:ImosStoreOutputFile = Join-Path -Path $script:BackupDirectory -ChildPath "ImoStoreOutput.txt" 
    }

    $script:JsonOutputDirectory = Join-Path -Path $script:BackupDirectory -ChildPath "Jsons"
   
    if([String]::IsNullOrEmpty($EseUtilPath))
    {
        $script:eseUtilExe = Join-Path -Path $script:scriptDir -ChildPath "ReplicatedStoreViewer"
    }    
    else 
    {
        $script:eseUtilExe = FixPwdPath -Path $EseUtilPath
    }
    $script:eseUtilExe = Join-Path -Path $script:eseUtilExe -ChildPath "eseutil.exe"

    if([String]::IsNullOrEmpty($SDNImosStoreExplorerPath))
    {
        $script:ImosExplorerExe = $script:scriptDir
    }    
    else 
    {
        $script:ImosExplorerExe = FixPwdPath -Path $SDNImosStoreExplorerPath
    }

    $script:ImosExplorerExe = Join-Path -Path $script:ImosExplorerExe -ChildPath "SDNImosStoreExplorer.exe"

    ValidateParameters

    #Os Version
    $script:OsVersion = [System.Environment]::OSVersion.Version.Build
    $script:RS1Version = 14393

    if($Credential -eq [System.Management.Automation.PSCredential]::Empty)
    {
        $script:DefaultCredParaSet = $true
    }
    else
    {
        $script:DefaultCredParaSet = $false
    }

    $script:NetworkControllerCred = $Credential

    if($SkipResetReplica.IsPresent)
    {
        $script:RestReplica = $false
    }
    else
    {
        $script:RestReplica = $true
    }

    if($OperationType -ne "ExtractOnly")
    {
        $discoveryUri = "https://$NCRestEndPoint/Networking/Discovery"
        $result = Invoke-WebRequestWithRetries -Uri $discoveryUri -UseBasicParsing -DisableKeepAlive -Method "Get"
        if([String]::IsNullOrEmpty($result))
        {
            $script:NCRestURI = "https://$NCRestEndPoint/Networking/v1"
        }
        else
        {
            $json = $result.Content | ConvertFrom-Json
            $script:NCRestURI = "https://$NCRestEndPoint/Networking/$($json.properties.currentRestVersion)"
        }
    }

    if ($MultisiteRestoreOperation -eq "ReplaySecondaryLocalResourcesOnly" -or
        $MultisiteRestoreOperation -eq "ReplaySecondaryPrerequisiteLocalResourcesOnly" -or
        $MultisiteRestoreOperation -eq "ReplayPrimarySite")
    {
        $script:MultisiteRestore = $true
        ValidatePreExtractMultisiteConditions
    }
    else 
    {
        $script:MultisiteRestore = $false
    }

    Write-VerboseLog "Using RestURL $script:NCRestURI"
    $script:Initialzed = $true
}

function ValidateParameters
{

    if($OperationType -ne $script:OperationTypes["Put"] -and [String]::IsNullOrEmpty($BackupType))
    {
        throw "BackupType is not specified, aborting restore"
    }

    if([String]::IsNullOrEmpty($script:BackupDirectory))
    {
        throw "Backup directory is not provided, aborting restore"
    }

    if($OperationType -ne $script:OperationTypes["ReplayOnly"] -and $OperationType -ne $script:OperationTypes["Put"])
    {
        if($(Test-Path $script:eseUtilExe) -eq $false) 
        { 
            throw "EseUtil exe not found at $($script:eseUtilExe). Use EseUtilPath to provide an alternate location, aborting restore"
        }

        if($(Test-Path $script:ImosExplorerExe) -eq $false)
        {
            throw "SDNImosStoreExplorer.exe not found at $($script:ImosExplorerExe). Use SDNImosStoreExplorerPath to provide an alternate loation, aborting restore"
        }

        if($(Test-Path -Path $script:ImosFile) -eq $false)
        {
            throw "ImosStore file not found at $script:ImosFile, aborting restore"
        }
    }

    if($OperationType -eq $script:OperationTypes["ReplayOnly"] -or $OperationType -eq $script:OperationTypes["Put"])
    {
        if($(Test-Path $script:JsonOutputDirectory) -eq $false)
        {
            throw "$script:JsonOutputDirectory is empty, aborting restore"
        }
    }

    if($OperationType -ne $script:OperationTypes["ExtractOnly"])
    {
        if([String]::IsNullOrEmpty($NCRestEndPoint))
        {
            throw "NCRestEndpoint can be empty only if OperationType is $($script:OperationTypes["ExtractOnly"]) , aborting restore"
        }
  
        if(-not $script:ForceOperation)
        {
            Write-Log "Checking NC Health"
            WaitForServiceFabricHealth -WaitTimeoutInSec 60 -DoInitialWait $false

            $result = Test-NetConnection -ComputerName $NCRestEndPoint -Port 443
            if($result.TcpTestSucceeded -eq $false)
            {
                throw "Cannot connect to NCRestEndpoint $NCRestEndPoint, aborting $OperationType"
            }
        }
    }

    if($OperationType -ne $script:OperationTypes["ExtractOnly"] -and `
        $OperationType -ne $script:OperationTypes["Put"] -and  `
        [String]::IsNullOrEmpty($script:AesCryptoKey))
    {
        Write-Log "AES Key not provided. Credentials should be manually readded after the restore" $script:LogLevels["Warn"]
    }

}

function PrepareBackup
{
    if(-not $script:OperationTypes["ReplayOnly"])
    {
        Cleanup
    }

    $origPath = (Get-Location).Path
    try
    {
        Set-Location $script:ImosDirectory

        Write-Log "Recovering Database from $($script:ImosDirectory)"
        $recoveryArgs = @("-r", "edb", "-t", "-i", "-d")
        $result = & $script:eseUtilExe $recoveryArgs
        Write-VerboseLog "Recovery Result: $($result)"

        Write-VerboseLog "Repairing Database $($script:ImosFile)"
        $repairArgs = @("-p", $script:ImosFile, "-o")
        $result = & $script:eseUtilExe $repairArgs
        Write-VerboseLog "Repair Result: $($result)"

        $stateArgs = @("-m", $script:ImosFile)
        $result = & $script:eseUtilExe $stateArgs

        $state = $result | Select-String "State:"
        Write-VerboseLog "Database state is $state"

        $cleanShutDown = $state | Select-String "clean shutdown"
        
        if($cleanShutDown -eq $null)
        {
            throw "Database recovery failed, database state is $state"
        }
    }
    finally
    {
        Set-Location $origPath
    }

    $dbExtractArgs = @("-dir", $script:ImosDirectory, "-out", $script:ImosStoreOutputFile)

    Write-VerboseLog "Extracting database to $($script:ImosStoreOutputFile)"
    $result = & $script:ImosExplorerExe $dbExtractArgs
    Write-VerboseLog "Extract Result: $($result)"

    if($(Test-Path $script:ImosStoreOutputFile) -eq $false)
    {
        throw "IMOS DB output file not generated, failing prepare backup"
    }

    GenerateJsons -OutDir $script:JsonOutputDirectory -ImosStoreDump $script:ImosStoreOutputFile

}
function PerformPreRestore
{   

    if(-not [String]::IsNullOrEmpty($script:AesCryptoKey))
    {
        Write-VerboseLog "Setting AesKey"
        #Set the AesKey
        $Arguments = @{
            AesKey = $script:AesCryptoKey;   
            };
            
        $result = Invoke-CimMethod -ClassName PS_NetworkController -Namespace $script:ncNameSpace -MethodName SetAesKey -Arguments $Arguments -ErrorAction Stop -Confirm:$false
    }

    # Set the SF restore property
    $result = SetAndValidateSFProperty -Section "fabric:/NetworkController/GlobalConfiguration" -Property "Global.UseValueInRestObject" -Value "True"
    
    #If this is a first replay restore on a fresh installation, replica reset can be skipped
    if($script:RestReplica -eq $true -and -not ($MultisiteRestoreOperation -eq "ReplaySecondaryLocalResourcesOnly"))
    {
        ResetReplicas
    }
    else
    {
        Write-Log "Skipping replica reset"
        RestartSDNApiPrimaryReplica
        WaitForServiceFabricHealth -ServiceUri $script:SDNApiServiceUri
    }
}

function StartRestore
{
    Write-Log "Starting resource replay"
    
    ImportJsonInOrder -BaseType "credentials"
    ImportJsonInOrder -BaseType "virtualnetworkmanager"
    ImportJsonInOrder -BaseType "virtualswitchmanager"
    ImportJsonInOrder -BaseType "iDnsServer"
    ImportJsonInOrder -BaseType "securityTags"
    ImportJsonInOrder -BaseType "accessControlLists"
    ImportJsonInOrder -BaseType "routetables"
    ImportJsonInOrder -BaseType "serviceInsertions"
    ImportJsonInOrder -BaseType "logicalnetworks"
    ImportJsonInOrder -BaseType "macPools"
    ImportJsonInOrder -BaseType "loadBalancerManager"
    ImportJsonInOrder -BaseType "publicIpAddresses"
    ImportJsonInOrder -BaseType "gatewaypools"
    ImportJsonInOrder -BaseType "servers"
    ImportJsonInOrder -BaseType "networkinterfaces" -Type "Fabric"
    ImportJsonInOrder -BaseType "virtualServers"
    ImportJsonInOrder -BaseType "loadBalancerMuxes"
    ImportJsonInOrder -BaseType "virtualnetworks"
    ImportJsonInOrder -BaseType "loadBalancers" -Type "PublicLB"
    ImportJsonInOrder -BaseType "loadBalancers" -Type "InternalLB"
    ImportJsonInOrder -BaseType "networkinterfaces" -Type "Tenant"
    ImportJsonInOrder -BaseType "gateways"
    ImportJsonInOrder -BaseType "virtualgateways"
}

function RestartSDNApiPrimaryReplica
{
    Write-Log "Restarting SDNAPI primary replica"
    $WarningPreference='silentlycontinue'
    $outNull = Connect-ServiceFabricCluster
    $WarningPreference='continue'
    $outNull = Restart-ServiceFabricReplica -ServiceName $script:SDNApiServiceUri -ReplicaKindPrimary
}
function PerformPostRestore
{
   
   try
   {
    Write-Log "Performing Post restore operations"
    # Reset the SF restore property
    $isChanged = SetAndValidateSFProperty -Section "fabric:/NetworkController/GlobalConfiguration" -Property "Global.UseValueInRestObject" -Value "False"

    if($isChanged -eq $true)
    {
        RestartSDNApiPrimaryReplica
        WaitForServiceFabricHealth -ServiceUri $script:SDNApiServiceUri -WaitTimeoutInSec 120
    }
   }
   catch
   {
       Write-ExceptionLog "Performing Post Restore failed" $_
   }
}

#region helper functions

<#
Get or set a property in service fabric.
If $Value is null, it is treated as a Get operation, otherwise a Put operation
#>

function GetOrPutProperty()
{
    param(
        [string]$Section,
        [string]$Property,
        [string]$Value,
        [string]$Operation
    )

    $client = $null
    try
    {
        $client=[System.Fabric.FabricClient]::new()

        $IsGet = $false
        if($Operation -eq "Get")
        {
            $IsGet = $true
        }

        if($IsGet)
        {
            $task = $client.PropertyManager.GetPropertyAsync($Section, $Property)
        }
        else
        {
            $task = $client.PropertyManager.PutPropertyAsync($Section, $Property, $Value)
        }        

        try
        {
            $task.Wait()
        }
        catch
        {

        }

        $hresult = 0
        if($task.IsFaulted -eq $true)
        {
            if($task.Exception.InnerException -ne $null)
            {
                $hresult = $task.Exception.InnerException.HResult
                $exception = $task.Exception.InnerException
            }
            else
            {
                $hresult = $hresult = $task.Exception.HResult
                $exception = $task.Exception
            }
        }
        
        if($IsGet)
        {
            if($hresult -eq $script:E_PropertyNotFound)
            {
                return $null
            }

            if($hresult -eq 0)
            {
                $curValue=[System.Fabric.NamedProperty].getmethod("GetValue").MakeGenericMethod([string]).Invoke($task.Result, $null);
                return $curValue
            }
        }    

        if($hresult -ne 0 )        
        {
            throw "GetOrPutSF failed with $exception"
        }
    }
    finally
    {
        if($client -ne $null)
        {
            $client.Dispose()
        }
    }
}
function SetAndValidateSFProperty
{
    param(
        [string]$Section,
        [string]$Property,
        [string]$Value
    )

    $WarningPreference='silentlycontinue'
    $outNull = Connect-ServiceFabricCluster
    $WarningPreference='continue'
    $retryInterval = 30
    $retryCount = 3
    $count = 1
    $changed = $false
    while($count -le $retryCount)
    {
        $client = $null
        try 
        {
            $curValue = GetOrPutProperty -Section $Section -Property $Property -Operation "Get"

            if($curValue -eq $Value)
            {
                break
            }

            $outNull = GetOrPutProperty -Section $Section -Property $Property -Value $Value -Operation "Set"
            $curValue = GetOrPutProperty -Section $Section -Property $Property -Operation "Get"

            if($curValue -eq $Value)
            {
                $changed = $true
                break
            }
        }
        catch 
        {
            if($count -eq $retryCount)
            {
                throw $_
            }
        }    
        $count += 1
    }
    return $changed
}
function WaitForServiceFabricHealth
{
    param(
        $WaitTimeoutInSec = $script:NCHealthWaitTimeoutInSec,        
        $DoInitialWait = $true,
        $ServiceUri = $null      
    )

    $WarningPreference='silentlycontinue'
    $outNull = Connect-ServiceFabricCluster
    $WarningPreference='continue'
    $waitST = Date

    Write-Log "Waiting upto $WaitTimeoutInSec seconds for NC services to become healthy"
    if($DoInitialWait)
    {
        #Its take a bit for the status to change, wait for a minute before checking.
        Start-Sleep 60
    }
    while($true)
    {
        if([String]::IsNullOrEmpty($ServiceUri))
        {
            $health = Get-ServiceFabricApplicationHealth -ApplicationName $script:NCApplicationUri
            $status = $health.DeployedApplicationHealthStates
            $aggreStatus = $status[0].AggregatedHealthState 
        }
        else
        {
            $health = Get-ServiceFabricPartition -ServiceName $ServiceUri | Get-ServiceFabricPartitionHealth
            $aggreStatus = $health.AggregatedHealthState
        }

        if($aggreStatus -ne "Ok")
        {
            Write-VerboseLog "Waiting for NC service replicas to become healthy..."
            $healtStr = $health | Out-String           

            #Check for timeout
            $curTime = Date
            $timeElapsed = ($curTime - $waitST).TotalSeconds
            if($timeElapsed -gt $WaitTimeoutInSec)
            {
                Write-VerboseLog $healtStr
                throw "Timed out waiting for NC service replica health, aborting restore"
            }

            Sleep 30
        }
        else
        {
            Write-VerboseLog "NC service is healthy"
            break
        }
    }
}
function ResetReplicas
{
    Write-Log "Resetting NC Service replicas"
    $WarningPreference='silentlycontinue'
    $outNull = Connect-ServiceFabricCluster
    $WarningPreference='continue'
    $NCServices = Get-ServiceFabricService -ApplicationName $script:NCApplicationUri
    foreach($ncService in $NCServices)
    {
        Write-VerboseLog "Resetting replicas of $($ncService.ServiceName), partition $($partition.PartitionId)"
        $partition = Get-ServiceFabricPartition -ServiceName $ncService.ServiceName
        $replicas = Get-ServiceFabricReplica -PartitionId $partition.PartitionId
        foreach($replica in $replicas)
        {
            Write-VerboseLog "Resetting replica $($replica.ReplicaOrInstanceId) of $($ncService.ServiceName)"
            try
            {
                if($script:OsVersion -eq $script:RS1Version)
                {
                
                    $result = Remove-ServiceFabricReplica -PartitionId $partition.PartitionId -NodeName $replica.NodeName `
                                -ReplicaOrInstanceId $replica.ReplicaOrInstanceId -ForceRemove -ErrorAction Stop            
                }
                else
                {
                    # Try graceful remove first in RS5+
                    $result = Remove-ServiceFabricReplica -PartitionId $partition.PartitionId -NodeName $replica.NodeName `
                                -ReplicaOrInstanceId $replica.ReplicaOrInstanceId -ErrorAction Stop           
                }

                Write-VerboseLog $result
            }
            catch
            {
                Write-VerboseLog "Reset Replica failed, retrying ForceRemove"
                $result = Remove-ServiceFabricReplica -PartitionId $partition.PartitionId -NodeName $replica.NodeName `
                            -ReplicaOrInstanceId $replica.ReplicaOrInstanceId -ForceRemove -ErrorAction Stop
                Write-VerboseLog $result
            }

        }
    }

    WaitForServiceFabricHealth
}


function GenerateJsons
{
    param(
    [Parameter(Mandatory=$true)]
    [string] $OutDir,
    [Parameter(Mandatory=$true)]
    [string] $ImosStoreDump
    )

    Write-Log "Dumping resources from $ImosStoreDump. This may take a few minutes.."

    $reader = [System.IO.File]::OpenText($ImosStoreDump)
    $countRecords = 0
    $startTime = Date
    try 
    {
        for() 
        {
            $line = $reader.ReadLine()
            if ($line -eq $null) { break }
            
            $objs = $line.Split(",")
            $objs[0] = $objs[0].replace("/", "\")
            $pathToken = $objs[0].Split("\")
            $dir = $OutDir + "\\" + $pathToken[1]
            $fileName = $pathToken[2] + ".json"
            $tokens = $objs[2].Split(" ")
            $output=""

            foreach($tok in $tokens)
            {    
                try
                {
                    $c= [char]([convert]::toint16($tok,16))
                    $output += $c
                }
                catch
                {
                }
            }

            New-Item -ItemType Directory -Force -Path $dir -Confirm:$false | Out-Null
            Set-Content -Path "$dir\$fileName" -Value $output -Force -Confirm:$false

            #Give a status update every 5 minutes
            $countRecords += 1
            $curTime = Date
            $diff = ($curTime - $startTime).TotalSeconds
            if($diff -ge 5)
            {
                $startTime = Date
                Write-Log "$countRecords resources extracted so far" $script:LogLevels["Verbose"]
            }            
        }
    }
    finally 
    {
        $reader.Close()
    }

    Write-Log "$countRecords resources extracted from imos database" $script:LogLevels["Info"]
}

function ImportJsonInOrder()
{
    param
    (
        [parameter(Mandatory=$true)]
        [string] $BaseType,
        [parameter(Mandatory=$false)]
        [string] $BaseUrl = $script:NCRestURI,
        [parameter(Mandatory=$false)]
        [string] $BaseFolder = $script:JsonOutputDirectory,
        [parameter(Mandatory=$false)]
        [string] $Type = "Generic"
    )

    if (($MultisiteRestoreOperation -eq "ReplaySecondaryLocalResourcesOnly" -and $BaseType -in $script:GlobalBaseTypes) -or
        ($MultisiteRestoreOperation -eq "ReplaySecondaryPrerequisiteLocalResourcesOnly" -and $BaseType -notin $script:PrerequisiteLocalBaseTypes))
    {
        return;
    }

    #TODO: Optimize multiple passes through the full resource set
    ImportJson -BaseType $BaseType -BaseUrl $BaseUrl -BaseFolder $BaseFolder -Type $Type -State $script:succeededProvState
    ImportJson -BaseType $BaseType -BaseUrl $BaseUrl -BaseFolder $BaseFolder -Type $Type -State $script:updatingProvState 
    ImportJson -BaseType $BaseType -BaseUrl $BaseUrl -BaseFolder $BaseFolder -Type $Type -State $script:failedProvState
}
function RemoveLocalReferencesToGlobalResources()
{
    param (
        [Parameter(Mandatory=$true)]
        [string] $Body,
        [Parameter(Mandatory=$true)]
        [string] $BaseType
    )

    $jsonBody = $Body | ConvertFrom-Json -ErrorAction Stop

    # Remove ACL/service insertion from logical subnets
    if ($BaseType -eq "logicalnetworks")
    {
        foreach ($subnet in $jsonbody.properties.subnets)
        {
            if ($null -ne $subnet.properties.accessControlList)
            {
                $subnet.properties.AccessControlList = $null
            }

            if ($null -ne $subnet.properties.ServiceInsertion)
            {
                $subnet.properties.ServiceInsertion = $null
            }
        }
    }

    return $jsonBody | ConvertTo-Json -Depth 50
}
function ImportJson()
{
    param
    (
        [parameter(Mandatory=$true)]
        [string] $BaseType,
        [parameter(Mandatory=$true)]
        [string] $BaseUrl,
        [parameter(Mandatory=$true)]
        [string] $BaseFolder,
        [parameter(Mandatory=$true)]
        [string] $Type,
        [parameter(Mandatory=$false)]
        [string] $State

    ) 

    $headers = @{"Accept"="application/json"}
    $content = "application/json; charset=UTF-8"
    $timeout = 10
    $method = "Put"    

    $folderPath = "$BaseFolder\$BaseType";
    $pathExists = Test-Path $folderPath;

    if (!$pathExists)
    {
        return;
    }    

    $fileNames = Get-ChildItem -Name $BaseFolder\$BaseType -File

    #Count retry if the provisioning state fails
    $retryCount = 3
    $retryIntervalInSeconds = 30        

    foreach($fileName in $fileNames )
    {        
        $name = $fileName.PSChildName
        $resourceId = $name.TrimEnd(".json")
        $_baseType = $BaseType;
    
        if ($resourceId -ne $null)
        {
            $_baseType += "/$resourceId"
        }

        $uri = "$BaseUrl/$_baseType" 
        $body = Get-Content $BaseFolder\$BaseType\$name -Raw

        if ($MultisiteRestoreOperation -eq "ReplaySecondaryPrerequisiteLocalResourcesOnly")
        {
            $body = RemoveLocalReferencesToGlobalResources -Body $body -BaseType $BaseType
        }

        $jsonBody = ConvertFrom-Json $body -ErrorAction Stop
        $origInstanceId = $jsonBody.instanceId
        $origProvState = $jsonBody.properties.provisioningState

        if ([String]::IsNullOrEmpty($origProvState)) 
        {
            if ($State -ne "Succeeded") 
            {
                continue
            }
        } 
        elseif($origProvState -ne $State)
        {
            continue
        }

        # TODO: Optimize
        if($BaseType -eq "networkInterfaces")
        {
            $isFabric = $false
            if($jsonBody.properties.ipConfigurations -eq $null -or
                $jsonBody.properties.ipConfigurations.Count -eq 0 -or 
                $jsonBody.properties.ipConfigurations[0].properties.subnet.resourceRef.Contains("logicalnetworks")
            )
            {
                $isFabric = $true
                #This is a lnet interface,check if there is any lb config
                foreach($ipconfig in $jsonBody.properties.ipConfigurations)
                {
                    if(($ipconfig.properties.loadBalancerBackendAddressPools -ne $null -and 
                        $ipconfig.properties.loadBalancerBackendAddressPools.Count -ne 0) -or
                        ($ipconfig.properties.loadBalancerInboundNatRules -ne $null -and 
                        $ipconfig.properties.loadBalancerInboundNatRules.Count -ne 0)
                    )
                    {
                        $isFabric = $false
                        break
                    }
                }
            }

            if($Type -eq "Fabric" -and $isFabric -eq $false)
            {
                continue
            }
            elseif($Type -eq "Tenant" -and $isFabric -eq $true)
            {
                continue
            }
        }

        if($BaseType -eq "loadbalancers")
        {
            $isIlb = $false
            $feipConfigs = $jsonBody.properties.frontendIPConfigurations
            if($feipConfigs -ne $null)
            {
                foreach($feip in $feipConfigs)
                {
                    if($feip.properties.subnet -ne $null)
                    {
                        $subnet = $feip.properties.subnet.resourceRef
                        if($subnet.StartsWith("/virtualNetworks",'OrdinalIgnoreCase') -eq $true)
                        {
                            $isIlb = $true
                            break
                        }
                    }
                }
            }

            if($Type -eq "PublicLB" -and $isIlb -eq $true)
            {
                continue
            }
            elseif($Type -eq "InternalLB" -and $isIlb -eq $false)
            {
                continue
            }
        }

        Write-Log $uri $script:LogLevels["Verbose"]

        $currentRetryCount = 1
        $isErrorFatal = $false
        while($currentRetryCount -le $retryCount)
        {
            try
            {              
     
                $outnull = Invoke-WebRequestWithRetries -Headers $headers -ContentType $content -Method $method -Uri $uri -Body $body -DisableKeepAlive -UseBasicParsing

                $provisioningST = Date
                $sleepTime = 1
                #Wait for provisioning to succceed
                while($true)
                {
                    $result = $null
                    $result = Invoke-WebRequestWithRetries -Uri $uri -DisableKeepAlive -UseBasicParsing -Method "Get"
                    $toplevel = convertfrom-json $result.Content
                    if ($toplevel.value -eq $null)
                    {
                        $jsonOut = $toplevel
                    } 
                    else
                    {
                        $jsonOut = $toplevel.value
                    }

                    $curInstanceId = $jsonOut.instanceId

                    if(($curInstanceId -ne $origInstanceId) -and $OperationType -ne $script:OperationTypes["Put"])
                    {
                        $msg = "$($_baseType) current instance id $curInstanceId does not match with backup instance id $origInstanceId, aborting restore" 
                        $isErrorFatal = $true
                        throw $msg
                    }

                    if($jsonOut.properties.provisioningState -eq "Failed")
                    {
                        if($BaseType -eq "iDnsServer")
                        {
                            $msg = "Provisioning state failed for $($_baseType). Please check the iDnsServer credentials and DNS server reachablity and re-add the resource"
                            Write-Log $msg
                            $script:WarningMessages += $msg
                            break
                        }
                        elseif($BaseType -eq "virtualgateways")
                        {
                            $msg = "Provisioning state failed for $($_baseType). Please check the NetworkConnection credentials and re-add the resource"
                            Write-Log $msg
                            $script:WarningMessages += $msg
                            break
                        }
                        elseif($origProvState -eq "Failed")
                        {
                            $msg = "Provisioning state failed for previously failed resource $($_baseType)."
                            Write-Log $msg
                            $script:WarningMessages += $msg
                            break
                        }
                        elseif($script:IgnorePutFailure)
                        {
                            $msg = "Provisioning state failed for resource $($_baseType), ignored"
                            Write-Log $msg
                            $script:WarningMessages += $msg
                            break
                        }
                        else
                        {
                            throw "Provisioning state failed for $($_baseType)"    
                        }
                        
                    }

                    if($jsonOut.properties.provisioningState -eq "Succeeded")
                    {
                        Write-Log "Provisioning succeed for $($BaseType) $($resourceId)" $script:LogLevels["Verbose"]
                        break;
                    }

                    $curTime = Date
                    $diff = ($curTime - $provisioningST).TotalSeconds

                    if($diff -ge $script:provisioningTimeoutInSec)
                    {
                        throw "Timed out waiting provisioning to succeed for $($BaseType) $($resourceId)"
                    }

                    Write-Log "Waiting provisioning state to succeed for $($BaseType) $($resourceId), current state $($jsonOut.properties.provisioningState)" $script:LogLevels["Verbose"]

                    Sleep $sleepTime
                }

                break
            }
            catch
            {
                if($isErrorFatal)
                {
                    throw
                }
                if($currentRetryCount -ge $retryCount)
                {
                    $msg = "Restore Failed for resource $($_baseType)`r`n$_`r`n$($_ | Out-String)`r`n"
                    
                    if($script:IgnorePutFailure)
                    {
                        Write-Log $msg
                        $script:WarningMessages += "Restore Failed for resource $($_baseType), ignored"
                    }
                    else 
                    {
                        Write-Log $msg $script:LogLevels["Fatal"]
                        throw $msg
                    }
                    
                }

                Write-Log "Retrying Failure `r`n$_`r`n$($_.Exception.Response)`r`n" $script:LogLevels["Debug"]
                $currentRetryCount += 1
                Start-Sleep $retryIntervalInSeconds
            }  
        }
    }  
}

function Invoke-WebRequestWithRetries {
    param(
        [System.Collections.IDictionary] $Headers,
        [string] $ContentType,
        [Microsoft.PowerShell.Commands.WebRequestMethod] $Method,
        [System.Uri] $Uri,
        [object] $Body,
        [Switch] $DisableKeepAlive,
        [Switch] $UseBasicParsing,
        [Parameter(mandatory=$false)]
        [bool] $shouldRetry = $true
    )
        
    $params = @{
        'Headers'=$headers;
        'ContentType'=$content;
        'Method'=$method;
        'uri'=$uri;
        'ErrorAction'='Stop';
        }
    
    if($Body -ne $null) 
    {
        $params.Add('Body', $Body)
    }

    if($DisableKeepAlive.IsPresent) 
    {
        $params.Add('DisableKeepAlive', $true)
    }

    if($UseBasicParsing.IsPresent) 
    {
        $params.Add('UseBasicParsing', $true)
    }

    if ($script:DefaultCredParaSet -eq $true)
    {
        $params.Add('UseDefaultCredentials', $true)
    }
    elseif($script:NetworkControllerCred -ne [System.Management.Automation.PSCredential]::Empty)
    {
        $params.Add('Credential', $script:NetworkControllerCred)
    }
        
    $retryIntervalInSeconds = 30
    $maxRetry = 6
    $retryCounter = 0
    
    do {
        try {
            $result = $null
            $result = Invoke-WebRequest @params
            break
        }
        catch {

            Write-VerboseLog "Invoke-WebRequestWithRetries: $($Method) Exception: $_"
            Write-VerboseLog "Invoke-WebRequestWithRetries: $($Method) Exception: $($_.Exception.Response)"
            
            if($_.Exception.Response.StatusCode.value__ -eq 404)
            {
                #Dont retry on Not Found
                break
            }

            $retryCounter++
            if($retryCounter -le $maxRetry) {

                Write-VerboseLog "Invoke-WebRequestWithRetries: retry $($Method) $($Uri) operation in $($retryIntervalInSeconds) seconds. Retry count: $($retryCounter)."
                sleep -Seconds $retryIntervalInSeconds
            }
            else {
                # last retry still fails, so throw the exception
                throw $_
            }
        }
    } while ($shouldRetry -and ($retryCounter -le $maxRetry)) 
    
    return $result       
}

Function Get-AesKey
{
    $result = Invoke-CimMethod -ClassName PS_NetworkController -Namespace $script:ncNameSpace -MethodName GetAESKey -ErrorAction Stop -Confirm:$false
    $key = $result.AesKey.Value    
    Write-Host $key
}

Function Write-Log {
    Param(
    [Parameter(Mandatory=$True)]
    [string]
    $Message,
    [Parameter(Mandatory=$False)]
    [String]
    $Level = $script:LogLevels["Info"]
    )

    $levels = ($script:LogLevels["Verbose"], $script:LogLevels["Info"], $script:LogLevels["Warn"], $script:LogLevels["Error"], $script:LogLevels["Fatal"])
    $enabledLogLevel = [array]::IndexOf($levels, $script:LogLevel)
    $currentLogLevel = [array]::IndexOf($levels, $Level)
    $Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss:fff")

    $Line = "$Stamp $Level $Message"
    Add-Content $script:LogFile -Value $Line -Confirm:$false

    if($currentLogLevel -ge $enabledLogLevel)
    {
        $writeHostPara = @{
            Object = $Line
        }
        
        if($Level -eq $script:LogLevels["Warn"])
        {
            $writeHostPara.Add("ForegroundColor", "Yellow")
        }
        elseif($Level -eq $script:LogLevels["Error"] -or $Level -eq $script:LogLevels["Fatal"])
        {
            $writeHostPara.Add("ForegroundColor", "Red")
        }

        Write-Host @writeHostPara
    }
    elseif($Level -eq $script:LogLevels["Info"])
    {
        Write-Debug $line
    }
    elseif($Level -eq $script:LogLevels["Verbose"])
    {
        Write-Verbose  $line
    }   
}

Function Write-ExceptionLog
{
    [CmdletBinding()]
    Param(
    [Parameter(Mandatory=$True)]
    [String]
    $Message,
    [Parameter(Mandatory=$True)]
    [Object]
    $ExpObj,
    [Parameter(Mandatory=$False)]
    [String]
    $Level = $script:LogLevels["Error"]
    )

    $strMsg = $ExpObj | Out-String
    Write-Log "$Message `r`n $strMsg" $Level
}

Function Write-VerboseLog
{
    [CmdletBinding()]
    Param(
    [Parameter(Mandatory=$True)]
    [String]
    $Message
    )

    Write-Log $Message $script:LogLevels["Verbose"]
}

function Cleanup
{
    Remove-Item $script:ImosStoreOutputFile -Force -ErrorAction SilentlyContinue -Confirm:$false
    Remove-Item $script:JsonOutputDirectory -Force -ErrorAction SilentlyContinue -Recurse -Confirm:$false
}

function ValidatePostExtractMultisiteConditions
{
    $multisiteResourcePath = Join-Path -Path $script:JsonOutputDirectory -ChildPath "multisite"
    if ($script:MultisiteRestore)
    {
        if (!(Test-Path -Path $multisiteResourcePath) -or !((Get-ChildItem -Path $multisiteResourcePath).Count -gt 0))
        {
            throw "Multisite resource not found for a multisite restore."
        }  
    }
    else
    {
        if ((Test-Path -Path $multisiteResourcePath) -and ((Get-ChildItem -Path $multisiteResourcePath).Count -gt 0))
        {
            $script:WarningMessages += "Multisite resource found, but MultisiteRestoreOperation parameter not given."
        } 
    }

}

function ValidatePreExtractMultisiteConditions
{
    if ($MultisiteRestoreOperation -eq "ReplaySecondaryPrerequisiteLocalResourcesOnly")
    {
        $multisite = Get-NetworkControllerMultisiteConfiguration -ConnectionUri "https://$NCRestEndPoint"
        foreach ($site in $multisite.Properties.Sites)
        {
            if ($site.Properties.State -eq "Connected")
            {
                throw "Multisite sync detected. The ReplaySecondaryPrerequisiteLocalResourcesOnly option should only be used for restoring local resources so that initial sync is successful."
            }
        }
    }
    elseif ($MultisiteRestoreOperation -eq "ReplaySecondaryLocalResourcesOnly")
    {
        $multisite = Get-NetworkControllerMultisiteConfiguration -ConnectionUri "https://$NCRestEndPoint"
        $multisiteSyncExists = $false
        foreach ($site in $multisite.Properties.Sites)
        {
            if ($site.Properties.State -eq "Connected")
            {
                $multisiteSyncExists = $true
                break
            }
        }

        if (-not $multisiteSyncExists)
        {
            throw "Multisite sync not detected. The ReplaySecondaryLocalResourcesOnly option should be used after peering so that local resources with references to global resources resolve correctly." 
        }
    }
}

#endregion

if ($PSBoundParameters['Debug']) {
    $DebugPreference = 'Continue'
}

if ($PSBoundParameters['Verbose']) {
    $DebugPreference = 'Continue'
}

if ($PSBoundParameters['Force']) {
    $script:ForceOperation = $true
}

if($OperationType -eq "ExtractOnly")
{
    $whatifMsg = "Extract the resources from $BackupDirectoryOrZipFile"
}
elseif($OperationType -eq $script:OperationTypes["Put"])
{
    $whatifMsg = "Put all resources from $BackupDirectoryOrZipFile\Jsons"
}
else
{
    $whatifMsg = "Restore networkcontroller resources from $BackupDirectoryOrZipFile."

    if(($BackupType -eq $script:BackupTypes["Manual"]) -and ([String]::IsNullOrEmpty($AesKey) -eq $true))
    {
        $whatifMsg += "`r`nAesKey is not provided. Credential resources and the virtual gateway connection credentials should be manually readded after the restore."
    }
    $whatifMsg += "`r`nThis operation will reset the existing NetworkController database and all the current data will be erased"
}

if($GetAesKey.IsPresent)
{
    Get-AesKey
}
elseif ($PSCmdlet.ShouldProcess($whatifMsg)) 
{
    try 
    {
        
        Initialize
        
        if($OperationType -eq "ExtractOnly" -or $OperationType -eq "ExtractAndReplay")
        {
            Cleanup
            PrepareBackup
            if($OperationType -eq "ExtractOnly")
            {
                $script:ResultMessages += "Extracted JSONs to $script:BackupDirectory"
            }
        }
        if($OperationType -eq "ReplayOnly" -or $OperationType -eq "ExtractAndReplay")
        {
            ValidatePostExtractMultisiteConditions
            PerformPreRestore
            StartRestore
            $script:ResultMessages += "Restored NetworkController from $script:BackupDirectory"
        }
        if($OperationType -eq $script:OperationTypes["Put"])
        {
            ValidatePostExtractMultisiteConditions
            StartRestore
            $script:ResultMessages += "Put all resources from $script:BackupDirectory"
        }
    }
    catch 
    {
        $script:ErrorMessages += "Restore failed: $_"
    }
    finally
    {       
        if($script:Initialzed -eq $true -and ($OperationType -eq "ReplayOnly" -or $OperationType -eq "ExtractAndReplay"))
        { 
            PerformPostRestore
        }

        if ($script:Initialzed -and ($OperationType -eq "ExtractAndReplay" -or $OperationType -eq "ExtractOnly"))
        {
            Write-Log "Cleaning up $script:ImosDirectory"
            Remove-Item $script:ImosDirectory -ErrorAction Stop -Recurse -Force
        }

        Write-Host "=======Result Summary======="
        foreach($msg in $script:WarningMessages)
        {
            Write-Host -ForegroundColor Yellow $msg
        }

         foreach($msg in $script:ErrorMessages)
        {
            Write-Host -ForegroundColor Red $msg
        }  

         foreach($msg in $script:ResultMessages)
        {
            Write-Host -ForegroundColor Green $msg
        }
        Write-Host "=======Result Summary======="       
    }
}

# SIG # Begin signature block
# MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCJg6GC3lSbHN0T
# 8cxCKnXFWjtwXcNuGQ65/tMZY59xN6CCDXYwggX0MIID3KADAgECAhMzAAADrzBA
# DkyjTQVBAAAAAAOvMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwOTAwWhcNMjQxMTE0MTkwOTAwWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOS8s1ra6f0YGtg0OhEaQa/t3Q+q1MEHhWJhqQVuO5amYXQpy8MDPNoJYk+FWA
# hePP5LxwcSge5aen+f5Q6WNPd6EDxGzotvVpNi5ve0H97S3F7C/axDfKxyNh21MG
# 0W8Sb0vxi/vorcLHOL9i+t2D6yvvDzLlEefUCbQV/zGCBjXGlYJcUj6RAzXyeNAN
# xSpKXAGd7Fh+ocGHPPphcD9LQTOJgG7Y7aYztHqBLJiQQ4eAgZNU4ac6+8LnEGAL
# go1ydC5BJEuJQjYKbNTy959HrKSu7LO3Ws0w8jw6pYdC1IMpdTkk2puTgY2PDNzB
# tLM4evG7FYer3WX+8t1UMYNTAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQURxxxNPIEPGSO8kqz+bgCAQWGXsEw
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMTgyNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAISxFt/zR2frTFPB45Yd
# mhZpB2nNJoOoi+qlgcTlnO4QwlYN1w/vYwbDy/oFJolD5r6FMJd0RGcgEM8q9TgQ
# 2OC7gQEmhweVJ7yuKJlQBH7P7Pg5RiqgV3cSonJ+OM4kFHbP3gPLiyzssSQdRuPY
# 1mIWoGg9i7Y4ZC8ST7WhpSyc0pns2XsUe1XsIjaUcGu7zd7gg97eCUiLRdVklPmp
# XobH9CEAWakRUGNICYN2AgjhRTC4j3KJfqMkU04R6Toyh4/Toswm1uoDcGr5laYn
# TfcX3u5WnJqJLhuPe8Uj9kGAOcyo0O1mNwDa+LhFEzB6CB32+wfJMumfr6degvLT
# e8x55urQLeTjimBQgS49BSUkhFN7ois3cZyNpnrMca5AZaC7pLI72vuqSsSlLalG
# OcZmPHZGYJqZ0BacN274OZ80Q8B11iNokns9Od348bMb5Z4fihxaBWebl8kWEi2O
# PvQImOAeq3nt7UWJBzJYLAGEpfasaA3ZQgIcEXdD+uwo6ymMzDY6UamFOfYqYWXk
# ntxDGu7ngD2ugKUuccYKJJRiiz+LAUcj90BVcSHRLQop9N8zoALr/1sJuwPrVAtx
# HNEgSW+AKBqIxYWM4Ev32l6agSUAezLMbq5f3d8x9qzT031jMDT+sUAoCw0M5wVt
# CUQcqINPuYjbS1WgJyZIiEkBMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMavr6NXWOzJEsPwTXzZX07E
# /j2Qr0yuBg97B8XJflmGMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAGKdTCMVcmkw89+iJ4mQVv/tsZEPAy0ssMJEqEL6b0MWPVgoW1CUCG6nm
# L6B2DzQFuPYhaTHPR/uSRlHtZbExQYzBlllwjoUx6FPMnoUNm4ao8ld+MH3Kk0Ci
# 9CH32UQVy9+nO6vSc9fMYvGOxExUZ3yZWN2s2soMzPnFU3E8gBaK6Qv1R7C9FMT9
# ny8oAwNP14PliFK1OWTo+8RIE3u9vJNvwL4TFfbwpDLQE7kEGl8KDy14GEbuu9Kp
# KrVITIieOeI3ztnI+VVEbIKCUi2R7cSJWAeugdux4fQbjZnHW3JO0NiLLw97Q4oh
# HgZFQ11nesAEJucPy/bEh2AYdU0BFqGCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC
# F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCCNFVjZ8KDaxfdZ1qzUOOrnc2X5pImkmI46v1khPogOggIGZmsMoVV7
# GBMyMDI0MDYyNDIyMjMyNS40OTJaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
# ghHtMIIHIDCCBQigAwIBAgITMwAAAfAqfB1ZO+YfrQABAAAB8DANBgkqhkiG9w0B
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzEyMDYxODQ1
# NTFaFw0yNTAzMDUxODQ1NTFaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0wNUUwLUQ5NDcxJTAjBgNV
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQC1Hi1Tozh3O0czE8xfRnrymlJNCaGWommPy0eINf+4
# EJr7rf8tSzlgE8Il4Zj48T5fTTOAh6nITRf2lK7+upcnZ/xg0AKoDYpBQOWrL9Ob
# FShylIHfr/DQ4PsRX8GRtInuJsMkwSg63bfB4Q2UikMEP/CtZHi8xW5XtAKp95cs
# 3mvUCMvIAA83Jr/UyADACJXVU4maYisczUz7J111eD1KrG9mQ+ITgnRR/X2xTDMC
# z+io8ZZFHGwEZg+c3vmPp87m4OqOKWyhcqMUupPveO/gQC9Rv4szLNGDaoePeK6I
# U0JqcGjXqxbcEoS/s1hCgPd7Ux6YWeWrUXaxbb+JosgOazUgUGs1aqpnLjz0YKfU
# qn8i5TbmR1dqElR4QA+OZfeVhpTonrM4sE/MlJ1JLpR2FwAIHUeMfotXNQiytYfR
# BUOJHFeJYEflZgVk0Xx/4kZBdzgFQPOWfVd2NozXlC2epGtUjaluA2osOvQHZzGO
# oKTvWUPX99MssGObO0xJHd0DygP/JAVp+bRGJqa2u7AqLm2+tAT26yI5veccDmNZ
# sg3vDh1HcpCJa9QpRW/MD3a+AF2ygV1sRnGVUVG3VODX3BhGT8TMU/GiUy3h7ClX
# OxmZ+weCuIOzCkTDbK5OlAS8qSPpgp+XGlOLEPaM31Mgf6YTppAaeP0ophx345oh
# twIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFNCCsqdXRy/MmjZGVTAvx7YFWpslMB8G
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQA4IvSbnr4jEPgo5W4xj3/+0dCGwsz863QG
# Z2mB9Z4SwtGGLMvwfsRUs3NIlPD/LsWAxdVYHklAzwLTwQ5M+PRdy92DGftyEOGM
# Hfut7Gq8L3RUcvrvr0AL/NNtfEpbAEkCFzseextY5s3hzj3rX2wvoBZm2ythwcLe
# ZmMgHQCmjZp/20fHWJgrjPYjse6RDJtUTlvUsjr+878/t+vrQEIqlmebCeEi+VQV
# xc7wF0LuMTw/gCWdcqHoqL52JotxKzY8jZSQ7ccNHhC4eHGFRpaKeiSQ0GXtlbGI
# bP4kW1O3JzlKjfwG62NCSvfmM1iPD90XYiFm7/8mgR16AmqefDsfjBCWwf3qheIM
# fgZzWqeEz8laFmM8DdkXjuOCQE/2L0TxhrjUtdMkATfXdZjYRlscBDyr8zGMlprF
# C7LcxqCXlhxhtd2CM+mpcTc8RB2D3Eor0UdoP36Q9r4XWCVV/2Kn0AXtvWxvIfyO
# Fm5aLl0eEzkhfv/XmUlBeOCElS7jdddWpBlQjJuHHUHjOVGXlrJT7X4hicF1o23x
# 5U+j7qPKBceryP2/1oxfmHc6uBXlXBKukV/QCZBVAiBMYJhnktakWHpo9uIeSnYT
# 6Qx7wf2RauYHIER8SLRmblMzPOs+JHQzrvh7xStx310LOp+0DaOXs8xjZvhpn+Wu
# Zij5RmZijDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQ
# MIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdGMDAtMDVFMC1EOTQ3MSUwIwYDVQQD
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQDC
# KAZKKv5lsdC2yoMGKYiQy79p/6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6iQKujAiGA8yMDI0MDYyNDE1MDk0
# NloYDzIwMjQwNjI1MTUwOTQ2WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDqJAq6
# AgEAMAoCAQACAhg5AgH/MAcCAQACAhO/MAoCBQDqJVw6AgEAMDYGCisGAQQBhFkK
# BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ
# KoZIhvcNAQELBQADggEBAAQhaNFST+wXTLA0B2/bOdBZGzv2YtZcWydb3gH+vcuU
# mPXRSykU6wR1C/KfeWEaSEv+0i9QQR4wND3Avi/tvaGTRdnrDtILv8dWHK+WqlXL
# MvLQ3hpSlEfDxroBTzLAN9B5iu/qSgBfPVi0lRKTr9phAWY0Go2Wvr9a9NwawaQD
# IBnHpUP34EtSMFhwrV384fZfZazdjgT2keXe8Q+29YB2qwbLjL+b16Nf7anVU+6I
# sjhMMoz7qbQmwFsbkVKjVX+R5GRZ/jOriKzkGbjQd9d5M2XbBNrDHL13Bxoj7CwP
# bNQvdOQrNQCyBZlhGH4DVPKqDdWj5JhpyOothyl0/6kxggQNMIIECQIBATCBkzB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAfAqfB1ZO+YfrQABAAAB
# 8DANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
# MC8GCSqGSIb3DQEJBDEiBCDoX3sULGpUf54UmtIfAcSZU5OXuTbL8i5NHFKZpVJC
# CzCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIFwBmqOlcv3kU7mAB5sWR74Q
# FAiS6mb+CM6asnFAZUuLMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAHwKnwdWTvmH60AAQAAAfAwIgQgUh/AaUHSlA4ukw+aWAieA9LT
# uI9k7DYdlXrRzFnz1kkwDQYJKoZIhvcNAQELBQAEggIAaze4t7gUkz/fUamuKe2R
# qjJAvnlPt7ad5fGa3ubfRL2K/4GGw9Cj7XsAEHPcd/Ez11s4tdX9kqkyygHNNujQ
# aa7GuNac2q70otZz9MTHuDZDkRp0BGr6RPpYF7WuO/bwOr9rCHpHbnkt917bobmj
# XLFd0VwiBfakz8zkU0DvAZOFFjtKKBlLOjbfyytQurZPJRov7QupUSvure74at2u
# qTIg4YtSGh7JS4M2ivssyJCrZpIVy8khWA2t6cTrOvuaWuKvRVrH+KpOUnw8b8Oh
# 4M0fxZkW0srtdsZ90o3iHhBvdRil/A7rYE9+XkMQA9Kfiksil0E66OHVbSCp5hJY
# zq4eOtGLp8oXLGZdTxbTMTLpdoUY/gscyt470NzmZQVAqfYe0pcap/3GBsLL9Lau
# Czlsu9lBsaYOlNd8fem1ngDwzwG/GfDwnH+TjTMg08LlOu/Yqd954X9boSjYhCEz
# OTsFUO4tjrM7GGnbeC2dfC41g0uaXm2x8L1Vxwh8BT7tgoKKVqEQFjz9RTKRJ+gg
# RP43mMchMZ9ta8CYOfFJUtMhMMUrzMtmTo2R1fIGBFy8CwLOtvktYPxQcKPjPmxD
# jal9xIKmDgOjt19f+C7qgiFx7HEWz+qgGDedQDib5pfcfOcJm0R7VbcZq5d6cXlE
# BCSrSLXfEy5f+dqr1XYsnoo=
# SIG # End signature block