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
6 changes: 5 additions & 1 deletion PiHoleShell/PiHoleShell.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,9 @@ Export-ModuleMember -Function @(
#Teleporter
'Get-PiHoleTeleporterDownload', `
#DomainManagement
'Get-PiHoleDomain', 'New-PiHoleDomain', 'Update-PiHoleDomain', 'Remove-PiHoleDomain'
'Get-PiHoleDomain', 'New-PiHoleDomain', 'Update-PiHoleDomain', 'Remove-PiHoleDomain', `
#ClientManagement
'Get-PiHoleClient', 'New-PiHoleClient', 'Update-PiHoleClient', 'Remove-PiHoleClient', 'Get-PiHoleClientSuggestion', `
#NetworkInformation
'Get-PiHoleNetworkGateway', 'Get-PiHoleNetworkRoute', 'Get-PiHoleNetworkInterface', 'Get-PiHoleNetworkDevice', 'Remove-PiHoleNetworkDevice'
)
97 changes: 97 additions & 0 deletions PiHoleShell/Public/ClientManagement/Get-PiHoleClient.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
function Get-PiHoleClient {
<#
.SYNOPSIS
Get clients

.DESCRIPTION
Request Pi-hole's configured clients (used to apply group-based rules to specific devices).
Omit -Client to get every configured client; specify it to get just that one.

.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 Client
A specific client to return - an IP address, IP subnet (CIDR), MAC address, hostname, or
interface (prefixed with a colon, e.g. ":eth0"). Omit to return every configured client

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

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

.EXAMPLE
Get-PiHoleClient -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password"

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

$Groups = Get-PiHoleGroup -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl

$Uri = "$($PiHoleServer.OriginalString)/api/clients"
if ($Client) {
$Uri += "/$([System.Uri]::EscapeDataString($Client))"
}

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

$Response = Invoke-RestMethod @Params

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

else {
$ObjectFinal = foreach ($Item in $Response.clients) {
$GroupNames = [System.Collections.ArrayList]@()
foreach ($Group in $Item.groups) {
$GroupNames += ($Groups | Where-Object { $_.Id -eq $Group }).Name
}

[PSCustomObject]@{
Client = $Item.client
Name = $Item.name
Comment = $Item.comment
Groups = $GroupNames
Id = $Item.id
DateAdded = (Convert-PiHoleUnixTimeToLocalTime -UnixTime $Item.date_added).LocalTime
DateModified = (Convert-PiHoleUnixTimeToLocalTime -UnixTime $Item.date_modified).LocalTime
}
}
Write-Output $ObjectFinal
}
}

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

finally {
if ($Sid) {
Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
}
}
}
75 changes: 75 additions & 0 deletions PiHoleShell/Public/ClientManagement/Get-PiHoleClientSuggestion.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
function Get-PiHoleClientSuggestion {
<#
.SYNOPSIS
Get client suggestions

.DESCRIPTION
Returns a list of clients Pi-hole has seen making DNS queries but that aren't yet configured as
a client (see New-PiHoleClient to add one).

.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 IgnoreSsl
Set to $true to skip SSL certificate validation

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

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

$Params = @{
Headers = @{sid = $($Sid) }
Uri = "$($PiHoleServer.OriginalString)/api/clients/_suggestions"
Method = "Get"
SkipCertificateCheck = $IgnoreSsl
ContentType = "application/json"
}

$Response = Invoke-RestMethod @Params

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

else {
$ObjectFinal = foreach ($Item in $Response.clients) {
[PSCustomObject]@{
HwAddr = $Item.hwaddr
MacVendor = $Item.macVendor
LastQuery = (Convert-PiHoleUnixTimeToLocalTime -UnixTime $Item.lastQuery).LocalTime
Addresses = $Item.addresses
Names = $Item.names
}
}
Write-Output $ObjectFinal
}
}

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

finally {
if ($Sid) {
Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
}
}
}
121 changes: 121 additions & 0 deletions PiHoleShell/Public/ClientManagement/New-PiHoleClient.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
function New-PiHoleClient {
<#
.SYNOPSIS
Add a new client

.DESCRIPTION
Adds a client to Pi-hole so group-based rules can be applied to it specifically. A client may
be identified by IP address, IP subnet (CIDR notation), MAC address, hostname, or the interface
it connects through (prefixed with a colon, e.g. ":eth0"). IP-based recognition is preferred -
MAC address, hostname, and interface recognition only work for devices Pi-hole has already seen.

.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 Client
The client to add - an IP address, IP subnet (CIDR), MAC address, hostname, or interface
(prefixed with a colon, e.g. ":eth0")

.PARAMETER Comment
An optional comment to store alongside the client

.PARAMETER Group
The group(s) this client applies to. Defaults to "Default"

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

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

.EXAMPLE
New-PiHoleClient -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password" -Client "192.168.1.50" -Comment "Kid's tablet"
#>
[CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#post-/clients')]
[Diagnostics.CodeAnalysis.SuppressMessage("PSUseShouldProcessForStateChangingFunctions", "", Justification = "Ignoring for now")]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
param (
[Parameter(Mandatory = $true)]
[System.URI]$PiHoleServer,
[Parameter(Mandatory = $true)]
[string]$Password,
[Parameter(Mandatory = $true)]
[string]$Client,
[string]$Comment = $null,
[string[]]$Group = "Default",
[bool]$IgnoreSsl = $false,
[bool]$RawOutput = $false
)
try {
$FindMatchingClient = Get-PiHoleClient -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl -Client $Client

if ($FindMatchingClient) {
throw "Client $Client already exists on $PiHoleServer! Please use Update-PiHoleClient to update it"
}

$AllGroups = Get-PiHoleGroup -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl
$AllGroupsNames = @()
$AllGroupsIds = @()
foreach ($GroupItem in $Group) {
$FoundGroup = $AllGroups | Where-Object { $_.Name -eq $GroupItem }
if ($FoundGroup) {
$AllGroupsNames += $FoundGroup.Name
$AllGroupsIds += $FoundGroup.Id
}
else {
throw "Cannot find $GroupItem on $PiHoleServer! Please use Get-PiHoleGroup to list all groups"
}
}

$Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl

$Body = @{
client = $Client
comment = $Comment
groups = [Object[]]($AllGroupsIds)
}

$Params = @{
Headers = @{sid = $($Sid) }
Uri = "$($PiHoleServer.OriginalString)/api/clients"
Method = "Post"
SkipCertificateCheck = $IgnoreSsl
Body = $Body | ConvertTo-Json -Depth 10
ContentType = "application/json"
}

$Response = Invoke-RestMethod @Params

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

else {
$ObjectFinal = foreach ($Item in $Response.clients) {
[PSCustomObject]@{
Client = $Item.client
Name = $Item.name
Comment = $Item.comment
Groups = $AllGroupsNames
Id = $Item.id
DateAdded = (Convert-PiHoleUnixTimeToLocalTime -UnixTime $Item.date_added).LocalTime
DateModified = (Convert-PiHoleUnixTimeToLocalTime -UnixTime $Item.date_modified).LocalTime
}
}
Write-Output $ObjectFinal
}
}

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

finally {
if ($Sid) {
Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
}
}
}
90 changes: 90 additions & 0 deletions PiHoleShell/Public/ClientManagement/Remove-PiHoleClient.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
function Remove-PiHoleClient {
<#
.SYNOPSIS
Remove a client

.DESCRIPTION
Removes a client from Pi-hole. The Pi-hole API deletes clients in a batch, so this sends a
single-item batch containing just the client you specify.

.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 Client
The client to remove - an IP address, IP subnet (CIDR), MAC address, hostname, or interface
(prefixed with a colon, e.g. ":eth0")

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

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

.EXAMPLE
Remove-PiHoleClient -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password" -Client "192.168.1.50"
#>
[CmdletBinding(SupportsShouldProcess = $true, HelpUri = 'https://ftl.pi-hole.net/master/docs/#post-/clients-batchDelete')]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
param (
[Parameter(Mandatory = $true)]
[System.URI]$PiHoleServer,
[Parameter(Mandatory = $true)]
[string]$Password,
[Parameter(Mandatory = $true)]
[string]$Client,
[bool]$IgnoreSsl = $false,
[bool]$RawOutput = $false
)
try {
$Target = "Pi-Hole client $Client"
if ($PSCmdlet.ShouldProcess($Target, "Remove client")) {
$Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl

$Body = @(
@{
item = $Client
}
)

#For some reason this needs to be here to make it an array
$Body = , $Body
$Params = @{
Headers = @{sid = $($Sid) }
Uri = "$($PiHoleServer.OriginalString)/api/clients:batchDelete"
Method = "Post"
SkipCertificateCheck = $IgnoreSsl
Body = $Body | ConvertTo-Json -Depth 10 -Compress
ContentType = "application/json"
}

$Response = Invoke-RestMethod @Params

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.
$Object = [PSCustomObject]@{
Client = $Client
Status = "Removed"
}
Write-Output $Object
}
}
}

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

finally {
if ($Sid) {
Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl
}
}
}
Loading
Loading