#
# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

#Requires -RunAsAdministrator

$ServiceLocatorVariable = "OTEL_DOTNET_AUTO_INSTALL_DIR"

function Get-Current-InstallDir() {
    return [System.Environment]::GetEnvironmentVariable($ServiceLocatorVariable, [System.EnvironmentVariableTarget]::Machine)
}

function Get-Newest-InstallVersion() { 
    $query = "https://api.github.com/repos/open-telemetry/opentelemetry-dotnet-instrumentation/releases/latest"

    $response = Invoke-WebRequest -Uri $query -UseBasicParsing
    $responseJson = $response.Content | ConvertFrom-Json
    $release = $responseJson.tag_name

    return $release
}

function Get-CLIInstallDir-From-InstallDir([string]$InstallDir) {
    $dir = "OpenTelemetry .NET AutoInstrumentation"
    
    if ($InstallDir -eq "<auto>") {
        return (Join-Path $Env:ProgramFiles $dir)
    }
    elseif (Test-Path $InstallDir -IsValid) {
        return $InstallDir
    }

    throw "Invalid install directory provided '$InstallDir'"
}

function Get-Temp-Directory() {
    $temp = $env:TEMP

    if (-not (Test-Path $temp)) {
        New-Item -ItemType Directory -Force -Path $temp | Out-Null
    }

    return $temp
}

function Prepare-Install-Directory([string]$InstallDir) {
    if (Test-Path $InstallDir) {
        # Cleanup old directory
        Remove-Item -LiteralPath $InstallDir -Force -Recurse
    }

    New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
}

function Reset-IIS() {    
    Start-Process "iisreset.exe" -NoNewWindow -Wait
}

function Download-OpenTelemetry([string]$Version, [string]$Path) {
    $archive = "opentelemetry-dotnet-instrumentation-windows.zip"
    $dlUrl = "https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/$Version/$archive"
    $dlPath = Join-Path $Path $archive

    Invoke-WebRequest -Uri $dlUrl -OutFile $dlPath -UseBasicParsing

    return $dlPath
}

function Download-OpenTelemetry-Install-Module([string]$Version, [string]$Path) {
    $module = "OpenTelemetry.DotNet.Auto.psm1"
    $dlUrl = "https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/$Version/$module"
    $dlPath = Join-Path $Path $module

    Invoke-WebRequest -Uri $dlUrl -OutFile $dlPath -UseBasicParsing

    return $dlPath
}

function Get-Environment-Variables-Table([string]$InstallDir, [string]$OTelServiceName) {
    $COR_PROFILER_PATH_32 = Join-Path $InstallDir "/win-x86/OpenTelemetry.AutoInstrumentation.Native.dll"
    $COR_PROFILER_PATH_64 = Join-Path $InstallDir "/win-x64/OpenTelemetry.AutoInstrumentation.Native.dll"
    $CORECLR_PROFILER_PATH_32 = Join-Path $InstallDir "/win-x86/OpenTelemetry.AutoInstrumentation.Native.dll"
    $CORECLR_PROFILER_PATH_64 = Join-Path $InstallDir "/win-x64/OpenTelemetry.AutoInstrumentation.Native.dll"

    $DOTNET_ADDITIONAL_DEPS = Join-Path $InstallDir "AdditionalDeps"
    $DOTNET_SHARED_STORE = Join-Path $InstallDir "store"
    $DOTNET_STARTUP_HOOKS = Join-Path $InstallDir "net/OpenTelemetry.AutoInstrumentation.StartupHook.dll"

    $OTEL_DOTNET_AUTO_HOME = $InstallDir
    
    $vars = @{
        # .NET Framework
        "COR_ENABLE_PROFILING"                = "1";
        "COR_PROFILER"                        = "{918728DD-259F-4A6A-AC2B-B85E1B658318}";
        "COR_PROFILER_PATH_32"                = $COR_PROFILER_PATH_32;
        "COR_PROFILER_PATH_64"                = $COR_PROFILER_PATH_64;
        # .NET Core
        "CORECLR_ENABLE_PROFILING"            = "1";
        "CORECLR_PROFILER"                    = "{918728DD-259F-4A6A-AC2B-B85E1B658318}";
        "CORECLR_PROFILER_PATH_32"            = $CORECLR_PROFILER_PATH_32;
        "CORECLR_PROFILER_PATH_64"            = $CORECLR_PROFILER_PATH_64;
        # ASP.NET Core
        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES" = "OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper";
        # .NET Common
        "DOTNET_ADDITIONAL_DEPS"              = $DOTNET_ADDITIONAL_DEPS;
        "DOTNET_SHARED_STORE"                 = $DOTNET_SHARED_STORE;
        "DOTNET_STARTUP_HOOKS"                = $DOTNET_STARTUP_HOOKS;
        # OpenTelemetry
        "OTEL_DOTNET_AUTO_HOME"               = $OTEL_DOTNET_AUTO_HOME;
    }

    if (-not [string]::IsNullOrWhiteSpace($OTelServiceName)) {
        $vars.Add("OTEL_SERVICE_NAME", $OTelServiceName)
    }

    return $vars
}

