Public/Install-SMBeat.ps1

#Requires -Version 5.1

function Install-SMBeat {
    <#
    .SYNOPSIS
        Copies the SMBeat module into WindowsPowerShell\Modules (local or remote).
    #>

    [CmdletBinding(DefaultParameterSetName = 'Local')]
    param(
        [Parameter(ParameterSetName = 'ComputerName', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]$ComputerName,

        [Parameter(ParameterSetName = 'Session')]
        [System.Management.Automation.Runspaces.PSSession[]]$Session,

        [pscredential]$Credential,
        [System.Management.Automation.Runspaces.AuthenticationMechanism]$Authentication,
        [switch]$UseSSL,
        [int]$Port,
        [System.Management.Automation.Remoting.PSSessionOption]$SessionOption,
        [int]$ThrottleLimit = 32
    )

    begin {
        Assert-SMBeatRemotingExclusive -ComputerName $ComputerName -CimSession $null -Session $Session
        $sourceRoot = $script:SMBeatModuleRoot
        $copyNames = @('SMBeat.psd1', 'SMBeat.psm1', 'Public', 'Private', 'Scripts', 'en-US', 'LICENSE')
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq 'Local') {
            $dest = Get-SMBeatInstallDestination
            if (-not (Test-Path -LiteralPath $dest)) {
                New-Item -ItemType Directory -Path $dest -Force | Out-Null
            }
            foreach ($name in $copyNames) {
                $from = Join-Path $sourceRoot $name
                if (Test-Path -LiteralPath $from) {
                    Copy-Item -LiteralPath $from -Destination $dest -Recurse -Force
                }
            }
            Get-Item -LiteralPath $dest
            return
        }

        $sessionsToClose = @()
        $targets = $Session
        if ($PSCmdlet.ParameterSetName -eq 'ComputerName') {
            $newParams = @{ ComputerName = $ComputerName; ErrorAction = 'Stop' }
            if ($Credential) { $newParams['Credential'] = $Credential }
            if ($PSBoundParameters.ContainsKey('Authentication')) { $newParams['Authentication'] = $Authentication }
            if ($UseSSL) { $newParams['UseSSL'] = $true }
            if ($Port) { $newParams['Port'] = $Port }
            if ($SessionOption) { $newParams['SessionOption'] = $SessionOption }
            $targets = @(New-PSSession @newParams)
            $sessionsToClose = $targets
        }

        try {
            foreach ($s in $targets) {
                try {
                    $dest = Invoke-Command -Session $s -ScriptBlock {
                        $path = Join-Path $env:ProgramFiles 'WindowsPowerShell\Modules\SMBeat'
                        New-Item -ItemType Directory -Path $path -Force | Out-Null
                        $path
                    }
                    foreach ($name in $copyNames) {
                        $from = Join-Path $sourceRoot $name
                        if (Test-Path -LiteralPath $from) {
                            Copy-Item -LiteralPath $from -Destination $dest -ToSession $s -Recurse -Force
                        }
                    }
                    [PSCustomObject]@{
                        ComputerName = $s.ComputerName
                        Path         = $dest
                        Success      = $true
                    }
                }
                catch {
                    $record = New-SMBeatErrorRecord -Message $_.Exception.Message -ComputerName $s.ComputerName -Exception $_.Exception
                    $PSCmdlet.WriteError($record)
                }
            }
        }
        finally {
            if ($sessionsToClose.Count -gt 0) {
                Remove-PSSession -Session $sessionsToClose -ErrorAction SilentlyContinue
            }
        }
    }
}

function Uninstall-SMBeat {
    <#
    .SYNOPSIS
        Removes the SMBeat collector and its data. The module files stay installed.
    .DESCRIPTION
        Stops and unregisters the scheduled task, tears down the ETW session, and
        deletes the data root (default %ProgramData%\SMBeat and any Path from config).
        Does not delete the module under WindowsPowerShell\Modules.
    .PARAMETER Force
        Do not prompt before deleting data.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Local', SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(ParameterSetName = 'ComputerName', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]$ComputerName,

        [Parameter(ParameterSetName = 'Session')]
        [System.Management.Automation.Runspaces.PSSession[]]$Session,

        [pscredential]$Credential,
        [System.Management.Automation.Runspaces.AuthenticationMechanism]$Authentication,
        [switch]$UseSSL,
        [int]$Port,
        [System.Management.Automation.Remoting.PSSessionOption]$SessionOption,
        [int]$ThrottleLimit = 32,
        [string]$Path,
        [switch]$Force
    )

    begin {
        Assert-SMBeatRemotingExclusive -ComputerName $ComputerName -CimSession $null -Session $Session
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq 'Local') {
            $roots = @(Get-SMBeatDataRootsToRemove -Path $Path)
            $label = $roots -join ', '
            if (-not $Force -and -not $PSCmdlet.ShouldProcess($label, 'Remove SMBeat collector and data (module stays)')) {
                return
            }
            $result = Uninstall-SMBeatLocal -Path $Path
            Write-Host ('SMBeat collector and data removed. Module left at {0}' -f $result.ModulePath)
            return $result
        }

        $payload = @{
            Path  = $Path
            Force = $true
        }
        $sb = {
            param($P)
            Import-Module SMBeat -ErrorAction Stop
            $splat = @{ Force = $true }
            if ($P.Path) { $splat['Path'] = $P.Path }
            Uninstall-SMBeat @splat
        }

        $invoke = @{
            ScriptBlock   = $sb
            ArgumentList  = @($payload)
            ThrottleLimit = $ThrottleLimit
        }
        if ($PSCmdlet.ParameterSetName -eq 'Session') {
            $invoke['Session'] = $Session
        }
        else {
            $invoke['ComputerName'] = $ComputerName
            if ($Credential) { $invoke['Credential'] = $Credential }
            if ($PSBoundParameters.ContainsKey('Authentication')) { $invoke['Authentication'] = $Authentication }
            if ($UseSSL) { $invoke['UseSSL'] = $true }
            if ($Port) { $invoke['Port'] = $Port }
            if ($SessionOption) { $invoke['SessionOption'] = $SessionOption }
        }
        Invoke-SMBeatRemoteCommand @invoke
    }
}