Domeneshop.psm1
|
[CmdletBinding()] param() $baseName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath) $script:PSModuleInfo = Import-PowerShellDataFile -Path "$PSScriptRoot\$baseName.psd1" $script:PSModuleInfo | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } $scriptName = $script:PSModuleInfo.Name Write-Debug "[$scriptName] - Importing module" #region [functions] - [private] Write-Debug "[$scriptName] - [functions] - [private] - Processing folder" #region [functions] - [private] - [Get-DomeneshopApiBaseUri] Write-Debug "[$scriptName] - [functions] - [private] - [Get-DomeneshopApiBaseUri] - Importing" function Get-DomeneshopApiBaseUri { <# .SYNOPSIS Get the API base URI from a resolved Domeneshop context. .DESCRIPTION Validate and normalize the API base URI stored on a resolved Domeneshop context. .EXAMPLE Get-DomeneshopApiBaseUri -Context $resolvedContext Get the normalized API base URI for a resolved context. .INPUTS None You can't pipe objects to Get-DomeneshopApiBaseUri. .OUTPUTS System.String The normalized Domeneshop API base URI. .NOTES The caller must resolve and validate the context before invoking this helper. .LINK https://api.domeneshop.no/docs/ #> [OutputType([string])] [CmdletBinding()] param( # The resolved Domeneshop context containing an API base URI. [Parameter(Mandatory)] [ValidateNotNull()] [object] $Context ) $apiBaseUri = [string] $Context.ApiBaseUri if ([string]::IsNullOrWhiteSpace($apiBaseUri)) { throw "The Domeneshop context [$($Context.ID)] does not define an API base URI." } $parsedUri = $null if (-not [uri]::TryCreate($apiBaseUri, [System.UriKind]::Absolute, [ref] $parsedUri)) { throw "The Domeneshop context [$($Context.ID)] contains an invalid API base URI." } $parsedUri.AbsoluteUri.TrimEnd('/') } Write-Debug "[$scriptName] - [functions] - [private] - [Get-DomeneshopApiBaseUri] - Done" #endregion [functions] - [private] - [Get-DomeneshopApiBaseUri] #region [functions] - [private] - [Invoke-DomeneshopApiRequest] Write-Debug "[$scriptName] - [functions] - [private] - [Invoke-DomeneshopApiRequest] - Importing" function Invoke-DomeneshopApiRequest { <# .SYNOPSIS Send an authenticated request to the Domeneshop API. .DESCRIPTION Build an HTTP Basic credential from a resolved Domeneshop context and send a REST request, optionally with a JSON body. .EXAMPLE Invoke-DomeneshopApiRequest -Method Get -Uri $uri -Context $resolvedContext Send an authenticated GET request. .EXAMPLE Invoke-DomeneshopApiRequest -Method Post -Uri $uri -Context $resolvedContext -Body $body Send an authenticated POST request with a JSON body. .INPUTS None You can't pipe objects to Invoke-DomeneshopApiRequest. .OUTPUTS System.Object The response returned by the Domeneshop API. .NOTES Transport errors are terminating so public callers fail fast. .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding()] param( # The HTTP method accepted by the Domeneshop API. [Parameter(Mandatory)] [ValidateSet('Get', 'Post', 'Put', 'Delete')] [string] $Method, # The absolute Domeneshop API endpoint URI. [Parameter(Mandatory)] [ValidateNotNull()] [uri] $Uri, # The resolved Domeneshop context containing valid credentials. [Parameter(Mandatory)] [ValidateNotNull()] [object] $Context, # The request payload to serialize as JSON. [Parameter()] [AllowNull()] [object] $Body ) $credential = [pscredential]::new( [string] $Context.Token, [securestring] $Context.Secret ) $params = @{ Method = $Method Uri = $Uri Authentication = 'Basic' Credential = $credential ErrorAction = 'Stop' } if ($PSBoundParameters.ContainsKey('Body')) { $params['ContentType'] = 'application/json' $params['Body'] = ($Body | ConvertTo-Json -Depth 100) } Write-Debug "Sending $Method request to [$Uri]." Invoke-RestMethod @params } Write-Debug "[$scriptName] - [functions] - [private] - [Invoke-DomeneshopApiRequest] - Done" #endregion [functions] - [private] - [Invoke-DomeneshopApiRequest] #region [functions] - [private] - [Auth] Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - Processing folder" #region [functions] - [private] - [Auth] - [Get-DomeneshopConfig] Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Get-DomeneshopConfig] - Importing" #Requires -Modules @{ ModuleName = 'Context'; ModuleVersion = '8.1.6' } function Get-DomeneshopConfig { <# .SYNOPSIS Get the Domeneshop module configuration. .DESCRIPTION Read the module configuration from the Context vault or return an in-memory default when no configuration has been stored. .EXAMPLE Get-DomeneshopConfig Get the current Domeneshop module configuration. .INPUTS None You can't pipe objects to Get-DomeneshopConfig. .OUTPUTS System.Object The stored or default Domeneshop module configuration. .NOTES This read helper does not create or update Context vault entries. .LINK https://psmodule.io/Domeneshop/Functions/Auth/Get-DomeneshopContext .LINK https://psmodule.io/Context/ #> [OutputType([object])] [CmdletBinding()] param() $vault = 'Domeneshop' $id = '__Domeneshop.Config' $config = Get-Context -ID $id -Vault $vault if (-not $config) { $config = [pscustomobject][ordered]@{ DefaultContext = $null ApiBaseUri = 'https://api.domeneshop.no/v0' } } $config } Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Get-DomeneshopConfig] - Done" #endregion [functions] - [private] - [Auth] - [Get-DomeneshopConfig] #region [functions] - [private] - [Auth] - [Resolve-DomeneshopContext] Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Resolve-DomeneshopContext] - Importing" function Resolve-DomeneshopContext { <# .SYNOPSIS Validate a resolved Domeneshop context. .DESCRIPTION Verify that a context resolved by a public command contains the credentials and API endpoint required by private helpers. .EXAMPLE Resolve-DomeneshopContext -Context $storedContext Validate and emit a stored Domeneshop context. .INPUTS None You can't pipe objects to Resolve-DomeneshopContext. .OUTPUTS System.Object The validated Domeneshop context. .NOTES This helper never selects or defaults a context. Public commands own that behavior. .LINK https://psmodule.io/Domeneshop/Functions/Auth/Get-DomeneshopContext .LINK https://psmodule.io/Context/ #> [OutputType([object])] [CmdletBinding()] param( # The context object selected by a public command. [Parameter(Mandatory)] [AllowNull()] [object] $Context ) if ($null -eq $Context) { throw "No Domeneshop context found. Run 'Connect-DomeneshopAccount' first." } if ([string]::IsNullOrWhiteSpace([string] $Context.Token) -or -not ($Context.Secret -is [securestring])) { throw "The Domeneshop context [$($Context.ID)] is missing valid credentials." } if ([string]::IsNullOrWhiteSpace([string] $Context.ApiBaseUri)) { throw "The Domeneshop context [$($Context.ID)] is missing an API base URI." } $Context } Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Resolve-DomeneshopContext] - Done" #endregion [functions] - [private] - [Auth] - [Resolve-DomeneshopContext] #region [functions] - [private] - [Auth] - [Set-DomeneshopDefaultContext] Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Set-DomeneshopDefaultContext] - Importing" #Requires -Modules @{ ModuleName = 'Context'; ModuleVersion = '8.1.6' } function Set-DomeneshopDefaultContext { <# .SYNOPSIS Set the default Domeneshop context name. .DESCRIPTION Persist the selected default context name in the Domeneshop configuration entry. .EXAMPLE Set-DomeneshopDefaultContext -Context 'production' Set production as the default Domeneshop context. .INPUTS None You can't pipe objects to Set-DomeneshopDefaultContext. .OUTPUTS None Set-DomeneshopDefaultContext doesn't emit output. .NOTES Supports WhatIf and Confirm before changing the module configuration. .LINK https://psmodule.io/Domeneshop/Functions/Auth/Connect-DomeneshopAccount .LINK https://psmodule.io/Context/ #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] param( # The name of an existing Domeneshop context. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-DomeneshopContextName -Context $_ })] [string] $Context ) $config = Get-DomeneshopConfig if ($PSCmdlet.ShouldProcess('Domeneshop module configuration', "Set [$Context] as the default context")) { $config.DefaultContext = $Context $null = Set-Context -ID '__Domeneshop.Config' -Vault 'Domeneshop' -Context $config } } Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Set-DomeneshopDefaultContext] - Done" #endregion [functions] - [private] - [Auth] - [Set-DomeneshopDefaultContext] #region [functions] - [private] - [Auth] - [Test-DomeneshopContextName] Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Test-DomeneshopContextName] - Importing" function Test-DomeneshopContextName { <# .SYNOPSIS Validate a Domeneshop credential context name. .DESCRIPTION Reject names reserved for internal Domeneshop module configuration. .EXAMPLE Test-DomeneshopContextName -Context 'production' Confirm that production can be used as a credential context name. .INPUTS None You can't pipe objects to Test-DomeneshopContextName. .OUTPUTS System.Boolean True when the context name is available for credentials. .NOTES Throws when the context name is reserved. .LINK https://psmodule.io/Domeneshop/Functions/Auth/Connect-DomeneshopAccount #> [OutputType([bool])] [CmdletBinding()] param( # The proposed credential context name. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Context ) if ([string]::IsNullOrWhiteSpace($Context)) { throw 'Context name cannot be empty or whitespace.' } if ($Context -eq '__Domeneshop.Config') { throw "Context name [$Context] is reserved for Domeneshop module configuration." } $true } Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Test-DomeneshopContextName] - Done" #endregion [functions] - [private] - [Auth] - [Test-DomeneshopContextName] #region [functions] - [private] - [Auth] - [Test-DomeneshopSecret] Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Test-DomeneshopSecret] - Importing" function Test-DomeneshopSecret { <# .SYNOPSIS Validate a Domeneshop API secret. .DESCRIPTION Reject unsupported and empty values before credential storage. .EXAMPLE Test-DomeneshopSecret -Secret $secret Confirm that the secret contains a value. .INPUTS None You can't pipe objects to Test-DomeneshopSecret. .OUTPUTS System.Boolean True when the secret has a supported type and is not empty. .NOTES Supported values are String and SecureString. .LINK https://psmodule.io/Domeneshop/Functions/Auth/Connect-DomeneshopAccount #> [OutputType([bool])] [CmdletBinding()] param( # The proposed API secret. [Parameter(Mandatory)] [ValidateNotNull()] [object] $Secret ) if ($Secret -isnot [string] -and $Secret -isnot [securestring]) { throw 'Secret must be a SecureString or String value.' } if ( ($Secret -is [string] -and [string]::IsNullOrWhiteSpace($Secret)) -or ($Secret -is [securestring] -and $Secret.Length -eq 0) ) { throw 'Secret cannot be empty or whitespace.' } $true } Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - [Test-DomeneshopSecret] - Done" #endregion [functions] - [private] - [Auth] - [Test-DomeneshopSecret] Write-Debug "[$scriptName] - [functions] - [private] - [Auth] - Done" #endregion [functions] - [private] - [Auth] Write-Debug "[$scriptName] - [functions] - [private] - Done" #endregion [functions] - [private] #region [functions] - [public] Write-Debug "[$scriptName] - [functions] - [public] - Processing folder" #region [functions] - [public] - [Auth] Write-Debug "[$scriptName] - [functions] - [public] - [Auth] - Processing folder" #region [functions] - [public] - [Auth] - [Connect-DomeneshopAccount] Write-Debug "[$scriptName] - [functions] - [public] - [Auth] - [Connect-DomeneshopAccount] - Importing" #Requires -Version 7.6 #Requires -Modules @{ ModuleName = 'Context'; ModuleVersion = '8.1.6' } function Connect-DomeneshopAccount { <# .SYNOPSIS Stores Domeneshop API credentials in a secure context. .DESCRIPTION Stores Domeneshop API credentials using the Context module. The first stored context becomes the default automatically, and Default replaces an existing default. When Secret is omitted, opens the Domeneshop API settings page and securely prompts for it. .EXAMPLE Connect-DomeneshopAccount -Token 'my-token' -Secret (Read-Host -AsSecureString) Store credentials in the default named context. .EXAMPLE Connect-DomeneshopAccount -Token 'my-token' Open the Domeneshop API settings page and securely prompt for the API secret. .INPUTS None You can't pipe objects to Connect-DomeneshopAccount. .OUTPUTS System.Object The stored context when PassThru is specified. .NOTES Credentials are encrypted by the Context module. .LINK https://psmodule.io/Domeneshop/Functions/Auth/Connect-DomeneshopAccount/ .LINK https://api.domeneshop.no/docs/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'String secrets remain supported for compatibility and are converted before storage.' )] [OutputType([object])] [CmdletBinding(SupportsShouldProcess)] param( # The Domeneshop API token used as the Basic authentication username. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [string] $Token, # The Domeneshop API secret as a string or secure string. [Parameter()] [ValidateNotNull()] [ValidateScript({ Test-DomeneshopSecret -Secret $_ })] [Alias('Key')] [object] $Secret, # The name used to store and retrieve this credential context. [Parameter()] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-DomeneshopContextName -Context $_ })] [string] $Context = 'default', # Replace the default context; the first stored context becomes the default automatically. [Parameter()] [switch] $Default, # Emit the stored context after it is saved. [Parameter()] [switch] $PassThru ) if ($PSCmdlet.ShouldProcess("Domeneshop context [$Context]", 'Store API credentials')) { if (-not $PSBoundParameters.ContainsKey('Secret')) { try { Start-Process -FilePath 'https://domene.shop/admin?view=api' } catch { Write-Warning "Unable to open the Domeneshop API settings page: $($_.Exception.Message)" } $Secret = Read-Host -Prompt 'Enter the Domeneshop API secret' -AsSecureString $null = Test-DomeneshopSecret -Secret $Secret } $secureSecret = if ($Secret -is [securestring]) { $Secret } else { ConvertTo-SecureString -AsPlainText $Secret -Force } $contextObject = [ordered]@{ Name = $Context Token = $Token Secret = $secureSecret AuthType = 'BasicAuth' ApiBaseUri = 'https://api.domeneshop.no/v0' ConnectedAt = Get-Date } $null = Set-Context -ID $Context -Vault 'Domeneshop' -Context $contextObject $config = Get-DomeneshopConfig if ($Default -or [string]::IsNullOrWhiteSpace([string] $config.DefaultContext)) { Set-DomeneshopDefaultContext -Context $Context -Confirm:$false } if ($PassThru) { Get-DomeneshopContext -Context $Context } } } Write-Debug "[$scriptName] - [functions] - [public] - [Auth] - [Connect-DomeneshopAccount] - Done" #endregion [functions] - [public] - [Auth] - [Connect-DomeneshopAccount] #region [functions] - [public] - [Auth] - [Get-DomeneshopContext] Write-Debug "[$scriptName] - [functions] - [public] - [Auth] - [Get-DomeneshopContext] - Importing" #Requires -Modules @{ ModuleName = 'Context'; ModuleVersion = '8.1.6' } function Get-DomeneshopContext { <# .SYNOPSIS Gets stored Domeneshop contexts. .DESCRIPTION Returns one or more contexts stored in the Domeneshop context vault. .EXAMPLE Get-DomeneshopContext Get the default Domeneshop context. .EXAMPLE Get-DomeneshopContext -ListAvailable List all stored Domeneshop credential contexts. .INPUTS None You can't pipe objects to Get-DomeneshopContext. .OUTPUTS System.Object One or more stored Domeneshop contexts. .NOTES The configuration entry used to track the default is excluded from list output. .LINK https://psmodule.io/Domeneshop/Functions/Auth/Get-DomeneshopContext/ .LINK https://psmodule.io/Context/ #> [OutputType([object])] [CmdletBinding(DefaultParameterSetName = 'Get default')] param( # The name of a specific stored context. [Parameter(Mandatory, ParameterSetName = 'Get named')] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-DomeneshopContextName -Context $_ })] [string] $Context, # List every stored Domeneshop credential context. [Parameter(Mandatory, ParameterSetName = 'List available')] [switch] $ListAvailable ) if ($ListAvailable) { $id = '*' } elseif ($PSBoundParameters.ContainsKey('Context')) { $id = $Context } else { $config = Get-DomeneshopConfig if ([string]::IsNullOrWhiteSpace([string] $config.DefaultContext)) { throw "No default Domeneshop context found. Run 'Connect-DomeneshopAccount' first." } $id = $config.DefaultContext } $contexts = @( Get-Context -ID $id -Vault 'Domeneshop' | Where-Object { $_.ID -ne '__Domeneshop.Config' } ) if (-not $ListAvailable -and $contexts.Count -eq 0) { throw "Domeneshop context [$id] was not found in the Domeneshop vault." } $contexts | Sort-Object -Property ID } Write-Debug "[$scriptName] - [functions] - [public] - [Auth] - [Get-DomeneshopContext] - Done" #endregion [functions] - [public] - [Auth] - [Get-DomeneshopContext] Write-Debug "[$scriptName] - [functions] - [public] - [Auth] - Done" #endregion [functions] - [public] - [Auth] #region [functions] - [public] - [Ddns] Write-Debug "[$scriptName] - [functions] - [public] - [Ddns] - Processing folder" #region [functions] - [public] - [Ddns] - [Update-DomeneshopDdns] Write-Debug "[$scriptName] - [functions] - [public] - [Ddns] - [Update-DomeneshopDdns] - Importing" function Update-DomeneshopDdns { <# .SYNOPSIS Update dynamic DNS for a hostname. .DESCRIPTION Calls the Domeneshop DDNS update endpoint. The API creates the A/AAAA record if it does not exist. .EXAMPLE Update-DomeneshopDdns -Hostname 'home.example.com' -MyIP '192.0.2.10' Update the dynamic DNS address for home.example.com. .INPUTS None You can't pipe objects to Update-DomeneshopDdns. .OUTPUTS System.Object The response returned by the Domeneshop DDNS endpoint. .NOTES Uses the caller's public IP address when MyIP is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Ddns/Update-DomeneshopDdns/ .LINK https://api.domeneshop.no/docs/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseSingularNouns', '', Justification = 'DDNS is the established singular name of the Domeneshop API capability.' )] [OutputType([object])] [CmdletBinding(SupportsShouldProcess)] param( # The fully qualified hostname to update. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [Alias('Host')] [string] $Hostname, # The IPv4 or IPv6 address to publish. [Parameter()] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [Alias('IP')] [string] $MyIP, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $uri = "$apiBaseUri/dyndns/update?hostname=$([uri]::EscapeDataString($Hostname))" if ($PSBoundParameters.ContainsKey('MyIP')) { $uri = "$uri&myip=$([uri]::EscapeDataString($MyIP))" } if ($PSCmdlet.ShouldProcess("Dynamic DNS host [$Hostname]", 'Update address')) { Invoke-DomeneshopApiRequest -Method Get -Uri $uri -Context $resolvedContext } } Write-Debug "[$scriptName] - [functions] - [public] - [Ddns] - [Update-DomeneshopDdns] - Done" #endregion [functions] - [public] - [Ddns] - [Update-DomeneshopDdns] Write-Debug "[$scriptName] - [functions] - [public] - [Ddns] - Done" #endregion [functions] - [public] - [Ddns] #region [functions] - [public] - [Dns] Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - Processing folder" #region [functions] - [public] - [Dns] - [Add-DomeneshopDnsRecord] Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - [Add-DomeneshopDnsRecord] - Importing" function Add-DomeneshopDnsRecord { <# .SYNOPSIS Add a DNS record to a Domeneshop domain. .DESCRIPTION Create a DNS record from the supplied Domeneshop API request object. .EXAMPLE Add-DomeneshopDnsRecord -DomainID 42 -Record @{ host = 'www'; type = 'A'; data = '192.0.2.10' } Add an A record to domain 42. .INPUTS None You can't pipe objects to Add-DomeneshopDnsRecord. .OUTPUTS System.Object The response returned by the Domeneshop API. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Dns/Add-DomeneshopDnsRecord/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(SupportsShouldProcess)] param( # The numeric identifier of the domain that owns the record. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $DomainID, # The DNS record request object accepted by the Domeneshop API. [Parameter(Mandatory)] [ValidateNotNull()] [object] $Record, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $uri = "$apiBaseUri/domains/$DomainID/dns" if ($PSCmdlet.ShouldProcess("Domain [$DomainID] DNS records", 'Add DNS record')) { Invoke-DomeneshopApiRequest -Method Post -Uri $uri -Context $resolvedContext -Body $Record } } Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - [Add-DomeneshopDnsRecord] - Done" #endregion [functions] - [public] - [Dns] - [Add-DomeneshopDnsRecord] #region [functions] - [public] - [Dns] - [Get-DomeneshopDnsRecord] Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - [Get-DomeneshopDnsRecord] - Importing" function Get-DomeneshopDnsRecord { <# .SYNOPSIS Gets DNS records for a Domeneshop domain. .DESCRIPTION Lists DNS records for a domain, or gets one DNS record by ID. .EXAMPLE Get-DomeneshopDnsRecord -DomainID 42 List every DNS record for domain 42. .EXAMPLE Get-DomeneshopDnsRecord -DomainID 42 -RecordID 7 Get DNS record 7 from domain 42. .INPUTS None You can't pipe objects to Get-DomeneshopDnsRecord. .OUTPUTS System.Object The matching Domeneshop DNS records. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Dns/Get-DomeneshopDnsRecord/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(DefaultParameterSetName = 'List')] param( # The numeric identifier of the domain that owns the records. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $DomainID, # The numeric identifier of a specific DNS record. [Parameter(Mandatory, ParameterSetName = 'Get by ID')] [ValidateRange(1, [int]::MaxValue)] [int] $RecordID, # A hostname filter for list requests. [Parameter(ParameterSetName = 'List')] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [Alias('Host')] [string] $RecordHost, # A DNS record type filter for list requests. [Parameter(ParameterSetName = 'List')] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [string] $Type, # A record-data filter for list requests. [Parameter(ParameterSetName = 'List')] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [string] $Data, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $uri = "$apiBaseUri/domains/$DomainID/dns" if ($PSCmdlet.ParameterSetName -eq 'Get by ID') { $uri = "$uri/$RecordID" } else { $queryParts = @() if ($PSBoundParameters.ContainsKey('RecordHost')) { $queryParts += "host=$([uri]::EscapeDataString($RecordHost))" } if ($PSBoundParameters.ContainsKey('Type')) { $queryParts += "type=$([uri]::EscapeDataString($Type))" } if ($PSBoundParameters.ContainsKey('Data')) { $queryParts += "data=$([uri]::EscapeDataString($Data))" } if ($queryParts.Count -gt 0) { $uri = "${uri}?{0}" -f ($queryParts -join '&') } } Invoke-DomeneshopApiRequest -Method Get -Uri $uri -Context $resolvedContext } Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - [Get-DomeneshopDnsRecord] - Done" #endregion [functions] - [public] - [Dns] - [Get-DomeneshopDnsRecord] #region [functions] - [public] - [Dns] - [Remove-DomeneshopDnsRecord] Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - [Remove-DomeneshopDnsRecord] - Importing" function Remove-DomeneshopDnsRecord { <# .SYNOPSIS Remove a DNS record from a Domeneshop domain. .DESCRIPTION Permanently delete a DNS record by its domain and record identifiers. .EXAMPLE Remove-DomeneshopDnsRecord -DomainID 42 -RecordID 7 Remove DNS record 7 from domain 42 after confirmation. .INPUTS None You can't pipe objects to Remove-DomeneshopDnsRecord. .OUTPUTS System.Object The response returned by the Domeneshop API. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Dns/Remove-DomeneshopDnsRecord/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param( # The numeric identifier of the domain that owns the record. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $DomainID, # The numeric identifier of the DNS record to remove. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $RecordID, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $uri = "$apiBaseUri/domains/$DomainID/dns/$RecordID" if ($PSCmdlet.ShouldProcess("Domain [$DomainID] DNS record [$RecordID]", 'Remove')) { Invoke-DomeneshopApiRequest -Method Delete -Uri $uri -Context $resolvedContext } } Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - [Remove-DomeneshopDnsRecord] - Done" #endregion [functions] - [public] - [Dns] - [Remove-DomeneshopDnsRecord] #region [functions] - [public] - [Dns] - [Set-DomeneshopDnsRecord] Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - [Set-DomeneshopDnsRecord] - Importing" function Set-DomeneshopDnsRecord { <# .SYNOPSIS Update a DNS record on a Domeneshop domain. .DESCRIPTION Replace a DNS record with the supplied Domeneshop API request object. .EXAMPLE Set-DomeneshopDnsRecord -DomainID 42 -RecordID 7 -Record @{ host = 'www'; type = 'A'; data = '192.0.2.20' } Update DNS record 7 on domain 42. .INPUTS None You can't pipe objects to Set-DomeneshopDnsRecord. .OUTPUTS System.Object The response returned by the Domeneshop API. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Dns/Set-DomeneshopDnsRecord/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(SupportsShouldProcess)] param( # The numeric identifier of the domain that owns the record. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $DomainID, # The numeric identifier of the DNS record to update. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $RecordID, # The replacement DNS record accepted by the Domeneshop API. [Parameter(Mandatory)] [ValidateNotNull()] [object] $Record, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $uri = "$apiBaseUri/domains/$DomainID/dns/$RecordID" if ($PSCmdlet.ShouldProcess("Domain [$DomainID] DNS record [$RecordID]", 'Update')) { Invoke-DomeneshopApiRequest -Method Put -Uri $uri -Context $resolvedContext -Body $Record } } Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - [Set-DomeneshopDnsRecord] - Done" #endregion [functions] - [public] - [Dns] - [Set-DomeneshopDnsRecord] Write-Debug "[$scriptName] - [functions] - [public] - [Dns] - Done" #endregion [functions] - [public] - [Dns] #region [functions] - [public] - [Domains] Write-Debug "[$scriptName] - [functions] - [public] - [Domains] - Processing folder" #region [functions] - [public] - [Domains] - [Get-DomeneshopDomain] Write-Debug "[$scriptName] - [functions] - [public] - [Domains] - [Get-DomeneshopDomain] - Importing" function Get-DomeneshopDomain { <# .SYNOPSIS Lists Domeneshop domains. .DESCRIPTION Uses the stored Domeneshop context credentials and calls the /domains endpoint. .EXAMPLE Get-DomeneshopDomain .EXAMPLE Get-DomeneshopDomain -Domain '.no' List domains ending in .no. .INPUTS None You can't pipe objects to Get-DomeneshopDomain. .OUTPUTS System.Object The matching Domeneshop domain records. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Domains/Get-DomeneshopDomain/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(DefaultParameterSetName = 'List')] param( # A domain-name filter for list requests. [Parameter(ParameterSetName = 'List')] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [string] $Domain, # The numeric identifier of a specific domain. [Parameter(Mandatory, ParameterSetName = 'Get by ID')] [ValidateRange(1, [int]::MaxValue)] [int] $DomainID, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $uri = switch ($PSCmdlet.ParameterSetName) { 'Get by ID' { "$apiBaseUri/domains/$DomainID"; break } default { $listUri = "$apiBaseUri/domains" if ($Domain) { $encodedDomain = [uri]::EscapeDataString($Domain) $listUri = "${listUri}?domain=$encodedDomain" } $listUri } } Invoke-DomeneshopApiRequest -Method Get -Uri $uri -Context $resolvedContext } Write-Debug "[$scriptName] - [functions] - [public] - [Domains] - [Get-DomeneshopDomain] - Done" #endregion [functions] - [public] - [Domains] - [Get-DomeneshopDomain] Write-Debug "[$scriptName] - [functions] - [public] - [Domains] - Done" #endregion [functions] - [public] - [Domains] #region [functions] - [public] - [Forwards] Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - Processing folder" #region [functions] - [public] - [Forwards] - [Add-DomeneshopForward] Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - [Add-DomeneshopForward] - Importing" function Add-DomeneshopForward { <# .SYNOPSIS Add an HTTP forward to a Domeneshop domain. .DESCRIPTION Create an HTTP forward from the supplied Domeneshop API request object. .EXAMPLE $forward = @{ host = 'www' ('URL'.ToLowerInvariant()) = 'HTTPS'.ToLowerInvariant() + '://example.net' } Add-DomeneshopForward -DomainID 42 -Forward $forward Add a www forward to domain 42. .INPUTS None You can't pipe objects to Add-DomeneshopForward. .OUTPUTS System.Object The response returned by the Domeneshop API. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Forwards/Add-DomeneshopForward/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(SupportsShouldProcess)] param( # The numeric identifier of the domain that owns the forward. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $DomainID, # The HTTP forward request object accepted by the Domeneshop API. [Parameter(Mandatory)] [ValidateNotNull()] [object] $Forward, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $uri = "$apiBaseUri/domains/$DomainID/forwards/" if ($PSCmdlet.ShouldProcess("Domain [$DomainID] HTTP forwards", 'Add HTTP forward')) { Invoke-DomeneshopApiRequest -Method Post -Uri $uri -Context $resolvedContext -Body $Forward } } Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - [Add-DomeneshopForward] - Done" #endregion [functions] - [public] - [Forwards] - [Add-DomeneshopForward] #region [functions] - [public] - [Forwards] - [Get-DomeneshopForward] Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - [Get-DomeneshopForward] - Importing" function Get-DomeneshopForward { <# .SYNOPSIS Gets HTTP forwards for a Domeneshop domain. .DESCRIPTION Lists forwards for a domain, or gets one forward by host. .EXAMPLE Get-DomeneshopForward -DomainID 42 List every HTTP forward for domain 42. .EXAMPLE Get-DomeneshopForward -DomainID 42 -ForwardHost 'www' Get the www forward for domain 42. .INPUTS None You can't pipe objects to Get-DomeneshopForward. .OUTPUTS System.Object The matching Domeneshop HTTP forwards. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Forwards/Get-DomeneshopForward/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(DefaultParameterSetName = 'List')] param( # The numeric identifier of the domain that owns the forwards. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $DomainID, # The hostname of a specific HTTP forward. [Parameter(Mandatory, ParameterSetName = 'Get by host')] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [Alias('Host')] [string] $ForwardHost, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $uri = if ($PSCmdlet.ParameterSetName -eq 'Get by host') { $encodedHost = [uri]::EscapeDataString($ForwardHost) "$apiBaseUri/domains/$DomainID/forwards/$encodedHost" } else { "$apiBaseUri/domains/$DomainID/forwards/" } Invoke-DomeneshopApiRequest -Method Get -Uri $uri -Context $resolvedContext } Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - [Get-DomeneshopForward] - Done" #endregion [functions] - [public] - [Forwards] - [Get-DomeneshopForward] #region [functions] - [public] - [Forwards] - [Remove-DomeneshopForward] Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - [Remove-DomeneshopForward] - Importing" function Remove-DomeneshopForward { <# .SYNOPSIS Remove an HTTP forward from a Domeneshop domain. .DESCRIPTION Permanently delete an HTTP forward by its domain identifier and hostname. .EXAMPLE Remove-DomeneshopForward -DomainID 42 -ForwardHost 'www' Remove the www forward from domain 42 after confirmation. .INPUTS None You can't pipe objects to Remove-DomeneshopForward. .OUTPUTS System.Object The response returned by the Domeneshop API. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Forwards/Remove-DomeneshopForward/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param( # The numeric identifier of the domain that owns the forward. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $DomainID, # The hostname of the HTTP forward to remove. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [Alias('Host')] [string] $ForwardHost, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $encodedHost = [uri]::EscapeDataString($ForwardHost) $uri = "$apiBaseUri/domains/$DomainID/forwards/$encodedHost" if ($PSCmdlet.ShouldProcess("Domain [$DomainID] HTTP forward [$ForwardHost]", 'Remove')) { Invoke-DomeneshopApiRequest -Method Delete -Uri $uri -Context $resolvedContext } } Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - [Remove-DomeneshopForward] - Done" #endregion [functions] - [public] - [Forwards] - [Remove-DomeneshopForward] #region [functions] - [public] - [Forwards] - [Set-DomeneshopForward] Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - [Set-DomeneshopForward] - Importing" function Set-DomeneshopForward { <# .SYNOPSIS Update an HTTP forward on a Domeneshop domain. .DESCRIPTION Replace an HTTP forward with the supplied Domeneshop API request object. .EXAMPLE $forward = @{ host = 'www' ('URL'.ToLowerInvariant()) = 'HTTPS'.ToLowerInvariant() + '://example.org' } Set-DomeneshopForward -DomainID 42 -ForwardHost 'www' -Forward $forward Update the www forward on domain 42. .INPUTS None You can't pipe objects to Set-DomeneshopForward. .OUTPUTS System.Object The response returned by the Domeneshop API. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Forwards/Set-DomeneshopForward/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(SupportsShouldProcess)] param( # The numeric identifier of the domain that owns the forward. [Parameter(Mandatory)] [ValidateRange(1, [int]::MaxValue)] [int] $DomainID, # The hostname of the HTTP forward to update. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })] [Alias('Host')] [string] $ForwardHost, # The replacement HTTP forward accepted by the Domeneshop API. [Parameter(Mandatory)] [ValidateNotNull()] [object] $Forward, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $encodedHost = [uri]::EscapeDataString($ForwardHost) $uri = "$apiBaseUri/domains/$DomainID/forwards/$encodedHost" if ($PSCmdlet.ShouldProcess("Domain [$DomainID] HTTP forward [$ForwardHost]", 'Update')) { Invoke-DomeneshopApiRequest -Method Put -Uri $uri -Context $resolvedContext -Body $Forward } } Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - [Set-DomeneshopForward] - Done" #endregion [functions] - [public] - [Forwards] - [Set-DomeneshopForward] Write-Debug "[$scriptName] - [functions] - [public] - [Forwards] - Done" #endregion [functions] - [public] - [Forwards] #region [functions] - [public] - [Invoices] Write-Debug "[$scriptName] - [functions] - [public] - [Invoices] - Processing folder" #region [functions] - [public] - [Invoices] - [Get-DomeneshopInvoice] Write-Debug "[$scriptName] - [functions] - [public] - [Invoices] - [Get-DomeneshopInvoice] - Importing" function Get-DomeneshopInvoice { <# .SYNOPSIS Gets Domeneshop invoices. .DESCRIPTION Lists invoices, or gets a specific invoice by invoice number. .EXAMPLE Get-DomeneshopInvoice List all Domeneshop invoices. .EXAMPLE Get-DomeneshopInvoice -InvoiceID 1001 Get invoice 1001. .INPUTS None You can't pipe objects to Get-DomeneshopInvoice. .OUTPUTS System.Object The matching Domeneshop invoices. .NOTES Uses the default context when Context is omitted. .LINK https://psmodule.io/Domeneshop/Functions/Invoices/Get-DomeneshopInvoice/ .LINK https://api.domeneshop.no/docs/ #> [OutputType([object])] [CmdletBinding(DefaultParameterSetName = 'List')] param( # The numeric identifier of a specific invoice. [Parameter(Mandatory, ParameterSetName = 'Get by ID')] [ValidateRange(1, [int]::MaxValue)] [Alias('InvoiceNo')] [int] $InvoiceID, # The stored credential context to use instead of the default. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Context ) $storedContext = if ($PSBoundParameters.ContainsKey('Context')) { Get-DomeneshopContext -Context $Context } else { Get-DomeneshopContext } $resolvedContext = Resolve-DomeneshopContext -Context $storedContext $apiBaseUri = Get-DomeneshopApiBaseUri -Context $resolvedContext $uri = if ($PSCmdlet.ParameterSetName -eq 'Get by ID') { "$apiBaseUri/invoices/$InvoiceID" } else { "$apiBaseUri/invoices" } Invoke-DomeneshopApiRequest -Method Get -Uri $uri -Context $resolvedContext } Write-Debug "[$scriptName] - [functions] - [public] - [Invoices] - [Get-DomeneshopInvoice] - Done" #endregion [functions] - [public] - [Invoices] - [Get-DomeneshopInvoice] Write-Debug "[$scriptName] - [functions] - [public] - [Invoices] - Done" #endregion [functions] - [public] - [Invoices] Write-Debug "[$scriptName] - [functions] - [public] - Done" #endregion [functions] - [public] #region Member exporter $exports = @{ Alias = '*' Cmdlet = '' Function = @( 'Connect-DomeneshopAccount' 'Get-DomeneshopContext' 'Update-DomeneshopDdns' 'Add-DomeneshopDnsRecord' 'Get-DomeneshopDnsRecord' 'Remove-DomeneshopDnsRecord' 'Set-DomeneshopDnsRecord' 'Get-DomeneshopDomain' 'Add-DomeneshopForward' 'Get-DomeneshopForward' 'Remove-DomeneshopForward' 'Set-DomeneshopForward' 'Get-DomeneshopInvoice' ) Variable = '' } Export-ModuleMember @exports #endregion Member exporter |