function Setup-Windows-Service([string]$InstallDir, [string]$WindowsServiceName, [string]$OTelServiceName) {  
    $varsTable = Get-Environment-Variables-Table -InstallDir $InstallDir -OTelServiceName $OTelServiceName
    $regPath = "HKLM:SYSTEM\CurrentControlSet\Services\"
    $regKey = Join-Path $regPath $WindowsServiceName
   
    if (Test-Path $regKey) {
        # Cleanup existing variables
        Cleanup-Environment-Variables -WindowsServiceName $WindowsServiceName

        # Migrate remaining external variables to install variables
        [string []] $varsList = Migrate-Environment-Variables -ServiceRegistryKey $regKey -InstallVars $varsTable

        # Set install variables
        Set-ItemProperty $regKey -Name Environment -Value $varsList
    }
    else {
        throw "Invalid service '$WindowsServiceName'. Service does not exist."
    }
}

function Cleanup-Environment-Variables([string]$WindowsServiceName) {
    [string[]] $filters = @(
        # .NET Framework
        "COR_ENABLE_PROFILING",
        "COR_PROFILER",
        "COR_PROFILER_PATH_32",
        "COR_PROFILER_PATH_64",
        # .NET Core
        "CORECLR_ENABLE_PROFILING",
        "CORECLR_PROFILER",
        "CORECLR_PROFILER_PATH_32",
        "CORECLR_PROFILER_PATH_64",
        # ASP.NET Core
        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES",
        # .NET Common
        "DOTNET_ADDITIONAL_DEPS",
        "DOTNET_SHARED_STORE",
        "DOTNET_STARTUP_HOOKS",
        # OpenTelemetry
        "OTEL_DOTNET_"
    )

    $regPath = "HKLM:SYSTEM\CurrentControlSet\Services\"
    $regKey = Join-Path $regPath $WindowsServiceName
   
    if (Test-Path $regKey) {
        $property = Get-ItemProperty $regKey
        if (-not $property.Environment) {
            # Nothing to clean
            return
        }

        $vars = Filter-Env-List -EnvValues $property.Environment -Filters $filters

        Set-ItemProperty $regKey -Name Environment -Value $vars
    }
    else {
        throw "Invalid service '$WindowsServiceName'. Service does not exist."
    }

    $remaining = Get-ItemPropertyValue $regKey -Name Environment
    if (-not $remaining) {
        Remove-ItemProperty $regKey -Name Environment
    }
}

function Migrate-Environment-Variables([string]$ServiceRegistryKey, [hashtable]$InstallVars) {
    $prop = Get-ItemProperty $ServiceRegistryKey

    if ($prop.Environment){
        foreach ($var in $prop.Environment) {
            # Split the string on the first '=' to get the key and value
            $splitVar = $var.Split('=', 2)
            $key = $splitVar[0]
            $value = $splitVar[1]

            # Reinstall external variable
            $InstallVars[$key] = $value
        }
    }

    [string []] $varsList = $InstallVars.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" } # [string []] definition is required for WS2016

    return $varsList
}

