<#
/// <copyright>
/// INTEL CONFIDENTIAL
///
/// Copyright 2014-2021 Intel Corporation
///
/// This software and the related documents are Intel copyrighted materials, and your use of
/// them is governed by the express license under which they were provided to you ("License").
/// Unless the License provides otherwise, you may not use, modify, copy, publish, distribute,
/// disclose or transmit this software or the related documents without Intel's prior written
/// permission.
///
/// This software and the related documents are provided as is, with no express or implied
/// warranties, other than those that are expressly stated in the License.
///
/// </copyright>
#>

    #User input arguments
    param(
       [Parameter(Mandatory=$True)]
       [string]$Action,

       [string]$ConfigPath,

       [switch]$BDF
    )

    #Global file variables
    $Script:context
    $Script:service
    $Script:lockobj
    $script:options
    $Script:Adapters=$null
    $Script:bSave=$False
    $Script:bRestore=$false
    $Script:bRemove=$False
    $Script:bUpSave=$False
    $Script:bUpRestore=$False
    $Script:bANSInstalled = $True
    $Script:colImportAdapters = $null
    $Script:EnabledSystemAdapters = $null
    $Script:bWriteLog=$False

    #Save off input parameters to be used in functions
    $Script:Action=$Action
    $Script:BDF=$BDF
    $Script:ConfigPath=$ConfigPath
    $Script:ScriptPath=$null


