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
4 changes: 3 additions & 1 deletion PiHoleShell/PiHoleShell.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ Export-ModuleMember -Function @(
#History
'Get-PiHoleHistory', 'Get-PiHoleHistoryDatabase', 'Get-PiHoleHistoryClient', 'Get-PiHoleHistoryDatabaseClient', `
#Teleporter
'Get-PiHoleTeleporterDownload'
'Get-PiHoleTeleporterDownload', `
#DomainManagement
'Get-PiHoleDomain', 'New-PiHoleDomain', 'Update-PiHoleDomain', 'Remove-PiHoleDomain'
)
123 changes: 123 additions & 0 deletions PiHoleShell/Public/DomainManagement/Get-PiHoleDomain.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
function Get-PiHoleDomain {
<#
.SYNOPSIS
Get domains

.DESCRIPTION
Request Pi-hole's per-domain allow/deny list entries (the newer domain-based API, distinct
from the Lists functions which manage whole allow/block list subscriptions). Omit all filters
to get every domain; narrow the results with -Type, -Kind, and -Domain.

.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 Domain
Only return this domain. Requires -Type and -Kind to also be specified

.PARAMETER Type
Only return domains of this type (Allow or Deny). Required if -Kind or -Domain is specified

.PARAMETER Kind
Only return domains of this kind (Exact match or Regex). Requires -Type to also be specified

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

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

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

.EXAMPLE
Get-PiHoleDomain -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password" -Type Allow -Kind Exact -Domain "example.com"
#>
[CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#get-/domains')]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
param (
[Parameter(Mandatory = $true)]
[System.URI]$PiHoleServer,
[Parameter(Mandatory = $true)]
[string]$Password,
[string]$Domain,
[ValidateSet("Allow", "Deny")]
[string]$Type,
[ValidateSet("Exact", "Regex")]
[string]$Kind,
[bool]$IgnoreSsl = $false,
[bool]$RawOutput = $false
)
try {
if ($Kind -and -not $Type) {
throw "-Type must be specified when -Kind is specified"
}
if ($Domain -and (-not $Type -or -not $Kind)) {
throw "-Type and -Kind must both be specified when -Domain is specified"
}

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

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

$Uri = "$($PiHoleServer.OriginalString)/api/domains"
if ($Type) {
$Uri += "/$($Type.ToLower())"
}
if ($Kind) {
$Uri += "/$($Kind.ToLower())"
}
if ($Domain) {
$Uri += "/$([System.Uri]::EscapeDataString($Domain))"
}

$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.domains) {
$GroupNames = [System.Collections.ArrayList]@()
foreach ($Group in $Item.groups) {
$GroupNames += ($Groups | Where-Object { $_.Id -eq $Group }).Name
}

[PSCustomObject]@{
Domain = $Item.domain
Unicode = $Item.unicode
Type = $Item.type
Kind = $Item.kind
Comment = $Item.comment
Groups = $GroupNames
Enabled = $Item.enabled
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
}
}
}
139 changes: 139 additions & 0 deletions PiHoleShell/Public/DomainManagement/New-PiHoleDomain.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
function New-PiHoleDomain {
<#
.SYNOPSIS
Add a new domain

.DESCRIPTION
Adds a domain to Pi-hole's per-domain allow/deny list (the newer domain-based API, distinct
from the Lists functions which manage whole allow/block list subscriptions). Use -Kind Regex
to add a regular expression instead of an exact domain match.

.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 Domain
The domain (or regular expression, if -Kind is Regex) to add

.PARAMETER Type
Whether this is an allowed or denied domain

.PARAMETER Kind
Whether -Domain is an exact match or a regular expression

.PARAMETER Comment
An optional comment to store alongside the domain

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

.PARAMETER Enabled
Whether the domain is enabled immediately. Defaults to $true

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

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

.EXAMPLE
New-PiHoleDomain -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password" -Domain "example.com" -Type Allow -Kind Exact
#>
[CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#post-/domains/-type-/-kind-')]
[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]$Domain,
[Parameter(Mandatory = $true)]
[ValidateSet("Allow", "Deny")]
[string]$Type,
[Parameter(Mandatory = $true)]
[ValidateSet("Exact", "Regex")]
[string]$Kind,
[string]$Comment = $null,
[string[]]$Group = "Default",
[bool]$Enabled = $true,
[bool]$IgnoreSsl = $false,
[bool]$RawOutput = $false
)
try {
$FindMatchingDomain = Get-PiHoleDomain -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl -Type $Type -Kind $Kind -Domain $Domain

if ($FindMatchingDomain) {
throw "Domain $Domain ($Type/$Kind) already exists on $PiHoleServer! Please use Update-PiHoleDomain 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 = @{
domain = $Domain
comment = $Comment
groups = [Object[]]($AllGroupsIds)
enabled = $Enabled
}

$Params = @{
Headers = @{sid = $($Sid) }
Uri = "$($PiHoleServer.OriginalString)/api/domains/$($Type.ToLower())/$($Kind.ToLower())"
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.domains) {
[PSCustomObject]@{
Domain = $Item.domain
Unicode = $Item.unicode
Type = $Item.type.SubString(0, 1).ToUpper() + $Item.type.SubString(1).ToLower()
Kind = $Item.kind.SubString(0, 1).ToUpper() + $Item.kind.SubString(1).ToLower()
Comment = $Item.comment
Groups = $AllGroupsNames
Enabled = $Item.enabled
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
}
}
}
105 changes: 105 additions & 0 deletions PiHoleShell/Public/DomainManagement/Remove-PiHoleDomain.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
function Remove-PiHoleDomain {
<#
.SYNOPSIS
Remove a domain

.DESCRIPTION
Removes a domain from Pi-hole's per-domain allow/deny list. The Pi-hole API deletes domains in
a batch, so this sends a single-item batch containing just the domain 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 Domain
The domain to remove

.PARAMETER Type
Whether this is an allowed or denied domain

.PARAMETER Kind
Whether -Domain is an exact match or a regular expression

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

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

.EXAMPLE
Remove-PiHoleDomain -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password" -Domain "example.com" -Type Allow -Kind Exact
#>
[CmdletBinding(SupportsShouldProcess = $true, HelpUri = 'https://ftl.pi-hole.net/master/docs/#post-/domains-batchDelete')]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")]
param (
[Parameter(Mandatory = $true)]
[System.URI]$PiHoleServer,
[Parameter(Mandatory = $true)]
[string]$Password,
[Parameter(Mandatory = $true)]
[string]$Domain,
[Parameter(Mandatory = $true)]
[ValidateSet("Allow", "Deny")]
[string]$Type,
[Parameter(Mandatory = $true)]
[ValidateSet("Exact", "Regex")]
[string]$Kind,
[bool]$IgnoreSsl = $false,
[bool]$RawOutput = $false
)
try {
$Target = "Pi-Hole domain $Domain of type $Type/$Kind"
if ($PSCmdlet.ShouldProcess($Target, "Remove domain")) {
$Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl

$Body = @(
@{
item = $Domain
type = $Type.ToLower()
kind = $Kind.ToLower()
}
)

#For some reason this needs to be here to make it an array
$Body = , $Body
$Params = @{
Headers = @{sid = $($Sid) }
Uri = "$($PiHoleServer.OriginalString)/api/domains: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]@{
Domain = $Domain
Type = $Type
Kind = $Kind
Status = "Removed"
}
Write-Output $Object
}
}
}

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

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