function Filter-Env-List([string[]]$EnvValues, [string[]]$Filters) {
    $remaining = @()

    foreach ($value in $EnvValues) {
        $match = $false

        foreach ($filter in $Filters) {
            if ($value -clike "$($filter)*") {
                $match = $true
                break
            }
        }

        if (-not $match) {
            $remaining += $value
        }
    }

    return $remaining
}

function Get-OpenTelemetry-Archive([string] $Version, [string] $LocalPath) {
    if ($LocalPath) {
        if (Test-Path $LocalPath) {
            return $LocalPath
        }

        throw "Could not find archive '$LocalPath'"
    }

    $tempDir = Get-Temp-Directory
    return Download-OpenTelemetry $Version $tempDir
}

function Test-AssemblyNotForGAC([string] $Name) {
    switch ($Name) {
        "netstandard.dll" { return $true }
        "grpc_csharp_ext.x64.dll" { return $true }
        "grpc_csharp_ext.x86.dll" { return $true }
    }
    return $false 
}

function Is-Greater-Version($v1, $v2) {
    # Helper convert version main part to version object
    function ToVersion($version) {
        return ($version -replace '^v' -replace '-.+$') -as [version]
    }

    # Helper convert version pre part to version object
    function RankPre($version) {
        # $null is full release
        $rank = "alpha", "beta", "rc", $null
        $ranked = $null

        if ($version -match "^v\d+\.\d+\.\d+?(\-(?<pre>[\w\.]+))") {
            $token = $matches['pre'].split('.')[0]
            $ranked = $matches['pre'] -replace $token, $rank.IndexOf($token)
        }
        else {
            $ranked = $rank.IndexOf($null)
            $ranked = "$($ranked).0"
        }

        return $ranked -as [version]
    }

    $v1Version = ToVersion $v1
    $v2Version = ToVersion $v2

    if ($v1Version -gt $v2Version) { return $true }
    if ($v1Version -lt $v2Version) { return $false }

    $v1PreRank = RankPre $v1
    $v2PreRank = RankPre $v2

    if ($v1PreRank -gt $v2PreRank) { return $true }

    return $false;
}

<#
    .SYNOPSIS
    Installs OpenTelemetry .NET Assemblies in GAC.
    .PARAMETER InstallDir
    Default: <auto> - the default path is Program Files dir.
    Install path of the OpenTelemetry .NET Automatic Instrumentation
    Possible values: <auto>, (Custom path)