#------------------------ Messages ------------------------
    $Script:Messages = @{
Usage = @"
Intel(R) SaveRestore.ps1
Copyright 2014-2021 Intel Corporation. All rights reserved.

  Usage: SaveRestore.ps1 -Action -ConfigPath -BDF
  Example: SaveRestore.ps1 –Action save –ConfigPath C:\*File Path*\config.txt
  -Action is required. Valid values are 'save' and 'restore.'
  -ConfigPath is optional. It specifies the path and file name of the main configuration save file. If not specified, it is the script path and default file name (saved_config.txt).
  NOTE: The Saved_StaticIP.txt file name does not change and is always saved and restored from the script path.
  -BDF is optional. If specified during a restore, it will restore settings to adapters using their bus/device/function.
  The default configuration file names are saved_config.txt and Saved_StaticIP.txt.
"@;

# Performing a save/restore of configuration file at $Script:ConfigPath
RunningSaveRestore        = "Performing a save/restore of configuration file at";

SettingOEMValue           = "Setting OEM custom value.";

# Restoring primary and secondary adapters on $Team.TeamName
RestoringTeamAdapters     = "Restoring primary and secondary adapters on";

# Restoring $Team.TeamName / $Vlan.VlanName
Restoring                 = "Restoring"

RestoringAdapterIP        = "Restoring adapter IP settings."
RestoringTeamIP           = "Restoring team IP settings."
RestoringVLANIP           = "Restoring VLAN IP settings."
RestoringAdapterSettings  = "Restoring adapter settings."
RestoringTeamSettings     = "Restoring team settings."
RestoringVLANSettings     = "Restoring VLAN settings."
RestoringAdapterPartition = "Restoring adapter partition settings."

NICPartitioningDetected   = "Detected a device configured for NIC partitioning. We recommend you use the -BDF option when restoring."

PerformingSave            = "Saving the configuration."
PerformingUpgradeSave     = "Upgrading and saving the configuration."
PerformingRestore         = "Restoring the configuration."
PerformingUpgradeRestore  = "Upgrading and restoring the configuration."
PerformingRemove          = "Removing the team and VLAN device configuration."

# $Script:ConfigPath file already exists. Do you want to overwrite it (y/n)?
FileExistsPrompt          = "file already exists. Do you want to overwrite it (y/n)?"

#------------------------ Errors ------------------------

# No objects found for $class
NoObjectsFound            = "No objects found for"

AdminRequired             = "The save/restore script requires administrative rights. Please log in as an Administrator and try again."
PROSetRequired            = "The save/restore script requires Intel® PROSet to be installed. Please install Intel PROSet and try again."

# Could not find the restore configuration file at $Script:ConfigPath. Please provide a path to the configuration file.
ConfigNotFoundPart1       = "Could not find the restore configuration file at"
ConfigNotFoundPart2       = "Please provide a valid path to the configuration file."

# Configuration path $FilePath does not exist. Please provide a valid path.
ConfigPathDoesntExist1    = "A directory in"
ConfigPathDoesntExist2    = "does not exist. Please provide a valid path."

ConfigFileNotSpecified    = "No configuration file name specified. Please provide a valid file name."

# Unable to set ... on ...
UnableToSetPart1          = "Unable to set"
UnableToSetPart2          = "on"
}

    #-----------------------------------------------------------[Functions]------------------------------------------------------------

    #Set WMI lock
    Function BeginApply
    {
        #retrieve instance
         $Script:service = Get-WMiobject -class IAnet_netservice -namespace root\intelncs2

        #create [in] parameters
         $inparams = $Script:service.GetMethodParameters("BeginApply")

        #spawn management object
         $Script:options = new-object System.Management.InvokeMethodOptions

        #add context for this script
         $Script:options.Context.Add('SaveRestoreApply', $true)
         $Script:options.Context.Add('GET_EXTENSIONS', $true)
         $Script:options.Context.Add('GET_EXT_KEYS_ONLY', $false)
         $Script:options.Context.Add('IANet_PartialData', 512)

        #obtain lock
         $Script:lockobj = $Script:service.InvokeMethod("BeginApply", $inparams, $Script:options)

        #this is necessary to set the lock into a new object
         $Script:context = new-object System.Management.ManagementNamedValueCollection

        #adding a new single named value to the collection with the value of the lock objects client handle
         $Script:context.Add('ClientSetId', [int] $Script:lockobj.ClientSetHandle)
         $Script:context.Add('SaveRestoreApply', $true)
    }

    #Remove WMI lock
    Function ReleaseLock
    {
        $inparams = $Script:service.GetMethodParameters("Apply")
        $inparams.ClientSetHandle=[int]$Script:lockobj.ClientSetHandle
        $result = $Script:Service.InvokeMethod("Apply", $inparams, $null)
    }

    Function NewObject ($class)
    {
        $classdef = New-Object System.Management.ManagementClass $class
        #the new object options
        $classdef.Options.UseAmendedQualifiers = $true
        $classdef.Options.Context = $Script:context

        return $classdef
    }

    Function DeleteObject ($object)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log $object
        #the delete options
        $deleteoptions = New-Object System.Management.DeleteOptions
        $deleteoptions.Context = $Script:context

        $object.Delete($deleteoptions)
    }

    Function SetSetting ($setting)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log $setting
        #the put options
        $putoptions = New-Object System.Management.PutOptions($Script:context)
        $putoptions.UseAmendedQualifiers = $true

        #perform put
        $setting.Put($putoptions)
    }

    #Get specified class object from WMI
    Function GetObject($class)
    {
        $querystring = [string] "SELECT * FROM $class"
        $query = New-Object System.Management.ObjectQuery($querystring)

        # the enumeration options
        $enumerate_option = New-Object System.Management.EnumerationOptions
        $enumerate_option.UseAmendedQualifiers = $true
        $enumerate_option.Context = $Script:context

        # setup scope
        $DMiXPath = New-Object System.Management.ManagementPath
        $DMiXPath.NamespacePath = "root\intelncs2"
        $scope = New-Object System.Management.ManagementScope($DMiXPath)
        $scope.Connect()

        #the searcher for the object
        $searcher = New-Object System.Management.ManagementObjectSearcher($scope, $query, $enumerate_option)
        $collection = $searcher.Get()

        #If the Get() above fails, it won't be caught until the return happens. So catch it and display an appropriate message.
        try
        {
            return $collection
        }
        catch
        {
            $ErrorMessage = [string]$Messages.NoObjectsFound
            Write-Error "$ErrorMessage $class"
            exit
        }
    }

    #Get associated objects given the object path and where condition
    Function GetAssociated($path, $where)
    {
        $querystring = [string] "ASSOCIATORS OF {$path} WHERE $where"

        $query = New-Object System.Management.ObjectQuery($querystring)

        # the enumeration options
        $enumerate_option = New-Object System.Management.EnumerationOptions
        $enumerate_option.UseAmendedQualifiers = $true
        $enumerate_option.Context = $Script:context

        # setup scope
        $DMiXPath = New-Object System.Management.ManagementPath
        $DMiXPath.NamespacePath = "root\intelncs2"
        $scope = New-Object System.Management.ManagementScope($DMiXPath)
        $scope.Connect()

        #the searcher for the object
        $searcher = New-Object System.Management.ManagementObjectSearcher($scope, $query, $enumerate_option)
        $collection = $searcher.Get()

        return $collection
    }

    #Invoke a method given it's name, class and parameters
    Function InvokeMethod($Class,$Method,$ColParameterName,$ColParameterValue)
    {
        $Invoke_option = New-Object System.Management.InvokeMethodOptions
        $Invoke_option.Context = $Script:context

        $params = $null
        if($null -ne $ColParameterName)
        {
            $params = $Class.psbase.GetMethodParameters($Method)
            if($ColParameterName.count -gt 1)
            {
                for($i=0; $i -lt $ColParameterName.count; $i++)
                {
                    $params.psbase.SetPropertyValue($ColParameterName[$i],$ColParameterValue[$i])
                }
            }
            else
            {
                $params.psbase.SetPropertyValue($ColParameterName,$ColParameterValue)
            }
        }

        return $Class.psbase.InvokeMethod($Method,$params,$Invoke_option)
    }

    #Function used to write objects to config file.
    Function WriteObjectToConfigFile($NewObjects,$Properties)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$NewObjects = $NewObjects"
        Write-Log "`$Properties = $Properties"
        if($NewObjects -ne $null)
        {
            $ExportObject = $NewObjects | select $Properties | ConvertTo-Csv -NoTypeInformation
            $ExportObject = $ExportObject[1..$ExportObject.Count]
            WriteTextToConfigFile $ExportObject
        }
    }

    #Function used to write text to config file.
    Function WriteTextToConfigFile($NewText)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$NewText = $NewText"
        $NewText | Out-File $Script:ConfigPath -Encoding utf8 -Append
    }

    #Function used to write objects to the IP config file.
    Function WriteObjectToIPFile($NewObjects,$Properties)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$NewObjects = $NewObjects"
        Write-Log "`$Properties = $Properties"
        if($NewObjects -ne $null)
        {
            $ExportObject = $NewObjects | select $Properties | ConvertTo-Csv -NoTypeInformation
            $ExportObject = $ExportObject[1..$ExportObject.Count]
            WriteTextToIPFile $ExportObject
        }
    }

    #Function used to write text to IP config file.
    Function WriteTextToIPFile($NewText)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$NewText = $NewText"
        $NewText | Out-File $Script:ScriptPath\Saved_StaticIP.txt -Encoding utf8 -Append
    }

    #Function used to read objects from config file given a file section.
    Function ReadFromConfigFile($FileSection)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$FileSection = $FileSection"
        $FileObjects=@()
        $FileObjects = Get-Content $Script:ConfigPath

        switch($FileSection)
        {
            "Adapters" {
                            #Find the section for adapters and add 1 to not include AdaptersStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"AdaptersStart") + 1
                            #Find the end of the section and remove 1 to not include AdaptersEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"AdaptersEnd") - 1

                            $colProperty = "Name","OriginalDisplayName","PermanentAddress","PCIDeviceID","SlotID"
                            break
                       }
            "AdapterSettings" {
                            #Find the section for adapters and add 1 to not include AdapterSettingsStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"AdapterSettingsStart") + 1
                            #Find the end of the section and remove 1 to not include AdapterSettingsEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"AdapterSettingsEnd") - 1

                            $colProperty = "Name","Caption","CurrentValue","CurrentValues","PermanentAddress","PCIDeviceID","SlotID"
                            break
                       }
            "OEMSetting" {
                            #Find the section for adapters and add 1 to not include OEMSettingStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"OEMSettingStart") + 1
                            #Find the end of the section and remove 1 to not include OEMSettingEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"OEMSettingEnd") - 1

                            $colProperty = "OEMCustomizeable"
                            break
                       }
            "PMSettings" {
                            #Find the section for adapters and add 1 to not include PMSettingsStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"PMSettingsStart") + 1
                            #Find the end of the section and remove 1 to not include PMSettingsEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"PMSettingsEnd") - 1

                            $colProperty = "Name","Caption","CurrentValue","CurrentValues","PermanentAddress","PCIDeviceID","SlotID"
                            break
                       }
            "Teams" {
                            #Find the section for adapters and add 1 to not include TeamsStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"TeamsStart") + 1
                            #Find the end of the section and remove 1 to not include TeamsEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"TeamsEnd") - 1

                            $colProperty = "TeamName","TeamMembers","TeamMode","PrimaryAdapter","SecondaryAdapter"
                            break
                       }
            "TeamSettings" {
                            #Find the section for adapters and add 1 to not include TeamSettingsStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"TeamSettingsStart") + 1
                            #Find the end of the section and remove 1 to not include TeamSettingsEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"TeamSettingsEnd") - 1

                            $colProperty = "TeamName","Caption","CurrentValue","CurrentValues"
                            break
                       }
            "Vlans" {
                            #Find the section for adapters and add 1 to not include VlansStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"VlansStart") + 1
                            #Find the end of the section and remove 1 to not include VlansEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"VlansEnd") - 1

                            $colProperty = "ParentName","VLANID","VLANNAME"
                            break
                       }
            "VlanSettings" {
                            #Find the section for adapters and add 1 to not include VlanSettingsStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"VlanSettingsStart") + 1
                            #Find the end of the section and remove 1 to not include VlanSettingsEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"VlanSettingsEnd") - 1

                            $colProperty = "ParentName","Name","VLANID","Caption","CurrentValue"
                            break
                       }
            "NICPARTSettings" {
                            #Find the section for adapters and add 1 to not include NICPARTSettingsStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"NICPARTSettingsStart") + 1
                            #Find the end of the section and remove 1 to not include NICPARTSettingsEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"NICPARTSettingsEnd") - 1

                            $colProperty = "Name","PartitionNumber","Identifier","MinBWPercent","MaxBWPercent"
                            break
                       }
             Default {
                        return
                     }
        }

        #If no items were found in the provided section return nothing
        if($CsvEnd -lt $CsvObjectStart)
        {
            return

        }
        #Else return only the provided section and convert the text into objects
        else
        {
            return $FileObjects[$CsvObjectStart..$CsvEnd] | ConvertFrom-Csv -Header $colProperty
        }
    }

    #Function used to read objects from config file.
    Function ReadFromIPFile($FileSection)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$FileSection = $FileSection"
        $FileObjects=@()
        $FileObjects = Get-Content $Script:ScriptPath\Saved_StaticIP.txt

        switch($FileSection)
        {
            "AdapterIPSettings" {
                            #Find the section for adapters and add 1 to not include AdapterIPSettingsStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"AdapterIPSettingsStart") + 1
                            #Find the end of the section and remove 1 to not include AdapterIPSettingsEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"AdapterIPSettingsEnd") - 1

                            $colProperty = "Name","Caption","CurrentValue","CurrentValues","PermanentAddress","PCIDeviceID","SlotID"
                            break
                       }
            "TeamIPSettings" {
                            #Find the section for adapters and add 1 to not include TeamIPSettingsStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"TeamIPSettingsStart") + 1
                            #Find the end of the section and remove 1 to not include TeamIPSettingsEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"TeamIPSettingsEnd") - 1

                            $colProperty = "TeamName","Caption","CurrentValue","CurrentValues"
                            break
                       }
            "VlanIPSettings" {
                            #Find the section for adapters and add 1 to not include VlanIPSettingsStart identifier
                            $CsvObjectStart = [array]::indexof($FileObjects,"VlanIPSettingsStart") + 1
                            #Find the end of the section and remove 1 to not include VlanIPSettingsEnd identifier
                            $CsvEnd = [array]::indexof($FileObjects,"VlanIPSettingsEnd") - 1

                            $colProperty = "ParentName","VLANID","Caption","CurrentValue","CurrentValues"
                            break
                       }
             Default {
                        return
                     }
        }

        #If no items were found in the provided section return nothing
        if($CsvEnd -lt $CsvObjectStart)
        {
            return

        }
        #Else return only the provided section and convert the text into objects
        else
        {
            return $FileObjects[$CsvObjectStart..$CsvEnd] | ConvertFrom-Csv -Header $colProperty
        }
    }

    #Add the parent adapter identifiers to the setting objects
    Function AddParentIDs($Setting)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$Setting = $Setting"
        #Add the PermanentAddress, PCIDeviceID and SlotID to the settings object
        $SettingAdapter = $Script:Adapters | where {$_.DeviceID -eq $Setting.ParentId}
        if($SettingAdapter)
        {
            $Setting | Add-Member -Name "PermanentAddress" -Value  $SettingAdapter.PermanentAddress -MemberType NoteProperty
            $Setting | Add-Member -Name "PCIDeviceID" -Value  $SettingAdapter.PCIDeviceID -MemberType NoteProperty
            $AdapterSlotID =  $SettingAdapter.SlotID.split(":")[0..2] #Return only the first three elements in array
            $AdapterBDF = [string]::Join(':',$AdapterSlotID)
            $Setting | Add-Member -Name "SlotID" -Value  $AdapterBDF -MemberType NoteProperty
        }
    }

    Function PrintUsage
    {
        Write-Host $Messages.Usage
        exit
    }

    Function CheckForAdminRights
    {
        $winIdent = [Security.Principal.WindowsIdentity]::GetCurrent()
        $WinPrinc = [Security.Principal.WindowsPrincipal] $winIdent
        $AdminId  = [Security.Principal.WindowsBuiltInRole] "Administrator"
        if(-Not $WinPrinc.IsInRole($AdminId))
        {
            Write-Error $Messages.AdminRequired
            exit
        }
    }

    Function CheckForDMiXInstall
    {
        Write-Log $MyInvocation.MyCommand
        $DMiXInstall = Get-ItemProperty -Path HKLM:\SOFTWARE\Intel\Network_Services\DMIX -Name InstalledDMIX -ErrorAction SilentlyContinue

        if((!$DMiXInstall) -or ($DMiXInstall.InstalledDMIX -ne 1))
        {
            Write-Error $Messages.PROSetRequired
            exit
        }
    }

    Function CheckForLogging
    {
        if ((Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Intel\NETWORK_SERVICES\NCS2).Trace -eq 1) {
            $Script:bWriteLog = $true;
        }
    }

    Function CheckForANSInstall
    {
        Write-Log $MyInvocation.MyCommand
        $ANSInstall = Get-ItemProperty -Path HKLM:\SOFTWARE\Intel\Network_Services\DMIX -Name InstalledDMIX_ANS -ErrorAction SilentlyContinue

        if((!$ANSInstall) -or ($ANSInstall.InstalledDMIX_ANS -ne 1))
        {
            $Script:bANSInstalled = $False
        }
    }

    Function CheckIfConfigFileExists
    {
        Write-Log $MyInvocation.MyCommand
        If(!(Test-Path $Script:ConfigPath -PathType Leaf))
        {
            $ErrorMessagePart1 = [string]$Messages.ConfigNotFoundPart1
            $ErrorMessagePart2 = [string]$Messages.ConfigNotFoundPart2

            Write-Error  "$ErrorMessagePart1 $Script:ConfigPath. $ErrorMessagePart2"
            exit
        }
    }

    Function CheckIfConfigShouldBeOverwritten
    {
        Write-Log $MyInvocation.MyCommand
        if(!(Test-Path $Script:ConfigPath.ToString() -PathType Container))
        {
            $FileExistsString = [string]$Messages.FileExistsPrompt
            $confirmation = Read-Host -Prompt "$Script:ConfigPath $FileExistsString"
            if ($confirmation -notmatch "[yY]")
            {
                exit
            }
        }
    }

    Function Write-Log($string)
    {
        if ($Script:bWriteLog)
        {
            $LogFile = $PSScriptRoot + "\SaveRestore.txt"
            if (!(Test-Path $LogFile))
            {
                New-Item -path $LogFile > $null
            }

            If (Test-Path $LogFile)
            {
                $timestamp = "[$(Get-Date -Format G)] "
                $string = $timestamp + $string
                Add-Content -Path $LogFile -Value $string
            }
        }
    }

    #Sets a global variable for the configuration file paths to be saved/restored from.
    Function SetupSaveRestoreLocation
    {
        Write-Log $MyInvocation.MyCommand
        $Script:ScriptPath = Get-Location

        #If the user did not specify a path, use the scripts path
        if($Script:bUpSave -eq $True -or $Script:bUpRestore -eq $True)
        {
            $Script:ConfigPath = Get-Location
            $Script:ConfigPath = $Script:ConfigPath + "\Upgrade_Saved_Config.txt"
        }
        elseif(!$Script:ConfigPath)
        {
            $Script:ConfigPath = Get-Location
            $Script:ConfigPath = $Script:ConfigPath + "\Saved_Config.txt"

            #Check if the file exist during a save
            If($Script:bSave -eq $True -and (Test-Path $Script:ConfigPath -PathType Leaf))
            {
                CheckIfConfigShouldBeOverwritten
            }
        }
        else
        {
            CheckIfConfigShouldBeOverwritten

            #Find position of the last backslash before the filename
            $FileNamePos = $Script:ConfigPath.ToString().LastIndexOf("\")

            #Separate the filename from the path to verify path exists
            $FilePath = $Script:ConfigPath.ToString().Substring(0,$FileNamePos)

            #Check that config path exists
            if(!(Test-Path $FilePath -PathType Container))
            {
                $ErrorMessagePart1 = [string]$Messages.ConfigPathDoesntExist1
                $ErrorMessagePart2 = [string]$Messages.ConfigPathDoesntExist2
                Write-Error "$ErrorMessagePart1 $FilePath $ErrorMessagePart2"
                exit
            }

            #Check the input of the config file when Saving
            if($Script:bSave -eq $True -or $Script:bUpSave -eq $True)
            {
                Try
                {
                    Out-File $Script:ConfigPath  -ErrorAction Stop
                }
                Catch
                {
                    Write-Error $Messages.ConfigFileNotSpecified
                    exit
                }
            }
            #Check the input of the config file when Restoring
            elseif(!(Test-Path $Script:ConfigPath))
            {
                Write-Error $Messages.ConfigFileNotSpecified
                exit
            }
        }

        Write-Host $Messages.RunningSaveRestore $Script:ConfigPath
    }

    #Get the present and enabled adapters on the system.
    Function GetAdaptersOnSystem
    {
        Write-Log $MyInvocation.MyCommand
        #Only store adapters that have a status of 3 (which means enabled)
        $Script:EnabledSystemAdapters = GetObject "IANet_PhysicalEthernetAdapter" | where {($_.StatusInfo -eq 3)}
        #Only store adapters that support DMiX or Extended DMiX capability or if it is intel vendor capable and this is an upgrade.
        $Script:Adapters = $Script:EnabledSystemAdapters | where {($_.Capabilities -eq 73) -or ($_.Capabilities -eq 74) -or (($_.Capabilities -eq 47) -and (($bUpSave -eq $True) -or ($bUpRestore -eq $True)))}
        #Save Teamable adapters to be referenced for teams
    }

    #Remove any present configuration files
    Function RemoveOldFiles
    {
        Write-Log $MyInvocation.MyCommand
        #check if the file exists before trying to remove it
        If(Test-Path $Script:ConfigPath -PathType Leaf)
        {
            Remove-Item $Script:ConfigPath
        }
        If(Test-Path $Script:ScriptPath\Saved_StaticIP.txt)
        {
            Remove-Item $Script:ScriptPath\Saved_StaticIP.txt
        }
    }

    #Save adapters on system to the configuration file
    Function SaveAdapters
    {
        Write-Log $MyInvocation.MyCommand
        WriteTextToConfigFile "AdaptersStart"

        $colProperty = "Name","OriginalDisplayName","PermanentAddress","PCIDeviceID","SlotID"
        WriteObjectToConfigFile $Script:EnabledSystemAdapters $colProperty

        WriteTextToConfigFile "AdaptersEnd"
        WriteTextToConfigFile ""
    }

    #Save OEM customization value
    Function SaveOEMCustomizeableSetting
    {
        Write-Log $MyInvocation.MyCommand
        WriteTextToConfigFile "OEMSettingStart"
        $OEMSetting = Get-ItemProperty -Path HKLM:\SOFTWARE\Intel\Network_Services\NCS2 -Name OEMCustomizeable -ErrorAction SilentlyContinue

        #If a value is present save it to the config file
        if($OEMSetting -ne $null)
        {
            WriteObjectToConfigFile $OEMSetting OEMCustomizeable
        }

        WriteTextToConfigFile "OEMSettingEnd"
        WriteTextToConfigFile ""
    }

    #Save any legacy power management settings
    Function SavePowerManagementSettings
    {
        Write-Log $MyInvocation.MyCommand
        $colProperty = "Name","Caption","CurrentValue",@{expression={$_.CurrentValues -join ","};label="CurrentValues"},"PermanentAddress","PCIDeviceID","SlotID"

        WriteTextToConfigFile "PMSettingsStart"

        foreach($Adapter in $Script:Adapters)
        {
            try
            {
                #Get the Power Management settings for adapter
                $PowerSettingValues = InvokeMethod $Adapter GetPowerUsageOptions $null $null
                $PowerSettingProperties = @{Name=$Adapter.Name;CurrentValue=$PowerSettingValues.AutoPowerSaveModeEnabled;
                                            CurrentValues={};PermanentAddress=$Adapter.PermanentAddress;PCIDeviceID=$Adapter.PCIDeviceID;
                                            SlotID=$Adapter.SlotID}

                #Check each Power Management setting to see if it NULL before trying to save it to the config file
                #if there is a value, create a custom object and save it to the config file
                if($PowerSettingValues.AutoPowerSaveModeEnabled -ne $null)
                {
                    $Setting = New-Object PSObject -Property $PowerSettingProperties
                    $Setting | Add-Member -Name "Caption" -Value  "AutoPowerSaveModeEnabled" -MemberType NoteProperty
                    WriteObjectToConfigFile $Setting $colProperty
                }
                if($PowerSettingValues.ReduceSpeedOnPowerDown -ne $null)
                {
                    $Setting = New-Object PSObject -Property $PowerSettingProperties
                    $Setting | Add-Member -Name "Caption" -Value  "ReduceSpeedOnPowerDown" -MemberType NoteProperty
                    WriteObjectToConfigFile $Setting $colProperty
                }
                if($PowerSettingValues.SmartPowerDown -ne $null)
                {
                    $Setting = New-Object PSObject -Property $PowerSettingProperties
                    $Setting | Add-Member -Name "Caption" -Value  "SmartPowerDown" -MemberType NoteProperty
                    WriteObjectToConfigFile $Setting $colProperty
                }
                if($PowerSettingValues.SavePowerNowEnabled -ne $null)
                {
                    $Setting = New-Object PSObject -Property $PowerSettingProperties
                    $Setting | Add-Member -Name "Caption" -Value  "SavePowerNowEnabled" -MemberType NoteProperty
                    WriteObjectToConfigFile $Setting $colProperty
                }
                if($PowerSettingValues.EnhancedASPMPowerSaver -ne $null)
                {
                    $Setting = New-Object PSObject -Property $PowerSettingProperties
                    $Setting | Add-Member -Name "Caption" -Value  "EnhancedASPMPowerSaver" -MemberType NoteProperty
                    WriteObjectToConfigFile $Setting $colProperty
                }
            }
            catch
            {
            }
        }

        WriteTextToConfigFile "PMSettingsEnd"
        WriteTextToConfigFile ""
    }

    #Some settings need to be saved in a certain order, this function stops them from being saved now so they can be saved later by returning
    #whether it should be saved immidiately or later (using true or false).
    Function SaveAdapterSettingLater($Setting, $bIPSetting)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$Setting = $Setting"
        Write-Log "`$bIPSetting = $bIPSetting"
        $bRet = $True

        #Don't save the settings now if it is an IP, DCB, performance profile, SRIOV, VMQueues, or NUMVF setting
        if(($bIPSetting -eq $False) -and ($Setting.GroupId -ne 12 ) -and ($Setting.GroupId -ne 8 ) -and ($Setting.Caption -ne "PerformanceProfile") -and
          ($Setting.Caption -ne "*FlowControl") -and ($Setting.Caption -ne "*PriorityVLANTag") -and ($Setting.Caption -ne "*SRIOV") -and ($Setting.Caption -ne "VMQueues") -and ($Setting.Caption -ne "*NumVFs"))
        {
            $bRet = $False
        }

        return $bRet
    }

    #Check if the given setting is an IP setting and save it in the IP config file and return if it is an IP setting (true or false).
    #Depending on the device different properties of the object need to be saved.
    Function SaveIPSetting($Setting,$DeviceType,$bEnabledDHCP,$bSaveIPv6Settings)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$DeviceType = $DeviceType"
        Write-Log "`$Setting = $Setting"
        Write-Log "`$bEnabledDHCP = $bEnabledDHCP"
        Write-Log "`$bSaveIPv6Settings = $bSaveIPv6Settings"
        $bIPSetting = $False
        $bSaveIpSetting = $False

        #Check if the passed in setting is one of these IP settings.
        #Some IP settings need DHCP enabled to restore.
        switch($Setting.Caption)
        {
            "IPAddress" {
                            $bIPSetting=$True
                            if($bEnabledDHCP -eq 0)
                            {
                                $bSaveIpSetting = $True
                            }
                        }
            "IPv4Address" {
                            $bIPSetting=$True
                            if($bEnabledDHCP -eq 0)
                            {
                                $bSaveIpSetting = $True
                            }
                        }
            "IPv6Address" {
                            $bIPSetting=$True
                            if($bSaveIPv6Settings -eq $true)
                            {
                                $bSaveIpSetting = $True
                            }
                        }
            "PrefixLength" {
                            $bIPSetting=$True
                            if($bSaveIPv6Settings -eq $true)
                            {
                                $bSaveIpSetting = $True
                            }
                           }
            "SubnetMask" {
                            $bIPSetting=$True
                            if($bEnabledDHCP -eq 0)
                            {
                                $bSaveIpSetting = $True
                            }
                        }
            "DefaultGateway" {
                            $bIPSetting=$True
                            if($bEnabledDHCP -eq 0)
                            {
                                $bSaveIpSetting = $True
                            }
                        }
            "DefaultGatewayIPv6" {
                                    $bIPSetting=$True
                                    if($bSaveIPv6Settings -eq $true)
                                    {
                                        $bSaveIpSetting = $True
                                    }
                                 }
            "NameServer" {
                            $bIPSetting=$True
                            if($bEnabledDHCP -eq 0)
                            {
                                $bSaveIpSetting = $True
                            }
                        }
            "NameServerList" {
                                 $bIPSetting=$True
                                 $bSaveIpSetting = $True
                             }
            "NetbiosOptions" {
                                 $bIPSetting=$True
                                 $bSaveIpSetting = $True
                             }
            default {}
        }

        #Save IP settings with different properties depending on the device type
        if($bSaveIpSetting -eq $True -and $DeviceType -eq "Adapter")
        {
            $colProperty = "Name","Caption","CurrentValue",@{expression={$_.CurrentValues -join ","};label="CurrentValues"},"PermanentAddress","PCIDeviceID","SlotID"
            WriteObjectToIPFile $Setting $colProperty
        }
        elseif($bSaveIpSetting -eq $True -and $DeviceType -eq "Team")
        {
            $colProperty = "TeamName","Caption","CurrentValue",@{expression={$_.CurrentValues -join ","};label="CurrentValues"}
            WriteObjectToIPFile $Setting $colProperty
        }
        elseif($bSaveIpSetting -eq $True -and $DeviceType -eq "Vlan")
        {
            $colProperty = "ParentName","VLANID","Caption","CurrentValue",@{expression={$_.CurrentValues -join ","};label="CurrentValues"}
            WriteObjectToIPFile $Setting $colProperty
        }

        return $bIPSetting
    }

    #Save the adapter settings
    Function SaveAdapterSettings
    {
        Write-Log $MyInvocation.MyCommand
        $colProperty = "Name","Caption","CurrentValue",@{expression={$_.CurrentValues -join ","};label="CurrentValues"},"PermanentAddress","PCIDeviceID","SlotID"

        #Save power management settings
        SavePowerManagementSettings

        WriteTextToConfigFile "AdapterSettingsStart"
        WriteTextToIPFile "AdapterIPSettingsStart"

        foreach($Adapter in $Script:Adapters)
        {
            #Get the settings associated with the adapter
            $colSettings = GetAssociated $Adapter.path.path "ResultClass = IANet_AdapterSetting"

            #Check status of EnablDHCP for IP settings later
            $bEnableDHCPCol = $colSettings | where {($_.Caption -eq "EnableDHCP")} | select $colProperty
            $bEnableDHCPv6Col = $colSettings | where {($_.Caption -eq "EnableDHCPv6")} | select $colProperty
            $bManualIPv6Col = $colSettings | where {($_.Caption -eq "ManualIPv6")} | select $colProperty

            #Get the DHCP enable value for the specific adapter
            $bEnableDHCP = $bEnableDHCPCol | where {($_.Name -eq $Adapter.Name)}

            #Get the DHCPv6 enable value, and the ManualIPv6 value
            #EnableDHCPv6 is 0 (disabled) only if the "Managed Address Configuration Flag" and the "Other Stateful Configuration Flag" are both 0
            #(see https://blogs.technet.microsoft.com/teamdhcp/2009/03/03/dhcpv6-understanding-of-address-configuration-in-automatic-mode-and-installation-of-dhcpv6-server/)
            #ManualIPv6 is 1 (manual) only if the PrefixOrigin and SuffixOrigin of any IPv6 address are both "Manual" - other values indicate autoconfiguration
            $bEnableDHCPv6 = $bEnableDHCPv6Col | where {($_.Name -eq $Adapter.Name)}
            $bManualIPv6 = $bManualIPv6Col | where {($_.Name -eq $Adapter.Name)}

            $bSaveIPv6Settings = $false
            #if EnableDHCPv6 = 0 and ManualIPv6 = 1, we want to save IPv6 addresses and settings. Otherwise, all IPv6 addresses were autoconfigured and we don't need to save/restore them
            if($bEnableDHCPv6.CurrentValue -eq 0 -and $bManualIPv6.CurrentValue -eq 1)
            {
                $bSaveIPv6Settings = $true
            }

            foreach($Setting in $colSettings)
            {
                AddParentIDs $Setting

                #Get the DHCP enable value for the specific adapter
                $bEnableDHCP = $bEnableDHCPCol | where {($_.Name -eq $Setting.Name)}

                #check if setting is an IP setting save them in the IP config file instead of the Saved_Config file
                $bIPSetting = SaveIPSetting $Setting "Adapter" $bEnableDHCP.CurrentValue $bSaveIPv6Settings

                #Check to see if the setting should be saved later
                $bRet = SaveAdapterSettingLater $Setting $bIPSetting
                if($bRet -eq $False)
                {
                    WriteObjectToConfigFile $Setting $colProperty
                }
            }

            #Check if DCB is being updated and if so, don't save the settings so the default values are restored
            $RestoreDCB = $True
            $FCoEUpdate = Get-ItemProperty -Path HKLM:\SOFTWARE\Intel\Prounstl -Name DCB_Update_FCoE  -ErrorAction SilentlyContinue
            if($FCoEUpdate -ne $null)
            {
                if($FCoEUpdate.DCB_Update_FCoE -eq 1)
                {
                    #FCoE is changing don't save settings
                    $RestoreDCB = $False
                }
            }
            $iSCSIUpdate = Get-ItemProperty -Path HKLM:\SOFTWARE\Intel\Prounstl -Name DCB_Update_iSCSI  -ErrorAction SilentlyContinue
            if($iSCSIUpdate -ne $null)
            {
                if($iSCSIUpdate.DCB_Update_iSCSI -eq 1)
                {
                    #iSCSI is changing don't save settings
                    $RestoreDCB = $False
                }
            }

            #Save *SRIOV after *VMQ
            $SRIOVSetting = $colSettings | where {($_.Caption -eq "*SRIOV")}
            WriteObjectToConfigFile $SRIOVSetting $colProperty

            #Save DCB Settings if this is not an upgrade or if it is an upgrade, and we are modifying DCB
            if(($Script:bUpSave -eq $False) -or ($RestoreDCB -eq $True))
            {
                $colDCBSettings = $colSettings | where {($_.GroupId -eq 12) -or ($_.GroupId -eq 8)}
                WriteObjectToConfigFile $colDCBSettings $colProperty
            }

            #Save the performance profile
            $ProfileSetting = $colSettings | where {($_.Caption -eq "PerformanceProfile")}
            WriteObjectToConfigFile $ProfileSetting $colProperty

            #Save the FlowControl after performance profile and DCB
            $FlowControlSetting = $colSettings | where {($_.Caption -eq "*FlowControl")}
            WriteObjectToConfigFile $FlowControlSetting $colProperty

            #Save the PriorityVLANTag after performance profile and DCB
            $PriorityVLANTagSetting = $colSettings | where {($_.Caption -eq "*PriorityVLANTag")}
            WriteObjectToConfigFile $PriorityVLANTagSetting $colProperty

            #Save VMQueues and *NUMVFs last
            $VMQQueuesSetting = $colSettings | where {($_.Caption -eq "VMQueues")}
            WriteObjectToConfigFile $VMQQueuesSetting $colProperty
            $NumVFsSetting = $colSettings | where {($_.Caption -eq "*NumVFs")}
            WriteObjectToConfigFile $NumVFsSetting $colProperty
        }

        WriteTextToConfigFile "AdapterSettingsEnd"
        WriteTextToConfigFile ""

        WriteTextToIPFile "AdapterIPSettingsEnd"
        WriteTextToIPFile ""
    }

    #Save team information
    Function SaveTeams
    {
        Write-Log $MyInvocation.MyCommand
        #Get current teams on system
        $colProperty = "TeamName","TeamMembers","TeamMode","PrimaryAdapter","SecondaryAdapter"
        $colItems = GetObject "IANet_TeamOfAdapters" | select $colProperty

        #teams are saving in order of last created to first created, and so are being restored in reverse (LIFO)
        #this causes addresses to be restored to the wrong team, so to prevent this we reverse the order of the array to restore teams in their correct order
        if($colItems.TeamName.Count -gt 1)
        {
            [array]::Reverse($colItems)
        }

        WriteTextToConfigFile "TeamsStart"

        #if there are teams on system save the team information to the config file.
        if($colItems)
        {
            #convert Memmber arrays into a "single" value to save
            foreach($item in $colItems)
            {
                $item.TeamMembers = $item.TeamMembers -join '|'
            }

            WriteObjectToConfigFile $colItems *
        }

        WriteTextToConfigFile "TeamsEnd"
        WriteTextToConfigFile ""
    }

    #Save team settings
    Function SaveTeamSettings
    {
        Write-Log $MyInvocation.MyCommand
        #Get the current team settings
        $colProperty = "TeamName","Caption","CurrentValue",@{expression={$_.CurrentValues -join ","};label="CurrentValues"}
        $colSettings = GetObject "IANet_TeamSetting"

        WriteTextToConfigFile "TeamSettingsStart"
        WriteTextToIPFile "TeamIPSettingsStart"

        #Check status of EnablDHCP for IP settings later
        $bEnableDHCPCol = $colSettings | where {($_.Caption -eq "EnableDHCP")} | select $colProperty
        $bEnableDHCPv6Col = $colSettings | where {($_.Caption -eq "EnableDHCPv6")} | select $colProperty
        $bManualIPv6Col = $colSettings | where {($_.Caption -eq "ManualIPv6")} | select $colProperty

        foreach($Setting in $colSettings)
        {
            #Get the DHCP enable value for the specific Team
            $bEnableDHCP = $bEnableDHCPCol | where {($_.TeamName -eq $Setting.Name)}

            #Get the DHCPv6 enable value, and the ManualIPv6 value
            #EnableDHCPv6 is 0 (disabled) only if the "Managed Address Configuration Flag" and the "Other Stateful Configuration Flag" are both 0
            #(see https://blogs.technet.microsoft.com/teamdhcp/2009/03/03/dhcpv6-understanding-of-address-configuration-in-automatic-mode-and-installation-of-dhcpv6-server/)
            #ManualIPv6 is 1 (manual) only if the PrefixOrigin and SuffixOrigin of any IPv6 address are both "Manual" - other values indicate autoconfiguration
            $bEnableDHCPv6 = $bEnableDHCPv6Col | where {($_.TeamName -eq $Setting.Name)}
            $bManualIPv6 = $bManualIPv6Col | where {($_.TeamName -eq $Setting.Name)}

            $bSaveIPv6Settings = $false
            #if EnableDHCPv6 = 0 and ManualIPv6 = 1, we want to save IPv6 addresses and settings. Otherwise, all IPv6 addresses were autoconfigured and we don't need to save/restore them
            if($bEnableDHCPv6.CurrentValue -eq 0 -and $bManualIPv6.CurrentValue -eq 1)
            {
                $bSaveIPv6Settings = $true
            }

            #Save the IP Settings in the IP config file
            $bIPSetting = SaveIPSetting $Setting "Team" $bEnableDHCP.CurrentValue $bSaveIPv6Settings

            if($bIPSetting -eq $False)
            {
                WriteObjectToConfigFile $Setting $colProperty
            }
        }

        WriteTextToConfigFile "TeamSettingsEnd"
        WriteTextToConfigFile ""

        WriteTextToIPFile "TeamIPSettingsEnd"
        WriteTextToIPFile ""
    }

    #Save vlan information
    Function SaveVlans
    {
        Write-Log $MyInvocation.MyCommand
        #Get the vlans on the system
        $colProperty = "ParentName","VLANID","VLANNAME"
        $colVlans = GetObject "IANet_VLAN" | select $colProperty

        #VLANs are saving in order of last created to first created, and so are being restored in reverse (LIFO)
        #this causes addresses to be restored to the wrong VLAN, so to prevent this we reverse the order of the array to restore VLANs in their correct order
        if($colVlans.VLANNAME.Count -gt 1)
        {
            [array]::Reverse($colVlans)
        }

        WriteTextToConfigFile "VlansStart"

        #Save untagged vlan last if there are vlans on the system
        if($colVlans)
        {
            $colTaggedVlans = $colVlans | where {($_.VLANID -ne 0)}
            WriteObjectToConfigFile $colTaggedVlans *
            $colUnTaggedVlans = $colVlans | where {($_.VLANID -eq 0)}
            WriteObjectToConfigFile $colUnTaggedVlans *
        }

        WriteTextToConfigFile "VlansEnd"
        WriteTextToConfigFile ""
    }

    #Save vlan settings
    Function SaveVlanSettings
    {
        Write-Log $MyInvocation.MyCommand
        #Get vlan settings on system
        $colProperty = "ParentName","Name","VLANID","Caption","CurrentValue"
        $colSettings = GetObject "IANet_VLANSetting"

        WriteTextToConfigFile "VlanSettingsStart"
        WriteTextToIPFile "VlanIPSettingsStart"

        #Check status of EnablDHCP for IP settings later
        $bEnableDHCPCol = $colSettings | where {($_.Caption -eq "EnableDHCP")} | select $colProperty
        $bEnableDHCPv6Col = $colSettings | where {($_.Caption -eq "EnableDHCPv6")} | select $colProperty
        $bManualIPv6Col = $colSettings | where {($_.Caption -eq "ManualIPv6")} | select $colProperty

        foreach($Setting in $colSettings)
        {
            #Get the DHCP enable value for the specific adapter
            $bEnableDHCP = $bEnableDHCPCol | where {($_.Name -eq $Setting.Name)}

            #Get the DHCPv6 enable value, and the ManualIPv6 value
            #EnableDHCPv6 is 0 (disabled) only if the "Managed Address Configuration Flag" and the "Other Stateful Configuration Flag" are both 0
            #(see https://blogs.technet.microsoft.com/teamdhcp/2009/03/03/dhcpv6-understanding-of-address-configuration-in-automatic-mode-and-installation-of-dhcpv6-server/)
            #ManualIPv6 is 1 (manual) only if the PrefixOrigin and SuffixOrigin of any IPv6 address are both "Manual" - other values indicate autoconfiguration
            $bEnableDHCPv6 = $bEnableDHCPv6Col | where {($_.Name -eq $Setting.Name)}
            $bManualIPv6 = $bManualIPv6Col | where {($_.Name -eq $Setting.Name)}

            $bSaveIPv6Settings = $false
            #if EnableDHCPv6 = 0 and ManualIPv6 = 1, we want to save IPv6 addresses and settings. Otherwise, all IPv6 addresses were autoconfigured and we don't need to save/restore them
            if($bEnableDHCPv6.CurrentValue -eq 0 -and $bManualIPv6.CurrentValue -eq 1)
            {
                $bSaveIPv6Settings = $true
            }

            #Save the IP Settings in the IP config file
            $bIPSetting = SaveIPSetting $Setting "Vlan" $bEnableDHCP.CurrentValue $bSaveIPv6Settings

            if($bIPSetting -eq $False)
            {
                WriteObjectToConfigFile $Setting $colProperty
            }
        }

        WriteTextToConfigFile "VlanSettingsEnd"
        WriteTextToConfigFile ""

        WriteTextToIPFile "VlanIPSettingsEnd"
        WriteTextToIPFile ""
    }

    #Save NICPART settings
    Function SaveNICPARTSettings
    {
        Write-Log $MyInvocation.MyCommand
        $colProperty = "Name","PartitionNumber","Identifier","MinBWPercent","MaxBWPercent"

        WriteTextToConfigFile "NICPARTSettingsStart"

        #start lock
        BeginApply

        try
        {
            #Get the partition information for all partitions on system and save them
            $PartitionArray = InvokeMethod $Script:service GetPartitionsForPort "szDeviceID" $null
                WriteObjectToConfigFile $PartitionArray.Partitions $colProperty
        }
        catch
        {
        }

        #release lock
        ReleaseLock

        WriteTextToConfigFile "NICPARTSettingsEnd"
        WriteTextToConfigFile ""
    }

    #Remove teams and vlans on system
    Function RemoveTeamsAndVlans
    {
        Write-Log $MyInvocation.MyCommand
        #Get Teams
        $colDevices = @()
        $colDevices += GetObject "IANet_TeamOfAdapters"

        BeginApply

        #Remove Teams in array
        foreach($Device in $colDevices)
        {
            try
            {
                DeleteObject $Device
            }
            catch
            {
            }
        }

        ReleaseLock

        #Get VLANs
        $colDevices = @()
        $colDevices += GetObject "IANet_Vlan"

        BeginApply

        #Remove VLANs in array
        foreach($Device in $colDevices)
        {
            try
            {
                DeleteObject $Device
            }
            catch
            {
            }
        }

        ReleaseLock
    }

    #Check that adapters in configuration file are present and enabled on the system.
    Function CheckAdaptersExist
    {
        Write-Log $MyInvocation.MyCommand
        $Script:colImportAdapters  = ReadFromConfigFile "Adapters"
        if($Script:colImportAdapters -ne $null)
        {
            foreach($Adapter in $Script:colImportAdapters)
            {
                #Find the matching file adapter using the appropriate property to compare name,address or ID
                if($Script:BDF -eq $False)
                {
                    $ImportedAdapter = $Script:EnabledSystemAdapters | where {($_.Name -eq $Adapter.Name)}
                    if(($null -eq $ImportedAdapter) -and ($bUpRestore -eq $True))
                    {
                        $ImportedAdapter = $Script:EnabledSystemAdapters | where {($_.PermanentAddress -eq $Adapter.PermanentAddress)}
                    }
                    elseif($null -eq $ImportedAdapter)
                    {
                        $ImportedAdapter = $Script:EnabledSystemAdapters | where {($_.PCIDeviceID -eq $Adapter.PCIDeviceID)}
                    }
                }
                else
                {
                    $ImportedAdapter = $Script:EnabledSystemAdapters | where {($_.SlotID -eq $Adapter.SlotID)}
                }

                if($null -eq $ImportedAdapter)
                {
                    Write-warning "Invalid device found. Please verify configuration file matches your system configuration."
                }
            }
        }
    }

    #Restore custome OEM value
    Function RestoreOEMCustomizeableSetting
    {
        Write-Log $MyInvocation.MyCommand
        $ImportOEMSetting = ReadFromConfigFile "OEMSetting"
        if($ImportOEMSetting -ne $null)
        {
            Write-Host $Messages.SettingOEMValue
            Set-ItemProperty -Path HKLM:\SOFTWARE\Intel\Network_Services\NCS2 -Name OEMCustomizeable -Value $ImportOEMSetting.OEMCustomizeable -Type DWord
        }
    }

    #Restore adapter settings
    Function RestoreAdapterSettings($colImportedSettings)
    {
        Write-Log $($MyInvocation.MyCommand)
        Write-Log "`$colImportedSettings = $colImportedSettings"
        #Get adapter settings on system
        $colSetting = GetObject "IANet_AdapterSetting"
        #Add parent identifiers to settings on system
        foreach($Setting in $colSetting)
        {
            AddParentIDs $Setting
        }

        #start lock
        BeginApply

        #Loop through the passed in settings from the config file
        foreach($ImportedSetting in $colImportedSettings) #for each setting in AdapterIPSettings
        {
            try
            {
                #Find the matching system setting using the appropriate property to compare name,address or ID
                if($Script:BDF -eq $False)
                {
                    #finds the setting with the same caption and device name
                    $Setting = $colSetting | where {($_.Name -eq $ImportedSetting.Name) -and ($_.Caption -eq $ImportedSetting.Caption)}
                    if(($null -eq $Setting) -and ($bUpRestore -eq $True))
                    {
                        $Setting = $colSetting | where {($_.PermanentAddress -eq $ImportedSetting.PermanentAddress) -and ($_.Caption -eq $ImportedSetting.Caption)}
                    }
                    elseif($null -eq $Setting)
                    {
                        $Setting = $colSetting | where {($_.PCIDeviceID -eq $ImportedSetting.PCIDeviceID) -and ($_.Caption -eq $ImportedSetting.Caption)}
                    }
                }
                else
                {
                    $Setting = $colSetting | where {($_.SlotID -eq $ImportedSetting.SlotID) -and ($_.Caption -eq $ImportedSetting.Caption)}
                }

                #If the setting in the file can't be found on the system, continue to the next setting
                if($Setting -eq $null)
                {
                    continue;
                }

                #See if 'CurrentValues' is a property over the system setting, if so set that value from the config file setting
                if(($Setting.Properties | select -ExpandProperty Name) -contains "CurrentValues")
                {
                    #If no value was present from the file setting, then set it to empty
                    if($ImportedSetting.CurrentValues -eq $null)
                    {
                        $Setting.CurrentValues = {}
                    }
                    else
                    {
                        $Setting.CurrentValues = $ImportedSetting.CurrentValues.Split(",")
                    }
                }
                else
                {
                    $Setting.CurrentValue = $ImportedSetting.CurrentValue
                }

                #IPv6 Address and DefaultGateway must be set with netsh, currently no API to set setting in Middleware
                if($Setting.Caption -eq "IPv6Address")
                {
                    #netsh interface ipv6 add address $ConnectionName $Address
                    $ConnectionName = Get-WmiObject IANet_AdapterSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $ImportedSetting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                    $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex


                    #delete current IPv6 addresses to overwrite them with saved config, if there are no addresses then we catch the exception and continue since there is nothing to delete.
                    try{
                    $AllAddresses = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "root/cimv2" | where {($_.InterfaceIndex -eq $ConnectionInterfaceIndex)} | select -ExpandProperty IPAddress -ErrorAction SilentlyContinue
                    } catch {}

                    $IPv6Array = @()
                    foreach($Address in $AllAddresses){
                        if(-not $Address.Contains(".")){
                            $IPv6Array += $Address
                        }
                    }

                    #wrap interfaceindex in quotes for netsh
                    $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                    foreach($IP in $IPv6Array){
                        $DeleteCommand = "netsh interface ipv6 delete address $ConnectionInterfaceIndex $IP"
                        Invoke-Expression $DeleteCommand
                    }


                    foreach($Value in $Setting.CurrentValues) #CurrentValues is a list of IPv6Addresses split by comma, set each address
                    {
                        $Command = "netsh interface ipv6 add address $ConnectionInterfaceIndex $Value"
                        Invoke-Expression $Command
                    }
                }

                elseif($Setting.Caption -eq "DefaultGatewayIPv6")
                {
                    #netsh interface ipv6 add route ::/0 $ConnectionName $Address
                    $ConnectionName = Get-WmiObject IANet_AdapterSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $ImportedSetting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                    $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex

                    #delete current IPv6 gateways to overwrite them with saved config, if there are no gateways then we catch the exception and continue since there is nothing to delete.
                    try{
                    $AllGateways = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "root/cimv2" | where {($_.InterfaceIndex -eq $ConnectionInterfaceIndex)} | select -ExpandProperty DefaultIPGateway -ErrorAction SilentlyContinue
                    } catch {}
                    $IPv6Array = @()
                    foreach($Address in $AllGateways){
                        if(-not $Address.Contains(".")){
                            $IPv6Array += $Address
                        }
                    }

                    #wrap interfaceindex in quotes for netsh
                    $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""


                    foreach($IP in $IPv6Array){
                        $DeleteCommand = "netsh interface ipv6 delete route ::/0 $ConnectionInterfaceIndex $IP"
                        Invoke-Expression $DeleteCommand
                    }

                    foreach($Value in $Setting.CurrentValues)
                    {
                        $Command = "netsh interface ipv6 add route ::/0 $ConnectionInterfaceIndex $Value"
                        Invoke-Expression $Command
                    }


                }

                elseif($Setting.Caption -eq "EnableDHCPv6")
                {
                    $EnableDHCPv6Setting = Get-WmiObject IANet_AdapterSettingEnum -Namepace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "EnableDHCPv6")} | select -ExpandProperty CurrentValue

                    if(($Setting.CurrentValue -eq "0") -and ($EnableDHCPv6Setting.CurrentValue -eq "1"))
                    {
                        $ConnectionName = Get-WmiObject IANet_AdapterSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                        $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex

                        $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                        $dhcpv6routercmd = "netsh int ipv6 set int $ConnectionInterfaceIndex routerdiscovery=disabled managedaddress=disabled otherstateful=disabled"

                        Invoke-Expression $dhcpv6routercmd
                        Start-Sleep -s 1
                    }

                    elseif(($Setting.CurrentValue -eq "1") -and ($EnableDHCPv6Setting.CurrentValue -eq "0"))
                    {
                        $ConnectionName = Get-WmiObject IANet_AdapterSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                        $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex

                        $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                        $dhcpv6managedcmd = "netsh int ipv6 set int $ConnectionInterfaceIndex managedaddress=enabled"

                        Invoke-Expression $dhcpv6managedcmd
                        Start-Sleep -s 1
                    }
                }

                else
                {
                    SetSetting $Setting
                }
            }
            catch
            {
                $ErrorMessagePart1 = [string]$Messages.UnableToSetPart1
                $ErrorMessagePart2 = [string]$Messages.UnableToSetPart2
                # Unable to set ... on ...
                Write-Verbose "$ErrorMessagePart1 $($Setting.Caption) $ErrorMessagePart2 $($Setting.Name)"
            }
        }

        #release lock
        ReleaseLock
    }

    #Set the primary and secondary adapters on the team
    Function RestorePrimaryAndSecondary($Team)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$Team = $Team"
        Write-Host $Messages.RestoringTeamAdapters $Team.TeamName

        #Find the adapter objects from the configuration file that match the team primary and secondary adapter names
        $PrimaryImportAdapter = $Script:colImportAdapters | where {$_.OriginalDisplayName -eq $Team.PrimaryAdapter}
        $SecondaryImportAdapter = $Script:colImportAdapters | where {$_.OriginalDisplayName -eq $Team.SecondaryAdapter}

        #Find the matching system adapter using the appropriate property to compare name,address or ID
        if($Script:BDF -eq $False)
        {
            $PrimaryAdapter = $Script:EnabledSystemAdapters | where {$_.Name -eq $PrimaryImportAdapter.OriginalDisplayName}
            if(($null -eq $PrimaryAdapter) -and ($bUpRestore -eq $True))
            {
                $PrimaryAdapter = $Script:EnabledSystemAdapters | where {($_.PermanentAddress -eq $PrimaryImportAdapter.PermanentAddress)}
            }
            elseif($null -eq $PrimaryAdapter)
            {
                $PrimaryAdapter = $Script:EnabledSystemAdapters | where {($_.PCIDeviceID -eq $PrimaryImportAdapter.PCIDeviceID)}
            }
        }
        else
        {
            $PrimaryAdapter = $Script:EnabledSystemAdapters | where {($_.SlotID -eq $PrimaryImportAdapter.SlotID)}
        }
        if($Script:BDF -eq $False)
        {
            $SecondaryAdapter = $Script:EnabledSystemAdapters | where {$_.Name -eq $SecondaryImportAdapter.OriginalDisplayName}
            if(($null -eq $PrimaryAdapter) -and ($bUpRestore -eq $True))
            {
                $SecondaryAdapter = $Script:EnabledSystemAdapters | where {($_.PermanentAddress -eq $SecondaryImportAdapter.PermanentAddress)}
            }
            elseif($null -eq $PrimaryAdapter)
            {
                $SecondaryAdapter = $Script:EnabledSystemAdapters | where {($_.PCIDeviceID -eq $SecondaryImportAdapter.PCIDeviceID)}
            }
        }
        else
        {
            $SecondaryAdapter = $Script:EnabledSystemAdapters | where {($_.SlotID -eq $SecondaryImportAdapter.SlotID)}
        }

        #Set the primary and secondary adapters on the team
        $Team = GetObject "IANet_TeamOfAdapters" | where {$_.TeamName -eq $Team.TeamName}
        $param = $Team.psbase.GetMethodParameters("ValidateAndSetAdapterPriority")
        $param.PrimaryAdapter = $PrimaryAdapter
        $param.SecondaryAdapter = $SecondaryAdapter
        $param.SetValuesOnNoError = 2
        $value = $Team.psbase.InvokeMethod("ValidateAndSetAdapterPriority", $param, $invokeoptions)
    }

    #Restore teams from configuration file
    Function RestoreTeams
    {
        Write-Log $MyInvocation.MyCommand
        #Get the team objects from the configuration file
        $colImportTeams = ReadFromConfigFile "Teams"

        if($colImportTeams -ne $null)
        {
            BeginApply

            foreach($Team in $colImportTeams)
            {
                Write-Host $Messages.Restoring $Team.TeamName

                try
                {
                    #Create a new team object
                    $classdef = NewObject root\intelncs2:IANet_TeamOfAdapters

                    #fill in the CreateTeam's method parameters with the new object
                    $param = $classdef.psbase.GetMethodParameters("CreateTeam2Ex")

                    $TeamedAdapters = $Team.TeamMembers.split("|")
                    $param.Adapters = @()

                    $bCreateTeam = $True
                    #Set the teammembers for creation
                    foreach($AdapterName in $TeamedAdapters)
                    {
                        $ImportedAdapter = $Script:colImportAdapters | where {$_.OriginalDisplayName -eq $AdapterName}
                        $Adapter = $null

                        if($null -ne $ImportedAdapter)
                        {
                            #Find the matching system adapter using the appropriate property to compare name,address or ID
                            if($Script:BDF -eq $False)
                            {
                                $Adapter = $Script:EnabledSystemAdapters | where {($_.Name -eq $ImportedAdapter.OriginalDisplayName)}
                                if(($null -eq $Adapter) -and ($bUpRestore -eq $True))
                                {
                                    $Adapter = $Script:EnabledSystemAdapters | where {($_.PermanentAddress -eq $ImportedAdapter.PermanentAddress)}
                                }
                                elseif($null -eq $Adapter)
                                {
                                    $Adapter = $Script:EnabledSystemAdapters | where {($_.PCIDeviceID -eq $ImportedAdapter.PCIDeviceID)}
                                }
                            }
                            else
                            {
                                $Adapter = $Script:EnabledSystemAdapters | where {($_.SlotID -eq $ImportedAdapter.SlotID)}
                            }
                        }


                        #If a teammember can't be found output an error and breakout of team creation
                        if($null -eq $Adapter)
                        {
                            Write-warning "Invalid device found. Please verify configuration file matches your system configuration."
                            $bCreateTeam = $False
                            break
                        }
                        $param.Adapters += $Adapter
                    }

                    #If an error was found, don't try and create the team
                    if($bCreateTeam -eq $False)
                    {
                        Continue
                    }

                    $param.TeamMode = $Team.TeamMode
                    $param.TeamName = $Team.TeamName

                    #the invoke options
                    $invokeoptions = New-Object System.Management.InvokeMethodOptions
                    $invokeoptions.Context = $Script:context

                    #call the CreateTeam method to create the new team
                    $value = $classdef.psbase.InvokeMethod("CreateTeam2Ex", $param, $invokeoptions)

                    #Set primary and secondary adapters
                    RestorePrimaryAndSecondary $Team
                }
                catch
                {
                }
            }

            ReleaseLock
        }
    }

    #Restore team settings
    Function RestoreTeamSettings($colImportSettings)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$colImportSettings = $colImportSettings"
        #Get the current team settings
        $colSetting = GetObject "IANet_TeamSetting"

        #start lock
        BeginApply

        foreach($Setting in $colSetting)
        {
            try
            {
                #If the current setting using 'CurrentValues' then set that value.
                if(($Setting.Properties | select -ExpandProperty Name) -contains "CurrentValues")
                {
                    $ValueObject = $colImportSettings | where {($_.TeamName -eq $Setting.Name) -and ($_.Caption -eq $Setting.Caption)} | select CurrentValues
                    if($ValueObject.CurrentValues -eq $null)
                    {
                        $Setting.CurrentValues = {}
                    }
                    else
                    {
                        $Setting.CurrentValues = $ValueObject.CurrentValues.Split(",")
                    }
                }
                else
                {
                    $ValueObject = $colImportSettings | where {($_.TeamName -eq $Setting.Name) -and ($_.Caption -eq $Setting.Caption)} | select CurrentValue
                    $Setting.CurrentValue = $ValueObject.CurrentValue
                }

                #If a matching system setting was found set it to the restore value
                if($ValueObject -ne $null)
                {
                    if($Setting.Caption -eq "IPv6Address")
                    {
                        #netsh interface ipv6 add address $ConnectionName $Address
                        $ConnectionName = Get-WmiObject IANet_TeamSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                        $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex


                        #delete current IPv6 addresses to overwrite them with saved config, if there are no addresses then we catch the exception and continue since there is nothing to delete.
                        try{
                        $AllAddresses = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "root/cimv2" | where {($_.InterfaceIndex -eq $ConnectionInterfaceIndex)} | select -ExpandProperty IPAddress -ErrorAction SilentlyContinue
                        } catch {}

                        $IPv6Array = @()
                        foreach($Address in $AllAddresses){
                            if(-not $Address.Contains(".")){
                                $IPv6Array += $Address
                            }
                        }

                        #wrap interfaceindex in quotes for netsh
                        $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                        foreach($IP in $IPv6Array){
                            $DeleteCommand = "netsh interface ipv6 delete address $ConnectionInterfaceIndex $IP"
                            Invoke-Expression $DeleteCommand
                        }

                        foreach($Value in $Setting.CurrentValues) #CurrentValues is a list of IPv6Addresses split by comma, set each address
                        {
                            $Command = "netsh interface ipv6 add address $ConnectionInterfaceIndex $Value"
                            Invoke-Expression $Command

                        }
                    }

                    elseif($Setting.Caption -eq "DefaultGatewayIPv6")
                    {
                        #netsh interface ipv6 add route ::/0 $ConnectionName $Address
                        $ConnectionName = Get-WmiObject IANet_TeamSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue


                        $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex



                        #delete current IPv6 gateways to overwrite them with saved config,  if there are no gateways then we catch the exception and continue since there is nothing to delete.

                        try{
                        $AllGateways = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "root/cimv2" | where {($_.InterfaceIndex -eq $ConnectionInterfaceIndex)} | select -ExpandProperty DefaultIPGateway -ErrorAction SilentlyContinue
                        } catch {}

                        $IPv6Array = @()
                        foreach($Address in $AllGateways){
                            if(-not $Address.Contains(".")){
                                $IPv6Array += $Address
                            }
                        }

                        #wrap interfaceindex in quotes for netsh
                        $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""


                        foreach($IP in $IPv6Array){
                            $DeleteCommand = "netsh interface ipv6 delete route ::/0 $ConnectionInterfaceIndex $IP"
                            Invoke-Expression $DeleteCommand
                        }

                        foreach($Value in $Setting.CurrentValues)
                        {
                            $Command = "netsh interface ipv6 add route ::/0 $ConnectionInterfaceIndex $Value"
                            Invoke-Expression $Command
                        }
                    }

                    elseif($Setting.Caption -eq "EnableDHCPv6")
                    {
                        $EnableDHCPv6Setting = Get-WmiObject IANet_TeamSettingEnum -Namepace "root/intelncs2" | where {($_.Name -eq $Setting.TeamName) -and ($_.Caption -eq "EnableDHCPv6")} | select -ExpandProperty CurrentValue

                        if(($Setting.CurrentValue -eq "0") -and ($EnableDHCPv6Setting.CurrentValue -eq "1"))
                        {
                            $ConnectionName = Get-WmiObject IANet_TeamSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.TeamName) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                            $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex

                            $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                            $dhcpv6routercmd = "netsh int ipv6 set int $ConnectionInterfaceIndex routerdiscovery=disabled managedaddress=disabled otherstateful=disabled"

                            Invoke-Expression $dhcpv6routercmd
                            Start-Sleep -s 1
                        }

                        elseif(($Setting.CurrentValue -eq "1") -and ($EnableDHCPv6Setting.CurrentValue -eq "0"))
                        {
                            $ConnectionName = Get-WmiObject IANet_TeamSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.TeamName) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                            $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex

                            $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                            $dhcpv6managedcmd = "netsh int ipv6 set int $ConnectionInterfaceIndex managedaddress=enabled"

                            Invoke-Expression $dhcpv6managedcmd
                            Start-Sleep -s 1
                        }
                    }

                    else
                    {
                        #set setting
                        SetSetting $Setting
                    }
                }
            }
            catch
            {
                $ErrorMessagePart1 = [string]$Messages.UnableToSetPart1
                $ErrorMessagePart2 = [string]$Messages.UnableToSetPart2
                # Unable to set ... on ...
                Write-Verbose "$ErrorMessagePart1 $($Setting.Caption) $ErrorMessagePart2 $($Setting.Name)"
            }
        }

        #release lock
        ReleaseLock
    }

    #Restore vlans
    Function RestoreVlans
    {
        Write-Log $MyInvocation.MyCommand
        #Get vlan objects from config file
        $colImportVlans = ReadFromConfigFile "Vlans"

        if($colImportVlans -ne $null)
        {

            foreach($Vlan in $colImportVlans)
            {
                BeginApply

                try
                {
                    #Check needs to be created on an adapter first
                    $ImportedAdapter = $Script:colImportAdapters | where {$_.Name -eq $Vlan.ParentName}
                    $Adapter = $null
                    $VlanCreator = $null

                    if($null -ne $ImportedAdapter)
                    {
                        #Find the matching system adapter using the appropriate property to compare name,address or ID
                        if($Script:BDF -eq $False)
                        {
                            $Adapter = $Script:Adapters | where {($_.Name -eq $ImportedAdapter.Name)}
                            if(($null -eq $Adapter) -and ($bUpRestore -eq $True))
                            {
                                $Adapter = $Script:Adapters | where {($_.PermanentAddress -eq $ImportedAdapter.PermanentAddress)}
                            }
                            elseif($null -eq $Adapter)
                            {
                                $Adapter = $Script:Adapters | where {($_.PCIDeviceID -eq $ImportedAdapter.PCIDeviceID)}
                            }
                        }
                        else
                        {
                            $Adapter = $Script:Adapters | where {($_.SlotID -eq $ImportedAdapter.SlotID)}
                        }
                    }

                    if($Adapter -ne $null)
                    {
                        #Get the vlan creation class associated with adapter parent object
                        $VlanCreator = GetAssociated $Adapter.path.path "ResultClass = IANet_802dot1QVLANService" | where {1}
                    }
                    #Check if vlan needs to be created on a team
                    else
                    {
                        #Get the logical ethernet adapter object for the team parent
                        $Team = GetObject "IANet_LogicalEthernetAdapter" | where {( NScompare $_.Name $Vlan.ParentName )}

                        if($Team -ne $null)
                        {
                            #Get the vlan creation class associated with team parent object
                            $VlanCreator = GetAssociated $Team.path.path "ResultClass = IANet_802dot1QVLANService" | where {1}
                        }
                    }

                    #If the vlan creation class was found continue on to create the vlan
                    if($VlanCreator -ne $null)
                    {
                        Write-Host $Messages.Restoring $Vlan.VlanName

                        #fill in the CreateVlan's method parameters
                        $param = $VlanCreator.psbase.GetMethodParameters("CreateVlan")

                        $param.Name = $Vlan.VLANNAME
                        $param.VLANNumber = $Vlan.VLANID

                        #the invoke options
                        $invokeoptions = New-Object System.Management.InvokeMethodOptions
                        $invokeoptions.Context = $Script:context

                        #call the CreateVlan method to create the new vlan
                        $value = $VlanCreator.psbase.InvokeMethod("CreateVlan", $param, $invokeoptions)
                    }
                }
                catch
                {
                }
                ReleaseLock
            }
        }
    }

    #Restore vlan settings
    Function RestoreVlanSettings($colImportSettings)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "`$colImportSettings = $colImportSettings"
        #Get the current Vlan settings
        $colSetting = GetObject "IANet_VLANSetting"

        #start lock
        BeginApply

        foreach($Setting in $colSetting)
        {
            try
            {
                #If the current setting using 'CurrentValues' then set that value.
                if(($Setting.Properties | select -ExpandProperty Name) -contains "CurrentValues")
                {
                    $ValueObject = $colImportSettings | where {( NSCompare $_.ParentName $Setting.ParentName ) -and ( $_.Caption -eq $Setting.Caption) -and ($_.VLANID -eq $Setting.VLANID)} | select CurrentValues
                    if($ValueObject.CurrentValues -eq $null)
                    {
                        $Setting.CurrentValues = {}
                    }
                    else
                    {
                        $Setting.CurrentValues = $ValueObject.CurrentValues.Split(",")
                    }
                }
                else
                {
                    $ValueObject = $colImportSettings | where {( NSCompare $_.ParentName $Setting.ParentName) -and ($_.Caption -eq $Setting.Caption) -and ($_.VLANID -eq $Setting.VLANID)} | select CurrentValue
                    $Setting.CurrentValue = $ValueObject.CurrentValue
                }

                #If a matching system setting was found set it to the restore value
                if($ValueObject -ne $null)
                {
                 if($Setting.Caption -eq "IPv6Address")
                    {
                        #netsh interface ipv6 add address $ConnectionName $Address
                        $ConnectionName = Get-WmiObject IANet_VLANSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                        $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex


                        #delete current IPv6 addresses to overwrite them with saved config, if there are no addresses then we catch the exception and continue since there is nothing to delete.
                        try{
                        $AllAddresses = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "root/cimv2" | where {($_.InterfaceIndex -eq $ConnectionInterfaceIndex)} | select -ExpandProperty IPAddress -ErrorAction SilentlyContinue
                        } catch {}

                        $IPv6Array = @()
                        foreach($Address in $AllAddresses){
                            if(-not $Address.Contains(".")){
                                $IPv6Array += $Address
                            }
                        }

                        #wrap interfaceindex in quotes for netsh
                        $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                        foreach($IP in $IPv6Array){
                            $DeleteCommand = "netsh interface ipv6 delete address $ConnectionInterfaceIndex $IP"
                            Invoke-Expression $DeleteCommand
                        }

                        foreach($Value in $Setting.CurrentValues) #CurrentValues is a list of IPv6Addresses split by comma, set each address
                        {
                            $Command = "netsh interface ipv6 add address $ConnectionInterfaceIndex $Value"
                            Invoke-Expression $Command
                        }
                    }

                    elseif($Setting.Caption -eq "DefaultGatewayIPv6")
                    {
                        #netsh interface ipv6 add route ::/0 $ConnectionName $Address
                        $ConnectionName = Get-WmiObject IANet_VLANSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                        $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex

                        #delete current IPv6 gateways to overwrite them with saved config, if there are no gateways then we catch the exception and continue since there is nothing to delete.
                        try{
                        $AllGateways = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "root/cimv2" | where {($_.InterfaceIndex -eq $ConnectionInterfaceIndex)} | select -ExpandProperty DefaultIPGateway -ErrorAction SilentlyContinue
                        } catch {}

                        $IPv6Array = @()
                        foreach($Address in $AllGateways){
                            if(-not $Address.Contains(".")){
                                $IPv6Array += $Address
                            }
                        }

                        #wrap interfaceindex in quotes for netsh
                        $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                        foreach($IP in $IPv6Array){
                            $DeleteCommand = "netsh interface ipv6 delete route ::/0 $ConnectionInterfaceIndex $IP"
                            Invoke-Expression $DeleteCommand
                        }

                        foreach($Value in $Setting.CurrentValues)
                        {
                            $Command = "netsh interface ipv6 add route ::/0 $ConnectionInterfaceIndex $Value"
                            Invoke-Expression $Command
                        }
                    }

                    elseif($Setting.Caption -eq "EnableDHCPv6")
                    {
                        $EnableDHCPv6Setting = Get-WmiObject IANet_VLANSettingEnum -Namepace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "EnableDHCPv6")} | select -ExpandProperty CurrentValue

                        if(($Setting.CurrentValue -eq "0") -and ($EnableDHCPv6Setting.CurrentValue -eq "1"))
                        {
                            $ConnectionName = Get-WmiObject IANet_VLANSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                            $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex

                            $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                            $dhcpv6routercmd = "netsh int ipv6 set int $ConnectionInterfaceIndex routerdiscovery=disabled managedaddress=disabled otherstateful=disabled"

                            Invoke-Expression $dhcpv6routercmd
                            Start-Sleep -s 1
                        }

                        elseif(($Setting.CurrentValue -eq "1") -and ($EnableDHCPv6Setting.CurrentValue -eq "0"))
                        {
                            $ConnectionName = Get-WmiObject IANet_VLANSettingString -Namespace "root/intelncs2" | where {($_.Name -eq $Setting.Name) -and ($_.Caption -eq "ConnectionName")} | select -ExpandProperty CurrentValue
                            $ConnectionInterfaceIndex = Get-WmiObject Win32_NetworkAdapter -Namespace "root/cimv2" | where {($_.NetConnectionID -eq $ConnectionName)} | select -ExpandProperty InterfaceIndex

                            $ConnectionInterfaceIndex = "`"$($ConnectionInterfaceIndex)`""

                            $dhcpv6managedcmd = "netsh int ipv6 set int $ConnectionInterfaceIndex managedaddress=enabled"

                            Invoke-Expression $dhcpv6managedcmd
                            Start-Sleep -s 1
                        }
                    }

                    else
                    {
                        #set setting
                        SetSetting $Setting
                    }
                }
            }
            catch
            {
                $ErrorMessagePart1 = [string]$Messages.UnableToSetPart1
                $ErrorMessagePart2 = [string]$Messages.UnableToSetPart2
                # Unable to set ... on ...
                Write-Verbose "$ErrorMessagePart1 $($Setting.Caption) $ErrorMessagePart2 $($Setting.Name)"
            }
        }

        #release lock
        ReleaseLock
    }

    #Restore the IP settings from the IP config file
    Function RestoreIPSettings
    {
        Write-Log $MyInvocation.MyCommand
        #Restore Adapter IP settings from the IP config file
        $colImportSettings = ReadFromIPFile "AdapterIPSettings"
        if($colImportSettings -ne $null)
        {
            Write-Host $Messages.RestoringAdapterIP
            RestoreAdapterSettings $colImportSettings
        }

        if($Script:bANSInstalled)
        {
            #Restore Team IP settings from the IP config file
            $colImportSettings = ReadFromIPFile "TeamIPSettings"
            if($colImportSettings -ne $null)
            {
                Write-Host $Messages.RestoringTeamIP
                RestoreTeamSettings $colImportSettings $colSetting
            }

            #Restore Vlan IP settings from the IP config file
            $colImportSettings = ReadFromIPFile "VlanIPSettings"
            if($colImportSettings -ne $null)
            {
                Write-Host $Messages.RestoringVLANIP
                RestoreVlanSettings $colImportSettings
            }
        }
    }

    #Restore devices and settings from configuration files
    Function RestoreAdvancedSettingsAndDevices
    {
        Write-Log $MyInvocation.MyCommand
        #Restore Adapter avanced settings from config file
        $colImportSettings = ReadFromConfigFile "AdapterSettings"
        if($colImportSettings -ne $null)
        {
            Write-Host $Messages.RestoringAdapterSettings
            RestoreAdapterSettings $colImportSettings
        }

        if(!$Script:bANSInstalled)
        {
            Write-warning "Intel(R) ANS is not present on the system. Any Team and VLAN information contained in the configuration file will not be restored."
        }
        else
        {
            #Restore Teams
            RestoreTeams

            #Restore Team advanced settings from config file
            $colImportSettings = ReadFromConfigFile "TeamSettings"
            if($colImportSettings -ne $null)
            {
                Write-Host $Messages.RestoringTeamSettings
                RestoreTeamSettings $colImportSettings
            }

            #Restore Vlans
            RestoreVlans

            #Restore Vlan avanced settings from config file
            $colImportSettings = ReadFromConfigFile "VlanSettings"
            if($colImportSettings -ne $null)
            {
                Write-Host $Messages.RestoringVLANSettings
                RestoreVlanSettings $colImportSettings
            }
        }

        if(Test-Path $Script:ScriptPath\Saved_StaticIP.txt)
        {
            #Restore IP Settings from IP config file
            RestoreIPSettings
        }
    }

    #Restore NICPART settings
    Function RestoreNICPARTSettings
    {
        Write-Log $MyInvocation.MyCommand
        #Restore NICPART settings from config file
        $colImportPartitions = ReadFromConfigFile "NICPARTSettings"
        Write-Host $Messages.RestoringAdapterPartition

        BeginApply

        try
        {
            if($colImportPartitions -ne $null)
            {
                #Get the partition information for all partitions on system and save them
                $PartitionObject = InvokeMethod $Script:service GetPartitionsForPort "szDeviceID" $null
                $PartitionArray = $PartitionObject.Partitions


                foreach($Partition in $PartitionArray)
                {
                    $ImportPartition = $colImportPartitions | where {($_.PartitionNumber -eq $Partition.PartitionNumber) -and ($_.Identifier -eq $Partition.Identifier)}

                    if($ImportPartition -ne $null)
                    {
                        if($Script:BDF -eq $True)
                        {
                            $Partition.Name = "IGNORE"
                            $Partition.DeviceGuid = "IGNORE"
                        }
                        else
                        {
                            $Partition.Identifier = "IGNORE"
                            $Partition.DeviceGuid = "IGNORE"
                        }

                        $Partition.MinBWPercent = $ImportPartition.MinBWPercent
                        $Partition.MaxBWPercent = $ImportPartition.MaxBWPercent
                    }
                }

                $Output = InvokeMethod $Script:service ValidateAndSetBAndwidthsForPort "Partitions","SetValuesOnNoError" $PartitionArray,"1"
            }
        }
        catch
        {
        }

        ReleaseLock
    }


    <#
    .SYNOPSIS
        Make sure users know about BDF parameter when using NICPART enabled adapters
    .DESCRIPTION
        By default, the save restore script uses the adapter's friendly name to match the name of the adapter
        in the configuration file to adapter in the system.  Because of the way Windows enumerates devices and
        assigns friendly names, the name of the adapter saved in the configuration file may not be the exact
        same adapter/port/partition on another system. The /bdf command line option should be used when
        restoring settings of NICPART enabled devices on multiple systems.
    .NOTES
        This must be invoked after GetAdaptersOnSystem - as it relies on the contents of $Script:Adapters.
        The global variable is used to save time when restoring settings on user systems (to avoid polling
        the system again just to get the same data).
    #>
    function CheckNICPART_BDFWarning
    {
        Write-Log $MyInvocation.MyCommand
        $NICPART = $Script:Adapters | where {($_.Capabilities -eq 76)}
        if ($NICPART -and $Script:BDF -eq $false)
        {
            Write-Host $Messages.NICPartitioningDetected
        }

    }

    <#
    .SYNOPSIS
        "No Space Compare" Compares two strings without whitespace
    .DESCRIPTION
        Intel(R) Network Connections Software recently introduced a change to ANS team prefixes. The old format
        was "TEAM : ". The new format is "TEAM: ". To preserve user configurations during an upgrade it is
        necessary to compare some devices without space differences.
    #>
    function NSCompare ($s1, $s2)
    {
        Write-Log $MyInvocation.MyCommand
        Write-Log "Comparing `$s1=$s1 and `$s2=$s2..."
        $s1temp = $s1 -replace '\s', ''
        $s2temp = $s2 -replace '\s', ''
        return ($s1temp -eq $s2temp)
    }

#-----------------------------------------------------------[Execution]------------------------------------------------------------

#Determine which action was specified by the user
switch($Script:Action)
{
    "Save" {$Script:bSave=$True
            Write-Host $Messages.PerformingSave
            break}
    "UpSave" {$Script:bUpSave=$True
            Write-Host $Messages.PerformingUpgradeSave
            break}
    "Restore" {$Script:bRestore=$True
            Write-Host $Messages.PerformingRestore
            break}
    "UpRestore" {$Script:bUpRestore=$True
            Write-Host $Messages.PerformingUpgradeRestore
            break}
    "Remove" {$Script:bRemove=$True
            Write-Host $Messages.PerformingRemove
            break}
    "?" {PrintUsage}
    "help"{PrintUsage}
    default {PrintUsage}
}

CheckForAdminRights
CheckForLogging
CheckForDMiXInstall
CheckForANSInstall

#Setup the save/restore path
SetupSaveRestoreLocation

#If a save is the primary action
if($Script:bSave -eq $true -or $Script:bUpSave -eq $True)
{
    GetAdaptersOnSystem

    RemoveOldFiles

    SaveAdapters

    SaveOEMCustomizeableSetting

    SaveAdapterSettings

    if($Script:bANSInstalled)
    {
        SaveTeams

        SaveTeamSettings

        SaveVlans

        SaveVlanSettings
    }

    SaveNICPARTSettings
}
#If a restore is the primary action
elseif($Script:bRestore -eq $true -or $Script:bUpRestore -eq $True)
{
    CheckIfConfigFileExists

    if($Script:bANSInstalled)
    {
        RemoveTeamsAndVlans
    }

    GetAdaptersOnSystem

    CheckNICPART_BDFWarning

    CheckAdaptersExist

    RestoreOEMCustomizeableSetting

    RestoreAdvancedSettingsAndDevices

    RestoreNICPARTSettings
}
#If remove is the primary action
elseif($Script:bRemove -eq $true)
{
    RemoveTeamsAndVlans
}

# SIG # Begin signature block
# MIIlnwYJKoZIhvcNAQcCoIIlkDCCJYwCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD+qGzhAFnfrgXw
# HqcLX83+lU3a2NSVbpnPk7EzpFBroqCCE3MwggXJMIIDsaADAgECAhNWAAAKbrWU
# vg7X2hOJAAAAAApuMA0GCSqGSIb3DQEBCwUAMHMxCzAJBgNVBAYTAlVTMQswCQYD
# VQQIEwJDQTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExGjAYBgNVBAoTEUludGVsIENv
# cnBvcmF0aW9uMSUwIwYDVQQDExxJbnRlbCBFeHRlcm5hbCBJc3N1aW5nIENBIDdC
# MB4XDTE5MDMyNzIxNTM1OFoXDTIxMDMyNjIxNTM1OFowXjELMAkGA1UEBhMCVVMx
# CzAJBgNVBAgTAkNBMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEaMBgGA1UEChMRSW50
# ZWwgQ29ycG9yYXRpb24xEDAOBgNVBAMMB05EX0FwcHMwggEiMA0GCSqGSIb3DQEB
# AQUAA4IBDwAwggEKAoIBAQC+GsJMp8x3QiNj8FsBEMclDhHHXgSm2vQw5j9FCLDT
# nCnARwrg/QWKwZbMNS2ew2mxoQl5xIeGglCBsjtqh0vfKNpHlpfs2BulN6bjMV0t
# R0dxmJ1APP9QfneTZSoGb3TCcodpmCzPWd1NRzRiG1Jsk4tbjBUVf98QoliMcaQw
# V/5nYRwbRkE0Fkim/wlUW4hUn1hbwO14lFmlwVwSIaQ3qaSAaxqlSUBJR/7+X6nO
# WpqyS2SNFas1PBpvb0333dQZ/ykJ+Nokl4f1/t4Q6jo0sNbb4dMOLY5/zL4O9Jiu
# R6G0BgcIeCxQSau1BItGjq/z3ecniXgDO7+awmPRE4ZHAgMBAAGjggFpMIIBZTAd
# BgNVHQ4EFgQUls3eK1wxqNVY3SbIJhqYmnkf4JkwHwYDVR0jBBgwFoAUssBnplaN
# J3kQdMP1xaWJZtbxLjYwNwYDVR0fBDAwLjAsoCqgKIYmaHR0cDovL3BraS5pbnRl
# bC5jb20vY3JsL0ludGVsQ0E3Qi5jcmwwZgYIKwYBBQUHAQEEWjBYMDIGCCsGAQUF
# BzAChiZodHRwOi8vcGtpLmludGVsLmNvbS9jcnQvSW50ZWxDQTdCLmNydDAiBggr
# BgEFBQcwAYYWaHR0cDovL09DU1AuaW50ZWwuY29tLzAMBgNVHRMBAf8EAjAAMAsG
# A1UdDwQEAwIHgDA9BgkrBgEEAYI3FQcEMDAuBiYrBgEEAYI3FQiGw4x1hJnlUYP9
# gSiFjp9TgpHACWeFifZkhemSUAIBZAIBEjATBgNVHSUEDDAKBggrBgEFBQcDAzAT
# BgNVHSAEDDAKMAgGBmeBDAEEATANBgkqhkiG9w0BAQsFAAOCAgEAgiYGt2WkRTtM
# HkGpe6tdmug5KiY/dUALvyrZQVNcTL91Pjgocio+tnlgKd9T0D7UvptKQXJU3xoO
# 1VZOJyuTtuCXfUrpd/+lhaKC2owxnAAWY4Os0jr1H2or4Ag20O/TdEQvzR9ucf9p
# EuRutwsbqKr6OMRM832Fyva2MdtRtvjZHjyX8vWQiUIsT1zuW5JlQVqSlOxn5EOI
# CdkDemyfujQPOWUkZEtTJyhTGydq19wGpiQbteR3KgbH7c1GBfGxeOZumKedItnx
# qnIcqnUTE1hd/CedwywlcWmJl9k2R2LBPcjZa7qij9AmDP0/2hnzwu07oDiPYbgz
# MFpfJUjjYwK5zWC/Oh2MNtlChLQhSFzl7/TVMPkPd23AB3Djv/RFxgAchtPDZ7M2
# PTJ0Iq67228a94qSpfY7LABBDC2jIdY+olevauCHWLh5TfymzUbuhoqRZsSa57A2
# H5exawXtfwXTMs4Ef2E59B7bmVHBdgDbTAMAV44+l3pYG38za0dIzKZ5wGnl7JMx
# w/BpMKtswyApItv3jc68GUhgLIIHYivliLURDfDo73RiAQhbzr6bD+MwC3u70jzK
# Y74jNwu8KfAimS9ES9N3hj5djhU7ZuZHZldnXjtsCu74yDrC6K+LA3DKivUiNh7f
# 2GCfrhTXSqw+/ymMJ/ufHTh1h5ghPzkwggZVMIIEPaADAgECAgphGFSGAAAAAAAk
# MA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
# YXRpb24xKTAnBgNVBAMTIE1pY3Jvc29mdCBDb2RlIFZlcmlmaWNhdGlvbiBSb290
# MB4XDTExMDQxMTIyMDYyMFoXDTIxMDQxMTIyMTYyMFowgYUxCzAJBgNVBAYTAkdC
# MRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQx
# GjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYDVQQDEyJDT01PRE8gUlNB
# IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
# MIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNwAHG8U9/E+ioSj0t/EFa9
# n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR62RRr55yzhaCCenavcZDX
# 7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onrayzT7Y+YHBSrfuXjbvzY
# qOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt4Q08RWD8MpZRJ7xnw8ou
# tmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIqm1y9TBsoilwie7SrmNnu
# 4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/vOldxJuvRZnio1oktLqp
# Vj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT8dm74YlguIwoVqwUHZwK
# 53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IEIlKVgJNp6I5MZfGRAy1w
# dALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfOKJwGRXa/ghgntNWutMtQ
# 5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPOGHFrK+ymircxXDpqR+DD
# eVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/s1Hap0flhFMCAwEAAaOB
# yzCByDARBgNVHSAECjAIMAYGBFUdIAAwHQYDVR0OBBYEFLuvfgI9+qbxPISOre44
# mOzZMjLUMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaA
# FGL7CiFbf0NuEdoJVFBr9dKWcfGeMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly9j
# cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY3Jvc29mdENvZGVW
# ZXJpZlJvb3QuY3JsMA0GCSqGSIb3DQEBBQUAA4ICAQCBmAeS/m8yX9nSS/V92XHg
# /fwWkgW0zmf1zEvUxxCYVPpSG0hYL3O/Gdk3oK0z81EFI3nZsndkiuu9w7Odt7Hm
# N9HSWX5B2Y+zFKsVd01s2kAkW7IHuFgsSwwrU1Gz3y65dqxpycLtZDd7jSF6zNyf
# vBcoBMwlRyQqhcxW5jk5h3UYH0b2kQ+qRvpN5kdU4jIsdu77zb1i4ZYkKQZLDP40
# SukQHXTlei+VS8xuuv3XNV+R5FlC3vsAjgjxUVEtYiWEFQgZEYZAYdUlUyMsKXc4
# zFjTjF+8GbhmBkxjENuyrDBsFryLvNIbxgMTFUalUPSaloS7chA421Ga1MVTJ8u/
# KBWeCGs9P0zADJEcvxmEizdRoBmdhVXFXaVkee8Qpev0Ixzab+MufRewN3YfTY3B
# AkEfNj4Ge8W3YC1BYlHe3eRRLafegfTD4ODpwxaA3ZxJfRfPy1VjB9ZpUvSknSSN
# vhvJgJmHRUjLScXtcDUAJnynD3Uy9+0Ij/C8pWCgItUzHvvlAiyVpgf0vhTecEyO
# qX5B3qnZUGSGb5Qk96v2g5VdDUXRjCOMAwoT5A65QwMKQ2ezEHRG5G29Zd5FQYZw
# cgQLut26WR9XE5OwC+2xFEFp0wkEWcc2jn22S53xIPzQ8Yu9aMo+sTHPQ9Bm9aPd
# r7HcwxeM+jEoxz5JJ6tqGzCCB0kwggUxoAMCAQICEAabXpkncoTIdn8TaKfesPMw
# DQYJKoZIhvcNAQEMBQAwgYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVy
# IE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBD
# QSBMaW1pdGVkMSswKQYDVQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0
# aG9yaXR5MB4XDTE1MTAyODAwMDAwMFoXDTIxMDYxNzIzNTk1OVowczELMAkGA1UE
# BhMCVVMxCzAJBgNVBAgTAkNBMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEaMBgGA1UE
# ChMRSW50ZWwgQ29ycG9yYXRpb24xJTAjBgNVBAMTHEludGVsIEV4dGVybmFsIElz
# c3VpbmcgQ0EgN0IwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6Wm/x
# XxoLVBASupDWousOXqw9zhWFmkoRymR/IoppFlfPIDokgNe74FPcqUvPbIGxvhqi
# AeBFe2wMZtRPj24i7fNSmKJ6NTeKbOy4MYZJZynHtLOIcNCZHivEEvfuHvO8JI4R
# hgOT0nkL567cWjGpoueACfpaklFnMO87sfSx/U6rGmgl59tVYHN7Fb8ATJBCnilm
# D3o8432gQpmQnjVcgxzlSN9BfLOe6Zcqw/oDRiOpJsJl8ZXiV02mqoCziVG3MpZD
# JfQvkPgOzM94dHUNri+doaAL/B1NI8aGtt1hx6a/lxHtFQxA4/3lEC2ds8Hv7etk
# eReMBq5pkutGAyrUIdFZ+vw26ZcWRnkoM6NZKxn0FeZwPEqJssk+jXdI1Ap8rEXl
# RClcE/PITY4mnvBUhQDtn+vuz+ITwbqt06rVRK20dB6rAN0Hcl6cprSQ4/ZGoQBU
# V8gmF+NIc9GYaNGRrKw6duxL4zaFkxliwCFIaAuKxZQlHSjVAtQH6f5QU4ORE2xh
# AAf8QPeXzY8yew16NwwIo/vsXvqNc+O6xJLQcfT7HGGVbVdbHmYtaiB+VTC7rIlf
# ynHtXBOOBoUgckbMIf0OYxDfHEc/EtL5UEsEG99v0sybjlcFIJWvY1aLIVAJz5Xv
# nzb3K3v/gHFQr31Lmp7Q90QYsGAalKOKMAie6wIDAQABo4IBxDCCAcAwHwYDVR0j
# BBgwFoAUu69+Aj36pvE8hI6t7jiY7NkyMtQwHQYDVR0OBBYEFLLAZ6ZWjSd5EHTD
# 9cWliWbW8S42MA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMD4G
# A1UdJQQ3MDUGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgGCisGAQQBgjcK
# AwwGCSsGAQQBgjcVBTAiBgNVHSAEGzAZMA0GCyqGSIb4TQEFAWkBMAgGBmeBDAEE
# ATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01P
# RE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcBAQRlMGMw
# OwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET1JTQUFk
# ZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5j
# b20wNQYDVR0eBC4wLKAqMAuBCWludGVsLmNvbTAboBkGCisGAQQBgjcUAgOgCwwJ
# aW50ZWwuY29tMA0GCSqGSIb3DQEBDAUAA4ICAQA1uwPqzJtgGhPQdVKOgJVFTp6/
# bsC7ZKrDbrECHUZeL+gvSMyEEPetmTv/+oVoKbDTfDHiGrR7wWbipTvHKRiYNa5j
# AahFIJVh2xBNuQ1r05lkzl+LuGwTRqBuWg0+55DrtzGhIfWN3jt7aTbxCAC5qr8c
# VmFW18ySPynU2WvYIi8OVvVq0UbogI85epI8Z0i34voZDzdn4t8pLQKqQygurixG
# QiS+bbtqiEmmTCDf5WVP+uHBvnHV+F71nWaSsjtk4eiurJlVF73bG9+gk08/VvI7
# g9XSt8EIWlJAQuM+kSD3NbSR8E3hNGlIecDtMMmTGoTVchmPbYA59FmrIBbY+f9w
# JiN77MUAMyJ8PSA67bQovHqBDOcLwT98MAxOULhnD9dkF7fDxSCFyo/O1SYqElS5
# /yL4qCc8yg6FNxTuAuUvZhViY4dqXs8p07iReLdhchd7wRmmGAgi2tCRJfYGCQkm
# sC2sgIh0M1/H4ETBMJl22HexRwHvaZIr7a5YKWOgNY7kHbcE8do6sjKAsci88OcP
# cQB6MzoG6KTYednZU82b/rJoW4iEhWsHcdBPkwoHYAM0CNJzvxQa3+PHBBstmZ6T
# HJWzh5hCWhyRY1I5io9KKsJMe3BpOjzx+y//DgqHlOQBas+btB+jDqnqKtyvK4xE
# Af06WH0yeKIZ1cl0xTGCEYIwghF+AgEBMIGKMHMxCzAJBgNVBAYTAlVTMQswCQYD
# VQQIEwJDQTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExGjAYBgNVBAoTEUludGVsIENv
# cnBvcmF0aW9uMSUwIwYDVQQDExxJbnRlbCBFeHRlcm5hbCBJc3N1aW5nIENBIDdC
# AhNWAAAKbrWUvg7X2hOJAAAAAApuMA0GCWCGSAFlAwQCAQUAoGowGQYJKoZIhvcN
# AQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARYw
# LwYJKoZIhvcNAQkEMSIEIJXmcPj5QsnJllzOHiVlJQ8Pb03/VemLoEIKP0IIIlbd
# MA0GCSqGSIb3DQEBAQUABIIBAEh3qHQTGReA8Ao2FrAJaPvCrThF+6NZv4ZQ9nxk
# b7LuwpPQ0CZNNutVK2bKx8J3k87R0vQELxBIXFafT6UXR3ufPGzJfx00LphvlJLY
# A3Q6aU1U630I60kQ6z5aNdepSAwbXABPSEEItLuB0RXTuU+eWB8FjPlS/VUgitvs
# 2+as0pjUYW42Ds6t3nGEhwdxtB2si1sIUf9I4jW4UmU23E0eG4/Zc0A3IHGnKRfO
# JIfy/8DWnX05MrcoyA1Zw64p8rzrSqlsMX5ziQmzOPa845BZaaZ12XCRBxFhubzB
# ePiHoH8RMoDMsD+xzIjLt8AQUItMT3RwJZogWm0Tutyd6pOhgg9cMIIPWAYKKwYB
# BAGCNwMDATGCD0gwgg9EBgkqhkiG9w0BBwKggg81MIIPMQIBAzEPMA0GCWCGSAFl
# AwQCAQUAMIIBIwYLKoZIhvcNAQkQAQSgggESBIIBDjCCAQoCAQEGCisGAQQBhFkK
# AwEwMTANBglghkgBZQMEAgEFAAQgFVqriWi3tEElMA54zLS6bkQyA/0/D+UAphBZ
# UKDnDYoCBmAJ5XU2MRgTMjAyMTAyMTAwMjIxMDYuNTYxWjAEgAIB9AIIMY9QaP+3
# DiKggZikgZUwgZIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxML
# U2FudGEgQ2xhcmExGjAYBgNVBAoTEUludGVsIENvcnBvcmF0aW9uMSYwJAYDVQQL
# Ex1UaGFsZXMgVFNTIEVTTjowRTM3LTk2NDktMDhDNTEcMBoGA1UEAxMTdGltZXN0
# YW1wLmludGVsLmNvbaCCC2IwggWAMIIEaKADAgECAhRpstHM8C4g3MlcYolPf55f
# X8BXvzANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVv
# VmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhv
# cml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhv
# cml0eTAeFw0xNDA1MzAxNjM1NTVaFw0yMTAzMTcxODMzMzNaMEkxCzAJBgNVBAYT
# AkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR8wHQYDVQQDExZRdW9WYWRp
# cyBJc3N1aW5nIENBIEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# xFGo/S/4GVzomXoxmkaTYkznYlslpJfLQAMQ95k82dZxzPOBJtzVP1lopp5keaUn
# wEHMmPbIJECJxKEqW0cgCRLw/8yOZL7u0QFfx6D8/JxWGbpRXbw6xc8Pqurg7eoe
# ZN3zGZOhS+dRkFsyArQFu1lc8MIXpMiJkrVXRNq8CDxKEzNyzwTaJ2/tdskoUkk4
# Ycuut3CKLYhYqBFjA4GMMqLTIMIqu2AocgEbwtP4y/oePDt1ZjuIj09iPm06qXC3
# i2aSTY3qnOHk/IRcEbGFMad0baSXdLaPPYt87/zBmM+82KohTJJJx+LHo/c0lLcU
# LxwJ5Lre1NNnOC0CP7I45uD0OIQZD5Wg7wxw7jjMg2VneRtAnLvEyUqQucYEChLR
# xZE4HEcBVQxKVa40SoioSEAQWcgJzJ5ngAsgulNpoV8Aip2FbMm2cqLDFVP704ys
# bkw9AVoSdRo17rH7RA5DOM8hVJ/1SgMf4cZr14xrmz0rx2/wFYrxwKT2JepbMy3T
# JoxFehYSbOWUFym4QWAz2klgNY+X/oIxsou0I2SENPZkVhuiV4lirEI5rAep/Gjh
# /D4WXfHEbwgpUtOvSovzfIDTu1aJafZaKsOzLEjLt9+LrwQdLUG2Odd5QdPGdhpQ
# OY6HhJ1OfNM1cOVir+qqx1NQcTs9RVJh/7nB1zoZu00CAwEAAaOCASgwggEkMBIG
# A1UdEwEB/wQIMAYBAf8CAQAwEQYDVR0gBAowCDAGBgRVHSAAMHEGCCsGAQUFBwEB
# BGUwYzAqBggrBgEFBQcwAYYeaHR0cDovL29jc3AucXVvdmFkaXNnbG9iYWwuY29t
# MDUGCCsGAQUFBzAChilodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9iYWwuY29tL3F2
# cmNhLmNydDAOBgNVHQ8BAf8EBAMCAQYwHwYDVR0jBBgwFoAUi0tt7dMpuQYZ7Dk5
# qfCXhGrL798wOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL2NybC5xdW92YWRpc2ds
# b2JhbC5jb20vcXZyY2EuY3JsMB0GA1UdDgQWBBTzNBIR8YzG9rKntJlLcIrKYKHL
# RTANBgkqhkiG9w0BAQsFAAOCAQEAufYTUrUXpypNhHdDCaTboGe0YA5C9AO9xP8s
# Wg+QLnjFY8hK7Cf2fOQp0M9gGPpoItoCUnYN8hdUxvYIHqHMguTDOm2ZInzEwHe0
# 5gUgR5NAOc/cVa3DRq8pTXmcZEwgX4ocVvxGoF/LmN2Rejm0r8R3mWuerN5vLXnq
# f9cTJJhSHP1pPu1yrD/QtAEZFO2w8MvznFEUI4zH3Gl9MoGW5B1HjwF2lIM+iI2S
# WxhYmGkDx/XT8mFSUOs0oP0mMDAPtf1w5ycsNwsc8+cepiwHQ7ZLiF6XH8EwfWBk
# KvMMcGhEUWNZn9tXwh//gOXCEZLYL+/VF0P/ZC1khFxSGmPCZzCCBdowggPCoAMC
# AQICFDyl1SUfdSmlujxztElvosGwxWyfMA0GCSqGSIb3DQEBCwUAMEkxCzAJBgNV
# BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR8wHQYDVQQDExZRdW9W
# YWRpcyBJc3N1aW5nIENBIEc0MB4XDTE4MDQyMDE2NTI0MVoXDTIxMDMxNzE4MzMz
# M1owgZIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLU2FudGEg
# Q2xhcmExGjAYBgNVBAoTEUludGVsIENvcnBvcmF0aW9uMSYwJAYDVQQLEx1UaGFs
# ZXMgVFNTIEVTTjowRTM3LTk2NDktMDhDNTEcMBoGA1UEAxMTdGltZXN0YW1wLmlu
# dGVsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOdnbUWZuuma
# 8aAj9IvmQ2SbHicPHikw+ekJkd2zpfHcqv60ar4OM9WcnE8oBkA4lHIWPeCAqir8
# IjJIBVj5Ot+/S8LrcsdfzBQw/S1CyNB/VTHxIy+Xl46ZP+O/LPfzZsxN5O50Ay6y
# I0IsDFhXUp9w8HyUmrYcg1/S8ZNgpxoXDTWsmGpkMXfOjLCAFriDieiwa3rHdWXx
# R5TRHhAX78wHIiF/w1wN/bkj3KfLFV4/8dwRw70J1GzYS/t0B9JMlchGzGlSWvX8
# c/KW//haVpJ3NkW5KKwY8IrtEHsIhu3MR1lvHyM5US22ECK3TNU9GR97QNurrwWW
# +cUWeRpaZUsCAwEAAaOCAW4wggFqMHMGCCsGAQUFBwEBBGcwZTAqBggrBgEFBQcw
# AYYeaHR0cDovL29jc3AucXVvdmFkaXNnbG9iYWwuY29tMDcGCCsGAQUFBzAChito
# dHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9iYWwuY29tL3F2aWNhZzQuY3J0ME8GA1Ud
# IARIMEYwRAYKKwYBBAG+WAGEWDA2MDQGCCsGAQUFBwIBFihodHRwOi8vd3d3LnF1
# b3ZhZGlzZ2xvYmFsLmNvbS9yZXBvc2l0b3J5MA4GA1UdDwEB/wQEAwIGwDAWBgNV
# HSUBAf8EDDAKBggrBgEFBQcDCDAfBgNVHSMEGDAWgBTzNBIR8YzG9rKntJlLcIrK
# YKHLRTA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnF1b3ZhZGlzZ2xvYmFs
# LmNvbS9xdmljYWc0LmNybDAdBgNVHQ4EFgQUuWuB0McdcXX/SxyYEwvnQIt55dQw
# DQYJKoZIhvcNAQELBQADggIBAKQskev/DnQ50Bz1B+4SrMHdF6RPeZHvmuuO8K5X
# DOMcpXxT6VpyBTeTxfj9cKy8S6KQPZgLZ41eDeynB9owdYygAjZKOcS5rO+SjPgy
# 7dGEzn48kAoGsBbfa4tKr8IkH61YuQbFX9nj0Y50EGIsglKw4PLq01M9MF0mZkPq
# nPNDzlvFKkXGLe5YX3xn9o+N7GSSWZrp6JDbyre55oIm2/80haTBxg1kuxUsFvsX
# geI/tcpCWLJiQLt9cebT/T+iKMSjTc7Gn/Daz2BSmbhFCFr/EqpKHTqC2DYkPovO
# JF/gaz9SXiB/MmjqShkraC5VV5IT5pgt/Rsd9Q0njCnRSW+cmOM4uD1gG3muOl9E
# p9O9G013+b3EQi2yn1PgIoydWcfw+CeEoK86fRt5QgejPJrJ38LUkwSsRjLumIdp
# yK4R+QXFTFf6nXJe6sqZHukS2UItwtxflmDR95aKUeoGI+Djb4nKfdH+lO4xYu8j
# mkyfWXY8BfVIETTYVGLhXQxEhgNZuZ89uEQBtaCHq63Wmb6tMX8TZUnQ5YZ5Vj6a
# MRhRN+E4bUI05fyeqqAXo5uhAPUKnS4jrvRHlwUJPWOAtSTauR/SOygdWKzesxXM
# 1XWcdeLaWnLl+FjehC7laqx1WR1lRxJ/vkXMDyN5vCjIOKxgBaNPc6EAE0DKnQ3K
# vIpFMYICjDCCAogCAQEwYTBJMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFk
# aXMgTGltaXRlZDEfMB0GA1UEAxMWUXVvVmFkaXMgSXNzdWluZyBDQSBHNAIUPKXV
# JR91KaW6PHO0SW+iwbDFbJ8wDQYJYIZIAWUDBAIBBQCggf0wGgYJKoZIhvcNAQkD
# MQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDJACOXUXQw3Awc6sA/PnZu
# 3pXBjxf9PaHP53GtVNnxpTCBrQYLKoZIhvcNAQkQAgwxgZ0wgZowgZcwfQQUKzpO
# a3N4X9ydc2+zzdUaUJGfN+IwZTBNpEswSTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
# EFF1b1ZhZGlzIExpbWl0ZWQxHzAdBgNVBAMTFlF1b1ZhZGlzIElzc3VpbmcgQ0Eg
# RzQCFDyl1SUfdSmlujxztElvosGwxWyfMBYEFOt+XK2uxYxibOk+0GeTQs/7nhvy
# MA0GCSqGSIb3DQEBAQUABIIBABV6CN5BgmFdwGNm1rZq6qsiuI2t5iJbPlUnrb3/
# sewBE6i1KIFS9b+CCXvSsmsTRTdgMMgCRHz5eVzYJQ1WE6JPUXNNADFZQUgWze3i
# ntmsbOktl0bY0y+5tCnAc6BopQakIEgPJXvn+axp4oWHrlcE2VKU+VBXlIgz90E3
# cJCDU2SuT4Hd+sqzNu9tp91hOsxOIN1hyYH63ujC0V3gbZK3H72aBIrfwuoxniG6
# jWBq8hoZhENYXxmbyaTD2Ix5Dp1rKj5ptvrgGFUl3i+8TFCo6urbvGs3IjHs68cW
# htNqqF9Tg1wYPTGd+B2xdyRJApryadBAtnOmP+Xelrh9FIA=
# SIG # End signature block
