Public/psf-sensors.ps1

function Add-FalconSensorTag {
    [CmdletBinding(DefaultParameterSetName = 'HostId')]
    param(
        [Parameter(ParameterSetName = 'HostId', Mandatory = $true, Position = 1)]
        [Parameter(ParameterSetName = 'HostIds', Mandatory = $true, Position = 1)]
        [ValidateScript({
            @($_).foreach{
                if ((Test-RegexValue $_) -eq 'tag') {
                    $true
                } else {
                    throw "Valid values include letters, numbers, hyphens, unscores and forward slashes. ['$_']"
                }
            }
        })]
        [array] $Tags,

        [Parameter(ParameterSetName = 'HostId', Mandatory = $true, ValueFromPipeline = $true, Position = 2)]
        [ValidatePattern('^\w{32}$')]
        [Alias('device_id')]
        [string] $Id,

        [Parameter(ParameterSetName = 'HostIds', Mandatory = $true, Position = 2)]
        [ValidatePattern('^\w{32}$')]
        [array] $Ids,

        [Parameter(ParameterSetName = 'HostId', Position = 3)]
        [Parameter(ParameterSetName = 'HostIds', Position = 3)]
        [boolean] $QueueOffline
    )
    begin {
        $Scripts = @{
            Linux   = 'IFS=, read -ra tag <<< "$(/opt/CrowdStrike/falconctl -g --tags | sed "s/^Sensor grouping ' +
                'tags are not set//; s/^tags=//; s/.$//"),$1" && IFS=$"\n" uniq=($(printf "%s\n" ${tag[*]} | sor' +
                't -u | xargs)) && uniq="$(echo ${uniq[*]} | tr " " ",")" && /opt/CrowdStrike/falconctl -d -f --' +
                'tags && /opt/CrowdStrike/falconctl -s --tags="$uniq" && /opt/CrowdStrike/falconctl -g --tags | ' +
                'sed "s/^Sensor grouping tags are not set//; s/^tags=//; s/.$//"'
            Mac     = 'IFS=, tag=($(/Applications/Falcon.app/Contents/Resources/falconctl grouping-tags get | se' +
                'd "s/^No grouping tags set//; s/^Grouping tags: //")) tag+=($1) && uniq=$(echo "${tag[@]}" | tr' +
                ' " " "\n" | sort -u | tr "\n" "," | sed "s/,$//") && /Applications/Falcon.app/Contents/Resource' +
                's/falconctl grouping-tags clear &> /dev/null && /Applications/Falcon.app/Contents/Resources/fal' +
                'conctl grouping-tags set "$uniq" &> /dev/null && /Applications/Falcon.app/Contents/Resources/fa' +
                'lconctl grouping-tags get | sed "s/^No grouping tags set//; s/^Grouping tags: //"'
            Windows = '$K = "HKEY_LOCAL_MACHINE\SYSTEM\CrowdStrike\{9b03c1d9-3138-44ed-9fae-d9f4c034b88d}\{16e04' +
                '23f-7058-48c9-a204-725362b67639}\Default"; $T = (reg query $K) -match "GroupingTags" | Where-Ob' +
                'ject { $_ }; $V = if ($T) { (($T -split "REG_SZ")[-1].Trim().Split(",") + $args.Split(",") | Se' +
                'lect-Object -Unique) -join "," } else { $args }; [void] (reg add $K /v GroupingTags /d $V /f); ' +
                '"$((((reg query $K) -match "GroupingTags") -split "REG_SZ")[-1].Trim())"'
        }
    }
    process {
        try {
            # Get device info to determine script and begin session
            if ($PSCmdlet.ParameterSetName -eq 'HostId') {
                $HostInfo = Get-FalconHost -Ids $PSBoundParameters.Id | Select-Object cid, device_id, platform_name
                $InvokeParam = @{
                    HostId    = $HostInfo.device_id
                    Command   = 'runscript'
                    Arguments = '-Raw=```' + $Scripts.($HostInfo.platform_name) + '``` -CommandLine="' +
                        ($PSBoundParameters.Tags -join ',') + '"'
                }
                if ($PSBoundParameters.QueueOffline) {
                    $InvokeParam['QueueOffline'] = $PSBoundParameters.QueueOffline
                }
                Invoke-FalconRtr @InvokeParam | ForEach-Object {
                    if ($PSBoundParameters.QueueOffline) {
                        # Output queued command result
                        $_
                    } else {
                        # Output device properties and 'tags' value
                        [PSCustomObject] @{
                            cid       = $HostInfo.cid
                            device_id = $HostInfo.device_id
                            tags      = if ($_.stdout) { ($_.stdout).Trim() } else { $_.stderr }
                        }
                    }
                }
            } else {
                $HostInfo = Get-FalconHost -Ids $PSBoundParameters.Ids |
                    Select-Object cid, device_id, platform_name
                foreach ($Platform in ($HostInfo.platform_name | Group-Object).Name) {
                    # Start sessions for each 'platform' type
                    $InvokeParam = @{
                        Command   = 'runscript'
                        Arguments =  '-Raw=```' + $Scripts.$Platform + '``` -CommandLine="' +
                            ($PSBoundParameters.Tags -join ',') + '"'
                        HostIds   = ($HostInfo | Where-Object { $_.platform_name -eq $Platform }).device_id
                    }
                    if ($PSBoundParameters.QueueOffline) {
                        $InvokeParam['QueueOffline'] = $PSBoundParameters.QueueOffline
                    }
                    Invoke-FalconRtr @InvokeParam | Select-Object aid, stdout, stderr, errors |
                    ForEach-Object {
                        # Output device properties and 'tags' value
                        [PSCustomObject] @{
                            cid       = ($HostInfo | Where-Object device_id -eq $_.aid).cid
                            device_id = $_.aid
                            tags      = if ($_.stdout) {
                                ($_.stdout).Trim()
                            } elseif ($_.stderr) {
                                $_.stderr
                            } else {
                                $_.errors
                            }
                        }
                    }
                }
            }
        } catch {
            throw $_
        }
    }
}
function Get-FalconSensorTag {
    [CmdletBinding(DefaultParameterSetName = 'HostId')]
    param(
        [Parameter(ParameterSetName = 'HostId', Mandatory = $true, ValueFromPipeline = $true, Position = 1)]
        [ValidatePattern('^\w{32}$')]
        [Alias('device_id')]
        [string] $Id,

        [Parameter(ParameterSetName = 'HostIds', Mandatory = $true, Position = 1)]
        [ValidatePattern('^\w{32}$')]
        [array] $Ids,

        [Parameter(ParameterSetName = 'HostId', Position = 3)]
        [Parameter(ParameterSetName = 'HostIds', Position = 3)]
        [boolean] $QueueOffline
    )
    begin {
        $Scripts = @{
            Linux = "/opt/CrowdStrike/falconctl -g --tags | sed 's/^Sensor grouping tags are not set.//; s/^tags" +
                "=//; s/.$//'"
            Mac     = "/Applications/Falcon.app/Contents/Resources/falconctl grouping-tags get | sed 's/^No grou" +
                "ping tags set//; s/^Grouping tags: //'"
            Windows = '$T = (reg query "HKEY_LOCAL_MACHINE\SYSTEM\CrowdStrike\{9b03c1d9-3138-44ed-9fae-d9f4c034b' +
                '88d}\{16e0423f-7058-48c9-a204-725362b67639}\Default") -match "GroupingTags"; if ($T) { "$(($T -' +
                'split "REG_SZ")[-1].Trim())" }'
        }
    }
    process {
        try {
            if ($PSCmdlet.ParameterSetName -eq 'HostId') {
                $HostInfo = Get-FalconHost -Ids $PSBoundParameters.Id | Select-Object cid, device_id, platform_name
                $InvokeParam = @{
                    HostId    = $HostInfo.device_id
                    Command   = 'runscript'
                    Arguments = '-Raw=```' + $Scripts.($HostInfo.platform_name) + '```'
                }
                if ($PSBoundParameters.QueueOffline) {
                    $InvokeParam['QueueOffline'] = $PSBoundParameters.QueueOffline
                }
                Invoke-FalconRtr @InvokeParam | ForEach-Object {
                    if ($PSBoundParameters.QueueOffline) {
                        # Output queued command result
                        $_
                    } else {
                        # Output device properties and 'tags' value
                        [PSCustomObject] @{
                            cid       = $HostInfo.cid
                            device_id = $HostInfo.device_id
                            tags      = if ($_.stdout) { ($_.stdout).Trim() } else { $_.stderr }
                        }
                    }
                }
            } else {
                $HostInfo = Get-FalconHost -Ids $PSBoundParameters.Ids | Select-Object cid,
                    device_id, platform_name
                foreach ($Platform in ($HostInfo.platform_name | Group-Object).Name) {
                    # Start session for each 'platform' type
                    $InvokeParam = @{
                        Command   = 'runscript'
                        Arguments =  '-Raw=```' + $Scripts.$Platform + '```'
                        HostIds   = ($HostInfo | Where-Object { $_.platform_name -eq $Platform }).device_id
                    }
                    if ($PSBoundParameters.QueueOffline) {
                        $InvokeParam['QueueOffline'] = $PSBoundParameters.QueueOffline
                    }
                    Invoke-FalconRtr @InvokeParam | Select-Object aid, stdout, stderr, errors | ForEach-Object {
                        # Output device properties and 'tags' value
                        [PSCustomObject] @{
                            cid       = ($HostInfo | Where-Object device_id -eq $_.aid).cid
                            device_id = $_.aid
                            tags      = if ($_.stdout) {
                                ($_.stdout).Trim()
                            } elseif ($_.stderr) {
                                $_.stderr
                            } else {
                                $_.errors
                            }
                        }
                    }
                }
            }
        } catch {
            throw $_
        }
    }
}
function Remove-FalconSensorTag {
    [CmdletBinding(DefaultParameterSetName = 'HostId')]
    param(
        [Parameter(ParameterSetName = 'HostId', Mandatory = $true, Position = 1)]
        [Parameter(ParameterSetName = 'HostIds', Mandatory = $true, Position = 1)]
        [ValidateScript({
            @($_).foreach{
                if ((Test-RegexValue $_) -eq 'tag') {
                    $true
                } else {
                    throw "Valid values include letters, numbers, hyphens, unscores and forward slashes. ['$_']"
                }
            }
        })]
        [array] $Tags,

        [Parameter(ParameterSetName = 'HostId', Mandatory = $true, ValueFromPipeline = $true, Position = 2)]
        [ValidatePattern('^\w{32}$')]
        [Alias('device_id')]
        [string] $Id,

        [Parameter(ParameterSetName = 'HostIds', Mandatory = $true, Position = 2)]
        [ValidatePattern('^\w{32}$')]
        [array] $Ids,

        [Parameter(ParameterSetName = 'HostId', Position = 3)]
        [Parameter(ParameterSetName = 'HostIds', Position = 3)]
        [boolean] $QueueOffline
    )
    begin {
        $Scripts = @{
            Linux   = 'IFS=, && read -ra del <<< "$1" && read -ra tag <<< "$(/opt/CrowdStrike/falconctl -g --tag' +
                's | sed "s/^Sensor grouping tags are not set.//; s/^tags=//; s/.$//")"; if [[ ${tag[@]} ]]; the' +
                'n /opt/CrowdStrike/falconctl -d -f --tags && for i in ${del[@]}; do tag=(${tag[@]/$i}); done &&' +
                'IFS=$"\n" && val=($(printf "%s\n" ${tag[*]} | xargs)) && val="$(echo ${val[*]} | tr " " ",")" &' +
                '& /opt/CrowdStrike/falconctl -s --tags="$val"; fi; /opt/CrowdStrike/falconctl -g --tags | sed "' +
                's/^Sensor grouping tags are not set.//; s/^tags=//; s/.$//"'
            Mac     = 'IFS=, tag=($(/Applications/Falcon.app/Contents/Resources/falconctl grouping-tags get | se' +
                'd "s/^No grouping tags set//; s/^Grouping tags: //")) del=("${(@s/,/)1}") && for i in ${del[@]}' +
                '; do tag=("${tag[@]/$i}"); done && tag=$(echo "${tag[@]}" | xargs | tr " " "," | sed "s/,$//") ' +
                '&& /Applications/Falcon.app/Contents/Resources/falconctl grouping-tags clear &> /dev/null && /A' +
                'pplications/Falcon.app/Contents/Resources/falconctl grouping-tags set "$tag" &> /dev/null && /A' +
                'pplications/Falcon.app/Contents/Resources/falconctl grouping-tags get | sed "s/^No grouping tag' +
                's set//; s/^Grouping tags: //"'
            Windows = '$K = "HKEY_LOCAL_MACHINE\SYSTEM\CrowdStrike\{9b03c1d9-3138-44ed-9fae-d9f4c034b88d}\{16e04' +
                '23f-7058-48c9-a204-725362b67639}\Default"; $T = (reg query $K) -match "GroupingTags"; if ($T) {' +
                ' $D = $args.Split(","); $V = ($T -split "REG_SZ")[-1].Trim().Split(",").Where({ $D -notcontains' +
                ' $_ }) -join ","; if ($V) { [void] (reg add $K /v GroupingTags /d $V /f) } else { [void] (reg d' +
                'elete $K /v GroupingTags /f) }}; $T = (reg query $K) -match "GroupingTags"; if ($T) { ($T -spli' +
                't "REG_SZ")[-1].Trim() }'
        }
    }
    process {
        try {
            if ($PSCmdlet.ParameterSetName -eq 'HostId') {
                $HostInfo = Get-FalconHost -Ids $PSBoundParameters.Id | Select-Object cid, device_id, platform_name
                $InvokeParam = @{
                    HostId    = $HostInfo.device_id
                    Command   = 'runscript'
                    Arguments = '-Raw=```' + $Scripts.($HostInfo.platform_name) + '``` -CommandLine="' +
                        ($PSBoundParameters.Tags -join ',') + '"'
                }
                if ($PSBoundParameters.QueueOffline) {
                    $InvokeParam['QueueOffline'] = $PSBoundParameters.QueueOffline
                }
                Invoke-FalconRtr @InvokeParam | ForEach-Object {
                    if ($PSBoundParameters.QueueOffline) {
                        # Output queued command result
                        $_
                    } else {
                        # Output device properties and 'tags' value
                        [PSCustomObject] @{
                            cid       = $HostInfo.cid
                            device_id = $HostInfo.device_id
                            tags      = if ($_.stdout) { ($_.stdout).Trim() } else { $_.stderr }
                        }
                    }
                }
            } else {
                $HostInfo = Get-FalconHost -Ids $PSBoundParameters.Ids | Select-Object cid,
                    device_id, platform_name
                foreach ($Platform in ($HostInfo.platform_name | Group-Object).Name) {
                    # Start session for each 'platform' type
                    $InvokeParam = @{
                        Command   = 'runscript'
                        Arguments =  '-Raw=```' + $Scripts.$Platform + '``` -CommandLine="' +
                            ($PSBoundParameters.Tags -join ',') + '"'
                        HostIds   = ($HostInfo | Where-Object { $_.platform_name -eq $Platform }).device_id
                    }
                    if ($PSBoundParameters.QueueOffline) {
                        $InvokeParam['QueueOffline'] = $PSBoundParameters.QueueOffline
                    }
                    Invoke-FalconRtr @InvokeParam | Select-Object aid, stdout, stderr, errors |
                    ForEach-Object {
                        # Output device properties and 'tags' value
                        [PSCustomObject] @{
                            cid       = ($HostInfo | Where-Object device_id -eq $_.aid).cid
                            device_id = $_.aid
                            tags      = if ($_.stdout) {
                                ($_.stdout).Trim()
                            } elseif ($_.stderr) {
                                $_.stderr
                            } else {
                                $_.errors
                            }
                        }
                    }
                }
            }
        } catch {
            throw $_
        }
    }
}
function Uninstall-FalconSensor {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1)]
        [ValidatePattern('^\w{32}$')]
        [Alias('device_id')]
        [string] $Id,

        [Parameter(Position = 2)]
        [boolean] $QueueOffline
    )
    begin {
        $Scripts = @{
            Linux   = 'manager=("$(if [[ $(command -v apt) ]]; then echo "apt-get purge falcon-sensor -y"; elif ' +
                '[[ $(command -v yum) ]]; then echo "yum remove falcon-sensor -y"; elif [[ $(command -v zypper) ' +
                ']]; then echo "zypper remove -y falcon-sensor"; fi)"); if [[ ${manager} ]]; then echo "Beginnin' +
                'g removal of the Falcon sensor" && eval "sudo ${manager} &" &>/dev/null; else echo "apt, yum or' +
                ' zypper must be present to begin removal"; fi'
            Mac     = $null
            Windows = 'Start-Sleep -Seconds 5; $RegPath = if ((Get-WmiObject win32_operatingsystem).osarchitectu' +
                're -eq "64-bit") { "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" } el' +
                'se { "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" }; if (Test-Path $RegPath) { $' +
                'RegKey = Get-ChildItem $RegPath | Where-Object { $_.GetValue("DisplayName") -like "*CrowdStrike' +
                ' Windows Sensor*" }; if ($RegKey) { $UninstallString = $RegKey.GetValue("QuietUninstallString")' +
                '; $Arguments = @("/c", $UninstallString); if ($args) { $Arguments += "MAINTENANCE_TOKEN=$args" ' +
                '}; $ArgumentList = $Arguments -join " "; Start-Process -FilePath cmd.exe -ArgumentList $Argumen' +
                'tList -PassThru | Select-Object Id, ProcessName | ForEach-Object { Write-Output "[$($_.Id)] $($' +
                '_.ProcessName) started removal of the Falcon sensor" }}} else { Write-Error "Unable to locate $' +
                'RegPath" }'
        }
    }
    process {
        try {
            $HostInfo = Get-FalconHost -Ids $PSBoundParameters.Id | Select-Object cid, device_id,
                platform_name, device_policies
            if ($HostInfo.platform_name -eq 'Mac') {
                throw 'Only Windows and Linux hosts are currently supported in PSFalcon.'
            }
            $IdValue = switch ($HostInfo.device_policies.sensor_update.uninstall_protection) {
                'ENABLED'          { $HostInfo.device_id }
                'MAINTENANCE_MODE' { 'MAINTENANCE' }
            }
            $Token = if ($IdValue) {
                (Get-FalconUninstallToken -DeviceId $IdValue -AuditMessage (
                    "Uninstall-FalconSensor [$((Show-FalconModule).UserAgent)]")).uninstall_token
            }
            $InitParam = @{
                HostId       = $HostInfo.device_id
                QueueOffline = if ($PSBoundParameters.QueueOffline -eq $true) { $true } else { $false }
            }
            $Init = Start-FalconSession @InitParam
            if ($Init.session_id) {
                $CmdParam = @{
                    SessionId = $Init.session_id
                    Command   = 'runscript'
                    Arguments = '-Raw=```' + $Scripts.($HostInfo.platform_name) + '```'
                }
                if ($Token) { $CmdParam.Arguments += " -CommandLine='$Token'" }
                $Request = Invoke-FalconAdminCommand @CmdParam
                if ($Init.offline_queued -eq $false -and $Request.cloud_request_id) {
                    do {
                        Start-Sleep -Seconds 5
                        $Confirm = Confirm-FalconAdminCommand -CloudRequestId $Request.cloud_request_id
                    } until (
                        $Confirm.complete -ne $false -or $Confirm.stdout -or $Confirm.stderr
                    )
                    @($HostInfo | Select-Object cid, device_id).foreach{
                        $Status = if ($Confirm.stdout) { ($Confirm.stdout).Trim() } else { $Confirm.stderr }
                        Add-Property -Object $_ -Name 'status' -Value $Status
                        $_
                    }
                } else {
                    $Request
                }
            }
        } catch {
            throw $_
        }
    }
}