From 74f7f46eb7f4057b7c7d1cb164c5b3ed63728d17 Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Fri, 27 Jun 2025 16:52:37 -0500 Subject: [PATCH 01/13] added more functions --- PiHoleShell/Public/FTLInformation.ps1 | 60 +++++++++++++++++++++++ PiHoleShell/Public/Teleporter.ps1 | 68 +++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 PiHoleShell/Public/Teleporter.ps1 diff --git a/PiHoleShell/Public/FTLInformation.ps1 b/PiHoleShell/Public/FTLInformation.ps1 index 98beadf..2f22165 100644 --- a/PiHoleShell/Public/FTLInformation.ps1 +++ b/PiHoleShell/Public/FTLInformation.ps1 @@ -127,6 +127,66 @@ This API hook returns a collection of host infos. break } + finally { + if ($Sid) { + Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl + } + } +} + +function Get-PiHoleLogWebserver { + <# +.SYNOPSIS +Get info about logs for webserver + + #> + #Work In Progress + [CmdletBinding()] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] + param ( + [Parameter(Mandatory = $true)] + [System.URI]$PiHoleServer, + [Parameter(Mandatory = $true)] + [string]$Password, + [int]$NextID, + [bool]$IgnoreSsl = $false, + [bool]$RawOutput = $false + ) + try { + $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl + + if ($NextID) { + $Uri = "$($PiHoleServer.OriginalString)/api/logs/webserver?nextId=$NextId" + + } + else { + $Uri = "$($PiHoleServer.OriginalString)/api/logs/webserver" + } + + $Params = @{ + Headers = @{sid = $($Sid) } + Uri = $Uri + Method = "Get" + SkipCertificateCheck = $IgnoreSsl + ContentType = "application/json" + } + + $Response = Invoke-RestMethod @Params + + if ($RawOutput) { + Write-Output $Response + } + + else { + $ObjectFinal = @() + } + } + + catch { + Write-Error -Message $_.Exception.Message + break + } + finally { if ($Sid) { Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl diff --git a/PiHoleShell/Public/Teleporter.ps1 b/PiHoleShell/Public/Teleporter.ps1 new file mode 100644 index 0000000..8c8d88b --- /dev/null +++ b/PiHoleShell/Public/Teleporter.ps1 @@ -0,0 +1,68 @@ +function Get-PiHoleTeleporter { + <# +.SYNOPSIS +Get info about logs for webserver + + #> + #Work In Progress + [CmdletBinding()] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] + param ( + [Parameter(Mandatory = $true)] + [System.URI]$PiHoleServer, + [Parameter(Mandatory = $true)] + [string]$Password, + [Parameter(Mandatory = $true)] + [System.IO.DirectoryInfo]$FolderPath, + [Parameter(Mandatory = $true)] + [string]$FileName, + [bool]$IgnoreSsl = $false, + [bool]$RawOutput = $false + ) + try { + $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl + + if (!(Test-Path -Path $FolderPath)) { + throw "$FolderPath does not exist!" + } + $OutFile = "$FolderPath\$FileName.tar.gz" + if (Test-Path -Path $OutFile) { + throw "$OutFile already exists!" + } + + $Params = @{ + Headers = @{sid = $($Sid) } + Uri = "$($PiHoleServer.OriginalString)/api/teleporter" + Method = "Get" + SkipCertificateCheck = $IgnoreSsl + ContentType = "application/json" + } + + $Response = Invoke-RestMethod @Params -OutFile $OutFile + + if ($RawOutput) { + Write-Output $Response + } + + else { + $ObjectFinal = @() + $Object = [PSCustomObject]@{ + Backup = "Completed" + FilePath = $OutFile + } + $ObjectFinal += $Object + Write-Output $ObjectFinal + } + } + + catch { + Write-Error -Message $_.Exception.Message + break + } + + finally { + if ($Sid) { + Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl + } + } +} \ No newline at end of file From eecac04f783ae75c26f5c0feaa8202ae40151cdd Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Fri, 27 Jun 2025 17:05:37 -0500 Subject: [PATCH 02/13] debugging --- PiHoleShell/Public/FTLInformation.ps1 | 2 +- PiHoleShell/Public/Teleporter.ps1 | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/PiHoleShell/Public/FTLInformation.ps1 b/PiHoleShell/Public/FTLInformation.ps1 index 2f22165..ad14d80 100644 --- a/PiHoleShell/Public/FTLInformation.ps1 +++ b/PiHoleShell/Public/FTLInformation.ps1 @@ -178,7 +178,7 @@ Get info about logs for webserver } else { - $ObjectFinal = @() + #$ObjectFinal = @() } } diff --git a/PiHoleShell/Public/Teleporter.ps1 b/PiHoleShell/Public/Teleporter.ps1 index 8c8d88b..b21f602 100644 --- a/PiHoleShell/Public/Teleporter.ps1 +++ b/PiHoleShell/Public/Teleporter.ps1 @@ -25,7 +25,8 @@ Get info about logs for webserver if (!(Test-Path -Path $FolderPath)) { throw "$FolderPath does not exist!" } - $OutFile = "$FolderPath\$FileName.tar.gz" + $FileName = "$FileName.tar.gz" + $OutFile = "$FolderPath\$FileName" if (Test-Path -Path $OutFile) { throw "$OutFile already exists!" } @@ -47,8 +48,10 @@ Get info about logs for webserver else { $ObjectFinal = @() $Object = [PSCustomObject]@{ - Backup = "Completed" - FilePath = $OutFile + FileName = $FileName + FilePath = $OutFile + RootFolder = $FolderPath + FileSizeKB = [math]::Ceiling((Get-Item $OutFile).Length / 1KB) } $ObjectFinal += $Object Write-Output $ObjectFinal From 3ac7ce899f1b67d4fd7f05855586ae568be9f64f Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Fri, 27 Jun 2025 17:08:54 -0500 Subject: [PATCH 03/13] debugging --- PiHoleShell/Public/Teleporter.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PiHoleShell/Public/Teleporter.ps1 b/PiHoleShell/Public/Teleporter.ps1 index b21f602..4da0524 100644 --- a/PiHoleShell/Public/Teleporter.ps1 +++ b/PiHoleShell/Public/Teleporter.ps1 @@ -1,4 +1,4 @@ -function Get-PiHoleTeleporter { +function Get-PiHoleTeleporterDownload { <# .SYNOPSIS Get info about logs for webserver From 12cd5f3656182426e5b03eb6f52b436d19996bde Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Tue, 1 Jul 2025 23:00:08 -0500 Subject: [PATCH 04/13] updated --- PiHoleShell/PiHoleShell.psd1 | Bin 3276 -> 1624 bytes PiHoleShell/PiHoleShell.psm1 | 19 ++- PiHoleShell/Public/ListManagement.ps1 | 187 +++++++++++++++++++++++++- 3 files changed, 204 insertions(+), 2 deletions(-) diff --git a/PiHoleShell/PiHoleShell.psd1 b/PiHoleShell/PiHoleShell.psd1 index 12c97c92eab9d15d3d0bcd8ee6e1eb29d8d8d9a9..431211d3eb965c486deb411cad1a5dd10617c8ab 100644 GIT binary patch delta 16 XcmX>jd4p#|1JmRdCYQ|?oKu(qIUNP$ literal 3276 zcmd6pYflqF6o$`d6aT{|d{~V_CCJUhgxVsK0I3xCY=MdudP#Q+8slGApLd2~ce~p{ zh%siex7l-h&v|F&{Qk9NKkTVJvFLUj+K`djKKFB**p+qf{skHTe9L@nE$i6_WF^F!>($xGkkzr)FVrgKD?)F8wldtoTFT4~bBZ={cSUgx zLfwmX7=k$})HSj?-@8+hHt^FY#t!y+cIp_=f&<$ilP4c}f-RxnvS;W^XNstI>AF5x#Vso#6Gc5kNem!*!0(}uu?ZhE^xc_{%TP}iYtf*gTx@U)o%frV|>=%QSPX0`Ho@CHC!qWwj zhwtneuok<^P^YRsVZBtD%$k1_*Ze9~u8tjM|3}_p`l6JfuJw)87#GN^TbfXrB{a3l zYFxg96AwfGRx6zFS?^MFH63!7o?oFg$L0pz$bsvWioM@-%&N?g9#MU3`cGn>ACFE? z_4%G}2gD}Egbpuu>(qCC9poaS&&#Xt6x-D;XbtOxQ=eJx`XYMt7t|!r(vX{duq^ diff --git a/PiHoleShell/PiHoleShell.psm1 b/PiHoleShell/PiHoleShell.psm1 index 20495c5..b3707eb 100644 --- a/PiHoleShell/PiHoleShell.psm1 +++ b/PiHoleShell/PiHoleShell.psm1 @@ -12,4 +12,21 @@ foreach ($File in $PublicFunctions) { foreach ($File in $PrivateFunctions) { . $File.FullName -} \ No newline at end of file +} + +Export-ModuleMember -Function @( + #Authentication.ps1 + 'Remove-PiHoleCurrentAuthSession' , 'Get-PiHoleCurrentAuthSession', 'Remove-PiHoleAuthSession', ` + #GroupManagement.ps1 + 'Get-PiHoleGroup', 'New-PiHoleGroup', 'Update-PiHoleGroup', 'Remove-PiHoleGroup', ` + #DnsControl.ps1 + 'Get-PiHoleDnsBlockingStatus', 'Set-PiHoleDnsBlocking', ` + #Config.ps1 + 'Get-PiHoleConfig', 'Get-PiHoleCurrentAuthSession', 'Remove-PiHoleAuthSession', ` + #Metrics.ps1 + 'Get-PiHoleStatsRecentBlocked', 'Get-PiHoleStatsQueryType', 'Get-PiHoleStatsTopDomain', 'Get-PiHoleStatsSummary', ` + #ListManagement.ps1 + 'Get-PiHoleList', 'Search-PiHoleListDomain', 'Add-PiHoleList', ` + #FTLInformation.ps1 + 'Get-PiHoleInfoMessage', 'Get-PiHoleInfoHost' +) \ No newline at end of file diff --git a/PiHoleShell/Public/ListManagement.ps1 b/PiHoleShell/Public/ListManagement.ps1 index 7366e3d..5801aa7 100644 --- a/PiHoleShell/Public/ListManagement.ps1 +++ b/PiHoleShell/Public/ListManagement.ps1 @@ -65,12 +65,197 @@ https://TODO AbpEntries = $Item.abp_entries Status = $Item.status } + if ($Object) { + $ObjectFinal += $Object + } - $ObjectFinal += $Object } + Write-Output $ObjectFinal + } + } + + catch { + Write-Error -Message $_.Exception.Message + break + } + + finally { + if ($Sid) { + Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl + } + } +} + +function Search-PiHoleListDomain { + <# +.SYNOPSIS +https://TODO + + #> + #Work In Progress + [CmdletBinding()] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] + param ( + [Parameter(Mandatory = $true)] + [System.URI]$PiHoleServer, + [Parameter(Mandatory = $true)] + [string]$Password, + [Parameter(Mandatory = $true)] + [System.URI]$Domain, + [bool]$PartialMatch = $false, + [int]$MaxResults = 20, + [bool]$IgnoreSsl = $false, + [bool]$RawOutput = $false + ) + try { + $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl + + $Body = @{ + n = $MaxResults + partial = $PartialMatch + name = $GroupName + } + + $Params = @{ + Headers = @{sid = $($Sid) } + Uri = "$($PiHoleServer.OriginalString)/api/search/$Domain" + Method = "Get" + SkipCertificateCheck = $IgnoreSsl + Body = $Body + ContentType = "application/json" + } + + $Response = Invoke-RestMethod @Params + + if ($RawOutput) { + Write-Output $Response + } + else { + $ObjectFinal = @() + foreach ($Item in $Response.lists) { + } Write-Output $ObjectFinal + } + } + + catch { + Write-Error -Message $_.Exception.Message + break + } + + finally { + if ($Sid) { + Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl + } + } +} + +function Add-PiHoleList { + <# +.SYNOPSIS +https://TODO + + #> + #Work In Progress + [CmdletBinding()] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] + param ( + [Parameter(Mandatory = $true)] + [System.URI]$PiHoleServer, + [Parameter(Mandatory = $true)] + [string]$Password, + [bool]$IgnoreSsl = $false, + [System.Uri]$Address, + [Parameter(Mandatory = $true)] + [ValidateSet("Allow", "Block")] + [string]$Type, + [string]$Comment = $null, + [string[]]$Group = "Default", + [bool]$Enabled = $true, + [bool]$RawOutput = $false + ) + try { + $FindMatchingList = Get-PiHoleList -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl | Where-Object { $_.Address -eq $Address } + + if ($FindMatchingList) { + throw "List $Address already exists on $PiHoleServer! Please use Update-PiHoleList to update the list" + } + + $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 + Write-Verbose -Message "Found Group $($FoundGroup.Name) with $($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 = @{ + address = $Address + type = $Type + groups = [Object[]]($AllGroupsIds) + comment = $Comment + enabled = $Enabled + } + + $Params = @{ + Headers = @{sid = $($Sid) } + Uri = "$($PiHoleServer.OriginalString)/api/lists" + Method = "Post" + SkipCertificateCheck = $IgnoreSsl + Body = $Body | ConvertTo-Json -Depth 10 + ContentType = "application/json" + } + $Response = Invoke-RestMethod @Params + + if ($Item.date_updated -eq 0) { + $DateUpdated = $null + } + else { + $DateUpdated = (Convert-PiHoleUnixTimeToLocalTime -UnixTime $Item.date_modified).LocalTime + } + + if ($RawOutput) { + Write-Output $Response + } + + else { + $ObjectFinal = @() + $Object = $null + foreach ($Item in $Response.lists) { + + $Object = [PSCustomObject]@{ + Address = $Item.address + 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 + Type = $Item.type.SubString(0, 1).ToUpper() + $Item.type.SubString(1).ToLower() + DateUpdated = $DateUpdated + Number = $Item.number + InvalidDomains = $Item.invalid_domains + AbpEntries = $Item.abp_entries + Status = $Item.status + } + if ($Object) { + $ObjectFinal += $Object + } + + } + Write-Output $ObjectFinal } } From 9fbd757a211f3e9b0104d912678e7e03ed495418 Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Tue, 1 Jul 2025 23:26:27 -0500 Subject: [PATCH 05/13] debugging --- PiHoleShell/PiHoleShell.psm1 | 2 +- PiHoleShell/Public/ListManagement.ps1 | 90 ++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/PiHoleShell/PiHoleShell.psm1 b/PiHoleShell/PiHoleShell.psm1 index b3707eb..6904a98 100644 --- a/PiHoleShell/PiHoleShell.psm1 +++ b/PiHoleShell/PiHoleShell.psm1 @@ -26,7 +26,7 @@ Export-ModuleMember -Function @( #Metrics.ps1 'Get-PiHoleStatsRecentBlocked', 'Get-PiHoleStatsQueryType', 'Get-PiHoleStatsTopDomain', 'Get-PiHoleStatsSummary', ` #ListManagement.ps1 - 'Get-PiHoleList', 'Search-PiHoleListDomain', 'Add-PiHoleList', ` + 'Get-PiHoleList', 'Search-PiHoleListDomain', 'Add-PiHoleList', 'Remove-PiHoleList', ` #FTLInformation.ps1 'Get-PiHoleInfoMessage', 'Get-PiHoleInfoHost' ) \ No newline at end of file diff --git a/PiHoleShell/Public/ListManagement.ps1 b/PiHoleShell/Public/ListManagement.ps1 index 5801aa7..415d40e 100644 --- a/PiHoleShell/Public/ListManagement.ps1 +++ b/PiHoleShell/Public/ListManagement.ps1 @@ -177,7 +177,7 @@ https://TODO ) try { $FindMatchingList = Get-PiHoleList -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl | Where-Object { $_.Address -eq $Address } - + if ($FindMatchingList) { throw "List $Address already exists on $PiHoleServer! Please use Update-PiHoleList to update the list" } @@ -197,7 +197,7 @@ https://TODO throw "Cannot find $GroupItem on $PiHoleServer! Please use Get-PiHoleGroup to list all groups" } } - + $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl $Body = @{ @@ -264,6 +264,92 @@ https://TODO break } + finally { + if ($Sid) { + Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl + } + } +} + +function Remove-PiHoleList { + <# +.SYNOPSIS +https://TODO + + #> + #Work In Progress (DOES NOT WORK) + [CmdletBinding()] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] + param ( + [Parameter(Mandatory = $true)] + [System.URI]$PiHoleServer, + [Parameter(Mandatory = $true)] + [string]$Password, + [bool]$IgnoreSsl = $false, + [System.Uri]$Address, + [bool]$RawOutput = $false + ) + try { + $FindMatchingList = Get-PiHoleList -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl | Where-Object { $_.Address -eq $Address } + + if ($FindMatchingList) { + + } + + $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl + + Add-Type -AssemblyName System.Web + $List = [System.Uri]::EscapeDataString($Address) + Write-Verbose -Message "List: $List" + + $Params = @{ + Headers = @{sid = $($Sid) } + Uri = "$($PiHoleServer.OriginalString)/api/lists/$List?type=block" + Method = "Delete" + SkipCertificateCheck = $IgnoreSsl + ContentType = "application/json" + } + + $Response = Invoke-RestMethod @Params + + if ($RawOutput) { + Write-Output $Response + } + + else { + $ObjectFinal = @() + $Object = $null + foreach ($Item in $Response.lists) { + + $Object = [PSCustomObject]@{ + Address = $Item.address + 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 + Type = $Item.type.SubString(0, 1).ToUpper() + $Item.type.SubString(1).ToLower() + DateUpdated = $DateUpdated + Number = $Item.number + InvalidDomains = $Item.invalid_domains + AbpEntries = $Item.abp_entries + Status = $Item.status + } + if ($Object) { + $ObjectFinal += $Object + } + + } + Write-Output $ObjectFinal + } + } + + catch { + Write-Error -Message $_.Exception.Message + break + } + finally { if ($Sid) { Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl From 6c6e226924a761040509033fa292d4d5c4476532 Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Tue, 1 Jul 2025 23:27:42 -0500 Subject: [PATCH 06/13] debugging --- PiHoleShell/Public/ListManagement.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PiHoleShell/Public/ListManagement.ps1 b/PiHoleShell/Public/ListManagement.ps1 index 415d40e..762cbc6 100644 --- a/PiHoleShell/Public/ListManagement.ps1 +++ b/PiHoleShell/Public/ListManagement.ps1 @@ -291,11 +291,11 @@ https://TODO ) try { $FindMatchingList = Get-PiHoleList -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl | Where-Object { $_.Address -eq $Address } - + if ($FindMatchingList) { - + } - + $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl Add-Type -AssemblyName System.Web From bc4e6ae537d8b32bd2f786f7b0e88e200dad9cfe Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Wed, 2 Jul 2025 00:04:30 -0500 Subject: [PATCH 07/13] debugging --- PiHoleShell/Public/ListManagement.ps1 | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/PiHoleShell/Public/ListManagement.ps1 b/PiHoleShell/Public/ListManagement.ps1 index 762cbc6..9acfac6 100644 --- a/PiHoleShell/Public/ListManagement.ps1 +++ b/PiHoleShell/Public/ListManagement.ps1 @@ -277,7 +277,7 @@ function Remove-PiHoleList { https://TODO #> - #Work In Progress (DOES NOT WORK) + #Work In Progress (NEED TO FINISH) [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] param ( @@ -287,6 +287,7 @@ https://TODO [string]$Password, [bool]$IgnoreSsl = $false, [System.Uri]$Address, + [string]$Type, [bool]$RawOutput = $false ) try { @@ -298,15 +299,28 @@ https://TODO $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl - Add-Type -AssemblyName System.Web - $List = [System.Uri]::EscapeDataString($Address) - Write-Verbose -Message "List: $List" + + $List = $Address + + + $Body = @( + @{ + item = $List + type = $Type.ToLower() # ensure lowercase "block" or "allow" + } + ) + + $Body = , $Body + $JsonBody = $Body | ConvertTo-Json -Depth 3 -Compress + Write-Host $JsonBody + $Params = @{ Headers = @{sid = $($Sid) } - Uri = "$($PiHoleServer.OriginalString)/api/lists/$List?type=block" - Method = "Delete" + Uri = "$($PiHoleServer.OriginalString)/api/lists:batchDelete" + Method = "Post" SkipCertificateCheck = $IgnoreSsl + Body = $Body | ConvertTo-Json -Depth 3 -Compress ContentType = "application/json" } From 30a27435f51427fbc17176d450df508504043701 Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Wed, 2 Jul 2025 00:17:32 -0500 Subject: [PATCH 08/13] debugging --- PiHoleShell/Public/ListManagement.ps1 | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/PiHoleShell/Public/ListManagement.ps1 b/PiHoleShell/Public/ListManagement.ps1 index 9acfac6..ccda9b6 100644 --- a/PiHoleShell/Public/ListManagement.ps1 +++ b/PiHoleShell/Public/ListManagement.ps1 @@ -299,28 +299,22 @@ https://TODO $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl - - $List = $Address - - - $Body = @( @{ - item = $List - type = $Type.ToLower() # ensure lowercase "block" or "allow" + item = $Address + type = $Type.ToLower() } ) + #For some reason this needs to be here to make it an array $Body = , $Body - $JsonBody = $Body | ConvertTo-Json -Depth 3 -Compress - Write-Host $JsonBody - + $Params = @{ Headers = @{sid = $($Sid) } Uri = "$($PiHoleServer.OriginalString)/api/lists:batchDelete" Method = "Post" SkipCertificateCheck = $IgnoreSsl - Body = $Body | ConvertTo-Json -Depth 3 -Compress + Body = $Body | ConvertTo-Json -Depth 10 -Compress ContentType = "application/json" } From 8c9a96f6fcb8cdee8fe13f40af5ac065277fa651 Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Mon, 7 Jul 2025 01:42:13 -0500 Subject: [PATCH 09/13] added new func --- PiHoleShell/PiHoleShell.psm1 | 2 + PiHoleShell/Public/Padd.ps1 | 130 +++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 PiHoleShell/Public/Padd.ps1 diff --git a/PiHoleShell/PiHoleShell.psm1 b/PiHoleShell/PiHoleShell.psm1 index 6904a98..48a1be1 100644 --- a/PiHoleShell/PiHoleShell.psm1 +++ b/PiHoleShell/PiHoleShell.psm1 @@ -23,6 +23,8 @@ Export-ModuleMember -Function @( 'Get-PiHoleDnsBlockingStatus', 'Set-PiHoleDnsBlocking', ` #Config.ps1 'Get-PiHoleConfig', 'Get-PiHoleCurrentAuthSession', 'Remove-PiHoleAuthSession', ` + #Padd.ps1 + 'Get-PiHolePadd', ` #Metrics.ps1 'Get-PiHoleStatsRecentBlocked', 'Get-PiHoleStatsQueryType', 'Get-PiHoleStatsTopDomain', 'Get-PiHoleStatsSummary', ` #ListManagement.ps1 diff --git a/PiHoleShell/Public/Padd.ps1 b/PiHoleShell/Public/Padd.ps1 new file mode 100644 index 0000000..478c93c --- /dev/null +++ b/PiHoleShell/Public/Padd.ps1 @@ -0,0 +1,130 @@ +function Get-PiHolePadd { + <# +.SYNOPSIS +https://TODO + + #> + #Work In Progress + [CmdletBinding()] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] + param ( + [Parameter(Mandatory = $true)] + [System.URI]$PiHoleServer, + [Parameter(Mandatory = $true)] + [string]$Password, + [System.URI]$List = $null, + [bool]$IgnoreSsl = $false, + [bool]$RawOutput = $false + ) + try { + $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl + + $Params = @{ + Headers = @{sid = $($Sid) } + Uri = "$($PiHoleServer.OriginalString)/api/padd" + Method = "Get" + SkipCertificateCheck = $IgnoreSsl + ContentType = "application/json" + } + + $Response = Invoke-RestMethod @Params + + if ($RawOutput) { + Write-Output $Response + } + + else { + $ObjectFinal = @() + $Object = $null + $IFaceV4RxBytes = [PSCustomObject]@{ + Value = $Response.iface.v4.rx_bytes.value + Unit = $Response.iface.v4.rx_bytes.unit + } + $IFaceV4TxBytes = [PSCustomObject]@{ + Value = $Response.iface.v4.tx_bytes.value + Unit = $Response.iface.v4.tx_bytes.unit + } + $IFaceV4 = [PSCustomObject]@{ + Addr = $Response.iface.v4.addr + RxBytes = $IFaceV4RxBytes + TxBytes = $IFaceV4TxBytes + NumAddrs = $Response.iface.v4.num_addrs + Name = $Response.iface.v4.name + GwAddr = $Response.iface.v4.gw_addr + } + $IFaceV6 = [PSCustomObject]@{ + Addr = $Response.iface.v6.addr + NumAddrs = $Response.iface.v6.num_addrs + Name = $Response.iface.v6.name + GwAddr = $Response.iface.b6.gw_addr + } + $IFace = [PSCustomObject]@{ + v4 = $IfaceV4 + v6 = $IfaceV6 + } + $Queries = [PSCustomObject]@{ + Total = $Response.queries.total + Blocked = $Response.queries.blocked + PercentBlocked = $Response.queries.percent_blocked + } + $Sensors = [PSCustomObject]@{ + CpuTemp = $Response.sensors.cpu_temp + HotLimit = $Response.sensors.hot_limit + Unit = $Response.sensors.unit + } + + $Object = [PSCustomObject]@{ + CpuPercent = $Response."%cpu" + MemoryPercent = $Response."%mem" + ActiveClients = $Response.active_clients + Blocking = $Response.blocking + Cache = [PSCustomObject]@{ + Size = $Response.cache.size + Inserted = $Response.cache.inserted + Evicted = $Reponse.cache.evicted + } + Config = [PSCustomObject]@{ + DhcpActive = $Response.config.dhcp_active + DhcpEnd = $Response.config.dhcp_end + DhcpIpv6 = $Response.config.dhcp_ipv6 + DhcpStart = $Response.config.dhcp_start + DnsDnssec = $Response.config.dns_dnssec + DnsDomain = $Response.config.dns_domain + DnsNumUpstreams = $Response.config.dns_num_upstreams + DnsPort = $Response.config.dns_port + DnsrevServerAactive = $Response.config.dns_revServer_active + PrivacyLevel = $Response.config.privacy_level + + } + GravitySize = $Response.gravity_size + HostModel = $Response.host_model + IFace = $IFace + NodeName = $Response.node_name + Pid = $Response.pid + Queries = $Queries + RecentBlocked = $Response.recent_blocked + Sensors = $Sensors + System = $Response.system + TopBlocked = $Response.top_blocked + TopClient = $Response.top_client + TopDomain = $Response.top_domain + Version = $Response.version + } + if ($Object) { + $ObjectFinal += $Object + } + Write-Output $ObjectFinal + } + } + + catch { + Write-Error -Message $_.Exception.Message + break + } + + finally { + if ($Sid) { + Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl + } + } +} \ No newline at end of file From f693680ba34d1e9c0159727e69eed6c26b45daa8 Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Mon, 7 Jul 2025 16:42:35 -0500 Subject: [PATCH 10/13] adding more func --- PiHoleShell/PiHoleShell.psm1 | 6 ++-- PiHoleShell/Public/Actions.ps1 | 55 ++++++++++++++++++++++++++++++++++ PiHoleShell/Public/Config.ps1 | 32 ++++++++++++++++++-- PiHoleShell/Public/Padd.ps1 | 15 +++++----- 4 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 PiHoleShell/Public/Actions.ps1 diff --git a/PiHoleShell/PiHoleShell.psm1 b/PiHoleShell/PiHoleShell.psm1 index 48a1be1..3d038d9 100644 --- a/PiHoleShell/PiHoleShell.psm1 +++ b/PiHoleShell/PiHoleShell.psm1 @@ -15,8 +15,10 @@ foreach ($File in $PrivateFunctions) { } Export-ModuleMember -Function @( - #Authentication.ps1 - 'Remove-PiHoleCurrentAuthSession' , 'Get-PiHoleCurrentAuthSession', 'Remove-PiHoleAuthSession', ` + #Actions.ps1 + 'Update-PiHoleActionsGravity' ` + #Authentication.ps1 + 'Remove-PiHoleCurrentAuthSession' , 'Get-PiHoleCurrentAuthSession', 'Remove-PiHoleAuthSession', ` #GroupManagement.ps1 'Get-PiHoleGroup', 'New-PiHoleGroup', 'Update-PiHoleGroup', 'Remove-PiHoleGroup', ` #DnsControl.ps1 diff --git a/PiHoleShell/Public/Actions.ps1 b/PiHoleShell/Public/Actions.ps1 new file mode 100644 index 0000000..1b4dfcf --- /dev/null +++ b/PiHoleShell/Public/Actions.ps1 @@ -0,0 +1,55 @@ +function Update-PiHoleActionsGravity { + <# +.SYNOPSIS +https://TODO + + #> + #Work In Progress + [CmdletBinding()] + [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/action/gravity" + Method = "Post" + SkipCertificateCheck = $IgnoreSsl + ContentType = "application/json" + } + + $Response = Invoke-RestMethod @Params + + if ($RawOutput) { + Write-Output $Response + } + + else { + $ObjectFinal = @() + $Object = $null + if ($Object) { + $ObjectFinal += $Object + } + Write-Output $ObjectFinal + } + } + + catch { + Write-Error -Message $_.Exception.Message + break + } + + finally { + if ($Sid) { + Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl + } + } +} \ No newline at end of file diff --git a/PiHoleShell/Public/Config.ps1 b/PiHoleShell/Public/Config.ps1 index 1ca4ad0..333ea34 100644 --- a/PiHoleShell/Public/Config.ps1 +++ b/PiHoleShell/Public/Config.ps1 @@ -31,8 +31,36 @@ https://TODO Write-Output $Response } else { - # $ObjectFinal = @() - # Write-Output $ObjectFinal | Select-Object -Unique + $ObjectFinal = @() + $Dns = [PSCustomObject]@{ + Upstreams = $Response.config.dns.upstreams + } + + $Dhcp = [PSCustomObject]@{ + Active = $Response.config.dhcp.active + Start = $Response.config.dhcp.start + End = $Response.config.dhcp.end + Hosts = $Response.config.dhcp.hosts + IgnoreUnknownClients = $Response.config.dhcp.ignoreUnknownClients + Ipv6 = $Response.config.dhcp.ipv6 + LeaseTime = $Response.config.dhcp.leaseTime + Logging = $Response.config.dhcp.logging + MultiDNS = $Response.config.dhcp.multiDNS + Netmask = $Response.config.dhcp.netmask + RapidCommit = $Response.config.dhcp.rapidCommit + Router = $Response.config.dhcp.router + } + + $Object = [PSCustomObject]@{ + Dns = $Dns + Dhcp = $Dhcp + + } + + if ($Object) { + $ObjectFinal += $Object + } + Write-Output $ObjectFinal } } diff --git a/PiHoleShell/Public/Padd.ps1 b/PiHoleShell/Public/Padd.ps1 index 478c93c..ea031cc 100644 --- a/PiHoleShell/Public/Padd.ps1 +++ b/PiHoleShell/Public/Padd.ps1 @@ -12,7 +12,6 @@ https://TODO [System.URI]$PiHoleServer, [Parameter(Mandatory = $true)] [string]$Password, - [System.URI]$List = $null, [bool]$IgnoreSsl = $false, [bool]$RawOutput = $false ) @@ -72,29 +71,29 @@ https://TODO HotLimit = $Response.sensors.hot_limit Unit = $Response.sensors.unit } + $Cache = [PSCustomObject]@{ + Size = $Response.cache.size + Inserted = $Response.cache.inserted + Evicted = $Reponse.cache.evicted + } $Object = [PSCustomObject]@{ CpuPercent = $Response."%cpu" MemoryPercent = $Response."%mem" ActiveClients = $Response.active_clients Blocking = $Response.blocking - Cache = [PSCustomObject]@{ - Size = $Response.cache.size - Inserted = $Response.cache.inserted - Evicted = $Reponse.cache.evicted - } + Cache = $Cache Config = [PSCustomObject]@{ DhcpActive = $Response.config.dhcp_active + DhcpStart = $Response.config.dhcp_start DhcpEnd = $Response.config.dhcp_end DhcpIpv6 = $Response.config.dhcp_ipv6 - DhcpStart = $Response.config.dhcp_start DnsDnssec = $Response.config.dns_dnssec DnsDomain = $Response.config.dns_domain DnsNumUpstreams = $Response.config.dns_num_upstreams DnsPort = $Response.config.dns_port DnsrevServerAactive = $Response.config.dns_revServer_active PrivacyLevel = $Response.config.privacy_level - } GravitySize = $Response.gravity_size HostModel = $Response.host_model From 93cec4a49a6cdc1fb674c891445d4c1216fd599b Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Tue, 11 Nov 2025 21:41:11 -0600 Subject: [PATCH 11/13] fixed spaces --- PiHoleShell/Public/Config.ps1 | 5 ++--- PiHoleShell/Public/ListManagement.ps1 | 1 - PiHoleShell/Public/Padd.ps1 | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/PiHoleShell/Public/Config.ps1 b/PiHoleShell/Public/Config.ps1 index 333ea34..a938f07 100644 --- a/PiHoleShell/Public/Config.ps1 +++ b/PiHoleShell/Public/Config.ps1 @@ -32,11 +32,11 @@ https://TODO } else { $ObjectFinal = @() - $Dns = [PSCustomObject]@{ + $Dns = [PSCustomObject]@{ Upstreams = $Response.config.dns.upstreams } - $Dhcp = [PSCustomObject]@{ + $Dhcp = [PSCustomObject]@{ Active = $Response.config.dhcp.active Start = $Response.config.dhcp.start End = $Response.config.dhcp.end @@ -54,7 +54,6 @@ https://TODO $Object = [PSCustomObject]@{ Dns = $Dns Dhcp = $Dhcp - } if ($Object) { diff --git a/PiHoleShell/Public/ListManagement.ps1 b/PiHoleShell/Public/ListManagement.ps1 index ccda9b6..e727f34 100644 --- a/PiHoleShell/Public/ListManagement.ps1 +++ b/PiHoleShell/Public/ListManagement.ps1 @@ -308,7 +308,6 @@ https://TODO #For some reason this needs to be here to make it an array $Body = , $Body - $Params = @{ Headers = @{sid = $($Sid) } Uri = "$($PiHoleServer.OriginalString)/api/lists:batchDelete" diff --git a/PiHoleShell/Public/Padd.ps1 b/PiHoleShell/Public/Padd.ps1 index ea031cc..7dc47a3 100644 --- a/PiHoleShell/Public/Padd.ps1 +++ b/PiHoleShell/Public/Padd.ps1 @@ -43,38 +43,38 @@ https://TODO Value = $Response.iface.v4.tx_bytes.value Unit = $Response.iface.v4.tx_bytes.unit } - $IFaceV4 = [PSCustomObject]@{ + $IFaceV4 = [PSCustomObject]@{ Addr = $Response.iface.v4.addr RxBytes = $IFaceV4RxBytes TxBytes = $IFaceV4TxBytes - NumAddrs = $Response.iface.v4.num_addrs + NumAddrs = $Response.iface.v4.num_addrs Name = $Response.iface.v4.name GwAddr = $Response.iface.v4.gw_addr } - $IFaceV6 = [PSCustomObject]@{ + $IFaceV6 = [PSCustomObject]@{ Addr = $Response.iface.v6.addr NumAddrs = $Response.iface.v6.num_addrs Name = $Response.iface.v6.name GwAddr = $Response.iface.b6.gw_addr } - $IFace = [PSCustomObject]@{ - v4 = $IfaceV4 + $IFace = [PSCustomObject]@{ + v4 = $IfaceV4 v6 = $IfaceV6 } - $Queries = [PSCustomObject]@{ + $Queries = [PSCustomObject]@{ Total = $Response.queries.total Blocked = $Response.queries.blocked PercentBlocked = $Response.queries.percent_blocked } - $Sensors = [PSCustomObject]@{ + $Sensors = [PSCustomObject]@{ CpuTemp = $Response.sensors.cpu_temp HotLimit = $Response.sensors.hot_limit Unit = $Response.sensors.unit } - $Cache = [PSCustomObject]@{ + $Cache = [PSCustomObject]@{ Size = $Response.cache.size Inserted = $Response.cache.inserted - Evicted = $Reponse.cache.evicted + Evicted = $Reponse.cache.evicted } $Object = [PSCustomObject]@{ @@ -83,7 +83,7 @@ https://TODO ActiveClients = $Response.active_clients Blocking = $Response.blocking Cache = $Cache - Config = [PSCustomObject]@{ + Config = [PSCustomObject]@{ DhcpActive = $Response.config.dhcp_active DhcpStart = $Response.config.dhcp_start DhcpEnd = $Response.config.dhcp_end From 2b70d2f1b40fa7d091c9590326dc92e12b881ea3 Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Tue, 11 Nov 2025 21:45:58 -0600 Subject: [PATCH 12/13] fixed --- PiHoleShell/Public/Actions.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PiHoleShell/Public/Actions.ps1 b/PiHoleShell/Public/Actions.ps1 index 1b4dfcf..7788c9a 100644 --- a/PiHoleShell/Public/Actions.ps1 +++ b/PiHoleShell/Public/Actions.ps1 @@ -5,7 +5,7 @@ https://TODO #> #Work In Progress - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess = $true)] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] param ( [Parameter(Mandatory = $true)] @@ -26,7 +26,9 @@ https://TODO ContentType = "application/json" } - $Response = Invoke-RestMethod @Params + if ($PSCmdlet.ShouldProcess("Pi-Hole server at $PiHoleServer", "Update gravity actions")) { + $Response = Invoke-RestMethod @Params + } if ($RawOutput) { Write-Output $Response From 6c04cf4f65f0ba29a942e528596b7b7d6f47150c Mon Sep 17 00:00:00 2001 From: Mike Madeja Date: Tue, 11 Nov 2025 21:49:19 -0600 Subject: [PATCH 13/13] fixed --- PiHoleShell/Public/ListManagement.ps1 | 100 +++++++++++++------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/PiHoleShell/Public/ListManagement.ps1 b/PiHoleShell/Public/ListManagement.ps1 index e727f34..c50efce 100644 --- a/PiHoleShell/Public/ListManagement.ps1 +++ b/PiHoleShell/Public/ListManagement.ps1 @@ -278,7 +278,7 @@ https://TODO #> #Work In Progress (NEED TO FINISH) - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess = $true)] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] param ( [Parameter(Mandatory = $true)] @@ -291,64 +291,68 @@ https://TODO [bool]$RawOutput = $false ) try { - $FindMatchingList = Get-PiHoleList -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl | Where-Object { $_.Address -eq $Address } + $Target = "Pi-Hole list $Address of type $Type" + if ($PSCmdlet.ShouldProcess($Target, "Remove list")) { + $FindMatchingList = Get-PiHoleList -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl | Where-Object { $_.Address -eq $Address } - if ($FindMatchingList) { + if ($FindMatchingList) { - } + } - $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl + $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl - $Body = @( - @{ - item = $Address - type = $Type.ToLower() + $Body = @( + @{ + item = $Address + type = $Type.ToLower() + } + ) + + #For some reason this needs to be here to make it an array + $Body = , $Body + $Params = @{ + Headers = @{sid = $($Sid) } + Uri = "$($PiHoleServer.OriginalString)/api/lists:batchDelete" + Method = "Post" + SkipCertificateCheck = $IgnoreSsl + Body = $Body | ConvertTo-Json -Depth 10 -Compress + ContentType = "application/json" } - ) - #For some reason this needs to be here to make it an array - $Body = , $Body - $Params = @{ - Headers = @{sid = $($Sid) } - Uri = "$($PiHoleServer.OriginalString)/api/lists:batchDelete" - Method = "Post" - SkipCertificateCheck = $IgnoreSsl - Body = $Body | ConvertTo-Json -Depth 10 -Compress - ContentType = "application/json" - } - - $Response = Invoke-RestMethod @Params + $Response = Invoke-RestMethod @Params - if ($RawOutput) { - Write-Output $Response - } + if ($RawOutput) { + Write-Output $Response + } - else { - $ObjectFinal = @() - $Object = $null - foreach ($Item in $Response.lists) { + else { + $ObjectFinal = @() + $Object = $null + foreach ($Item in $Response.lists) { + + $Object = [PSCustomObject]@{ + Address = $Item.address + 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 + Type = $Item.type.SubString(0, 1).ToUpper() + $Item.type.SubString(1).ToLower() + DateUpdated = $DateUpdated + Number = $Item.number + InvalidDomains = $Item.invalid_domains + AbpEntries = $Item.abp_entries + Status = $Item.status + } + if ($Object) { + $ObjectFinal += $Object + } - $Object = [PSCustomObject]@{ - Address = $Item.address - 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 - Type = $Item.type.SubString(0, 1).ToUpper() + $Item.type.SubString(1).ToLower() - DateUpdated = $DateUpdated - Number = $Item.number - InvalidDomains = $Item.invalid_domains - AbpEntries = $Item.abp_entries - Status = $Item.status } - if ($Object) { - $ObjectFinal += $Object - } - + Write-Output $ObjectFinal } - Write-Output $ObjectFinal + } }