#>
function Register-AssembliesInGAC() {
    $installDir = Get-Current-InstallDir

    # Register .NET Framework dlls in GAC
    [System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") | Out-Null
    $publish = New-Object System.EnterpriseServices.Internal.Publish 
    $dlls = Get-ChildItem -Path $installDir\netfx\ -Filter *.dll -File
    for ($i = 0; $i -lt $dlls.Count; $i++) {
        $percentageComplete = $i / $dlls.Count * 100
        Write-Progress -Activity "Registering .NET Framework dlls in GAC" `
            -Status "Module $($i+1) out of $($dlls.Count). Installing $($dlls[$i].Name):" `
            -PercentComplete $percentageComplete

        if (Test-AssemblyNotForGAC $dlls[$i].Name) {
            continue
        }

        $publish.GacInstall($dlls[$i].FullName)
    }
    Write-Progress -Activity "Registering .NET Framework dlls in GAC" -Status "Ready" -Completed
}

<#
    .SYNOPSIS
    Installs OpenTelemetry .NET Automatic Instrumentation.
    .PARAMETER InstallDir
    Default: <auto> - the default path is Program Files dir.
    Install path of the OpenTelemetry .NET Automatic Instrumentation
    Possible values: <auto>, (Custom path)
#>
function Install-OpenTelemetryCore() {
    param(
        [Parameter(Mandatory = $false)]
        [string]$InstallDir = "<auto>",
        [Parameter(Mandatory = $false)]
        [string]$LocalPath,
        [Parameter(Mandatory = $false)]
        [bool]$RegisterAssembliesInGAC = $true
    )

    $version = "v{{VERSION}}"
    $installDir = Get-CLIInstallDir-From-InstallDir $InstallDir
    $archivePath = $null
    $deleteArchive = $true

    try {
        if ($LocalPath) {
            # Keep the archive if it's user downloaded
            $deleteArchive = $false
        }

        $archivePath = Get-OpenTelemetry-Archive $version $LocalPath

        Prepare-Install-Directory $installDir

        # Extract files from zip
        Expand-Archive $archivePath $installDir -Force

        # OpenTelemetry service locator
        [System.Environment]::SetEnvironmentVariable($ServiceLocatorVariable, $installDir, [System.EnvironmentVariableTarget]::Machine)

        if ($RegisterAssembliesInGAC){
            Register-AssembliesInGAC $installDir
        }
    } 
    catch {
        $message = $_
        Write-Error "Could not setup OpenTelemetry .NET Automatic Instrumentation. $message"
    } 
    finally {
        if ($archivePath -and $deleteArchive) {
            # Cleanup
            Remove-Item $archivePath
        }
    }
}

<#
    .SYNOPSIS
    Uninstalls OpenTelemetry .NET Automatic Instrumentation.
#>
function Uninstall-OpenTelemetryCore() {
    $installDir = Get-Current-InstallDir

    if (-not $installDir) {
        throw "OpenTelemetry Core is already removed."
    }

    # Unregister .NET Framwework dlls from GAC
    [System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") | Out-Null
    $publish = New-Object System.EnterpriseServices.Internal.Publish 
    $dlls = Get-ChildItem -Path $installDir\netfx\ -Filter *.dll -File
    for ($i = 0; $i -lt $dlls.Count; $i++) {
        $percentageComplete = $i / $dlls.Count * 100
        Write-Progress -Activity "Unregistering .NET Framework dlls from GAC" `
            -Status "Module $($i+1) out of $($dlls.Count). Uninstalling $($dlls[$i].Name):" `
            -PercentComplete $percentageComplete

        if (Test-AssemblyNotForGAC $dlls[$i].Name) {
            continue
        }

        $publish.GacRemove($dlls[$i].FullName)
    }
    Write-Progress -Activity "Unregistering .NET Framework dlls from GAC" -Status "Ready" -Completed

    Remove-Item -LiteralPath $installDir -Force -Recurse

    # Remove OTel service locator variable
    [System.Environment]::SetEnvironmentVariable($ServiceLocatorVariable, $null, [System.EnvironmentVariableTarget]::Machine)
}

<#
    .SYNOPSIS
    Update OpenTelemetry .NET Automatic Instrumentation.
    .PARAMETER RegisterIIS
    Registers IIS 
#>
function Update-OpenTelemetryCore() {
    param(
        [Parameter(Mandatory = $false)]
        [bool]$RegisterIIS = $false,
        [Parameter(Mandatory = $false)]
        [bool]$RegisterAssembliesInGAC = $true
    )

    $currentInstallVersion = Get-OpenTelemetryInstallVersion
    if (-not $currentInstallVersion) {
        return
    }

    $newestInstallVersion = Get-Newest-InstallVersion
    if (-not(Is-Greater-Version $newestInstallVersion $currentInstallVersion)) {
        Write-Warning "Newest version of OpenTelemetry .NET Automatic Instrumentation is already installed."

        return
    }

    Write-Information -MessageData "Updating OpenTelemetry .NET Automatic Instrumentation from $($currentInstallVersion) to $($newestInstallVersion)." -InformationAction Continue

    $installDir = Get-Current-InstallDir

    Uninstall-OpenTelemetryCore
    if ($RegisterIIS) {
        Unregister-OpenTelemetryForIIS -NoReset $true
    }

    $tempDir = Get-Temp-Directory
    $modulePath = Download-OpenTelemetry-Install-Module $newestInstallVersion $tempDir

    Remove-Module OpenTelemetry.Dotnet.Auto
    Import-Module $modulePath

    Install-OpenTelemetryCore -InstallDir $installDir -RegisterAssembliesInGAC $$RegisterAssembliesInGAC
    if ($RegisterIIS) {
        Register-OpenTelemetryForIIS
    }

    # Release module
    Remove-Module OpenTelemetry.Dotnet.Auto
    
    # Cache install module
    Move-Item -Path $modulePath -Destination $installDir
}

<#
    .SYNOPSIS
    Setups environment variables to enable automatic instrumentation started from the current PowerShell session.
    .PARAMETER OTelServiceName
    Specifies OpenTelemetry service name to identify your service.
#>
function Register-OpenTelemetryForCurrentSession() {
    param(
        [Parameter(Mandatory = $true)]
        [string]$OTelServiceName
    )

    $installDir = Get-Current-InstallDir

    if (-not $installDir) {
        throw "OpenTelemetry Core must be setup first. Run 'Install-OpenTelemetryCore' to setup OpenTelemetry Core."
    }

    $varsTable = Get-Environment-Variables-Table -InstallDir $installDir -OTelServiceName $OTelServiceName

    foreach ($var in $varsTable.Keys) {
        Set-Item "env:$var" $varsTable[$var]
    }
}

<#
    .SYNOPSIS
    Setups IIS environment variables to enable automatic instrumentation.
    Performs IIS reset after registration.
#>
function Register-OpenTelemetryForIIS() {
    $installDir = Get-Current-InstallDir

    if (-not $installDir) {
        throw "OpenTelemetry Core must be setup first. Run 'Install-OpenTelemetryCore' to setup OpenTelemetry Core."
    }

    if ($installDir -notlike "$env:ProgramFiles\*") {
        Write-Warning "OpenTelemetry is installed to custom path. Make sure that IIS user has access to the path."
    }

    Setup-Windows-Service -InstallDir $installDir -WindowsServiceName "W3SVC"
    Setup-Windows-Service -InstallDir $installDir -WindowsServiceName "WAS"

    Reset-IIS
}

<#
    .SYNOPSIS
    Setups specific Windows service environment variables to enable automatic instrumentation.
    Performs service restart after registration.
    .PARAMETER WindowsServiceName
    Actual Windows service name in registry.
    .PARAMETER OTelServiceName
    Specifies OpenTelemetry service name to identify your service.
#>
function Register-OpenTelemetryForWindowsService() {
    param(
        [Parameter(Mandatory = $true)]
        [string]$WindowsServiceName,
        [Parameter(Mandatory = $true)]
        [string]$OTelServiceName
    )

    $installDir = Get-Current-InstallDir

    if (-not $installDir) {
        throw "OpenTelemetry Core must be setup first. Run 'Install-OpenTelemetryCore' to setup OpenTelemetry Core."
    }

    Setup-Windows-Service -InstallDir $installDir -WindowsServiceName $WindowsServiceName -OTelServiceName $OTelServiceName
    Restart-Service -Name $WindowsServiceName -Force
}

<#
    .SYNOPSIS
    Removes environment variables to disable automatic instrumentation started from the current PowerShell session.
#>
function Unregister-OpenTelemetryForCurrentSession() {
    # .NET Framework
    $env:COR_ENABLE_PROFILING = $null
    $env:COR_PROFILER = $null
    $env:COR_PROFILER_PATH_32 = $null
    $env:COR_PROFILER_PATH_64 = $null

    # .NET Core
    $env:CORECLR_ENABLE_PROFILING = $null
    $env:CORECLR_PROFILER = $null
    $env:CORECLR_PROFILER_PATH_32 = $null
    $env:CORECLR_PROFILER_PATH_64 = $null

    # ASP.NET Core
    $env:ASPNETCORE_HOSTINGSTARTUPASSEMBLIES = $null

    # .NET Common
    $env:DOTNET_ADDITIONAL_DEPS = $null
    $env:DOTNET_SHARED_STORE = $null
    $env:DOTNET_STARTUP_HOOKS = $null

    # OpenTelemetry
    Get-ChildItem env: | Where-Object { $_.Name -like "OTEL_DOTNET_*" } | ForEach-Object { Set-Item "env:$($_.Name)" $null }
}

<#
    .SYNOPSIS
    Removes IIS environment variables to disable automatic instrumentation.
    Performs IIS reset after removal by default.
    .PARAMETER NoReset
    Does not perform IIS reset.
#>
function Unregister-OpenTelemetryForIIS() {
    param(
        [Parameter(Mandatory = $false)]
        [bool]$NoReset = $false
    )

    Cleanup-Environment-Variables -WindowsServiceName "W3SVC"
    Cleanup-Environment-Variables -WindowsServiceName "WAS"

    if (-not $NoReset) {
        Reset-IIS
    }
}

<#
    .SYNOPSIS
    Removes specific Windows service environment variables to disable automatic instrumentation.
    Performs service restart after removal.
    .PARAMETER WindowsServiceName
    Actual Windows service Name in registry.
#>
function Unregister-OpenTelemetryForWindowsService() {
    param(
        [Parameter(Mandatory = $true)]
        [string]$WindowsServiceName
    )  

    Cleanup-Environment-Variables -WindowsServiceName $WindowsServiceName
    Restart-Service -Name $WindowsServiceName -Force
}

<#
    .SYNOPSIS
    Locates OpenTelemetry .NET Automatic Instrumentation's install path. 
#>
function Get-OpenTelemetryInstallDirectory() {
    $installDir = Get-Current-InstallDir

    if ($installDir) {
        return $installDir
    }

    Write-Warning "OpenTelemetry .NET Automatic Instrumentation is not installed."
}

<#
    .SYNOPSIS
    Gets OpenTelemetry .NET Automatic Instrumentation install version. 
#>
function Get-OpenTelemetryInstallVersion() {
    $installDir = Get-OpenTelemetryInstallDirectory

    if ($installDir) {
        $mainDllPath = [IO.Path]::Combine($installDir, 'net', 'OpenTelemetry.AutoInstrumentation.dll')

        return "v" + ([System.Diagnostics.FileVersionInfo]::GetVersionInfo($mainDllPath).ProductVersion).split('+', 2)[0]
    }
}

<#
    .SYNOPSIS
    Enables OpenTelemetry .NET Automatic Instrumentation for IIS AppPool
    .PARAMETER AppPoolName
    IIS AppPool name
#>
function Enable-OpenTelemetryForIISAppPool() {
    param(
        [Parameter(Mandatory = $true)]
        [string]$AppPoolName
    ) 

    Remove-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/applicationPools/add[@name='$($AppPoolName)']/environmentVariables" -name "." -AtElement @{name='COR_ENABLE_PROFILING'}
}

<#
    .SYNOPSIS
    Disables OpenTelemetry .NET Automatic Instrumentation for IIS AppPool
    .PARAMETER AppPoolName
    IIS AppPool name
#>
function Disable-OpenTelemetryForIISAppPool() {
    param(
        [Parameter(Mandatory = $true)]
        [string]$AppPoolName
    )

    Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/applicationPools/add[@name='$($AppPoolName)']/environmentVariables" -name "." -value @{name='COR_ENABLE_PROFILING';value='0'}
}

Export-ModuleMember -Function Install-OpenTelemetryCore
Export-ModuleMember -Function Register-OpenTelemetryForIIS
Export-ModuleMember -Function Register-OpenTelemetryForWindowsService
Export-ModuleMember -Function Register-OpenTelemetryForCurrentSession
Export-ModuleMember -Function Uninstall-OpenTelemetryCore
Export-ModuleMember -Function Update-OpenTelemetryCore
Export-ModuleMember -Function Unregister-OpenTelemetryForIIS
Export-ModuleMember -Function Unregister-OpenTelemetryForWindowsService
Export-ModuleMember -Function Unregister-OpenTelemetryForCurrentSession
Export-ModuleMember -Function Get-OpenTelemetryInstallDirectory
Export-ModuleMember -Function Get-OpenTelemetryInstallVersion
Export-ModuleMember -Function Enable-OpenTelemetryForIISAppPool
Export-ModuleMember -Function Disable-OpenTelemetryForIISAppPool
Export-ModuleMember -Function Register-AssembliesInGAC
