├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .markdownlint.json ├── .vscode ├── settings.json └── tasks.json ├── CHANGELOG.md ├── LICENSE ├── PoShDynDnsApi ├── Classes │ └── PoShDynDnsApi.Class.ps1 ├── PoShDynDnsApi.psd1 ├── PoShDynDnsApi.psm1 ├── Private │ ├── Compare-ObjectProperties.ps1 │ ├── Invoke-DynDnsRequestCore.ps1 │ ├── Invoke-DynDnsRequestDesktop.ps1 │ └── Write-DynDnsOutput.ps1 ├── Public │ ├── Add-DynDnsHttpRedirect.ps1 │ ├── Add-DynDnsRecord.ps1 │ ├── Add-DynDnsZone.ps1 │ ├── Connect-DynDnsSession.ps1 │ ├── Disconnect-DynDnsSession.ps1 │ ├── Get-DynDnsHistory.ps1 │ ├── Get-DynDnsHttpRedirect.ps1 │ ├── Get-DynDnsJob.ps1 │ ├── Get-DynDnsNodeList.ps1 │ ├── Get-DynDnsRecord.ps1 │ ├── Get-DynDnsSession.ps1 │ ├── Get-DynDnsTask.ps1 │ ├── Get-DynDnsUser.ps1 │ ├── Get-DynDnsZone.ps1 │ ├── Get-DynDnsZoneChanges.ps1 │ ├── Get-DynDnsZoneNotes.ps1 │ ├── Lock-DynDnsZone.ps1 │ ├── New-DynDnsRecord.ps1 │ ├── Publish-DynDnsZoneChanges.ps1 │ ├── Remove-DynDnsHttpRedirect.ps1 │ ├── Remove-DynDnsNode.ps1 │ ├── Remove-DynDnsRecord.ps1 │ ├── Remove-DynDnsZone.ps1 │ ├── Send-DynDnsSession.ps1 │ ├── Test-DynDnsSession.ps1 │ ├── Undo-DynDnsZoneChanges.ps1 │ ├── Unlock-DynDnsZone.ps1 │ └── Update-DynDnsRecord.ps1 └── en-US │ ├── PoShDynDnsApi-help.xml │ └── about_PoShDynDnsApi.help.txt ├── README.md ├── Requirements.psd1 ├── ScriptAnalyzerSettings.psd1 ├── build.ps1 ├── build.psake.ps1 ├── build.settings.ps1 └── tests ├── Meta ├── Coverage.Tests.ps1 ├── Help.Tests.ps1 ├── Meta.Tests.ps1 └── MetaFixers.psm1 ├── PoShDynDnsApi.Tests.ps1 ├── Shared.ps1 └── Unit ├── Private ├── Compare-ObjectProperties.Tests.ps1 ├── Invoke-DynDnsRequestCore.Tests.ps1 ├── Invoke-DynDnsRequestDesktop.Tests.ps1 └── Write-DynDnsOutput.Tests.ps1 └── Public ├── Add-DynDnsHttpRedirect.Tests.ps1 ├── Add-DynDnsRecord.Tests.ps1 ├── Add-DynDnsZone.Tests.ps1 ├── Connect-DynDnsSession.Tests.ps1 ├── Disconnect-DynDnsSession.Tests.ps1 ├── Get-DynDnsHistory.Tests.ps1 ├── Get-DynDnsHttpRedirect.Tests.ps1 ├── Get-DynDnsJob.Tests.ps1 ├── Get-DynDnsNodeList.Tests.ps1 ├── Get-DynDnsRecord.Tests.ps1 ├── Get-DynDnsSession.Tests.ps1 ├── Get-DynDnsTask.Tests.ps1 ├── Get-DynDnsUser.Tests.ps1 ├── Get-DynDnsZone.Tests.ps1 ├── Get-DynDnsZoneChanges.Tests.ps1 ├── Get-DynDnsZoneNotes.Tests.ps1 ├── Lock-DynDnsZone.Tests.ps1 ├── New-DynDnsRecord.Tests.ps1 ├── Publish-DynDnsZoneChanges.Tests.ps1 ├── Remove-DynDnsHttpRedirect.Tests.ps1 ├── Remove-DynDnsNode.Tests.ps1 ├── Remove-DynDnsRecord.Tests.ps1 ├── Remove-DynDnsZone.Tests.ps1 ├── Send-DynDnsSession.Tests.ps1 ├── Test-DynDnsSession.Tests.ps1 ├── Undo-DynDnsZoneChanges.Tests.ps1 ├── Unlock-DynDnsZone.Tests.ps1 └── Update-DynDnsRecord.Tests.ps1 /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve the module 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: thedavecarroll 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **PowerShell Environment (please complete the following information):** 23 | - OS: [e.g. Windows 10, Ubuntu 18.04] 24 | - Edition [e.g. desktop,core] 25 | - Version [e.g. 5.1,6,6.1] 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # BuildOutput folder 2 | BuildOutput/ 3 | 4 | # git data 5 | .git 6 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD006": false, 3 | "MD013": false, 4 | "MD029": false, 5 | "MD033": false 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | //-------- Files configuration -------- 3 | 4 | // When enabled, will trim trailing whitespace when you save a file. 5 | "files.trimTrailingWhitespace": true, 6 | 7 | 8 | //-------- PowerShell Configuration -------- 9 | 10 | // Use a custom PowerShell Script Analyzer settings file for this workspace. 11 | // Relative paths for this setting are always relative to the workspace root dir. 12 | "powershell.scriptAnalysis.settingsPath": "src/ScriptAnalyzerSettings.psd1" 13 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${relativeFile}: the current opened file relative to workspaceRoot 5 | // ${fileBasename}: the current opened file's basename 6 | // ${fileDirname}: the current opened file's dirname 7 | // ${fileExtname}: the current opened file's extension 8 | // ${cwd}: the current working directory of the spawned process 9 | { 10 | // See https://go.microsoft.com/fwlink/?LinkId=733558 11 | // for the documentation about the tasks.json format 12 | "version": "0.1.0", 13 | 14 | // Start PowerShell 15 | "windows": { 16 | "command": "${env:windir}/System32/WindowsPowerShell/v1.0/powershell.exe", 17 | "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass" ] 18 | }, 19 | "linux": { 20 | "command": "/usr/bin/pwsh", 21 | "args": [ "-NoProfile" ] 22 | }, 23 | "osx": { 24 | "command": "/usr/local/bin/pwsh", 25 | "args": [ "-NoProfile" ] 26 | }, 27 | 28 | // The command is a shell script 29 | "isShellCommand": true, 30 | 31 | // Show the output window always 32 | "showOutput": "always", 33 | 34 | // Associate with test task runner 35 | "tasks": [ 36 | { 37 | "taskName": "Clean", 38 | "suppressTaskName": true, 39 | "showOutput": "always", 40 | "args": [ 41 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Clean'; Invoke-psake build.psake.ps1 -taskList Clean;", 42 | "Invoke-Command { Write-Host 'Completed Clean task in task runner.' }" 43 | ] 44 | }, 45 | { 46 | "taskName": "Build", 47 | "suppressTaskName": true, 48 | "isBuildCommand": true, 49 | "showOutput": "always", 50 | "args": [ 51 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Build'; Invoke-psake build.psake.ps1 -taskList Build;", 52 | "Invoke-Command { Write-Host 'Completed Build task in task runner.' }" 53 | ] 54 | }, 55 | { 56 | "taskName": "BuildHelp", 57 | "suppressTaskName": true, 58 | "showOutput": "always", 59 | "args": [ 60 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList BuildHelp'; Invoke-psake build.psake.ps1 -taskList BuildHelp;", 61 | "Invoke-Command { Write-Host 'Completed BuildHelp task in task runner.' }" 62 | ] 63 | }, 64 | { 65 | "taskName": "Analyze", 66 | "suppressTaskName": true, 67 | "showOutput": "always", 68 | "args": [ 69 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Analyze'; Invoke-psake build.psake.ps1 -taskList Analyze;", 70 | "Invoke-Command { Write-Host 'Completed Analyze task in task runner.' }" 71 | ] 72 | }, 73 | { 74 | "taskName": "Install", 75 | "suppressTaskName": true, 76 | "showOutput": "always", 77 | "args": [ 78 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Install'; Invoke-psake build.psake.ps1 -taskList Install;", 79 | "Invoke-Command { Write-Host 'Completed Install task in task runner.' }" 80 | ] 81 | }, 82 | { 83 | "taskName": "Publish", 84 | "suppressTaskName": true, 85 | "showOutput": "always", 86 | "args": [ 87 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Publish'; Invoke-psake build.psake.ps1 -taskList Publish;", 88 | "Invoke-Command { Write-Host 'Completed Publish task in task runner.' }" 89 | ] 90 | }, 91 | { 92 | "taskName": "Test", 93 | "suppressTaskName": true, 94 | "isTestCommand": true, 95 | "showOutput": "always", 96 | "args": [ 97 | "Write-Host 'Invoking Pester'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};", 98 | "Invoke-Command { Write-Host 'Completed Test task in task runner.' }" 99 | ], 100 | "problemMatcher": [ 101 | { 102 | "owner": "powershell", 103 | "fileLocation": ["absolute"], 104 | "severity": "error", 105 | "pattern": [ 106 | { 107 | "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", 108 | "message": 1 109 | }, 110 | { 111 | "regexp": "^\\s+at\\s+[^,]+,\\s*(.*?):\\s+line\\s+(\\d+)$", 112 | "file": 1, 113 | "line": 2 114 | } 115 | ] 116 | } 117 | ] 118 | } 119 | ] 120 | } 121 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # PoShDynDnsApi 2 | 3 | ## Changelog 4 | 5 | All notable changes to this project will be documented in this file. 6 | 7 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 8 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 9 | 10 | ## [0.1.2] - 2020-08-14 11 | 12 | Bugfix; Update Strongly Recommended 13 | 14 | ### Fixed 15 | 16 | - [Issue #14](https://github.com/thedavecarroll/PoShDynDnsApi/issues/14) - `Get-DynDnsRecord` - Providing Node causes warning 17 | 18 | ## [0.1.1] - 2020-08-14 19 | 20 | Bugfix and Maintenance; Update Strongly Recommended 21 | 22 | ### Fixed 23 | 24 | - [Issue #11](https://github.com/thedavecarroll/PoShDynDnsApi/issues/11) - `Get-DynDnsRecord` - Retrieve full zone tree when Node not provided using RecordType as filter 25 | - [Issue #10](https://github.com/thedavecarroll/PoShDynDnsApi/issues/10) - [New Feature] Avoid using the same name for both the `[DynDnsHistory]` type & the `$DynDnsHistory` collection 26 | - [Issue #8](https://github.com/thedavecarroll/PoShDynDnsApi/issues/8) - Remove `Write-Output` 27 | - [Issue #7](https://github.com/thedavecarroll/PoShDynDnsApi/issues/7) - DynDnsSession RefreshTime not updated by standard calls 28 | - [Issue #6](https://github.com/thedavecarroll/PoShDynDnsApi/issues/6) - `Remove-DynDnsNode` continues even if zone does not exist 29 | - [Issue #3](https://github.com/thedavecarroll/PoShDynDnsApi/issues/3) - `Remove-DynDnsHttpRedirect` Does Not Honor PublishWait 30 | - [Issue #2](https://github.com/thedavecarroll/PoShDynDnsApi/issues/2) - Rewrite `Invoke-DynDnsRequest` for Cross-Platform/Edition Capability *Actually released in 0.1.0* 31 | 32 | ### Maintenance 33 | 34 | - [Issue #13](https://github.com/thedavecarroll/PoShDynDnsApi/issues/13) - Update LicenceUri in module manifest 35 | - [Issue #12](https://github.com/thedavecarroll/PoShDynDnsApi/issues/12) - Update Module Copyright 36 | 37 | ## [0.1.0] - 2019-01-06 38 | 39 | ### Added 40 | 41 | - Initial CHANGELOG. 42 | - Provides several functions to interact with the Dyn Managed DNS REST API. 43 | - See [README](https://github.com/thedavecarroll/PoShDynDnsApi/blob/main/README.md) for full details. 44 | 45 | [0.1.2]: https://github.com/thedavecarroll/PoShDynDnsApi/tree/3183f6130f68685c1c4868084d76785ee331b72c 46 | [0.1.1]: https://github.com/thedavecarroll/PoShDynDnsApi/tree/ed52c351d475932325b295db0850c9a4ebae7826 47 | [0.1.0]: https://github.com/thedavecarroll/PoShDynDnsApi/tree/589ca85b4db038c6ef167660365dbfeca0215f5e 48 | 49 | [comment]: # (See the following for CHANGELOG example - https://github.com/PowerShell/PowerShellForGitHub/blob/main/CHANGELOG.md.) 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dave Carroll 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PoShDynDnsApi/Classes/PoShDynDnsApi.Class.ps1: -------------------------------------------------------------------------------- 1 | class DynDnsRawData { 2 | hidden [PSCustomObject]$RawData 3 | } 4 | 5 | class DynDnsRecord : DynDnsRawData { 6 | [string]$Zone 7 | [string]$Name 8 | [string]$Type 9 | [int]$TTL 10 | hidden [string]$RecordId 11 | 12 | DynDnsRecord () {} 13 | DynDnsRecord ([PSCustomObject]$DnsRecord) { 14 | $this.Zone = $DnsRecord.zone 15 | $this.Name = $DnsRecord.fqdn 16 | $this.Type = $DnsRecord.record_type 17 | $this.TTL = $DnsRecord.ttl 18 | $this.RecordId = $DnsRecord.record_id 19 | $this.RawData = $DnsRecord 20 | } 21 | } 22 | 23 | class DynDnsRecord_A : DynDnsRecord { 24 | [ipaddress]$Address 25 | 26 | DynDnsRecord_A () { } 27 | DynDnsRecord_A ([PSCustomObject]$DnsRecord) { 28 | $this.Zone = $DnsRecord.zone 29 | $this.Name = $DnsRecord.fqdn 30 | $this.Type = $DnsRecord.record_type 31 | $this.TTL = $DnsRecord.ttl 32 | $this.Address = $DnsRecord.rdata.address 33 | $this.RecordId = $DnsRecord.record_id 34 | $this.RawData = $DnsRecord 35 | } 36 | } 37 | 38 | class DynDnsRecord_TXT : DynDnsRecord { 39 | [string[]]$Strings 40 | 41 | DynDnsRecord_TXT () { } 42 | DynDnsRecord_TXT ([PSCustomObject]$DnsRecord) { 43 | $this.Zone = $DnsRecord.zone 44 | $this.Name = $DnsRecord.fqdn 45 | $this.Type = $DnsRecord.record_type 46 | $this.Strings = $DnsRecord.rdata.txtdata 47 | $this.TTL = $DnsRecord.ttl 48 | $this.RecordId = $DnsRecord.record_id 49 | $this.RawData = $DnsRecord 50 | } 51 | } 52 | 53 | class DynDnsRecord_CNAME : DynDnsRecord { 54 | [string]$NameHost 55 | 56 | DynDnsRecord_CNAME () { } 57 | DynDnsRecord_CNAME ([PSCustomObject]$DnsRecord) { 58 | $this.Zone = $DnsRecord.zone 59 | $this.Name = $DnsRecord.fqdn 60 | $this.Type = $DnsRecord.record_type 61 | $this.NameHost = $DnsRecord.rdata.cname 62 | $this.TTL = $DnsRecord.ttl 63 | $this.RecordId = $DnsRecord.record_id 64 | $this.RawData = $DnsRecord 65 | } 66 | } 67 | 68 | class DynDnsRecord_MX : DynDnsRecord { 69 | [string]$Exchange 70 | [int]$Preference 71 | 72 | DynDnsRecord_MX () { } 73 | DynDnsRecord_MX ([PSCustomObject]$DnsRecord) { 74 | $this.Zone = $DnsRecord.zone 75 | $this.Name = $DnsRecord.fqdn 76 | $this.Type = $DnsRecord.record_type 77 | $this.Exchange = $DnsRecord.rdata.exchange 78 | $this.Preference = $DnsRecord.rdata.preference 79 | $this.TTL = $DnsRecord.ttl 80 | $this.RecordId = $DnsRecord.record_id 81 | $this.RawData = $DnsRecord 82 | } 83 | } 84 | 85 | class DynDnsRecord_SRV : DynDnsRecord { 86 | [string]$Target 87 | [int]$Port 88 | [int]$Priority 89 | [int]$Weight 90 | 91 | DynDnsRecord_SRV () { } 92 | DynDnsRecord_SRV ([PSCustomObject]$DnsRecord) { 93 | $this.Zone = $DnsRecord.zone 94 | $this.Name = $DnsRecord.fqdn 95 | $this.Type = $DnsRecord.record_type 96 | $this.Target = $DnsRecord.rdata.target 97 | $this.Port = $DnsRecord.rdata.port 98 | $this.Priority = $DnsRecord.rdata.priority 99 | $this.Weight = $DnsRecord.rdata.weight 100 | $this.TTL = $DnsRecord.ttl 101 | $this.RecordId = $DnsRecord.record_id 102 | $this.RawData = $DnsRecord 103 | } 104 | } 105 | 106 | class DynDnsRecord_PTR : DynDnsRecord { 107 | [string]$NameHost 108 | 109 | DynDnsRecord_PTR () { } 110 | DynDnsRecord_PTR ([PSCustomObject]$DnsRecord) { 111 | $this.Zone = $DnsRecord.zone 112 | $this.Name = $DnsRecord.fqdn 113 | $this.Type = $DnsRecord.record_type 114 | $this.NameHost = $DnsRecord.rdata.ptrdname 115 | $this.TTL = $DnsRecord.ttl 116 | $this.RecordId = $DnsRecord.record_id 117 | $this.RawData = $DnsRecord 118 | } 119 | } 120 | 121 | class DynDnsRecord_NS : DynDnsRecord { 122 | [string]$NameHost 123 | [string]$Authoritative 124 | 125 | DynDnsRecord_NS () { } 126 | DynDnsRecord_NS ([PSCustomObject]$DnsRecord) { 127 | $this.Zone = $DnsRecord.zone 128 | $this.Name = $DnsRecord.fqdn 129 | $this.Type = $DnsRecord.record_type 130 | $this.NameHost = $DnsRecord.rdata.nsdname 131 | $this.Authoritative = $DnsRecord.service_class 132 | $this.TTL = $DnsRecord.ttl 133 | $this.RecordId = $DnsRecord.record_id 134 | $this.RawData = $DnsRecord 135 | } 136 | } 137 | 138 | class DynDnsRecord_SOA : DynDnsRecord { 139 | [string]$Administrator 140 | [int]$SerialNumber 141 | [string]$PrimaryServer 142 | [int]$TimeToExpiration 143 | [int]$TimeToZoneFailureRetry 144 | [int]$TimeToZoneRefresh 145 | [int]$DefaultTTL 146 | 147 | DynDnsRecord_SOA () { } 148 | DynDnsRecord_SOA ([PSCustomObject]$DnsRecord) { 149 | $this.Zone = $DnsRecord.zone 150 | $this.Name = $DnsRecord.fqdn 151 | $this.Type = $DnsRecord.record_type 152 | $this.Administrator = $DnsRecord.rdata.rname 153 | $this.SerialNumber = $DnsRecord.rdata.serial 154 | $this.PrimaryServer = $DnsRecord.rdata.mname 155 | $this.TimeToExpiration = $DnsRecord.rdata.expire 156 | $this.TimeToZoneFailureRetry = $DnsRecord.rdata.retry 157 | $this.TimeToZoneRefresh = $DnsRecord.rdata.refresh 158 | $this.DefaultTTL = $DnsRecord.rdata.minimum 159 | $this.TTL = $DnsRecord.ttl 160 | $this.RawData = $DnsRecord 161 | } 162 | } 163 | 164 | class DynDnsHistory { 165 | [datetime]$Timestamp 166 | [string]$Command 167 | [string]$Status 168 | [string]$JobId 169 | [string]$Method 170 | [string]$Body 171 | [string]$Uri 172 | [string]$StatusCode 173 | [string]$StatusDescription 174 | [string]$ElapsedTime 175 | [hashtable]$Arguments 176 | 177 | DynDnsHistory () {} 178 | DynDnsHistory ([PsCustomObject]$DynDnsHistory) { 179 | $this.Timestamp = [System.DateTime]::Now 180 | $this.Command = $DynDnsHistory.Command 181 | $this.Status = $DynDnsHistory.Status 182 | $this.JobId = $DynDnsHistory.JobId 183 | $this.Method = $DynDnsHistory.Method 184 | $this.Body = $DynDnsHistory.Body 185 | $this.Uri = $DynDnsHistory.Uri 186 | $this.StatusCode = $DynDnsHistory.StatusCode 187 | $this.StatusDescription = $DynDnsHistory.StatusDescription 188 | $this.ElapsedTime = $DynDnsHistory.ElapsedTime 189 | $this.Arguments = $DynDnsHistory.Arguments 190 | } 191 | } 192 | 193 | class DynDnsTask : DynDnsRawData { 194 | [int]$TaskId 195 | [object]$Created 196 | [object]$Modified 197 | [string]$CustomerName 198 | [string]$Zone 199 | [string]$TaskName 200 | [string]$Status 201 | [string]$Message 202 | [string]$Blocking 203 | [int]$Steps 204 | [int]$StepCount 205 | [object[]]$Arugments 206 | [string]$Debug 207 | 208 | DynDnsTask () { } 209 | DynDnsTask ([PSCustomObject]$DynTask) { 210 | [datetime]$origin = '1970-01-01 00:00:00' 211 | 212 | $this.TaskId = $DynTask.task_id 213 | $this.Created = $origin.AddSeconds($DynTask.created_ts).ToLocalTime() 214 | $this.Modified = $origin.AddSeconds($DynTask.modified_ts).ToLocalTime() 215 | $this.CustomerName = $DynTask.customer_name 216 | $this.Zone = $DynTask.zone_name 217 | $this.TaskName = $DynTask.name 218 | $this.Status = $DynTask.status 219 | $this.Message = $DynTask.message 220 | $this.Blocking = $DynTask.blocking 221 | $this.Steps = $DynTask.total_steps 222 | $this.StepCount = $DynTask.step_count 223 | $this.Arugments = $DynTask.args 224 | $this.Debug = $DynTask.debug 225 | $this.RawData = $DynTask 226 | } 227 | } 228 | 229 | class DynDnsZone : DynDnsRawData { 230 | [string]$Zone 231 | [int]$SerialNumber 232 | [string]$SerialStyle 233 | [string]$Type 234 | 235 | DynDnsZone () { } 236 | DynDnsZone ([PSCustomObject]$DynDnsZone) { 237 | $this.Zone = $DynDnsZone.zone 238 | $this.SerialNumber = $DynDnsZone.serial 239 | $this.SerialStyle = $DynDnsZone.serial_style 240 | $this.Type = $DynDnsZone.zone_type 241 | $this.RawData = $DynDnsZone 242 | } 243 | } 244 | 245 | class DynDnsZoneNote : DynDnsRawData { 246 | [string]$Zone 247 | [object]$Timestamp 248 | [string]$Type 249 | [string]$User 250 | [string]$SerialNumber 251 | [string]$Note 252 | 253 | DynDnsZoneNote () { } 254 | DynDnsZoneNote ([PSCustomObject]$DynDnsZoneNote) { 255 | [datetime]$origin = '1970-01-01 00:00:00' 256 | 257 | $this.Zone = $DynDnsZoneNote.zone 258 | $this.Timestamp = $origin.AddSeconds($DynDnsZoneNote.timestamp).ToLocalTime() 259 | $this.Type = $DynDnsZoneNote.type 260 | $this.User = $DynDnsZoneNote.user_name 261 | $this.SerialNumber = $DynDnsZoneNote.serial 262 | $this.Note = $DynDnsZoneNote.note.trim() 263 | $this.RawData = $DynDnsZoneNote 264 | } 265 | } 266 | 267 | class DynDnsZoneChanges : DynDnsRawData { 268 | [string]$Zone 269 | [string]$UserId 270 | [string]$Type 271 | [string]$Name 272 | [string]$SerialNumber 273 | [int]$TTL 274 | [pscustomobject]$RecordData 275 | 276 | DynDnsZoneChanges () { } 277 | DynDnsZoneChanges ([PSCustomObject]$DynDnsZoneChanges) { 278 | $this.Zone = $DynDnsZoneChanges.zone 279 | $this.UserId = $DynDnsZoneChanges.user_id 280 | $this.Type = $DynDnsZoneChanges.rdata_type 281 | $this.Name = $DynDnsZoneChanges.fqdn 282 | $this.SerialNumber = $DynDnsZoneChanges.serial 283 | $this.TTL = $DynDnsZoneChanges.ttl 284 | $this.RecordData = $DynDnsZoneChanges.rdata 285 | $this.RawData = $DynDnsZoneChanges 286 | } 287 | } 288 | 289 | class DynDnsHttpRedirect : DynDnsRawData { 290 | [string]$Zone 291 | [string]$Name 292 | [string]$Url 293 | [string]$ResponseCode 294 | [object]$IncludeUri 295 | 296 | DynDnsHttpRedirect () { } 297 | DynDnsHttpRedirect ([PSCustomObject]$DynDnsHttpRedirect) { 298 | if ($DynDnsHttpRedirect.keep_uri -match 'Y') { 299 | $KeepUri = $true 300 | } else { 301 | $KeepUri = $false 302 | } 303 | $this.Zone = $DynDnsHttpRedirect.zone 304 | $this.Name = $DynDnsHttpRedirect.fqdn 305 | $this.Url = $DynDnsHttpRedirect.url 306 | $this.ResponseCode = $DynDnsHttpRedirect.code 307 | $this.IncludeUri = $KeepUri 308 | $this.RawData = $DynDnsHttpRedirect 309 | } 310 | } 311 | 312 | class DynDnsUser : DynDnsRawData { 313 | [string]$User 314 | [string]$Status 315 | [string]$CustomerName 316 | [string]$Nickname 317 | [string]$FirstName 318 | [string]$LastName 319 | [string]$Email 320 | [string]$Phone 321 | [string[]]$Groups 322 | 323 | DynDnsUser () {} 324 | DynDnsUser ([PSCustomObject]$DynDnsUser) { 325 | $this.User = $DynDnsUser.user_name 326 | $this.Status = $DynDnsUser.status 327 | $this.CustomerName = $DynDnsUser.organization 328 | $this.Nickname = $DynDnsUser.nickname 329 | $this.FirstName = $DynDnsUser.first_name 330 | $this.LastName = $DynDnsUser.last_name 331 | $this.Email = $DynDnsUser.email 332 | $this.Phone = $DynDnsUser.phone 333 | $this.Groups = $DynDnsUser.group_name 334 | $this.RawData = $DynDnsUser 335 | } 336 | } 337 | 338 | class DynDnsHttpResponse { 339 | [string]$Method 340 | [string]$Body 341 | [string]$Uri 342 | [string]$StatusCode 343 | [string]$StatusDescription 344 | 345 | DynDnsHttpResponse () {} 346 | DynDnsHttpResponse ([PsCustomObject]$DynDnsHttpResponse) { 347 | $this.Method = $DynDnsHttpResponse.Method 348 | $this.Body = $DynDnsHttpResponse.Body 349 | $this.Uri = $DynDnsHttpResponse.Uri 350 | $this.StatusCode = $DynDnsHttpResponse.StatusCode 351 | $this.StatusDescription = $DynDnsHttpResponse.StatusDescription 352 | } 353 | } 354 | 355 | class DynDnsRestResponse { 356 | [PSCustomObject]$Response 357 | [PSCustomObject]$Data 358 | [decimal]$ElapsedTime 359 | 360 | DynDnsRestResponse () { } 361 | DynDnsRestResponse ([PSCustomObject]$DynDnsRestResponse) { 362 | $this.Response = $DynDnsRestResponse.Response 363 | $this.Data = $DynDnsRestResponse.Data 364 | $this.ElapsedTime = $DynDnsRestResponse.ElapsedTime 365 | } 366 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/PoShDynDnsApi.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PSGet_PoShDynDnsApi' 3 | # 4 | # Generated by: Dave Carroll 5 | # 6 | # Generated on: 12/28/2018 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PoShDynDnsApi.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.1.2' 16 | 17 | # Supported PSEditions 18 | CompatiblePSEditions = 'Desktop', 'Core' 19 | 20 | # ID used to uniquely identify this module 21 | GUID = '594b1a08-4339-47a2-8c89-a53d8a699bf1' 22 | 23 | # Author of this module 24 | Author = 'Dave Carroll' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'NA' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) 2018-2020 Dave Carroll. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'PowerShell module used to interact with Dyn Managed DNS REST API' 34 | 35 | # Minimum version of the Windows PowerShell engine required by this module 36 | PowerShellVersion = '5.1' 37 | 38 | # Name of the Windows PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the Windows PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # CLRVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = 'Add-DynDnsHttpRedirect', 'Add-DynDnsRecord', 'Add-DynDnsZone', 73 | 'Connect-DynDnsSession', 'Disconnect-DynDnsSession', 74 | 'Get-DynDnsHistory', 'Get-DynDnsHttpRedirect', 'Get-DynDnsJob', 75 | 'Get-DynDnsNodeList', 'Get-DynDnsRecord', 'Get-DynDnsSession', 76 | 'Get-DynDnsTask', 'Get-DynDnsUser', 'Get-DynDnsZone', 77 | 'Get-DynDnsZoneChanges', 'Get-DynDnsZoneNotes', 'Lock-DynDnsZone', 78 | 'New-DynDnsRecord', 'Publish-DynDnsZoneChanges', 79 | 'Remove-DynDnsHttpRedirect', 'Remove-DynDnsNode', 80 | 'Remove-DynDnsRecord', 'Remove-DynDnsZone', 'Send-DynDnsSession', 81 | 'Test-DynDnsSession', 'Undo-DynDnsZoneChanges', 'Unlock-DynDnsZone', 82 | 'Update-DynDnsRecord' 83 | 84 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 85 | # CmdletsToExport = @() 86 | 87 | # Variables to export from this module 88 | # VariablesToExport = @() 89 | 90 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 91 | # AliasesToExport = @() 92 | 93 | # DSC resources to export from this module 94 | # DscResourcesToExport = @() 95 | 96 | # List of all modules packaged with this module 97 | # ModuleList = @() 98 | 99 | # List of all files packaged with this module 100 | FileList = 'en-US\about_PoShDynDnsApi.help.txt', 101 | 'en-US\PoShDynDnsApi-help.xml', 102 | 'Private\Compare-ObjectProperties.ps1', 103 | 'Private\Invoke-DynDnsRequestCore.ps1', 104 | 'Private\Invoke-DynDnsRequestDesktop.ps1', 105 | 'Private\Write-DynDnsOutput.ps1', 106 | 'Public\Add-DynDnsHttpRedirect.ps1', 'Public\Add-DynDnsRecord.ps1', 107 | 'Public\Add-DynDnsZone.ps1', 'Public\Connect-DynDnsSession.ps1', 108 | 'Public\Disconnect-DynDnsSession.ps1', 109 | 'Public\Get-DynDnsHistory.ps1', 'Public\Get-DynDnsHttpRedirect.ps1', 110 | 'Public\Get-DynDnsJob.ps1', 'Public\Get-DynDnsNodeList.ps1', 111 | 'Public\Get-DynDnsRecord.ps1', 'Public\Get-DynDnsSession.ps1', 112 | 'Public\Get-DynDnsTask.ps1', 'Public\Get-DynDnsUser.ps1', 113 | 'Public\Get-DynDnsZone.ps1', 'Public\Get-DynDnsZoneChanges.ps1', 114 | 'Public\Get-DynDnsZoneNotes.ps1', 'Public\Lock-DynDnsZone.ps1', 115 | 'Public\New-DynDnsRecord.ps1', 116 | 'Public\Publish-DynDnsZoneChanges.ps1', 117 | 'Public\Remove-DynDnsHttpRedirect.ps1', 118 | 'Public\Remove-DynDnsNode.ps1', 'Public\Remove-DynDnsRecord.ps1', 119 | 'Public\Remove-DynDnsZone.ps1', 'Public\Send-DynDnsSession.ps1', 120 | 'Public\Test-DynDnsSession.ps1', 121 | 'Public\Undo-DynDnsZoneChanges.ps1', 'Public\Unlock-DynDnsZone.ps1', 122 | 'Public\Update-DynDnsRecord.ps1', 'PoShDynDnsApi.psd1', 123 | 'PoShDynDnsApi.psm1' 124 | 125 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 126 | PrivateData = @{ 127 | 128 | #Category of this module 129 | Category = 'DNS Server' 130 | 131 | #IsPrerelease of this module 132 | IsPrerelease = $true 133 | 134 | PSData = @{ 135 | 136 | # Tags applied to this module. These help with module discovery in online galleries. 137 | Tags = 'PoShDynDnsApi','DNS','DynDns','DynManagedDns','PowerShell','AzureAutomationNotSupported' 138 | 139 | # A URL to the license for this module. 140 | LicenseUri = 'https://github.com/thedavecarroll/PoShDynDnsApi/blob/main/LICENSE' 141 | 142 | # A URL to the main website for this project. 143 | ProjectUri = 'https://github.com/thedavecarroll/PoShDynDnsApi' 144 | 145 | # A URL to an icon representing this module. 146 | # IconUri = '' 147 | 148 | # ReleaseNotes of this module 149 | # ReleaseNotes = '' 150 | 151 | # External dependent modules of this module 152 | # ExternalModuleDependencies = '' 153 | 154 | #RequireLicenseAcceptance of this module 155 | RequireLicenseAcceptance = $false 156 | 157 | } # End of PSData hashtable 158 | 159 | } # End of PrivateData hashtable 160 | 161 | # HelpInfo URI of this module 162 | HelpInfoURI = 'https://powershell.anovelidea.org/modulehelp/PoShDynDnsApi/' 163 | 164 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 165 | # DefaultCommandPrefix = '' 166 | 167 | } 168 | 169 | -------------------------------------------------------------------------------- /PoShDynDnsApi/PoShDynDnsApi.psm1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.1 2 | 3 | #region info 4 | <# 5 | The following members are exported via the module's data file (.psd1) 6 | Functions 7 | TypeData 8 | FormatData 9 | #> 10 | #endregion info 11 | 12 | #region discover module name 13 | $ScriptPath = Split-Path $MyInvocation.MyCommand.Path 14 | $ModuleName = $ExecutionContext.SessionState.Module 15 | Write-Verbose -Message "Loading module $ModuleName" 16 | #endregion discover module name 17 | 18 | #Set-StrictMode -Version Latest 19 | try { 20 | Add-Type -AssemblyName System.Net.Http -ErrorAction Stop 21 | } 22 | catch { 23 | $PSCmdlet.ThrowTerminatingError($_) 24 | } 25 | 26 | #region load module variables 27 | Write-Verbose -Message "Creating modules variables" 28 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 29 | $DynDnsSession = [ordered]@{ 30 | ClientUrl = 'https://api.dynect.net' 31 | User = $null 32 | Customer = $null 33 | ApiVersion = $null 34 | AuthToken = $null 35 | StartTime = $null 36 | ElapsedTime = $null 37 | RefreshTime = $null 38 | } 39 | 40 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 41 | $DynDnsHistoryList = [System.Collections.Generic.List[object]]::new() 42 | #endregion load module variables 43 | 44 | #region Handle Module Removal 45 | $OnRemoveScript = { 46 | # Remove-Variable -Name DynDnsSession -Scope Script -Force 47 | } 48 | $ExecutionContext.SessionState.Module.OnRemove += $OnRemoveScript 49 | Register-EngineEvent -SourceIdentifier ([System.Management.Automation.PsEngineEvent]::Exiting) -Action $OnRemoveScript 50 | #endregion Handle Module Removal 51 | 52 | #region dot source public and private function definition files, export publich functions 53 | try { 54 | foreach ($Scope in 'Public','Private') { 55 | Get-ChildItem (Join-Path -Path $ScriptPath -ChildPath $Scope) -Filter *.ps1 | ForEach-Object { 56 | . $_.FullName 57 | if ($Scope -eq 'Public') { 58 | Export-ModuleMember -Function $_.BaseName -ErrorAction Stop 59 | } 60 | } 61 | } 62 | } 63 | catch { 64 | Write-Error ("{0}: {1}" -f $_.BaseName,$_.Exception.Message) 65 | exit 1 66 | } 67 | #endregion dot source public and private function definition files, export publich functions 68 | 69 | #region PSEdition detection 70 | if ($PSEdition -eq 'Core') { 71 | Set-Alias -Name 'Invoke-DynDnsRequest' -Value 'Invoke-DynDnsRequestCore' 72 | } else { 73 | Set-Alias -Name 'Invoke-DynDnsRequest' -Value 'Invoke-DynDnsRequestDesktop' 74 | } 75 | #endregion PSEdition detection 76 | 77 | -------------------------------------------------------------------------------- /PoShDynDnsApi/Private/Compare-ObjectProperties.ps1: -------------------------------------------------------------------------------- 1 | # https://blogs.technet.microsoft.com/janesays/2017/04/25/compare-all-properties-of-two-objects-in-windows-powershell/ 2 | function Compare-ObjectProperties { 3 | [CmdLetBinding()] 4 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseSingularNouns', Justification='Compares all properties')] 5 | Param( 6 | [PSObject]$ReferenceObject, 7 | [PSObject]$DifferenceObject 8 | ) 9 | $objprops = $ReferenceObject | Get-Member -MemberType Property,NoteProperty | ForEach-Object Name 10 | $objprops += $DifferenceObject | Get-Member -MemberType Property,NoteProperty | ForEach-Object Name 11 | $objprops = $objprops | Sort-Object | Select-Object -Unique 12 | $diffs = @() 13 | foreach ($objprop in $objprops) { 14 | $diff = Compare-Object $ReferenceObject $DifferenceObject -Property $objprop 15 | if ($diff) { 16 | $diffprops = @{ 17 | PropertyName=$objprop 18 | RefValue=($diff | Where-Object {$_.SideIndicator -eq '<='} | ForEach-Object $($objprop) ) 19 | DiffValue=($diff | Where-Object {$_.SideIndicator -eq '=>'} | ForEach-Object $($objprop)) 20 | } 21 | $diffs += New-Object PSObject -Property $diffprops 22 | } 23 | } 24 | if ($diffs) {return ($diffs | Select-Object PropertyName,RefValue,DiffValue)} 25 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Private/Invoke-DynDnsRequestCore.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-DynDnsRequestCore { 2 | [CmdLetBinding()] 3 | param( 4 | [Parameter(ParameterSetName='Default')] 5 | [ValidateSet('Get','Post','Put','Delete')] 6 | [System.Net.Http.HttpMethod]$Method='Get', 7 | 8 | [Parameter(ParameterSetName='Default')] 9 | [ValidateScript({$_ -match '^/REST/'})] 10 | [String]$UriPath, 11 | 12 | [Parameter(ParameterSetName='Default')] 13 | [Parameter(ParameterSetName='Session')] 14 | [Alias('JsonBody','Json')] 15 | [ValidateScript({$_ | ConvertFrom-Json})] 16 | [AllowNull()] 17 | $Body, 18 | 19 | [Parameter(ParameterSetName='Default')] 20 | [Switch]$SkipSessionCheck, 21 | 22 | [Parameter(ParameterSetName='Session')] 23 | [ValidateSet('Connect','Disconnect','Test','Send')] 24 | [string]$SessionAction 25 | ) 26 | 27 | $EmptyBody = @{} | ConvertTo-Json 28 | $HttpClient = [System.Net.Http.Httpclient]::new() 29 | $HttpClient.Timeout = [System.TimeSpan]::new(0, 0, 90) 30 | $HttpClient.DefaultRequestHeaders.TransferEncodingChunked = $false 31 | $Accept = [System.Net.Http.Headers.MediaTypeWithQualityHeaderValue]::new('application/json') 32 | $HttpClient.DefaultRequestHeaders.Accept.Add($Accept) 33 | $HttpClient.BaseAddress = [Uri]$DynDnsSession.ClientUrl 34 | 35 | if ($PsCmdlet.ParameterSetName -eq 'Session') { 36 | $UriPath = '/REST/Session/' 37 | switch ($SessionAction) { 38 | 'Connect' { 39 | if ($Body) { 40 | $HttpRequest = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Post,$UriPath) 41 | $HttpRequest.Content = [System.Net.Http.StringContent]::new($Body, [System.Text.Encoding]::UTF8, 'application/json') 42 | } else { 43 | Write-Warning -Message 'No login credentials provided.' 44 | return 45 | } 46 | } 47 | 'Disconnect' { 48 | if ($DynDnsSession.AuthToken) { 49 | $HttpClient.DefaultRequestHeaders.Add('Auth-Token',$DynDnsSession.AuthToken) 50 | $HttpRequest = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Delete,$UriPath) 51 | $HttpRequest.Content = [System.Net.Http.StringContent]::new($EmptyBody, [System.Text.Encoding]::UTF8, 'application/json') 52 | } else { 53 | Write-Warning -Message 'No authentication token found. Please use Connect-DynDnsSession to obtain a new token.' 54 | return 55 | } 56 | } 57 | 'Send' { 58 | if ($DynDnsSession.AuthToken) { 59 | $HttpClient.DefaultRequestHeaders.Add('Auth-Token',$DynDnsSession.AuthToken) 60 | $HttpRequest = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Put,$UriPath) 61 | $HttpRequest.Content = [System.Net.Http.StringContent]::new($EmptyBody, [System.Text.Encoding]::UTF8, 'application/json') 62 | } else { 63 | Write-Warning -Message 'No authentication token found. Please use Connect-DynDnsSession to obtain a new token.' 64 | return 65 | } 66 | } 67 | 'Test' { 68 | $HttpRequest = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Get,$UriPath) 69 | $HttpRequest.Content = [System.Net.Http.StringContent]::new($EmptyBody, [System.Text.Encoding]::UTF8, 'application/json') 70 | if ($DynDnsSession.AuthToken) { 71 | $HttpClient.DefaultRequestHeaders.Add('Auth-Token',$DynDnsSession.AuthToken) 72 | } else { 73 | Write-Verbose -Message 'No authentication token found.' 74 | return 75 | } 76 | } 77 | } 78 | } else { 79 | if ($DynDnsSession.AuthToken) { 80 | $HttpClient.DefaultRequestHeaders.Add('Auth-Token',$DynDnsSession.AuthToken) 81 | } else { 82 | Write-Warning -Message 'No authentication token found. Please use Connect-DynDnsSession to obtain a new token.' 83 | return 84 | } 85 | 86 | $HttpRequest = [System.Net.Http.HttpRequestMessage]::new($Method,$UriPath) 87 | if ($Body -and $Method -match 'Post|Put|Delete') { 88 | $HttpRequest.Content = [System.Net.Http.StringContent]::new($Body, [System.Text.Encoding]::UTF8, 'application/json') 89 | } else { 90 | $HttpRequest.Content = [System.Net.Http.StringContent]::new($EmptyBody, [System.Text.Encoding]::UTF8, 'application/json') 91 | } 92 | 93 | } 94 | 95 | $StopWatch = [System.Diagnostics.Stopwatch]::StartNew() 96 | $HttpResponseMessage = $HttpClient.SendAsync($HttpRequest) 97 | if ($HttpResponseMessage.IsFaulted) { 98 | $PsCmdlet.ThrowTerminatingError($HttpResponseMessage.Exception) 99 | } 100 | $Result = $HttpResponseMessage.Result 101 | try { 102 | $Content = $Result.Content.ReadAsStringAsync().Result | ConvertFrom-Json 103 | } 104 | catch { 105 | $Content = $null 106 | } 107 | $ElapsedTime = $StopWatch.Elapsed.TotalSeconds 108 | $StopWatch.Stop() 109 | 110 | if ($SessionAction) { 111 | $ResponseBody = $null 112 | } else { 113 | $ResponseBody = $Body 114 | } 115 | 116 | $Response = [DynDnsHttpResponse]::New([PSCustomObject]@{ 117 | Method = $HttpRequest.Method.ToString().ToUpper() 118 | Body = $ResponseBody 119 | Uri = $HttpRequest.RequestUri.ToString() 120 | StatusCode = $Result.StatusCode 121 | StatusDescription = $Result.ReasonPhrase 122 | }) 123 | 124 | if ($Content.status -eq 'success') { 125 | $DynDnsSession.RefreshTime = [System.DateTime]::Now 126 | } 127 | 128 | [DynDnsRestResponse]::New( 129 | [PsCustomObject]@{ 130 | Response = $Response 131 | Data = $Content 132 | ElapsedTime = $ElapsedTime 133 | } 134 | ) 135 | 136 | $HttpClient.Dispose() 137 | 138 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Private/Invoke-DynDnsRequestDesktop.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-DynDnsRequestDesktop { 2 | [CmdLetBinding()] 3 | param( 4 | [Parameter(ParameterSetName='Default')] 5 | [ValidateSet('Get','Post','Put','Delete')] 6 | [Microsoft.PowerShell.Commands.WebRequestMethod]$Method='Get', 7 | 8 | [Parameter(ParameterSetName='Default')] 9 | [ValidateScript({$_ -match '^/REST/'})] 10 | [String]$UriPath, 11 | 12 | [Parameter(ParameterSetName='Default')] 13 | [Parameter(ParameterSetName='Session')] 14 | [Alias('JsonBody','Json')] 15 | [ValidateScript({$_ | ConvertFrom-Json})] 16 | [AllowNull()] 17 | $Body, 18 | 19 | [Parameter(ParameterSetName='Default')] 20 | [Switch]$SkipSessionCheck, 21 | 22 | [Parameter(ParameterSetName='Session')] 23 | [ValidateSet('Connect','Disconnect','Test','Send')] 24 | [string]$SessionAction 25 | ) 26 | 27 | $RestParams = @{ 28 | ContentType = 'application/json' 29 | ErrorAction = 'Stop' 30 | Verbose = $false 31 | } 32 | 33 | if ($PsCmdlet.ParameterSetName -eq 'Session') { 34 | $RestParams.Add('Uri',"$($DynDnsSession.ClientUrl)/REST/Session/") 35 | switch ($SessionAction) { 36 | 'Connect' { 37 | $RestParams.Add('Method','Post') 38 | if ($Body) { 39 | $RestParams.Add('Body',$Body) 40 | } else { 41 | Write-Warning -Message 'No login credentials provided.' 42 | return 43 | } 44 | } 45 | 'Disconnect' { 46 | $RestParams.Add('Method','Delete') 47 | if ($DynDnsSession.AuthToken) { 48 | $RestParams.Add('Headers',@{'Auth-Token' = "$($DynDnsSession.AuthToken)"}) 49 | } else { 50 | Write-Warning -Message 'No authentication token found. Please use Connect-DynDnsSession to obtain a new token.' 51 | return 52 | } 53 | } 54 | 'Send' { 55 | $RestParams.Add('Method','Put') 56 | if ($DynDnsSession.AuthToken) { 57 | $RestParams.Add('Headers',@{'Auth-Token' = "$($DynDnsSession.AuthToken)"}) 58 | } else { 59 | Write-Warning -Message 'No authentication token found. Please use Connect-DynDnsSession to obtain a new token.' 60 | return 61 | } 62 | } 63 | 'Test' { 64 | if ($DynDnsSession.AuthToken) { 65 | $RestParams.Add('Method','Get') 66 | $RestParams.Add('WarningAction','SilentlyContinue') 67 | $RestParams.Add('Headers',@{'Auth-Token' = "$($DynDnsSession.AuthToken)"}) 68 | } else { 69 | Write-Verbose -Message 'No authentication token found.' 70 | return 71 | } 72 | } 73 | } 74 | } else { 75 | if ($DynDnsSession.AuthToken) { 76 | $RestParams.Add('Headers',@{'Auth-Token' = "$($DynDnsSession.AuthToken)"}) 77 | } else { 78 | Write-Warning -Message 'No authentication token found. Please use Connect-DynDnsSession to obtain a new token.' 79 | return 80 | } 81 | $RestParams.Add('Uri',"$($DynDnsSession.ClientUrl)$UriPath") 82 | $RestParams.Add('Method',$Method) 83 | if ($Body -and $Method -match 'Post|Put|Delete') { 84 | $RestParams.Add('Body',$Body) 85 | } 86 | } 87 | 88 | $StopWatch = [System.Diagnostics.Stopwatch]::StartNew() 89 | $OriginalProgressPreference = $ProgressPreference 90 | $ProgressPreference = 'SilentlyContinue' 91 | try { 92 | $DynDnsResponse = Invoke-WebRequest @RestParams -ErrorVariable ErrorResponse 93 | $Content = $DynDnsResponse.Content 94 | } 95 | catch { 96 | $DynDnsResponse = $ErrorResponse.ErrorRecord.Exception.Response 97 | $ResponseReader = [System.IO.StreamReader]::new($DynDnsResponse.GetResponseStream()) 98 | $Content = $ResponseReader.ReadToEnd() 99 | $ResponseReader.Close() 100 | } 101 | $ElapsedTime = $StopWatch.Elapsed.TotalSeconds 102 | $StopWatch.Stop() 103 | $ProgressPreference = $OriginalProgressPreference 104 | 105 | try { 106 | $Data = $Content | ConvertFrom-Json 107 | } 108 | catch { 109 | Write-Warning -Message 'Unable to convert response to JSON.' 110 | $Data = $null 111 | } 112 | 113 | if ($SessionAction) { 114 | $ResponseBody = $null 115 | } else { 116 | $ResponseBody = $Body 117 | } 118 | 119 | $Response = [DynDnsHttpResponse]::New([PSCustomObject]@{ 120 | Method = $RestParams.Method.ToString().ToUpper() 121 | Body = $ResponseBody 122 | Uri = $RestParams.Uri.ToString() 123 | StatusCode = $DynDnsResponse.StatusCode 124 | StatusDescription = $DynDnsResponse.ReasonPhrase 125 | }) 126 | 127 | if ($Data.status -eq 'success') { 128 | $DynDnsSession.RefreshTime = [System.DateTime]::Now 129 | } 130 | 131 | [DynDnsRestResponse]::New( 132 | [PsCustomObject]@{ 133 | Response = $Response 134 | Data = $Data 135 | ElapsedTime = $ElapsedTime 136 | } 137 | ) 138 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Private/Write-DynDnsOutput.ps1: -------------------------------------------------------------------------------- 1 | function Write-DynDnsOutput { 2 | [CmdLetBinding()] 3 | param( 4 | [PsObject]$DynDnsResponse, 5 | [switch]$SkipSuccess 6 | ) 7 | 8 | if ($DynDnsSession.ApiVersion) { 9 | $ApiVersion = 'API-' + $DynDnsSession.ApiVersion 10 | } else { 11 | $ApiVersion = $null 12 | } 13 | 14 | $Status = $JobId = $null 15 | if ($null -ne $DynDnsResponse.Data.status) { 16 | $Status = $DynDnsResponse.Data.status 17 | } 18 | if ($null -ne $DynDnsResponse.Data.job_id) { 19 | $JobId = $DynDnsResponse.Data.job_id 20 | } 21 | 22 | $MyFunction = Get-PSCallStack | Where-Object {$_.Command -notmatch 'DynDnsRequest|DynDnsOutput|ScriptBlock'} 23 | 24 | if ($MyFunction.Arguments) { 25 | $Arguments = $MyFunction.Arguments -Split ',' | Sort-Object -Unique | ForEach-Object { 26 | if ($_ -match '\w+=\S+\w+') { $matches[0] } } | Where-Object { 27 | $_ -notmatch 'Debug|Verbose|InformationAction|WarningAction|ErrorAction|Variable|null' 28 | } 29 | $Arguments = $Arguments | ForEach-Object { $_.Replace('\','\\') | ConvertFrom-StringData } 30 | } 31 | 32 | if ($DynDnsResponse.Response.Uri -match 'Session') { 33 | $Command = $MyFunction.Command | Where-Object { $_ -match 'DynDnsSession' } | Select-Object -First 1 34 | $FilteredArguments = @{} 35 | } else { 36 | $MyFunction = $MyFunction | Select-Object -First 1 37 | $Command = $MyFunction.Command 38 | 39 | if ($MyFunction.Arguments) { 40 | $Arguments = $MyFunction.Arguments -Split ',' | Sort-Object -Unique | ForEach-Object { 41 | if ($_ -match '\w+=\S+\w+') { $matches[0] } } | Where-Object { 42 | $_ -notmatch 'Debug|Verbose|InformationAction|WarningAction|ErrorAction|Variable' 43 | } 44 | $Arguments = $Arguments | ForEach-Object { $_.Replace('\','\\') | ConvertFrom-StringData } 45 | 46 | $FilteredArguments = @{} 47 | foreach ($Key in ($Arguments.Keys | Sort-Object -Unique)) { 48 | $FilteredArguments.Add($Key,$Arguments.$Key.Replace('$null','')) 49 | } 50 | } 51 | } 52 | 53 | $InformationOutput = [DynDnsHistory]::New(@{ 54 | Command = $Command 55 | Status = $Status 56 | JobId = $JobId 57 | Method = $DynDnsResponse.Response.Method 58 | Uri = $DynDnsResponse.Response.Uri 59 | Body = $DynDnsResponse.Response.Body 60 | StatusCode = $DynDnsResponse.Response.StatusCode 61 | StatusDescription = $DynDnsResponse.Response.StatusDescription 62 | ElapsedTime = "{0:N3}" -f $DynDnsResponse.ElapsedTime 63 | Arguments = $FilteredArguments 64 | }) 65 | 66 | if ($InformationOutput.StatusCode) { 67 | $DynDnsHistoryList.Add($InformationOutput) 68 | Write-Information -MessageData $InformationOutput 69 | } else { 70 | Write-Warning -Message 'No StatusCode returned.' 71 | } 72 | 73 | switch ($Command) { 74 | 'Add-DynDnsZone' { 75 | foreach ($Info in $Message.INFO) { 76 | ($Info -Split (':',2))[1].Trim() 77 | } 78 | } 79 | 'Publish-DynDnsZoneChanges' { 80 | if ($DynDnsResponse.Data.msgs.INFO -match 'Missing SOA record' ) { 81 | 'The attempt to import {0} has failed. Please delete the zone and reattempt the import after fixing errors.' -f $DynDnsResponse.Response.Uri.Split('/')[-1] 82 | } 83 | } 84 | } 85 | 86 | foreach ($Message in $DynDnsResponse.Data.msgs) { 87 | $VerboseMessage = ($ApiVersion,$Message.LVL,$Message.SOURCE,$Message.INFO -join ' : ') 88 | $ErrorMessage = ($ApiVersion,$Message.LVL,$Message.SOURCE,$Message.ERR_CD,$Message.INFO -join ' : ') 89 | switch ($Message.LVL) { 90 | 'INFO' { 91 | Write-Verbose -Message $VerboseMessage 92 | } 93 | 'ERROR' { 94 | if ($Message.ERR_CD -eq 'NOT_FOUND' -and $Message.INFO -notlike '*No such zone') { 95 | Write-Verbose -Message $ErrorMessage 96 | } else { 97 | Write-Warning -Message $ErrorMessage 98 | } 99 | } 100 | default { 101 | Write-Warning -Message $ErrorMessage 102 | } 103 | } 104 | } 105 | 106 | if ($SkipSuccess) { 107 | return 108 | } 109 | 110 | if ($Status -eq 'success') { 111 | foreach ($DataResponse in $DynDnsResponse.Data.data) { 112 | if ($Command -notmatch 'Remove|Undo') { 113 | switch ($Command.Split('-')[1]) { 114 | 'DynDnsRecord' { 115 | switch ($DataResponse.record_type) { 116 | 'A' { [DynDnsRecord_A]::New($DataResponse) } 117 | 'TXT' { [DynDnsRecord_TXT]::New($DataResponse) } 118 | 'CNAME' { [DynDnsRecord_CNAME]::New($DataResponse) } 119 | 'MX' { [DynDnsRecord_MX]::New($DataResponse) } 120 | 'SRV' { [DynDnsRecord_SRV]::New($DataResponse) } 121 | 'NS' { [DynDnsRecord_NS]::New($DataResponse) } 122 | 'PTR' { [DynDnsRecord_PTR]::New($DataResponse) } 123 | 'SOA' { [DynDnsRecord_SOA]::New($DataResponse) } 124 | default { 125 | [DynDnsRecord]::New($DataResponse) 126 | } 127 | } 128 | } 129 | 'DynDnsZone' { 130 | [DynDnsZone]::New($DataResponse) 131 | } 132 | 'DynDnsTask' { 133 | [DynDnsTask]::New($DataResponse) 134 | } 135 | 'DynDnsZoneNotes' { 136 | [DynDnsZoneNote]::New($DataResponse) 137 | } 138 | 'DynDnsHttpRedirect' { 139 | [DynDnsHttpRedirect]::New($DataResponse) 140 | } 141 | 'DynDnsUser' { 142 | [DynDnsUser]::New($DataResponse) 143 | } 144 | 'DynDnsZoneChanges' { 145 | [DynDnsZoneChanges]::New($DataResponse) 146 | } 147 | 'DynDnsSession' { 148 | return 149 | } 150 | default { 151 | $DataResponse 152 | } 153 | } 154 | } 155 | } 156 | } 157 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Add-DynDnsHttpRedirect.ps1: -------------------------------------------------------------------------------- 1 | function Add-DynDnsHttpRedirect { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true)] 8 | [string]$Zone, 9 | 10 | [string]$Node, 11 | 12 | [Parameter(Mandatory=$true)] 13 | [string]$Url, 14 | 15 | [ValidateSet('301','302')] 16 | [string]$ResponseCode = '301', 17 | 18 | [switch]$IncludeUri 19 | ) 20 | 21 | if (-Not (Test-DynDnsSession)) { 22 | return 23 | } 24 | 25 | if ($Url -notmatch '^http://|^https://') { 26 | Write-Warning -Message "The URL provided does not begin with 'http://' or 'https://'." 27 | return 28 | } 29 | 30 | if ($Node) { 31 | if ($Node -match $Zone ) { 32 | $Fqdn = $Node 33 | } else { 34 | $Fqdn = $Node + '.' + $Zone 35 | } 36 | } else { 37 | $Fqdn = $Zone 38 | } 39 | 40 | if ($IncludeUri) { 41 | $KeepUri = 'Y' 42 | } else { 43 | $KeepUri = 'N' 44 | } 45 | 46 | $JsonBody = @{ 47 | code = $ResponseCode 48 | keep_uri = $KeepUri 49 | url = $Url 50 | } | ConvertTo-Json 51 | 52 | Write-Warning -Message 'This will autopublish the HTTP redirect to the zone.' 53 | 54 | if ($PSCmdlet.ShouldProcess("$Fqdn","Create HTTP redirect to $Url")) { 55 | $NewHttpRedirect = Invoke-DynDnsRequest -UriPath "/REST/HTTPRedirect/$Zone/$Fqdn" -Method Post -Body $JsonBody 56 | Write-DynDnsOutput -DynDnsResponse $NewHttpRedirect 57 | } else { 58 | Write-Verbose 'Whatif : Created new HTTP redirect' 59 | } 60 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Add-DynDnsRecord.ps1: -------------------------------------------------------------------------------- 1 | function Add-DynDnsRecord { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true)] 8 | [string]$Zone, 9 | [string]$Node, 10 | [Parameter(Mandatory=$true)] 11 | [DynDnsRecord]$DynDnsRecord 12 | ) 13 | 14 | if (-Not (Test-DynDnsSession)) { 15 | return 16 | } 17 | 18 | if ($DynDnsRecord.record_type -eq 'SOA') { 19 | Write-Warning -Message 'You cannot add a new SOA record with this command. Please use Update-DynDnsRecord to update the ResponsiblePerson, SerialStyle, or TTL.' 20 | return 21 | } 22 | 23 | if ($Node) { 24 | if ($Node -match $Zone ) { 25 | $Fqdn = $Node 26 | } else { 27 | $Fqdn = $Node + '.' + $Zone 28 | } 29 | } else { 30 | $Fqdn = $Zone 31 | } 32 | 33 | $UriPath = "/REST/$($DynDnsRecord.Type)Record/$Zone/$Fqdn/" 34 | 35 | $DynDnsRecord.RawData.Remove('record_type') 36 | $JsonBody = $DynDnsRecord.RawData | ConvertTo-Json 37 | 38 | if ($PSCmdlet.ShouldProcess("$($DynDnsRecord.Type) - $Fqdn","Adding DNS record")) { 39 | $NewHttpRedirect = Invoke-DynDnsRequest -UriPath $UriPath -Method Post -Body $JsonBody 40 | Write-DynDnsOutput -DynDnsResponse $NewHttpRedirect 41 | } else { 42 | Write-Verbose 'Whatif : Added DNS record' 43 | } 44 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Add-DynDnsZone.ps1: -------------------------------------------------------------------------------- 1 | function Add-DynDnsZone { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true,ParameterSetName='Zone')] 8 | [Parameter(Mandatory=$true,ParameterSetName='ZoneFile')] 9 | [string]$Zone, 10 | 11 | [Parameter(Mandatory=$true,ParameterSetName='Zone')] 12 | [string]$ResponsiblePerson, 13 | 14 | [Parameter(ParameterSetName='Zone')] 15 | [ValidateSet('increment','epoch','day','minute')] 16 | [string]$SerialStyle = 'day', 17 | 18 | [Parameter(ParameterSetName='Zone')] 19 | [int]$TTL = 3600, 20 | 21 | [Parameter(ParameterSetName='ZoneFile')] 22 | [ValidateScript({Test-Path $_})] 23 | [string]$ZoneFile 24 | ) 25 | 26 | if (-Not (Test-DynDnsSession)) { 27 | return 28 | } 29 | 30 | $EmailRegex = '^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$' 31 | 32 | switch ($PsCmdlet.ParameterSetName) { 33 | 'Zone' { 34 | if ($ResponsiblePerson -notmatch $EmailRegex) { 35 | Write-Warning -Message 'The value provided for ResponsiblePerson does not appear to be a valid email. Please try again.' 36 | return 37 | } 38 | $UriPath = "/REST/Zone/$Zone" 39 | $JsonBody = @{ 40 | rname = $ResponsiblePerson.Replace('@','.') 41 | serial_style = $SerialStyle 42 | ttl = $TTL.ToString() 43 | } | ConvertTo-Json 44 | } 45 | 'ZoneFile' { 46 | $UriPath = "/REST/ZoneFile/$Zone" 47 | $JsonBody = @{ 48 | file = "$(Get-Content -Path $ZoneFile -Raw)" 49 | } | ConvertTo-Json 50 | } 51 | } 52 | 53 | if ($PSCmdlet.ShouldProcess("$Zone","Create DNS zone by $($PsCmdlet.ParameterSetName) method")) { 54 | $NewZone = Invoke-DynDnsRequest -UriPath $UriPath -Method Post -Body $JsonBody 55 | '' 56 | Write-DynDnsOutput -DynDnsResponse $NewZone 57 | if ($NewZone.Data.Status -eq 'success') { 58 | 'Note: Be sure to use the function Publish-DynDnsZoneChanges with the -Force switch in order publish the domain.' 59 | } 60 | } else { 61 | Write-Verbose 'Whatif : Created new zone' 62 | } 63 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Connect-DynDnsSession.ps1: -------------------------------------------------------------------------------- 1 | function Connect-DynDnsSession { 2 | [CmdLetBinding()] 3 | param( 4 | [Parameter(Mandatory = $true, HelpMessage = 'The Dyn API user (not DynID)')] 5 | [Alias('ApiUserName','UserName')] 6 | [string]$User, 7 | [Parameter(Mandatory = $true, HelpMessage = "The customer name for the Dyn API user")] 8 | [Alias('CustomerName')] 9 | [string]$Customer, 10 | [Parameter(Mandatory = $true, HelpMessage = 'The Dyn API user password')] 11 | [Alias('pwd','pass')] 12 | [SecureString]$Password, 13 | [switch]$Force 14 | ) 15 | 16 | if ($DynDnsSession.AuthToken) { 17 | Write-Verbose -Message 'Existing authentication token found.' 18 | } 19 | if (Test-DynDnsSession -WarningAction SilentlyContinue) { 20 | if ($Force) { 21 | $Disconnect = Disconnect-DynDnsSession 22 | Write-DynDnsOutput -DynDnsResponse $Disconnect 23 | if ($Disconnect.Data.status -eq 'failure') { 24 | return 25 | } 26 | } else { 27 | Write-Warning -Message 'There is a valid active session. Use the -Force parameter to logoff and create a new session.' 28 | Write-Warning -Message 'All unpublished changes will be discarded should you proceed with creating a new session.' 29 | return 30 | } 31 | } elseif ($DynDnsSession.AuthToken) { 32 | Write-Verbose -Message 'There is an expired authentication token which will be overridden upon successful creationn of a new session.' 33 | } 34 | 35 | $JsonBody = @{ 36 | customer_name = "$Customer" 37 | user_name = "$User" 38 | password = ([pscredential]::new('user',$Password).GetNetworkCredential().Password) 39 | } | ConvertTo-Json 40 | 41 | $DynDnsSession.User = $User 42 | $DynDnsSession.Customer = $Customer 43 | 44 | $Session = Invoke-DynDnsRequest -SessionAction 'Connect' -Body $JsonBody 45 | if ($Session.Data.status -eq 'success') { 46 | $DynDnsSession.AuthToken = $Session.Data.data.token 47 | $DynDnsSession.ApiVersion = $Session.Data.data.version 48 | $DynDnsSession.StartTime = [System.DateTime]::Now 49 | $DynDnsSession.ElapsedTime = [System.Diagnostics.Stopwatch]::StartNew() 50 | Write-DynDnsOutput -DynDnsResponse $Session 51 | } else { 52 | Write-DynDnsOutput -DynDnsResponse $Session 53 | } 54 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Disconnect-DynDnsSession.ps1: -------------------------------------------------------------------------------- 1 | function Disconnect-DynDnsSession { 2 | [CmdLetBinding()] 3 | param() 4 | 5 | $Session = Invoke-DynDnsRequest -SessionAction 'Disconnect' 6 | if ($Session.Data.status -eq 'success') { 7 | Write-DynDnsOutput -DynDnsResponse $Session 8 | } else { 9 | Write-DynDnsOutput -DynDnsResponse $Session -WarningAction Continue 10 | } 11 | $DynDnsSession.AuthToken = $null 12 | $DynDnsSession.User = $null 13 | $DynDnsSession.Customer = $null 14 | $DynDnsSession.StartTime = $null 15 | $DynDnsSession.ElapsedTime = $null 16 | $DynDnsSession.RefreshTime = $null 17 | 18 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsHistory.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsHistory { 2 | [CmdLetBinding(DefaultParameterSetName='Default')] 3 | [OutputType('DynDnsHistory')] 4 | param( 5 | [Parameter(ParameterSetName='Default')] 6 | [Parameter(ParameterSetName='Skip')] 7 | [Parameter(ParameterSetName='SkipLast')] 8 | [int]$First, 9 | [Parameter(ParameterSetName='Default')] 10 | [Parameter(ParameterSetName='Skip')] 11 | [Parameter(ParameterSetName='SkipLast')] 12 | [int]$Last, 13 | [Parameter(ParameterSetName='Skip')] 14 | [int]$Skip, 15 | [Parameter(ParameterSetName='SkipLast')] 16 | [int]$SkipLast 17 | ) 18 | 19 | $DynDnsHistoryParms = @{} 20 | if ($First) { 21 | $DynDnsHistoryParms.Add('First',$First) 22 | } 23 | if ($Last) { 24 | $DynDnsHistoryParms.Add('Last',$Last) 25 | } 26 | if ($psCmdlet.ParameterSetName -eq 'Skip') { 27 | $DynDnsHistoryParms.Add('Skip',$Skip) 28 | } 29 | if ($psCmdlet.ParameterSetName -eq 'SkipLast') { 30 | $DynDnsHistoryParms.Add('SkipLast',$SkipLast) 31 | } 32 | 33 | $DynDnsHistoryList | Select-Object @DynDnsHistoryParms 34 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsHttpRedirect.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsHttpRedirect { 2 | [CmdLetBinding()] 3 | param( 4 | [Parameter(Mandatory=$true)] 5 | [string]$Zone, 6 | [string]$Node 7 | ) 8 | 9 | if ($Node) { 10 | if ($Node -match $Zone ) { 11 | $Fqdn = $Node 12 | } else { 13 | $Fqdn = $Node + '.' + $Zone 14 | } 15 | $Uri = "/REST/HTTPRedirect/$Zone/$Fqdn" 16 | } else { 17 | $Uri = "/REST/HTTPRedirect/$Zone" 18 | } 19 | 20 | if (-Not (Test-DynDnsSession)) { 21 | return 22 | } 23 | 24 | $HttpRedirects = Invoke-DynDnsRequest -UriPath $Uri 25 | Write-DynDnsOutput -DynDnsResponse $HttpRedirects -SkipSuccess 26 | if ($HttpRedirects.Data.status -eq 'failure') { 27 | return 28 | } 29 | 30 | if ($Node) { 31 | Write-DynDnsOutput -DynDnsResponse $HttpRedirects 32 | } else { 33 | foreach ($UriPath in $HttpRedirects.Data.data) { 34 | $RedirectData = Invoke-DynDnsRequest -UriPath $UriPath 35 | Write-DynDnsOutput -DynDnsResponse $RedirectData 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsJob.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsJob { 2 | [CmdLetBinding()] 3 | param( 4 | [Parameter(Mandatory=$true)] 5 | [string]$JobId 6 | ) 7 | 8 | if (-Not (Test-DynDnsSession)) { 9 | return 10 | } 11 | 12 | $JobData = Invoke-DynDnsRequest -UriPath "/REST/Job/$JobId" 13 | Write-DynDnsOutput -DynDnsResponse $JobData 14 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsNodeList.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsNodeList { 2 | [CmdLetBinding()] 3 | param( 4 | [Parameter(Mandatory=$true)] 5 | [string]$Zone, 6 | [string]$Node 7 | ) 8 | 9 | if (-Not (Test-DynDnsSession)) { 10 | return 11 | } 12 | 13 | if ($Node) { 14 | if ($Node -match $Zone ) { 15 | $Fqdn = $Node 16 | } else { 17 | $Fqdn = $Node + '.' + $Zone 18 | } 19 | $UriPath = "/REST/NodeList/$Zone/$Fqdn/" 20 | } else { 21 | $UriPath = "/REST/NodeList/$Zone/" 22 | } 23 | 24 | $NodeList = Invoke-DynDnsRequest -UriPath $UriPath 25 | Write-DynDnsOutput -DynDnsResponse $NodeList 26 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsRecord.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsRecord { 2 | [CmdLetBinding()] 3 | param( 4 | [Parameter(Mandatory=$true)] 5 | [string]$Zone, 6 | [ValidateSet('SOA','NS','MX','TXT','SRV','CNAME','PTR','A','All',IgnoreCase=$false)] 7 | [string]$RecordType = 'All', 8 | [string]$Node 9 | ) 10 | 11 | if (-Not (Test-DynDnsSession)) { 12 | return 13 | } 14 | 15 | if ($Node) { 16 | if ($Node -match $Zone ) { 17 | $UriPath = '/REST/{0}Record/{1}/{1}' -f $RecordType,$Zone 18 | } else { 19 | $UriPath = '/REST/{0}Record/{1}/{2}.{1}' -f $RecordType,$Zone,$Node 20 | } 21 | } else { 22 | 'No node provided. {0} record types for zone tree will be returned.' -f $RecordType | Write-Verbose 23 | $UriPath = '/REST/AllRecord/{0}' -f $Zone 24 | } 25 | 26 | $Records = Invoke-DynDnsRequest -UriPath $UriPath 27 | Write-DynDnsOutput -DynDnsResponse $Records -SkipSuccess 28 | if ($Records.Data.status -eq 'failure') { 29 | return 30 | } 31 | 32 | if ($RecordType -eq 'All') { 33 | $RecordTypeFilter = '\S+' # match on all 34 | } else { 35 | $RecordTypeFilter = '/{0}Record' -f $RecordType # match only on requested record type 36 | } 37 | 38 | foreach ($UriPath in $Records.Data.data) { 39 | if ($UriPath -match $RecordTypeFilter) { 40 | $RecordData = Invoke-DynDnsRequest -UriPath $UriPath 41 | Write-DynDnsOutput -DynDnsResponse $RecordData 42 | } else { 43 | 'Skipping {0} due to RecordType' -f $UriPath | Write-Verbose 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsSession.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsSession { 2 | [CmdLetBinding()] 3 | param() 4 | 5 | $DynDnsSession | ConvertTo-Json | ConvertFrom-Json 6 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsTask.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsTask { 2 | [CmdLetBinding()] 3 | param( 4 | [int]$TaskId 5 | ) 6 | 7 | if (-Not (Test-DynDnsSession)) { 8 | return 9 | } 10 | 11 | if ($TaskId) { 12 | $UriPath = "/REST/Task/$($TaskId.ToString())" 13 | } else { 14 | $UriPath = "/REST/Task/" 15 | } 16 | 17 | $TaskData = Invoke-DynDnsRequest -UriPath $UriPath 18 | Write-DynDnsOutput -DynDnsResponse $TaskData 19 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsUser.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsUser { 2 | [CmdLetBinding()] 3 | param( 4 | [Alias('ApiUserName','UserName')] 5 | [string]$User 6 | ) 7 | 8 | if ($User) { 9 | $UriPath = "/REST/User/$User" 10 | } else { 11 | $UriPath = "/REST/User/" 12 | } 13 | 14 | if (-Not (Test-DynDnsSession)) { 15 | return 16 | } 17 | 18 | $Users = Invoke-DynDnsRequest -UriPath $UriPath 19 | Write-DynDnsOutput -DynDnsResponse $Users -SkipSuccess 20 | if ($Users.Data.status -eq 'failure') { 21 | return 22 | } 23 | 24 | if ($User) { 25 | Write-DynDnsOutput -DynDnsResponse $Users 26 | } else { 27 | foreach ($UriPath in $Users.Data.data) { 28 | $UserData = Invoke-DynDnsRequest -UriPath $UriPath 29 | Write-DynDnsOutput -DynDnsResponse $UserData 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsZone.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsZone { 2 | [CmdLetBinding()] 3 | param( 4 | [string]$Zone 5 | ) 6 | 7 | if ($Zone) { 8 | $UriPath = "/REST/Zone/$Zone" 9 | } else { 10 | $UriPath = "/REST/Zone/" 11 | } 12 | 13 | if (-Not (Test-DynDnsSession)) { 14 | return 15 | } 16 | 17 | $Zones = Invoke-DynDnsRequest -UriPath $UriPath 18 | Write-DynDnsOutput -DynDnsResponse $Zones -SkipSuccess 19 | if ($Zones.Data.status -eq 'failure') { 20 | return 21 | } 22 | 23 | if ($Zone) { 24 | Write-DynDnsOutput -DynDnsResponse $Zones 25 | } else { 26 | foreach ($UriPath in $Zones.Data.data) { 27 | $ZoneData = Invoke-DynDnsRequest -UriPath $UriPath 28 | Write-DynDnsOutput -DynDnsResponse $ZoneData 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsZoneChanges.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsZoneChanges { 2 | [CmdLetBinding()] 3 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseSingularNouns','Retrieves all zone changes')] 4 | param( 5 | [Parameter(Mandatory=$true)] 6 | [string]$Zone 7 | ) 8 | 9 | if (-Not (Test-DynDnsSession)) { 10 | return 11 | } 12 | 13 | $ZoneChanges = Invoke-DynDnsRequest -UriPath "/REST/ZoneChanges/$Zone" 14 | Write-DynDnsOutput -DynDnsResponse $ZoneChanges 15 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Get-DynDnsZoneNotes.ps1: -------------------------------------------------------------------------------- 1 | function Get-DynDnsZoneNotes { 2 | [CmdLetBinding()] 3 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseSingularNouns','Retrieves zone notes')] 4 | param( 5 | [Parameter(Mandatory=$true)] 6 | [string]$Zone, 7 | [ValidateRange(1,1000)] 8 | [int]$Limit = 1000, 9 | [ValidateRange(0,1000)] 10 | [int]$Offset = 0 11 | ) 12 | 13 | if (-Not (Test-DynDnsSession)) { 14 | return 15 | } 16 | 17 | $JsonBody = @{ 18 | zone = $Zone 19 | limit = $Limit 20 | offset = $Offset 21 | } | ConvertTo-Json 22 | 23 | $ZoneNotes = Invoke-DynDnsRequest -UriPath "/REST/ZoneNoteReport" -Method Post -Body $JsonBody 24 | Write-DynDnsOutput -DynDnsResponse $ZoneNotes 25 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Lock-DynDnsZone.ps1: -------------------------------------------------------------------------------- 1 | function Lock-DynDnsZone { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true)] 8 | [string]$Zone 9 | ) 10 | 11 | if (-Not (Test-DynDnsSession)) { 12 | return 13 | } 14 | 15 | $JsonBody = @{ 16 | freeze = $true 17 | } | ConvertTo-Json 18 | 19 | if ($PSCmdlet.ShouldProcess($Zone,"Freeze zone")) { 20 | $LockZone = Invoke-DynDnsRequest -UriPath "/REST/Zone/$Zone" -Method Put -Body $JsonBody 21 | Write-DynDnsOutput -DynDnsResponse $LockZone 22 | } else { 23 | Write-Verbose 'Whatif : Zone frozen' 24 | } 25 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/New-DynDnsRecord.ps1: -------------------------------------------------------------------------------- 1 | function New-DynDnsRecord { 2 | [CmdLetBinding()] 3 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseShouldProcessForStateChangingFunctions','Creates instance of DynDns record; does not make changes')] 4 | param( 5 | 6 | [Parameter(Mandatory=$true,ParameterSetName='ARecord')] 7 | [ipaddress]$IPv4Address, 8 | 9 | [Parameter(Mandatory=$true,ParameterSetName='TXTRecord')] 10 | [string]$Text, 11 | 12 | [Parameter(Mandatory=$true,ParameterSetName='CNAMERecord')] 13 | [string]$CName, 14 | 15 | [Parameter(Mandatory=$true,ParameterSetName='MXRecord')] 16 | [string]$MailServer, 17 | [Parameter(Mandatory=$true,ParameterSetName='MXRecord')] 18 | [string]$Preference, 19 | 20 | [Parameter(Mandatory=$true,ParameterSetName='SRVRecord')] 21 | [int]$Port, 22 | [Parameter(Mandatory=$true,ParameterSetName='SRVRecord')] 23 | [int]$Priority, 24 | [Parameter(Mandatory=$true,ParameterSetName='SRVRecord')] 25 | [string]$Target, 26 | [Parameter(Mandatory=$true,ParameterSetName='SRVRecord')] 27 | [int]$Weight, 28 | 29 | [Parameter(Mandatory=$true,ParameterSetName='SOARecord')] 30 | [string]$ResponsiblePerson, 31 | 32 | [int]$TTL = 0 33 | ) 34 | 35 | switch ($PsCmdlet.ParameterSetName) { 36 | 'ARecord' { 37 | [DynDnsRecord_A]::New(@{ 38 | rdata = @{ 39 | address = $IPv4Address.IPAddressToString 40 | } 41 | ttl = $TTL.ToString() 42 | record_type = 'A' 43 | }) 44 | } 45 | 'TXTRecord' { 46 | [DynDnsRecord_TXT]::New(@{ 47 | rdata = @{ 48 | txtdata = $Text 49 | } 50 | ttl = $TTL.ToString() 51 | record_type = 'TXT' 52 | }) 53 | } 54 | 'CNAMERecord' { 55 | [DynDnsRecord_CNAME]::New(@{ 56 | rdata = @{ 57 | cname = $Cname 58 | } 59 | ttl = $TTL.ToString() 60 | record_type = 'CNAME' 61 | }) 62 | } 63 | 'SRVRecord' { 64 | [DynDnsRecord_SRV]::New(@{ 65 | rdata = @{ 66 | port = $Port.ToString() 67 | priority = $Priority.ToString() 68 | target = $Target 69 | weight = $Weight.ToString() 70 | } 71 | ttl = $TTL.ToString() 72 | record_type = 'SRV' 73 | }) 74 | } 75 | 'SOARecord' { 76 | [DynDnsRecord_SOA]::New(@{ 77 | rdata = @{ 78 | rname = $ResponsiblePerson.Replace('@','.') 79 | } 80 | serial_style = $SerialStyle 81 | ttl = '3600' 82 | record_type = 'SOA' 83 | }) 84 | } 85 | 'MXRecord' { 86 | [DynDnsRecord_MX]::New(@{ 87 | rdata = @{ 88 | exchange = $MailServer 89 | preference = $Preference.ToString() 90 | } 91 | ttl = $TTL.ToString() 92 | record_type = 'MX' 93 | }) 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Publish-DynDnsZoneChanges.ps1: -------------------------------------------------------------------------------- 1 | function Publish-DynDnsZoneChanges { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true)] 8 | [string]$Zone, 9 | [string]$Notes, 10 | [switch]$Force 11 | ) 12 | 13 | if (-Not (Test-DynDnsSession)) { 14 | return 15 | } 16 | 17 | $PendingZoneChanges = Get-DynDnsZoneChanges -Zone $Zone 18 | if ($PendingZoneChanges) { 19 | Write-Verbose -Message ($PendingZoneChanges | Out-String).Trim() 20 | } else { 21 | Write-Warning -Message 'There are no pending zone changes.' 22 | if (-Not $Force) { 23 | return 24 | } else { 25 | Write-Verbose -Message '-Force switch used.' 26 | } 27 | } 28 | 29 | if ($Notes) { 30 | $BodyNotes = "REST-Api-PoSh: $Notes" 31 | } else { 32 | $BodyNotes = 'REST-Api-PoSh' 33 | } 34 | 35 | $JsonBody = @{ 36 | publish = $true 37 | notes = $BodyNotes 38 | } | ConvertTo-Json 39 | 40 | if ($PSCmdlet.ShouldProcess($Zone,"Publish zone changes")) { 41 | $PublishZoneChanges = Invoke-DynDnsRequest -UriPath "/REST/Zone/$Zone" -Method Put -Body $JsonBody 42 | '' 43 | Write-DynDnsOutput -DynDnsResponse $PublishZoneChanges 44 | } else { 45 | Write-Verbose 'Whatif : Published zone changes' 46 | } 47 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Remove-DynDnsHttpRedirect.ps1: -------------------------------------------------------------------------------- 1 | function Remove-DynDnsHttpRedirect { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 8 | [DynDnsHttpRedirect[]]$DynDnsHttpRedirect, 9 | 10 | [switch]$PublishWait 11 | ) 12 | 13 | begin { 14 | 15 | if (-Not (Test-DynDnsSession)) { 16 | return 17 | } 18 | 19 | if ($PublishWait) { 20 | $JsonBody = @{ 21 | publish = "N" 22 | } | ConvertTo-Json 23 | 24 | } else { 25 | $JsonBody = @{ 26 | publish = "Y" 27 | } | ConvertTo-Json 28 | 29 | Write-Warning -Message 'This will autopublish the HTTP redirect deletion to the zone.' 30 | } 31 | 32 | } 33 | 34 | process { 35 | 36 | foreach ($Redirect in $DynDnsHttpRedirect) { 37 | 38 | $Fqdn = $Redirect.RawData.fqdn 39 | $Zone = $Redirect.RawData.zone 40 | $Url = $Redirect.RawData.url 41 | 42 | $Redirect 43 | 44 | if ($PSCmdlet.ShouldProcess("$Url",'Delete HTTP redirect')) { 45 | $RemoveRedirect = Invoke-DynDnsRequest -UriPath "/REST/HTTPRedirect/$Zone/$Fqdn" -Method Delete -Body $JsonBody 46 | Write-DynDnsOutput -DynDnsResponse $RemoveRedirect 47 | } else { 48 | Write-Verbose 'Whatif : Removed HTTP redirect' 49 | } 50 | } 51 | } 52 | 53 | end { 54 | 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Remove-DynDnsNode.ps1: -------------------------------------------------------------------------------- 1 | function Remove-DynDnsNode { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true)] 8 | [string]$Zone, 9 | [Parameter(Mandatory=$true)] 10 | [ValidateNotNullOrEmpty()] 11 | [string]$Node, 12 | [switch]$Force 13 | ) 14 | 15 | if (-Not (Test-DynDnsSession)) { 16 | return 17 | } 18 | 19 | if ($Node) { 20 | if ($Node -match $Zone ) { 21 | $Fqdn = $Node 22 | } else { 23 | $Fqdn = $Node + '.' + $Zone 24 | } 25 | } else { 26 | $Fqdn = $Zone 27 | } 28 | 29 | if ($Fqdn -notmatch $Zone ) { 30 | Write-Warning -Message "The zone ($Zone) does not contain $Fqdn." 31 | return 32 | } 33 | 34 | if ($null -eq (Get-DynDnsZone -Zone $Zone)) { 35 | return 36 | } 37 | 38 | $ZoneRecords = Get-DynDnsRecord -Zone $Zone -Node $Node -RecordType All 39 | $HttpRedirects = Get-DynDnsHttpRedirect -Zone $Zone -Node $Node 40 | 41 | if ($ZoneRecords -or $HttpRedirects) { 42 | if (-Not $Force) { 43 | Write-Warning -Message "The node ($Fqdn) contains records or services. Use the -Force switch if you wish to proceed." 44 | return 45 | } else { 46 | $Message = "`n" 47 | $Message += "`n" + ('-' * 80) + "`n" 48 | $Message += 'PROCEEDING WILL DELETE ALL RECORDS AND SERVICES CONTAINED WITHIN THE NODE' + "`n" 49 | $Message += 'THIS INCLUDES ALL CHILD NODES' + "`n" 50 | $Message += '-' * 80 + "`n" 51 | 52 | if ($ZoneRecords) { 53 | $Message += "`n" 54 | $Header = "Zone records for ${Fqdn}:" 55 | $Message += "$Header`n" 56 | $Message += '-' * $Header.Length + "`n" 57 | $Message += ($ZoneRecords | Out-String).Trim() 58 | $Message += "`n" 59 | } 60 | if ($HttpRedirects) { 61 | $Message += "`n" 62 | $Header = "HTTP redirects for ${Fqdn}:" 63 | $Message += "$Header`n" 64 | $Message += '-' * $Header.Length + "`n" 65 | $Message += ($HttpRedirects | Out-String).Trim() 66 | $Message += "`n" 67 | } 68 | $Message += "`n" + ('-' * 80) + "`n" 69 | $Message += "`n" 70 | $Message 71 | } 72 | } 73 | 74 | if ($PSCmdlet.ShouldProcess("$Fqdn",'Delete node, child nodes, and all records')) { 75 | $RemoveNode = Invoke-DynDnsRequest -UriPath "/REST/Node/$Zone/$Fqdn" -Method Delete 76 | Write-DynDnsOutput -DynDnsResponse $RemoveNode 77 | } else { 78 | Write-Verbose 'Whatif : Removed node' 79 | } 80 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Remove-DynDnsRecord.ps1: -------------------------------------------------------------------------------- 1 | function Remove-DynDnsRecord { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 8 | [DynDnsRecord[]]$DynDnsRecord 9 | ) 10 | 11 | begin { 12 | 13 | if (-Not (Test-DynDnsSession)) { 14 | return 15 | } 16 | 17 | } 18 | 19 | process { 20 | 21 | foreach ($Record in $DynDnsRecord) { 22 | 23 | $Fqdn = $Record.RawData.fqdn 24 | $Zone = $Record.RawData.zone 25 | $RecordType = $Record.RawData.record_type 26 | $RecordId = $Record.RecordId 27 | 28 | $Record 29 | 30 | if ($PSCmdlet.ShouldProcess("$Fqdn","Delete DNS $RecordType record")) { 31 | $RemoveRedirect = Invoke-DynDnsRequest -UriPath "/REST/$($RecordType)Record/$Zone/$Fqdn/$RecordId" -Method Delete 32 | Write-DynDnsOutput -DynDnsResponse $RemoveRedirect 33 | } else { 34 | Write-Verbose 'Whatif : Removed DNS record' 35 | } 36 | } 37 | } 38 | 39 | end { 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Remove-DynDnsZone.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Remove-DynDnsZone { 3 | [CmdLetBinding( 4 | SupportsShouldProcess=$true, 5 | ConfirmImpact='High' 6 | )] 7 | param( 8 | [Parameter(Mandatory=$true)] 9 | [string]$Zone 10 | ) 11 | 12 | if (-Not (Test-DynDnsSession)) { 13 | return 14 | } 15 | 16 | Write-Warning -Message 'Continuing will immediately delete the zone.' 17 | 18 | if ($PSCmdlet.ShouldProcess("$Zone",'Delete DNS zone and all its records')) { 19 | $DeleteZone = Invoke-DynDnsRequest -UriPath "/REST/Zone/$Zone" -Method Delete 20 | Write-DynDnsOutput -DynDnsResponse $DeleteZone 21 | } else { 22 | Write-Verbose 'Whatif : Deleted DNS zone and all its records' 23 | } 24 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Send-DynDnsSession.ps1: -------------------------------------------------------------------------------- 1 | function Send-DynDnsSession { 2 | [CmdLetBinding()] 3 | param() 4 | 5 | if (-Not (Test-DynDnsSession)) { 6 | return 7 | } 8 | 9 | $Session = Invoke-DynDnsRequest -SessionAction 'Send' 10 | Write-DynDnsOutput -DynDnsResponse $Session 11 | if ($Session.Data.status -eq 'success') { 12 | $DynDnsSession.RefreshTime = [System.DateTime]::Now 13 | } 14 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Test-DynDnsSession.ps1: -------------------------------------------------------------------------------- 1 | function Test-DynDnsSession { 2 | [CmdLetBinding()] 3 | [OutputType('System.Boolean')] 4 | param() 5 | 6 | $Session = Invoke-DynDnsRequest -SessionAction 'Test' -WarningAction SilentlyContinue 7 | Write-DynDnsOutput -DynDnsResponse $Session -WarningAction SilentlyContinue 8 | if ($Session.Data.status -eq 'success') { 9 | $true 10 | } else { 11 | $false 12 | } 13 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Undo-DynDnsZoneChanges.ps1: -------------------------------------------------------------------------------- 1 | function Undo-DynDnsZoneChanges { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseSingularNouns','Discards all pending zone changes')] 7 | param( 8 | [Parameter(Mandatory=$true)] 9 | [string]$Zone, 10 | [switch]$Force 11 | ) 12 | 13 | if (-Not (Test-DynDnsSession)) { 14 | return 15 | } 16 | 17 | $PendingZoneChanges = Get-DynDnsZoneChanges -Zone $Zone 18 | if ($PendingZoneChanges) { 19 | $PendingZoneChanges 20 | } else { 21 | Write-Warning -Message 'There are no pending zone changes.' 22 | if (-Not $Force) { 23 | return 24 | } else { 25 | Write-Verbose -Message '-Force switch used.' 26 | } 27 | } 28 | 29 | if ($PSCmdlet.ShouldProcess($Zone,"Discard zone changes")) { 30 | $UndoZoneChanges = Invoke-DynDnsRequest -UriPath "/REST/ZoneChanges/$Zone" -Method Delete 31 | Write-DynDnsOutput -DynDnsResponse $UndoZoneChanges 32 | } else { 33 | Write-Verbose 'Whatif : Discarded zone changes' 34 | } 35 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Unlock-DynDnsZone.ps1: -------------------------------------------------------------------------------- 1 | function Unlock-DynDnsZone { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true)] 8 | [string]$Zone 9 | ) 10 | 11 | if (-Not (Test-DynDnsSession)) { 12 | return 13 | } 14 | 15 | $JsonBody = @{ 16 | thaw = $true 17 | } | ConvertTo-Json 18 | 19 | if ($PSCmdlet.ShouldProcess($Zone,"Thaw zone")) { 20 | $LockZone = Invoke-DynDnsRequest -UriPath "/REST/Zone/$Zone" -Method Put -Body $JsonBody 21 | Write-DynDnsOutput -DynDnsResponse $LockZone 22 | } else { 23 | Write-Verbose 'Whatif : Zone thawed' 24 | } 25 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/Public/Update-DynDnsRecord.ps1: -------------------------------------------------------------------------------- 1 | function Update-DynDnsRecord { 2 | [CmdLetBinding( 3 | SupportsShouldProcess=$true, 4 | ConfirmImpact='High' 5 | )] 6 | param( 7 | [Parameter(Mandatory=$true)] 8 | [DynDnsRecord]$DynDnsRecord, 9 | [Parameter(Mandatory=$true)] 10 | [DynDnsRecord]$UpdatedDynDnsRecord 11 | ) 12 | 13 | if (-Not (Test-DynDnsSession)) { 14 | return 15 | } 16 | 17 | if ($DynDnsRecord.GetType() -ne $UpdatedDynDnsRecord.GetType()) { 18 | Write-Warning -Message "The original record type does not match the updated record type." 19 | return 20 | } else { 21 | Write-Verbose -Message "The original record type matches the updated record type." 22 | } 23 | 24 | $Fqdn = $DynDnsRecord.Name 25 | $Zone = $DynDnsRecord.Zone 26 | $RecordType = $DynDnsRecord.Type 27 | $RecordId = $DynDnsRecord.RecordId 28 | 29 | if ($RecordType -eq 'SOA') { 30 | $Body = $UpdatedDynDnsRecord.RawData | ConvertTo-Json | ConvertFrom-Json 31 | Add-Member -InputObject $Body -MemberType NoteProperty -Name serial_style -Value $DynDnsRecord.RawData.serial_style -Force 32 | $JsonBody = $Body | Select-Object * -ExcludeProperty record_type | ConvertTo-Json 33 | } else { 34 | $JsonBody = $UpdatedDynDnsRecord.RawData | ConvertTo-Json | ConvertFrom-Json | Select-Object * -ExcludeProperty record_type | ConvertTo-Json 35 | } 36 | 37 | $UpdatedAttributes = Compare-ObjectProperties -ReferenceObject $DynDnsRecord -DifferenceObject $UpdatedDynDnsRecord | ForEach-Object { 38 | if ($_.DiffValue.length -gt 0 -and $_.DiffValue -ne 0) { $_ } 39 | } 40 | $UpdatedAttributes = $UpdatedAttributes | Select-Object @{label='Attribute';expression={$_.PropertyName}}, 41 | @{label='Original';expression={$_.RefValue}},@{label='Updated';expression={$_.DiffValue}} | Out-String 42 | 43 | $OriginalRecord = "`n" + '-' * 80 + "`n" 44 | $OriginalRecord += "Original DNS Record:`n" 45 | $OriginalRecord += ($DynDnsRecord | Out-String).Trim() + "`n`n" 46 | Write-Verbose -Message $OriginalRecord 47 | 48 | $Updates = "`n" + '-' * 80 + "`n" 49 | $Updates += "Update DNS Record Attributes::`n" 50 | $Updates += $UpdatedAttributes.Trim() + "`n" 51 | $Updates += "`n" + '-' * 80 + "`n" 52 | Write-Verbose -Message $Updates 53 | 54 | if ($PSCmdlet.ShouldProcess("$Fqdn","Update DNS $RecordType record")) { 55 | $UpdateDnsRecord = Invoke-DynDnsRequest -UriPath "/REST/$($RecordType)Record/$Zone/$Fqdn/$RecordId" -Method Put -Body $JsonBody 56 | Write-DynDnsOutput -DynDnsResponse $UpdateDnsRecord 57 | } else { 58 | Write-Verbose "Whatif : Updated DNS $RecordType record" 59 | } 60 | } -------------------------------------------------------------------------------- /PoShDynDnsApi/en-US/about_PoShDynDnsApi.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | PoShDynDnsApi 3 | 4 | SHORT DESCRIPTION 5 | PowerShell module used to interact with Dyn Managed DNS REST API. 6 | 7 | LONG DESCRIPTION 8 | This module interacts with the Dyn Managed DNS REST API. 9 | 10 | With it, you are able to add/update/remove DNS records or HTTP redirect 11 | services for your zones managed by Dyn. You can also add or remove your own 12 | DNS zone, view/publish/discard pending zone changes made within your 13 | session. You can view all zone publish notes. You can also list users within 14 | your managed service. 15 | 16 | SEE ALSO 17 | Add-DynDnsHttpRedirect 18 | Add-DynDnsRecord 19 | Add-DynDnsZone 20 | Connect-DynDnsSession 21 | Disconnect-DynDnsSession 22 | Get-DynDnsHistory 23 | Get-DynDnsHttpRedirect 24 | Get-DynDnsJob 25 | Get-DynDnsNodeList 26 | Get-DynDnsRecord 27 | Get-DynDnsSession 28 | Get-DynDnsTask 29 | Get-DynDnsUser 30 | Get-DynDnsZone 31 | Get-DynDnsZoneChanges 32 | Get-DynDnsZoneNotes 33 | Lock-DynDnsZone 34 | New-DynDnsRecord 35 | Publish-DynDnsZoneChanges 36 | Remove-DynDnsHttpRedirect 37 | Remove-DynDnsNode 38 | Remove-DynDnsRecord 39 | Remove-DynDnsZone 40 | Send-DynDnsSession 41 | Test-DynDnsSession 42 | Undo-DynDnsZoneChanges 43 | Unlock-DynDnsZone 44 | Update-DynDnsRecord 45 | 46 | https://help.dyn.com/understanding-works-api/ 47 | https://help.dyn.com/rest-resources/ 48 | 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PoShDynDnsApi 2 | 3 | **PoShDynDnsApi** is an unofficial PowerShell module used to interact with Dyn Managed DNS REST API. 4 | 5 | For online help, please visit the [module help site](https://powershell.anovelidea.org/modulehelp/PoShDynDnsApi/). 6 | 7 | ## ARCHIVAL NOTICE 8 | 9 | Due to the [announcement by Oracle](https://www.oracle.com/corporate/acquisitions/dyn/technologies/enterprise-customer-faq.html) for the sunset of Dyn Managed DNS on May 31, 2023, this module is no longer useful and has been archived. 10 | 11 | Feel free to review and use the code as examples PowerShell custom classes with inheritence as well as providing one way to support Windows PowerShell 5.1 and PowerShell 7. 12 | 13 | ## Session Commands 14 | 15 | Use the following commands to create, extend, test, or remove a session. Additionally, you can view the current session 16 | information or view the current session history. 17 | 18 | ### Connect-DynDnsSession 19 | 20 | The `Connect-DynDnsSession` command creates a new session. 21 | 22 | ### Send-DynDnsSession 23 | 24 | The `Send-DynDnsSession` command extends the current session. 25 | 26 | ### Test-DynDnsSession 27 | 28 | The `Test-DynDnsSession` command verifies that a session is still active. 29 | 30 | ### Disconnect-DynDnsSession 31 | 32 | The `Disconnect-DynDnsSession` command terminates an existing, valid session. 33 | 34 | ### Get-DynDnsSession 35 | 36 | The `Get-DynDnsSession` command retrieves information about the current session. 37 | 38 | ### Get-DynDnsHistory 39 | 40 | The `Get-DynDnsHistory` command shows the history of commands that have been sent in the current session. 41 | 42 | ## Zone Commands 43 | 44 | Use the following commands to create a zone by providing required parameters or by providing a zone file, and you can 45 | view the zone record. You can also remove a zone. You can view pending changes and publish them or discard them. And 46 | you can view the publish notes. Additionally, you can freeze or thaw the zone. 47 | 48 | ### Get-DynDnsZone 49 | 50 | The `Get-DynDnsZone` command will return all zones associated with the customer, or the specified zone. 51 | 52 | ### Add-DynDnsZone 53 | 54 | The `Add-DynDnsZone` command creates a primary DNS zone in the customer's Dyn DNS Managed account. 55 | 56 | ### Remove-DynDnsZone 57 | 58 | The `Remove-DynDnsZone` command immediately deletes the primary DNS zone from the customer's Dyn DNS Managed account. 59 | 60 | ### Get-DynDnsZoneNotes 61 | 62 | The `Get-DynDnsZoneNotes` command generates a report containing the Zone Notes for the specified zone. 63 | 64 | ### Get-DynDnsZoneChanges 65 | 66 | The `Get-DynDnsZoneChanges` command will retrieve all unpublished changes for the current session for the specified zone. 67 | 68 | ### Publish-DynDnsZoneChanges 69 | 70 | The `Publish-DynDnsZoneChanges` command publishes pending zone changes. 71 | 72 | ### Undo-DynDnsZoneChanges 73 | 74 | The `Undo-DynDnsZoneChanges` deletes changes to the specified zone that have been created during the current session, 75 | but not yet published to the zone. 76 | 77 | ### Lock-DynDnsZone 78 | 79 | The `Lock-DynDnsZone` command prevents other users from making changes to the zone. 80 | 81 | ### Unlock-DynDnsZone 82 | 83 | The `Unlock-DynDnsZone` command removes the restriction that prevents other users from making changes to the zone. 84 | 85 | ## Record Commands 86 | 87 | Use the follow commands to view, add, update, or remove DNS records of the following types: A, TXT, CNAME, MX, SRV, 88 | or PTR. There is a command to create a new record object that can be used to add or update a record. 89 | 90 | ### Get-DynDnsRecord 91 | 92 | The `Get-DynDnsRecord` command retrieves one or all records of the specified type from a specified zone/node. 93 | 94 | ### New-DynDnsRecord 95 | 96 | The `New-DynDnsRecord` command creates DNS record object of the specified type. 97 | 98 | ### Add-DynDnsRecord 99 | 100 | The `Add-DynDnsRecord` command creates a new DNS record of the specified type at the indicated zone/node level. 101 | 102 | ### Update-DynDnsRecord 103 | 104 | The `Update-DynDnsRecord` command updates an existing DNS record in the specified zone. 105 | 106 | ### Remove-DynDnsRecord 107 | 108 | The `Remove-DynDnsRecord` command deletes one or all records of the specified type from a specified zone/node. 109 | 110 | ## HttpRedirect Commands 111 | 112 | Use the following commands to view, create, or delete an HTTP redirect service. 113 | 114 | ### Get-DynDnsHttpRedirect 115 | 116 | The `Get-DynDnsHttpRedirect` command retrieves one or all HTTP Redirect services on the zone/node indicated. 117 | 118 | ### Add-DynDnsHttpRedirect 119 | 120 | The `Add-DynDnsHttpRedirect` command creates a new HTTP Redirect service on the zone/node indicated. 121 | 122 | ### Remove-DynDnsHttpRedirect 123 | 124 | The `Remove-DynDnsHttpRedirect` command deletes one or more existing HTTP Redirect services from the zone/node indicated. 125 | 126 | ## Node Commands 127 | 128 | Use the following commands to list nodes and remove a node. 129 | 130 | ### Get-DynDnsNodeList 131 | 132 | The `Get-DynDnsNodeList` command retrieves a list of all node names at or below the given zone node. 133 | 134 | ### Remove-DynDnsNode 135 | 136 | The `Remove-DynDnsNode` command removes the indicated node, any records within the node, and any nodes underneath the node. 137 | 138 | ## Miscellaneous Commands 139 | 140 | These commands will allow you to view users, jobs, or tasks. 141 | 142 | ### Get-DynDnsUser 143 | 144 | The `Get-DynDnsUser` command retrieves information on a specified user or for all users. 145 | 146 | ### Get-DynDnsTask 147 | 148 | The `Get-DynDnsTask` command retrieves a list of all current DNS API tasks or a single pending API task based on the task ID. 149 | 150 | ### Get-DynDnsJob 151 | 152 | The `Get-DynDnsJob` command retrieves the result from a previous job. 153 | 154 | ## More Information 155 | 156 | Please check out the following links for more information on the Dyn Managed DNS REST API. 157 | 158 | * [Managed DNS Master Topics List](https://help.dyn.com/managed-dns-master-topics-list/) 159 | * [Dyn Community Traffic Management Forum](https://www.dyncommunity.com/spaces/41/traffic-management.html) 160 | * [DNS API Quick-Start Guide](https://help.dyn.com/dns-api-guide/) 161 | * [Understanding How The API Works](https://help.dyn.com/understanding-works-api/) 162 | * [REST Resources](https://help.dyn.com/rest-resources/) 163 | * [RESTful API Interface](https://help.dyn.com/rest/) 164 | -------------------------------------------------------------------------------- /Requirements.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | PSDependOptions = @{ 3 | Target = 'CurrentUser' 4 | } 5 | 'psake' = @{ 6 | Version = '4.7.4' 7 | } 8 | 'BuildHelpers' = @{ 9 | Version = '2.0.0' 10 | } 11 | } -------------------------------------------------------------------------------- /ScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Use Severity when you want to limit the generated diagnostic records to a 3 | # subset of: Error, Warning and Information. 4 | # Uncomment the following line if you only want Errors and Warnings but 5 | # not Information diagnostic records. 6 | #Severity = @('Error','Warning') 7 | 8 | # Use IncludeRules when you want to run only a subset of the default rule set. 9 | #IncludeRules = @('PSAvoidDefaultValueSwitchParameter', 10 | # 'PSMisleadingBacktick', 11 | # 'PSMissingModuleManifestField', 12 | # 'PSReservedCmdletChar', 13 | # 'PSReservedParams', 14 | # 'PSShouldProcess', 15 | # 'PSUseApprovedVerbs', 16 | # 'PSUseDeclaredVarsMoreThanAssigments') 17 | 18 | # Use ExcludeRules when you want to run most of the default set of rules except 19 | # for a few rules you wish to "exclude". Note: if a rule is in both IncludeRules 20 | # and ExcludeRules, the rule will be excluded. 21 | #ExcludeRules = @('PSAvoidUsingWriteHost') 22 | 23 | # You can use the following entry to supply parameters to rules that take parameters. 24 | # For instance, the PSAvoidUsingCmdletAliases rule takes a whitelist for aliases you 25 | # want to allow. 26 | #Rules = @{ 27 | # Do not flag 'cd' alias. 28 | # PSAvoidUsingCmdletAliases = @{Whitelist = @('cd')} 29 | 30 | # Check if your script uses cmdlets that are compatible on PowerShell Core, 31 | # version 6.0.0-alpha, on Linux. 32 | # PSUseCompatibleCmdlets = @{Compatibility = @("core-6.0.0-alpha-linux")} 33 | #} 34 | } 35 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | [cmdletbinding(DefaultParameterSetName = 'Task')] 2 | param( 3 | # Build task(s) to execute 4 | [parameter(ParameterSetName = 'Task', Position = 0)] 5 | [ValidateSet('Init','Clean','Compile','Analyze','Test')] 6 | [string[]]$Task = 'default', 7 | 8 | # Bootstrap dependencies 9 | [switch]$Bootstrap, 10 | 11 | # List available build tasks 12 | [parameter(ParameterSetName = 'Help')] 13 | [switch]$Help 14 | ) 15 | 16 | $ErrorActionPreference = 'Stop' 17 | 18 | # Bootstrap dependencies 19 | if ($Bootstrap.IsPresent) { 20 | Get-PackageProvider -Name Nuget -ForceBootstrap > $null 21 | Set-PSRepository -Name PSGallery -InstallationPolicy Trusted 22 | if (-not (Get-Module -Name PSDepend -ListAvailable)) { 23 | Install-Module -Name PSDepend -Repository PSGallery -Scope CurrentUser 24 | } 25 | Import-Module -Name PSDepend -Verbose:$false 26 | Invoke-PSDepend -Path './Requirements.psd1' -Install -Import -Force -WarningAction SilentlyContinue 27 | } 28 | 29 | "`n" + ('-' * 70) 30 | 31 | # Execute psake task(s) 32 | $psakeFile = './build.psake.ps1' 33 | if ($PSCmdlet.ParameterSetName -eq 'Help') { 34 | Get-PSakeScriptTasks -buildFile $psakeFile | 35 | Format-Table -Property Name, Description, Alias, DependsOn 36 | } else { 37 | Set-BuildEnvironment -Force 38 | 39 | Invoke-psake -buildFile $psakeFile -taskList $Task -nologo 40 | exit ( [int]( -not $psake.build_success ) ) 41 | } -------------------------------------------------------------------------------- /build.psake.ps1: -------------------------------------------------------------------------------- 1 | Properties { 2 | $ProjectRoot = $env:BHProjectPath 3 | if(-not $ProjectRoot) { 4 | $ProjectRoot = $PSScriptRoot 5 | } 6 | 7 | $Timestamp = Get-date -UFormat "%Y%m%d-%H%M%S" 8 | $PSVersion = $PSVersionTable.PSVersion.Major 9 | 10 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 11 | $TestResultsName = "TestResults_PS$PSVersion`_$TimeStamp.xml" 12 | 13 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 14 | $Line = "`n" + ('-' * 70) 15 | 16 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 17 | $ModulePath = $env:BHModulePath 18 | $BuildOutput = Join-Path -Path $env:BHBuildOutput -ChildPath $env:BHProjectName 19 | 20 | $Manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest 21 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 22 | $psd1 = $env:BHPSModuleManifest 23 | 24 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 25 | $VersionFolder = Join-Path -Path $BuildOutput -ChildPath $Manifest.ModuleVersion 26 | 27 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 28 | $TestsPath = Join-Path -Path $ProjectRoot -ChildPath 'Tests' 29 | 30 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 31 | $PrivateFunctionsPath = Join-Path -Path $ModulePath -ChildPath 'Private' 32 | 33 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 34 | $PublicFunctionsPath = Join-Path -Path $ModulePath -ChildPath 'Public' 35 | 36 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 37 | $ClassPath = Join-Path -Path $ModulePath -ChildPath 'Classes' 38 | 39 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 40 | $ExternalHelpPath = Join-Path -Path $ModulePath -ChildPath 'en-US' 41 | 42 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 43 | $PathSeparator = [IO.Path]::DirectorySeparatorChar 44 | 45 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 46 | $DotNetFramework = 'netstandard2.0' 47 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 48 | $Release = 'Release' 49 | } 50 | 51 | Task Default -Depends Test 52 | 53 | Task Init -Description 'Initialize build environment' { 54 | "STATUS: Testing with PowerShell $PSVersion" 55 | '' 56 | "Build System Details:" 57 | Get-Item ENV:BH* 58 | '' 59 | "Version Folder:".PadRight(20) + $VersionFolder 60 | '' 61 | "Loading modules:" 62 | 'Pester', 'platyPS', 'PSScriptAnalyzer' | Foreach-Object { 63 | " $_" 64 | if (-not (Get-Module -Name $_ -ListAvailable -Verbose:$false -ErrorAction SilentlyContinue)) { 65 | Install-Module -Name $_ -Repository PSGallery -Scope CurrentUser -AllowClobber -Confirm:$false -ErrorAction Stop 66 | } 67 | Import-Module -Name $_ -Verbose:$false -Force -ErrorAction Stop 68 | } 69 | $Line 70 | } 71 | 72 | Task Clean -Depends Init -Description 'Cleans module output directory' { 73 | Remove-Module -Name $env:BHProjectName -Force -ErrorAction SilentlyContinue 74 | 75 | if (Test-Path -Path $BuildOutput) { 76 | Get-ChildItem -Path $BuildOutput -Recurse | Remove-Item -Force -Recurse 77 | } else { 78 | $null = New-Item -Path $BuildOutput -ItemType Directory 79 | } 80 | 81 | " Cleaned previous output directory [$BuildOutput]" 82 | $Line 83 | } 84 | 85 | Task Compile -Depends Clean -Description 'Compiles module from source' { 86 | 87 | # create module output directory 88 | $null = New-Item -Path $VersionFolder -ItemType Directory 89 | 90 | # append items to psm1 91 | Write-Verbose -Message 'Creating psm1...' 92 | $psm1 = Copy-Item -Path (Join-Path -Path $ModulePath -ChildPath "$env:BHProjectName.psm1") -Destination (Join-Path -Path $VersionFolder -ChildPath "$($ENV:BHProjectName).psm1") -PassThru 93 | 94 | # append psm1 95 | '#region classes' | Add-Content -Path $psm1 -Encoding UTF8 96 | Write-Verbose -Message "Appending folder $_ to psm1..." 97 | Get-ChildItem -Path (Join-Path -Path $ModulePath -ChildPath 'Classes') -Recurse -File | 98 | Get-Content -Raw | Add-Content -Path $psm1 -Encoding UTF8 99 | '#endregion classes' | Add-Content -Path $psm1 -Encoding UTF8 100 | 101 | # copy LICENSE, README.md, and CHANGELOG.md file 102 | 103 | 104 | # copy private and public functions to build output version folder 105 | 'Private','Public' | ForEach-Object { 106 | Write-Verbose -Message "Copying folder $_ to BuildOutput..." 107 | $BuildFolderPath = Join-Path -Path $VersionFolder -ChildPath $_ 108 | $FolderPath = Join-Path -Path $ModulePath -ChildPath $_ 109 | $HasFiles = Get-ChildItem -Path $FolderPath -File 110 | if ($HasFiles) { 111 | $null = New-Item -Path $BuildFolderPath -ItemType Directory 112 | $HasFiles | ForEach-Object { Copy-Item -Path $_.FullName -Destination $BuildFolderPath } 113 | } 114 | } 115 | 116 | # copy psd1 to build version output folder 117 | Copy-Item -Path $psd1 -Destination $VersionFolder 118 | $BuildManifest = Get-ChildItem -Path $VersionFolder -Include *.psd1 -Recurse | Select-Object -First 1 -ExpandProperty FullName 119 | 120 | # copy external help to build version output folder 121 | if (Test-Path -Path $ExternalHelpPath) { 122 | $BuildExternalHelpPath = Join-Path -Path $VersionFolder -ChildPath 'en-US' 123 | $HasExternalHelp = Get-ChildItem -Path $ExternalHelpPath -File 124 | if ($HasExternalHelp) { 125 | $null = New-Item -Path $BuildExternalHelpPath -ItemType Directory 126 | $HasExternalHelp | ForEach-Object { Copy-Item -Path $_.FullName -Destination $BuildExternalHelpPath } 127 | } 128 | } 129 | 130 | $Files = Get-ChildItem -Path $ModulePath -Recurse -Exclude '.gitignore','*.Class.ps1' -File 131 | $FileList = $Files.FullName | ForEach-Object { $_.Replace("$ModulePath$PathSeparator",'')} 132 | 133 | $FunctionsToExport = (Get-ChildItem -Path (Join-Path -Path $ModulePath -ChildPath 'Public') -Recurse -File | ForEach-Object { $_.BaseName }) 134 | 135 | $Formats = Join-Path -Path $ModulePath -ChildPath 'TypeData' | Join-Path -ChildPath "$ModuleName.Format.ps1xml" 136 | if (Test-Path -Path $Formats) { 137 | $FormatsToProcess = $Formats.Replace("$ModulePath$PathSeparator",'') 138 | } 139 | 140 | $TypeData = Join-Path -Path $ModulePath -ChildPath 'TypeData' | Join-Path -ChildPath "$ModuleName.Types.ps1xml" 141 | if (Test-Path -Path $TypeData) { 142 | $TypesToProcess = $TypeData.Replace("$ModulePath$PathSeparator",'') 143 | } 144 | 145 | $UpdateManifestParams = @{} 146 | if ($FileList) { $UpdateManifestParams['FileList'] = $FileList } 147 | if ($FunctionsToExport) { $UpdateManifestParams['FunctionsToExport'] = $FunctionsToExport } 148 | if ($FormatsToProcess) { $UpdateManifestParams['FormatsToProcess'] = $FormatsToProcess } 149 | if ($TypesToProcess) { $UpdateManifestParams['TypesToProcess'] = $TypesToProcess } 150 | 151 | '' 152 | ' Adding the following to module manifest:' 153 | $UpdateManifestParams.Keys | ForEach-Object { " $_"} 154 | Update-ModuleManifest -Path $BuildManifest @UpdateManifestParams 155 | 156 | '' 157 | " Created compiled module at [$VersionFolder]" 158 | $Line 159 | } 160 | 161 | Task Test -Depends Init, Analyze, Pester -Description 'Run test suite' 162 | 163 | Task Analyze -Description 'Run PSScriptAnalyzer' -Depends Compile { 164 | $Analysis = Invoke-ScriptAnalyzer -Path $VersionFolder -Verbose:$false 165 | $AnalyzeErrors = $Analysis | Where-Object {$_.Severity -eq 'Error'} 166 | $AnalyzeWarnings = $Analysis | Where-Object {$_.Severity -eq 'Warning'} 167 | 168 | ' PSScriptAnalyzer results:' 169 | ($Analysis | Group-Object -Property Severity,RuleName | Select-Object -Property Count,Name | Out-String).Trim().Split("`n") | Foreach-Object { (' ' * 8) + $_ } 170 | '' 171 | 172 | if (($AnalyzeErrors.Count -eq 0) -and ($AnalyzeWarnings.Count -eq 0)) { 173 | ' PSScriptAnalyzer passed without errors or warnings' 174 | } 175 | 176 | if (@($AnalyzeErrors).Count -gt 0) { 177 | Write-Error -Message 'One or more Script Analyzer errors were found. Build cannot continue!' 178 | $AnalyzeErrors | Format-Table 179 | } 180 | 181 | if (@($AnalyzeWarnings).Count -gt 0) { 182 | Write-Warning -Message 'One or more Script Analyzer warnings were found. These should be corrected.' 183 | $AnalyzeWarnings | Format-Table 184 | } 185 | $Line 186 | } 187 | 188 | Task Pester -Description 'Run Pester tests' -Depends Analyze { 189 | Push-Location 190 | Set-Location -Path $VersionFolder 191 | if(-not $ENV:BHProjectPath) { 192 | Set-BuildEnvironment -Path (Join-Path -Path $PSScriptRoot -ChildPath '..') -Passthru 193 | } 194 | 195 | $origModulePath = $env:PSModulePath 196 | if ( $env:PSModulePath.split($pathSeperator) -notcontains $BuildOutput ) { 197 | $env:PSModulePath = ($BuildOutput + $pathSeperator + $origModulePath) 198 | } 199 | 200 | Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue -Verbose:$false 201 | Import-Module (Join-Path -Path $VersionFolder -ChildPath "${env:BHProjectName}.psd1") -Force -Verbose:$false 202 | $TestResultsXml = Join-Path -Path $BuildOutput -ChildPath $TestResultsName 203 | $TestResults = Invoke-Pester -Path $TestsPath -PassThru -OutputFile $TestResultsXml -OutputFormat NUnitXml -Show Describe,Context,Failed,Summary 204 | 205 | <# 206 | # Upload test artifacts to AppVeyor 207 | if ($env:APPVEYOR_JOB_ID) { 208 | $wc = New-Object 'System.Net.WebClient' 209 | $wc.UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", $TestResultsXml) 210 | } 211 | #> 212 | 213 | if ($TestResults.FailedCount -gt 0) { 214 | $TestResults | Format-List 215 | Write-Error -Message 'One or more Pester tests failed. Build cannot continue!' 216 | } 217 | Pop-Location 218 | $env:PSModulePath = $origModulePath 219 | 220 | '' 221 | $Line 222 | } 223 | 224 | 225 | Task Build -Depends Compile, CreateMarkdownHelp, CreateExternalHelp { 226 | # External help 227 | $helpXml = New-ExternalHelp "$projectRoot\docs\reference\functions" -OutputPath (Join-Path -Path $VersionFolder -ChildPath 'en-US') -Force 228 | " Module XML help created at [$helpXml]" 229 | } 230 | -------------------------------------------------------------------------------- /build.settings.ps1: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Customize these properties and tasks for your module. 3 | ############################################################################### 4 | 5 | Push-Location $PSScriptRoot 6 | Set-BuildEnvironment -Force 7 | 8 | Properties { 9 | # ----------------------- Basic properties -------------------------------- 10 | 11 | # The name of your module should match the basename of the PSD1 file. 12 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 13 | $ModuleName = $env:BHProjectName 14 | <#$ModuleName = Get-Item $SrcRootDir/*.psd1 | 15 | Where-Object { $null -ne (Test-ModuleManifest -Path $_ -ErrorAction SilentlyContinue) } | 16 | Select-Object -First 1 | Foreach-Object BaseName 17 | #> 18 | 19 | 20 | # The root directories for the module's docs, src and test. 21 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 22 | $DocsRootDir = "$PSScriptRoot\docs" 23 | $SrcRootDir = "$PSScriptRoot\$ModuleName" 24 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 25 | $TestRootDir = "$PSScriptRoot\test" 26 | 27 | 28 | # The $OutDir is where module files and updatable help files are staged for signing, install and publishing. 29 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 30 | $ModuleVersion = (Test-ModuleManifest -Path $SrcRootDir\$ModuleName.psd1).Version.ToString() 31 | 32 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 33 | $OutDir = "$PSScriptRoot\Release\$ModuleName" 34 | 35 | # The local installation directory for the install task. Defaults to your home Modules location. 36 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 37 | $InstallPath = Join-Path (Split-Path $profile.CurrentUserAllHosts -Parent) ` 38 | "Modules\$ModuleName\$ModuleVersion" 39 | 40 | # Default Locale used for help generation, defaults to en-US. 41 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 42 | $DefaultLocale = 'en-US' 43 | 44 | # Items in the $Exclude array will not be copied to the $OutDir e.g. $Exclude = @('.gitattributes') 45 | # Typically you wouldn't put any file under the src dir unless the file was going to ship with 46 | # the module. However, if there are such files, add their $SrcRootDir relative paths to the exclude list. 47 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 48 | $Exclude = @() 49 | 50 | # ------------------ Script analysis properties --------------------------- 51 | 52 | # Enable/disable use of PSScriptAnalyzer to perform script analysis. 53 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 54 | $ScriptAnalysisEnabled = $false 55 | 56 | # When PSScriptAnalyzer is enabled, control which severity level will generate a build failure. 57 | # Valid values are Error, Warning, Information and None. "None" will report errors but will not 58 | # cause a build failure. "Error" will fail the build only on diagnostic records that are of 59 | # severity error. "Warning" will fail the build on Warning and Error diagnostic records. 60 | # "Any" will fail the build on any diagnostic record, regardless of severity. 61 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 62 | [ValidateSet('Error', 'Warning', 'Any', 'None')] 63 | $ScriptAnalysisFailBuildOnSeverityLevel = 'Error' 64 | 65 | # Path to the PSScriptAnalyzer settings file. 66 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 67 | $ScriptAnalyzerSettingsPath = "$PSScriptRoot\ScriptAnalyzerSettings.psd1" 68 | 69 | # ------------------- Script signing properties --------------------------- 70 | 71 | # Set to $true if you want to sign your scripts. You will need to have a code-signing certificate. 72 | # You can specify the certificate's subject name below. If not specified, you will be prompted to 73 | # provide either a subject name or path to a PFX file. After this one time prompt, the value will 74 | # saved for future use and you will no longer be prompted. 75 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 76 | $ScriptSigningEnabled = $false 77 | 78 | # Specify the Subject Name of the certificate used to sign your scripts. Leave it as $null and the 79 | # first time you build, you will be prompted to enter your code-signing certificate's Subject Name. 80 | # This variable is used only if $SignScripts is set to $true. 81 | # 82 | # This does require the code-signing certificate to be installed to your certificate store. If you 83 | # have a code-signing certificate in a PFX file, install the certificate to your certificate store 84 | # with the command below. You may be prompted for the certificate's password. 85 | # 86 | # Import-PfxCertificate -FilePath .\myCodeSigingCert.pfx -CertStoreLocation Cert:\CurrentUser\My 87 | # 88 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 89 | $CertSubjectName = $null 90 | 91 | # Certificate store path. 92 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 93 | $CertPath = "Cert:\" 94 | 95 | # -------------------- File catalog properties ---------------------------- 96 | 97 | # Enable/disable generation of a catalog (.cat) file for the module. 98 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 99 | $CatalogGenerationEnabled = $true 100 | 101 | # Select the hash version to use for the catalog file: 1 for SHA1 (compat with Windows 7 and 102 | # Windows Server 2008 R2), 2 for SHA2 to support only newer Windows versions. 103 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 104 | $CatalogVersion = 2 105 | 106 | # ---------------------- Testing properties ------------------------------- 107 | 108 | # Enable/disable Pester code coverage reporting. 109 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 110 | $CodeCoverageEnabled = $true 111 | 112 | # CodeCoverageFiles specifies the files to perform code coverage analysis on. This property 113 | # acts as a direct input to the Pester -CodeCoverage parameter, so will support constructions 114 | # like the ones found here: https://github.com/pester/Pester/wiki/Code-Coverage. 115 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 116 | $CodeCoverageFiles = "$SrcRootDir\*.ps1", "$SrcRootDir\*.psm1" 117 | 118 | # -------------------- Publishing properties ------------------------------ 119 | 120 | # Your NuGet API key for the PSGallery. Leave it as $null and the first time you publish, 121 | # you will be prompted to enter your API key. The build will store the key encrypted in the 122 | # settings file, so that on subsequent publishes you will no longer be prompted for the API key. 123 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 124 | $NuGetApiKey = $null 125 | 126 | # Name of the repository you wish to publish to. If $null is specified the default repo (PowerShellGallery) is used. 127 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 128 | $PublishRepository = $null 129 | 130 | # Path to the release notes file. Set to $null if the release notes reside in the manifest file. 131 | # The contents of this file are used during publishing for the ReleaseNotes parameter. 132 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 133 | $ReleaseNotesPath = "$PSScriptRoot\ReleaseNotes.md" 134 | 135 | # ----------------------- Misc properties --------------------------------- 136 | 137 | # In addition, PFX certificates are supported in an interactive scenario only, 138 | # as a way to import a certificate into the user personal store for later use. 139 | # This can be provided using the CertPfxPath parameter. PFX passwords will not be stored. 140 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 141 | $SettingsPath = "$env:LOCALAPPDATA\Plaster\NewModuleTemplate\SecuredBuildSettings.clixml" 142 | 143 | # Specifies an output file path to send to Invoke-Pester's -OutputFile parameter. 144 | # This is typically used to write out test results so that they can be sent to a CI 145 | # system like AppVeyor. 146 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 147 | $TestOutputFile = $null 148 | 149 | # Specifies the test output format to use when the TestOutputFile property is given 150 | # a path. This parameter is passed through to Invoke-Pester's -OutputFormat parameter. 151 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 152 | $TestOutputFormat = "NUnitXml" 153 | } 154 | 155 | ############################################################################### 156 | # Customize these tasks for performing operations before and/or after file staging. 157 | ############################################################################### 158 | 159 | # Executes before the StageFiles task. 160 | Task BeforeStageFiles { 161 | } 162 | 163 | # Executes after the StageFiles task. 164 | Task AfterStageFiles { 165 | } 166 | 167 | ############################################################################### 168 | # Customize these tasks for performing operations before and/or after Build. 169 | ############################################################################### 170 | 171 | # Executes before the BeforeStageFiles phase of the Build task. 172 | Task BeforeBuild { 173 | } 174 | 175 | # Executes after the Build task. 176 | Task AfterBuild { 177 | } 178 | 179 | ############################################################################### 180 | # Customize these tasks for performing operations before and/or after BuildHelp. 181 | ############################################################################### 182 | 183 | # Executes before the BuildHelp task. 184 | Task BeforeBuildHelp { 185 | } 186 | 187 | # Executes after the BuildHelp task. 188 | Task AfterBuildHelp { 189 | } 190 | 191 | ############################################################################### 192 | # Customize these tasks for performing operations before and/or after BuildUpdatableHelp. 193 | ############################################################################### 194 | 195 | # Executes before the BuildUpdatableHelp task. 196 | Task BeforeBuildUpdatableHelp { 197 | } 198 | 199 | # Executes after the BuildUpdatableHelp task. 200 | Task AfterBuildUpdatableHelp { 201 | } 202 | 203 | ############################################################################### 204 | # Customize these tasks for performing operations before and/or after GenerateFileCatalog. 205 | ############################################################################### 206 | 207 | # Executes before the GenerateFileCatalog task. 208 | Task BeforeGenerateFileCatalog { 209 | } 210 | 211 | # Executes after the GenerateFileCatalog task. 212 | Task AfterGenerateFileCatalog { 213 | } 214 | 215 | ############################################################################### 216 | # Customize these tasks for performing operations before and/or after Install. 217 | ############################################################################### 218 | 219 | # Executes before the Install task. 220 | Task BeforeInstall { 221 | } 222 | 223 | # Executes after the Install task. 224 | Task AfterInstall { 225 | } 226 | 227 | ############################################################################### 228 | # Customize these tasks for performing operations before and/or after Publish. 229 | ############################################################################### 230 | 231 | # Executes before the Publish task. 232 | Task BeforePublish { 233 | } 234 | 235 | # Executes after the Publish task. 236 | Task AfterPublish { 237 | } 238 | -------------------------------------------------------------------------------- /tests/Meta/Coverage.Tests.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $PublicFunctions = Get-ChildItem -Path $sut\Public -File -Exclude '*.tests.ps1' -Recurse 5 | $PrivateFunctions = Get-ChildItem -Path $sut\Private -File -Exclude '*.tests.ps1' -Recurse 6 | 7 | Describe -Name 'Function Unit Test Coverage' -Tag 'Coverage' { 8 | 9 | Context 'Public' { 10 | foreach ($Function in $PublicFunctions) { 11 | $BaseName = $Function.BaseName 12 | $FunctionTestFile = "$root\Tests\Unit\Public\$BaseName.tests.ps1" 13 | 14 | it "$BaseName has Pester unit test file" { 15 | Test-Path -Path $FunctionTestFile | should be $true 16 | } 17 | } 18 | } 19 | 20 | Context 'Private' { 21 | foreach ($Function in $PrivateFunctions) { 22 | $BaseName = $Function.BaseName 23 | $FunctionTestFile = "$root\Tests\Unit\Private\$BaseName.tests.ps1" 24 | 25 | it "$BaseName has Pester unit test file" { 26 | Test-Path -Path $FunctionTestFile | should be $true 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tests/Meta/Help.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Taken with love from @juneb_get_help (https://raw.githubusercontent.com/juneb/PesterTDD/master/Module.Help.Tests.ps1) 2 | # See https://github.com/devblackops/NetScaler for details 3 | 4 | # Get module commands 5 | # Remove all versions of the module from the session. Pester can't handle multiple versions. 6 | Get-Module $env:BHProjectName | Remove-Module 7 | Import-Module $env:BHModulePath -Verbose:$false -ErrorAction Stop 8 | $moduleVersion = (Test-ModuleManifest $env:BHPSModuleManifest | Select-Object -ExpandProperty Version).ToString() 9 | $ms = [Microsoft.PowerShell.Commands.ModuleSpecification]@{ ModuleName = $env:BHProjectName; RequiredVersion = $moduleVersion } 10 | $commands = Get-Command -FullyQualifiedModule $ms -CommandType Cmdlet, Function, Workflow # Not alias 11 | 12 | ## When testing help, remember that help is cached at the beginning of each session. 13 | ## To test, restart session. 14 | 15 | foreach ($command in $commands) { 16 | $commandName = $command.Name 17 | 18 | # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets 19 | $help = Get-Help $commandName -ErrorAction SilentlyContinue 20 | 21 | Describe -Name "Test help for $commandName" -Tag 'Help' { 22 | 23 | # If help is not found, synopsis in auto-generated help is the syntax diagram 24 | It "should not be auto-generated" { 25 | $help.Synopsis | Should Not BeLike '*`[``]*' 26 | } 27 | 28 | # Should be a description for every function 29 | It "gets description for $commandName" { 30 | $help.Description | Should Not BeNullOrEmpty 31 | } 32 | 33 | Context "Test example help for $commandName" { 34 | 35 | $examples = @($help.examples.example) 36 | 37 | # Should be at least one example 38 | It "$commandName contains at least one examplegets example code from $commandName" { 39 | $examples.Count -ge 1 | Should Be $true 40 | } 41 | 42 | for ($i = 0; $i -lt $examples.Count; $i++) { 43 | 44 | $example = $examples[$i] 45 | 46 | It "gets example $i code from $commandName" { 47 | $example.Code | Should Not BeNullOrEmpty 48 | } 49 | 50 | It "gets example $i remarks from $commandName" { 51 | $example.Remarks | Should Not BeNullOrEmpty 52 | } 53 | 54 | It "example $i code contains command $commandName" { 55 | ## Command may be on the second line (within the remarks) so concatenate them 56 | $example.Code + $example.remarks.Text | Should Match $commandName 57 | } 58 | } 59 | 60 | } 61 | 62 | Context "Test parameter help for $commandName" { 63 | 64 | $common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 65 | 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable', 'Confirm', 'Whatif' 66 | 67 | $parameters = $command.ParameterSets.Parameters | Sort-Object -Property Name -Unique | Where-Object { $_.Name -notin $common } 68 | $parameterNames = $parameters.Name 69 | 70 | ## Without the filter, WhatIf and Confirm parameters are still flagged in "finds help parameter in code" test 71 | $helpParameters = $help.Parameters.Parameter | Where-Object { $_.Name -notin $common } | Sort-Object -Property Name -Unique 72 | $helpParameterNames = $helpParameters.Name 73 | 74 | foreach ($parameter in $parameters) { 75 | $parameterName = $parameter.Name 76 | $parameterHelp = $help.parameters.parameter | Where-Object Name -EQ $parameterName 77 | 78 | # Should be a description for every parameter 79 | It "gets help for parameter: $parameterName : in $commandName" { 80 | $parameterHelp.Description.Text | Should Not BeNullOrEmpty 81 | } 82 | 83 | # Required value in Help should match IsMandatory property of parameter 84 | It "help for $parameterName parameter in $commandName has correct Mandatory value" { 85 | $codeMandatory = $parameter.IsMandatory.toString() 86 | $parameterHelp.Required | Should Be $codeMandatory 87 | } 88 | 89 | # Parameter type in Help should match code 90 | # It "help for $commandName has correct parameter type for $parameterName" { 91 | # $codeType = $parameter.ParameterType.Name 92 | # # To avoid calling Trim method on a null object. 93 | # $helpType = if ($parameterHelp.parameterValue) { $parameterHelp.parameterValue.Trim() } 94 | # $helpType | Should be $codeType 95 | # } 96 | } 97 | 98 | foreach ($helpParm in $HelpParameterNames) { 99 | # Shouldn't find extra parameters in help. 100 | It "finds help parameter in code: $helpParm" { 101 | $helpParm -in $parameterNames | Should Be $true 102 | } 103 | } 104 | } 105 | 106 | Context "Help Links should be Valid for $commandName" { 107 | $links = $help.relatedLinks.navigationLink.uri 108 | 109 | foreach ($link in $links) { 110 | if ($link) { 111 | # Should have a valid uri if one is provided. 112 | It "[$link] should have 200 Status Code for $commandName" { 113 | $Results = Invoke-WebRequest -Uri $link -UseBasicParsing 114 | $Results.StatusCode | Should Be '200' 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /tests/Meta/Meta.Tests.ps1: -------------------------------------------------------------------------------- 1 | Set-StrictMode -Version latest 2 | 3 | # Make sure MetaFixers.psm1 is loaded - it contains Get-TextFilesList 4 | Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'MetaFixers.psm1') -Verbose:$false -Force 5 | 6 | $projectRoot = $ENV:BHProjectPath 7 | if(-not $projectRoot) { 8 | $projectRoot = $PSScriptRoot 9 | } 10 | 11 | Describe 'Text files formatting' { 12 | 13 | $allTextFiles = Get-TextFilesList $projectRoot 14 | 15 | Context 'Files encoding' { 16 | It "Doesn't use Unicode encoding" { 17 | $unicodeFilesCount = 0 18 | $allTextFiles | ForEach-Object { 19 | if (Test-FileUnicode $_) { 20 | $unicodeFilesCount += 1 21 | Write-Warning "File $($_.FullName) contains 0x00 bytes. It's probably uses Unicode and need to be converted to UTF-8. Use Fixer 'Get-UnicodeFilesList `$pwd | ConvertTo-UTF8'." 22 | } 23 | } 24 | $unicodeFilesCount | Should Be 0 25 | } 26 | } 27 | 28 | Context 'Indentations' { 29 | It 'Uses spaces for indentation, not tabs' { 30 | $totalTabsCount = 0 31 | $allTextFiles | ForEach-Object { 32 | $fileName = $_.FullName 33 | (Get-Content $_.FullName -Raw) | Select-String "`t" | % { 34 | Write-Warning "There are tab in $fileName. Use Fixer 'Get-TextFilesList `$pwd | ConvertTo-SpaceIndentation'." 35 | $totalTabsCount++ 36 | } 37 | } 38 | $totalTabsCount | Should Be 0 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /tests/Meta/MetaFixers.psm1: -------------------------------------------------------------------------------- 1 | # Taken with love from https://github.com/PowerShell/DscResource.Tests/blob/master/MetaFixers.psm1 2 | # See https://github.com/devblackops/NetScaler for details 3 | 4 | <# 5 | This module helps fix problems, found by Meta.Tests.ps1 6 | #> 7 | 8 | $ErrorActionPreference = 'stop' 9 | Set-StrictMode -Version latest 10 | 11 | function ConvertTo-UTF8() { 12 | [CmdletBinding()] 13 | [OutputType([void])] 14 | param( 15 | [Parameter(ValueFromPipeline=$true, Mandatory=$true)] 16 | [System.IO.FileInfo]$fileInfo 17 | ) 18 | 19 | process { 20 | $content = Get-Content -Raw -Encoding Unicode -Path $fileInfo.FullName 21 | [System.IO.File]::WriteAllText($fileInfo.FullName, $content, [System.Text.Encoding]::UTF8) 22 | } 23 | } 24 | 25 | function ConvertTo-SpaceIndentation() { 26 | [CmdletBinding()] 27 | [OutputType([void])] 28 | param( 29 | [Parameter(ValueFromPipeline=$true, Mandatory=$true)] 30 | [System.IO.FileInfo]$fileInfo 31 | ) 32 | 33 | process { 34 | $content = (Get-Content -Raw -Path $fileInfo.FullName) -replace "`t",' ' 35 | [System.IO.File]::WriteAllText($fileInfo.FullName, $content) 36 | } 37 | } 38 | 39 | function Get-TextFilesList { 40 | [CmdletBinding()] 41 | [OutputType([System.IO.FileInfo])] 42 | param( 43 | [Parameter(Mandatory=$true)] 44 | [string]$root 45 | ) 46 | 47 | Get-ChildItem -File -Recurse $root | Where-Object { @('.gitignore', '.gitattributes', '.ps1', '.psm1', '.psd1', '.json', '.xml', '.cmd', '.mof') -contains $_.Extension } 48 | } 49 | 50 | function Test-FileUnicode { 51 | [CmdletBinding()] 52 | [OutputType([bool])] 53 | param( 54 | [Parameter(ValueFromPipeline=$true, Mandatory=$true)] 55 | [System.IO.FileInfo]$fileInfo 56 | ) 57 | 58 | process { 59 | 60 | $path = $fileInfo.FullName 61 | $bytes = [System.IO.File]::ReadAllBytes($path) 62 | $zeroBytes = @($bytes -eq 0) 63 | return [bool]$zeroBytes.Length 64 | 65 | } 66 | } 67 | 68 | function Get-UnicodeFilesList() { 69 | [CmdletBinding()] 70 | [OutputType([System.IO.FileInfo])] 71 | param( 72 | [Parameter(Mandatory=$true)] 73 | [string]$root 74 | ) 75 | 76 | Get-TextFilesList $root | Where-Object { Test-FileUnicode $_ } 77 | } -------------------------------------------------------------------------------- /tests/PoShDynDnsApi.Tests.ps1: -------------------------------------------------------------------------------- 1 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '', Scope='*', Target='SuppressImportModule')] 2 | $SuppressImportModule = $true 3 | . (Join-Path -Path $PSScriptRoot -ChildPath 'Shared.ps1') 4 | 5 | '' 6 | " module manifest path: $ModuleManifestPath" 7 | '' 8 | 9 | Describe 'Module Manifest Tests' { 10 | It 'Passes Test-ModuleManifest' { 11 | Test-ModuleManifest -Path $ModuleManifestPath 12 | $? | Should Be $true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Shared.ps1: -------------------------------------------------------------------------------- 1 | # Dot source this script in any Pester test script that requires the module to be imported. 2 | 3 | $ModuleManifestName = 'PoShDynDnsApi.psd1' 4 | $ModuleManifestPath = "$VersionFolder\$ModuleManifestName" 5 | 6 | if (!$SuppressImportModule) { 7 | # -Scope Global is needed when running tests from inside of psake, otherwise 8 | # the module's functions cannot be found in the PoShDynDnsApi\ namespace 9 | Import-Module $ModuleManifestPath -Scope Global 10 | } 11 | 12 | -------------------------------------------------------------------------------- /tests/Unit/Private/Compare-ObjectProperties.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Compare-ObjectProperties' -Tag 'Unit','Private' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Private/Invoke-DynDnsRequestCore.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Invoke-DynDnsRequestCore' -Tag 'Unit','Private' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Private/Invoke-DynDnsRequestDesktop.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Invoke-DynDnsRequestDesktop' -Tag 'Unit','Private' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Private/Write-DynDnsOutput.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Write-DynDnsOutput' -Tag 'Unit','Private' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Add-DynDnsHttpRedirect.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Add-DynDnsHttpRedirect' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Add-DynDnsRecord.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Add-DynDnsRecord' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Add-DynDnsZone.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Add-DynDnsZone' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Connect-DynDnsSession.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Connect-DynDnsSession' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Disconnect-DynDnsSession.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Disconnect-DynDnsSession' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsHistory.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsHistory' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsHttpRedirect.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsHttpRedirect' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsJob.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsJob' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsNodeList.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsNodeList' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsRecord.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsRecord' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsSession.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsSession' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsTask.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsTask' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsUser.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsUser' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsZone.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsZone' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsZoneChanges.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsZoneChanges' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Get-DynDnsZoneNotes.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Get-DynDnsZoneNotes' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Lock-DynDnsZone.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Lock-DynDnsZone' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/New-DynDnsRecord.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'New-DynDnsRecord' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Publish-DynDnsZoneChanges.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Publish-DynDnsZoneChanges' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Remove-DynDnsHttpRedirect.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Remove-DynDnsHttpRedirect' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Remove-DynDnsNode.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Remove-DynDnsNode' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Remove-DynDnsRecord.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Remove-DynDnsRecord' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Remove-DynDnsZone.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Remove-DynDnsZone' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Send-DynDnsSession.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Send-DynDnsSession' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Test-DynDnsSession.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Test-DynDnsSession' -Tag 'Unit','Public' { 3 | 4 | Set-BuildEnvironment -Force 5 | Import-Module $env:BHPSModuleManifest 6 | 7 | InModuleScope 'PoShDynDnsApi' { 8 | 9 | #Mock -CommandName Invoke-DynDnsRequestDesktop -MockWith { $true } 10 | #Mock -CommandName Invoke-DynDnsRequestCore -MockWith { $true } 11 | Mock -CommandName Invoke-DynDnsRequest -MockWith { $true } -ParameterFilter { 12 | $SessionAction = 'Test' 13 | } 14 | Mock -CommandName Write-DynDnsOutput -MockWith {} 15 | 16 | It 'Returns $true if existing authentication token is valid' { 17 | Test-DynDnsSession | Should be $true 18 | } 19 | 20 | } 21 | 22 | } 23 | 24 | <# 25 | [OutputType('System.Boolean')] 26 | param() 27 | 28 | $Session = Invoke-DynDnsRequest -SessionAction 'Test' -WarningAction SilentlyContinue 29 | Write-DynDnsOutput -DynDnsResponse $Session -WarningAction SilentlyContinue 30 | if ($Session.Data.status -eq 'success') { 31 | $true 32 | } else { 33 | $false 34 | } 35 | #> -------------------------------------------------------------------------------- /tests/Unit/Public/Undo-DynDnsZoneChanges.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Undo-DynDnsZoneChanges' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Unlock-DynDnsZone.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Unlock-DynDnsZone' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/Unit/Public/Update-DynDnsRecord.Tests.ps1: -------------------------------------------------------------------------------- 1 |  2 | Describe -Name 'Update-DynDnsRecord' -Tag 'Unit','Public' { 3 | 4 | InModuleScope 'PoShDynDnsApi' { 5 | 6 | It -Pending 'UnitTestPending' { 7 | 8 | } 9 | 10 | } 11 | 12 | } 13 | 14 | --------------------------------------------------------------------------------