Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PiHoleShell/PiHoleShell.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Export-ModuleMember -Function @(
'Get-PiHoleList', 'Search-PiHoleListDomain', 'Add-PiHoleList', 'Remove-PiHoleList', 'Update-PiHoleList', `
#FTLInformation
'Get-PiHoleInfoMessage', 'Get-PiHoleInfoHost', 'Get-PiHoleInfoClient', 'Get-PiHoleInfoLogin', 'Get-PiHoleInfoSystem', 'Get-PiHoleInfoFtl', `
'Get-PiHoleInfoSensors', 'Get-PiHoleInfoDatabase', 'Get-PiHoleInfoVersion', 'Get-PiHoleInfoMetrics', 'Get-PiHoleInfoMessageCount', 'Remove-PiHoleInfoMessage', 'Get-PiHoleLogWebserver', `
'Get-PiHoleInfoSensors', 'Get-PiHoleInfoDatabase', 'Get-PiHoleInfoVersion', 'Get-PiHoleInfoMetrics', 'Get-PiHoleInfoMessageCount', 'Remove-PiHoleInfoMessage', 'Get-PiHoleLogWebserver', 'Get-PiHoleLogDnsmasq', 'Get-PiHoleLogFtl', `
#History
'Get-PiHoleHistory', 'Get-PiHoleHistoryDatabase', 'Get-PiHoleHistoryClient', 'Get-PiHoleHistoryDatabaseClient', `
#Teleporter
Expand Down
91 changes: 91 additions & 0 deletions PiHoleShell/Public/FTLInformation/Get-PiHoleLogDnsmasq.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
function Get-PiHoleLogDnsmasq {
<#
.SYNOPSIS
Get DNS log content

.DESCRIPTION
Request content from the log of the embedded DNS resolver dnsmasq. Every response includes a
NextID; pass it back as -NextID on your next call to only get lines added since then, making
periodic polling for new log lines easy without checking for duplicates.

.PARAMETER PiHoleServer
The URL to the PiHole Server, for example "http://pihole.domain.com:8080", or "http://192.168.1.100"

.PARAMETER Password
The API Password you generated from your PiHole server

.PARAMETER NextID
Only return log lines added after this ID (returned as NextID on a previous call). Omit to
get the full available log

.PARAMETER IgnoreSsl
Set to $true to skip SSL certificate validation

.PARAMETER RawOutput
This will dump the response instead of the formatted object

.EXAMPLE
Get-PiHoleLogDnsmasq -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password"
#>
[CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#get-/logs/dnsmasq')]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
param (
[Parameter(Mandatory = $true)]
[System.URI]$PiHoleServer,
[Parameter(Mandatory = $true)]
[string]$Password,
[Nullable[int]]$NextID,
[bool]$IgnoreSsl = $false,
[bool]$RawOutput = $false
)
try {
$Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl

$Uri = "$($PiHoleServer.OriginalString)/api/logs/dnsmasq"
if ($PSBoundParameters.ContainsKey('NextID')) {
$Uri += "?nextID=$NextID"
}

$Params = @{
Headers = @{sid = $($Sid) }
Uri = $Uri
Method = "Get"
SkipCertificateCheck = $IgnoreSsl
ContentType = "application/json"
}

$Response = Invoke-RestMethod @Params

if ($RawOutput) {
Write-Output $Response
}

else {
$Log = foreach ($Item in $Response.log) {
[PSCustomObject]@{
Timestamp = (Convert-PiHoleUnixTimeToLocalTime -UnixTime $Item.timestamp).LocalTime
Message = $Item.message
Priority = $Item.prio
}
}

$Object = [PSCustomObject]@{
Log = $Log
NextID = $Response.nextID
Pid = $Response.pid
File = $Response.file
}
Write-Output $Object
}
}

catch {
Write-Error -Message $_.Exception.Message
}

finally {
if ($Sid) {
Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
}
}
}
91 changes: 91 additions & 0 deletions PiHoleShell/Public/FTLInformation/Get-PiHoleLogFtl.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
function Get-PiHoleLogFtl {
<#
.SYNOPSIS
Get FTL log content

.DESCRIPTION
Request content from FTL's own log. Every response includes a NextID; pass it back as -NextID
on your next call to only get lines added since then, making periodic polling for new log
lines easy without checking for duplicates.

.PARAMETER PiHoleServer
The URL to the PiHole Server, for example "http://pihole.domain.com:8080", or "http://192.168.1.100"

.PARAMETER Password
The API Password you generated from your PiHole server

.PARAMETER NextID
Only return log lines added after this ID (returned as NextID on a previous call). Omit to
get the full available log

.PARAMETER IgnoreSsl
Set to $true to skip SSL certificate validation

.PARAMETER RawOutput
This will dump the response instead of the formatted object

.EXAMPLE
Get-PiHoleLogFtl -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password"
#>
[CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#get-/logs/ftl')]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
param (
[Parameter(Mandatory = $true)]
[System.URI]$PiHoleServer,
[Parameter(Mandatory = $true)]
[string]$Password,
[Nullable[int]]$NextID,
[bool]$IgnoreSsl = $false,
[bool]$RawOutput = $false
)
try {
$Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl

$Uri = "$($PiHoleServer.OriginalString)/api/logs/ftl"
if ($PSBoundParameters.ContainsKey('NextID')) {
$Uri += "?nextID=$NextID"
}

$Params = @{
Headers = @{sid = $($Sid) }
Uri = $Uri
Method = "Get"
SkipCertificateCheck = $IgnoreSsl
ContentType = "application/json"
}

$Response = Invoke-RestMethod @Params

if ($RawOutput) {
Write-Output $Response
}

else {
$Log = foreach ($Item in $Response.log) {
[PSCustomObject]@{
Timestamp = (Convert-PiHoleUnixTimeToLocalTime -UnixTime $Item.timestamp).LocalTime
Message = $Item.message
Priority = $Item.prio
}
}

$Object = [PSCustomObject]@{
Log = $Log
NextID = $Response.nextID
Pid = $Response.pid
File = $Response.file
}
Write-Output $Object
}
}

catch {
Write-Error -Message $_.Exception.Message
}

finally {
if ($Sid) {
Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
}
}
}
82 changes: 53 additions & 29 deletions PiHoleShell/Public/GroupManagement/Remove-PiHoleGroup.ps1
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
function Remove-PiHoleGroup {
<#
.SYNOPSIS
Delete a group
Delete one or more groups

.DESCRIPTION
Deletes a group from Pi-hole. Any lists or clients assigned to it are unassigned, not deleted.
Deletes one or more groups from Pi-hole in a single batch call. Any lists or clients assigned
to a deleted group are unassigned, not deleted.

.PARAMETER PiHoleServer
The URL to the PiHole Server, for example "http://pihole.domain.com:8080", or "http://192.168.1.100"
Expand All @@ -13,7 +14,7 @@ The URL to the PiHole Server, for example "http://pihole.domain.com:8080", or "h
The API Password you generated from your PiHole server

.PARAMETER GroupName
The name of the group to delete
The name(s) of the group(s) to delete

.PARAMETER IgnoreSsl
Set to $true to skip SSL certificate validation
Expand All @@ -23,49 +24,72 @@ This will dump the response instead of the formatted object

.EXAMPLE
Remove-PiHoleGroup -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password" -GroupName "Kids"

.EXAMPLE
Remove-PiHoleGroup -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password" -GroupName "Kids", "Guests"
#>
[CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#delete-/groups/-name-')]
[Diagnostics.CodeAnalysis.SuppressMessage("PSUseShouldProcessForStateChangingFunctions", "", Justification = "Ignoring for now")]
[CmdletBinding(SupportsShouldProcess = $true, HelpUri = 'https://ftl.pi-hole.net/master/docs/#post-/groups-batchDelete')]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
param (
[Parameter(Mandatory = $true)]
[System.URI]$PiHoleServer,
[Parameter(Mandatory = $true)]
[string]$Password,
[Parameter(Mandatory = $true)]
[string]$GroupName,
[string[]]$GroupName,
[bool]$IgnoreSsl = $false,
[bool]$RawOutput = $false

)
try {
$Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl
$Target = "Pi-Hole group(s) $($GroupName -join ', ')"
if ($PSCmdlet.ShouldProcess($Target, "Remove group(s)")) {
# The batch delete API silently succeeds even for a group name that doesn't exist,
# rather than reporting it as an error - check every name exists first so this
# function never falsely reports a nonexistent group as "Deleted".
$AllGroups = Get-PiHoleGroup -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl
foreach ($Name in $GroupName) {
if (-not ($AllGroups | Where-Object { $_.Name -eq $Name })) {
throw "Cannot find $Name on $PiHoleServer! Please use Get-PiHoleGroup to list all groups"
}
}

$GetGroupName = Get-PiHoleGroup -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl -GroupName $GroupName
$Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl

if (-not $GetGroupName) {
throw "Cannot find $GroupName on $PiHoleServer! Please use Get-PiHoleGroup to list all groups"
}
# @() forces this to stay an array even for a single name - without it, PowerShell
# unwraps a one-item foreach result to a bare hashtable, which ConvertTo-Json then
# serializes as a JSON object instead of an array, and the API rejects it with 400.
$Body = @(foreach ($Name in $GroupName) {
@{ item = $Name }
})

$Params = @{
Headers = @{sid = $($Sid) }
Uri = "$($PiHoleServer.OriginalString)/api/groups/$GroupName"
Method = "Delete"
SkipCertificateCheck = $IgnoreSsl
ContentType = "application/json"
}
$Params = @{
Headers = @{sid = $($Sid) }
Uri = "$($PiHoleServer.OriginalString)/api/groups:batchDelete"
Method = "Post"
SkipCertificateCheck = $IgnoreSsl
# -InputObject (not piped) so ConvertTo-Json serializes the array as-is instead of
# unwrapping it into individual pipeline objects first.
Body = ConvertTo-Json -InputObject $Body -Depth 10 -Compress
ContentType = "application/json"
}

$Response = Invoke-RestMethod @Params
$Response = Invoke-RestMethod @Params

if ($RawOutput) {
Write-Output $Response
}
else {
$Object = [PSCustomObject]@{
Name = $GroupName
Status = "Deleted"
if ($RawOutput) {
Write-Output $Response
}

else {
# A successful delete returns 204 No Content, so there's no response body to
# build a rich object from.
$ObjectFinal = foreach ($Name in $GroupName) {
[PSCustomObject]@{
Name = $Name
Status = "Deleted"
}
}
Write-Output $ObjectFinal
}
Write-Output $Object
}
}

Expand All @@ -78,4 +102,4 @@ Remove-PiHoleGroup -PiHoleServer "http://pihole.domain.com:8080" -Password "your
Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
}
}
}
}
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ See [docs/EXAMPLES.md](docs/EXAMPLES.md) for real, captured output from every fu
|---|---|
| `Get-PiHoleGroup` | Get groups |
| `New-PiHoleGroup` | Creates a new group |
| `Remove-PiHoleGroup` | Delete a group |
| `Remove-PiHoleGroup` | Delete one or more groups |
| `Update-PiHoleGroup` | Update a group |

### List Management
Expand Down Expand Up @@ -180,6 +180,8 @@ See [docs/EXAMPLES.md](docs/EXAMPLES.md) for real, captured output from every fu
| `Get-PiHoleInfoSensors` | Get info about various sensors |
| `Get-PiHoleInfoSystem` | Get info about various system parameters |
| `Get-PiHoleInfoVersion` | Get Pi-hole version |
| `Get-PiHoleLogDnsmasq` | Get DNS log content |
| `Get-PiHoleLogFtl` | Get FTL log content |
| `Get-PiHoleLogWebserver` | Get webserver log content |
| `Get-PiHoleNetworkDevice` | Get info about the devices in your local network as seen by your Pi-hole |
| `Get-PiHoleNetworkGateway` | Get info about the gateway of your Pi-hole |
Expand Down
Loading
Loading