PSShlink.psm1
#region Private functions function GetShlinkConnection { param ( [Parameter()] [String]$Server, [Parameter()] [SecureString]$ApiKey, [Parameter()] [Switch]$ServerOnly ) function SetShlinkServer { param ( [Parameter()] [String]$Server ) if ($Server -notmatch '^http[s]?:\/\/') { Write-Warning ("Rewriting Shlink server address to be 'https://{0}' instead of using http://. To use HTTP, instead of HTTPS, specify 'http://' in your -ShlinkServer." -f $Server) -Verbose $Script:ShlinkServer = "https://{0}" -f $Server } else { $Script:ShlinkServer = $Server } } $Script:MinSupportedShlinkVersion = [Version]"2.9.0" if (-not ("System.Web.HttpUtility" -as [Type])) { Add-Type -AssemblyName "System.Web" -ErrorAction "Stop" } if ([String]::IsNullOrWhiteSpace($Server) -And -not $Script:ShlinkServer) { # User has not yet used use a -ShlinkServer paramater from any of the functions, therefore prompt SetShlinkServer -Server (Read-Host -Prompt "Enter your Shlink server URL (e.g. https://example.com)") } elseif (-not [String]::IsNullOrWhiteSpace($Server) -And $Script:ShlinkServer -ne $Server) { # User has previously used a -ShlinkServer parameter and is using it right now, and its value is different to what was used last in any of the functions # In other words, it has changed and they wish to use a different server, and that new server will be used for subsequent calls unless they specify a different server again. SetShlinkServer -Server $Server # Set this to false so we can go through the motions again of checking the new Shlink server's version number $Script:GetShlinkConnectionHasRun = $false # We no longer know if the new server's Shlink version is supported for PSShlink Clear-Variable -Name "ShlinkVersionIsSupported" -Scope "Script" -ErrorAction "SilentlyContinue" } if ([String]::IsNullOrWhitespace($ApiKey) -And -not $Script:ShlinkApiKey -And -not $ServerOnly) { # User has not yet used use a -ShlinkApiKey paramater from any of the functions, therefore prompt $Script:ShlinkApiKey = Read-Host -Prompt "Enter your Shlink server API key" -AsSecureString } elseif (-not [String]::IsNullOrWhiteSpace($ApiKey) -And $Script:ShlinkApiKey -ne $ApiKey) { # User has previously used a -ShlinkApiKey parameter and is using it right now, and its value is different to what was used last in any of the functions # In other words, it has changed - they wish to use a different API key, and that new API key will be used for subsequent calls unless they specify a different API key again. $Script:ShlinkApiKey = $ApiKey } # Query the Shlink server for version, only on the first run of GetShlinkConnection, otherwise it # will enter an infinite loop of recursion and hit the PowerShell recursion limit. I want a user # experience of being warned each time they use a function on an unsupported Shlink server version. if (-not $Script:GetShlinkConnectionHasRun) { $Script:GetShlinkConnectionHasRun = $true $Script:ShlinkVersion = Get-ShlinkServer -ShlinkServer $Script:ShlinkServer -ErrorAction "SilentlyContinue" | Select-Object -ExpandProperty Version if (-not $Script:ShlinkVersion) { $Script:GetShlinkConnectionHasRun = $false Write-Error -Message ("Could not determine the version of Shlink on '{0}'" -f $Script:ShlinkServer) -Category "InvalidData" -TargetObject $Script:ShlinkServer -ErrorAction "Stop" } elseif ([Version]$Script:ShlinkVersion -lt [Version]$Script:MinSupportedShlinkVersion) { $Script:ShlinkVersionIsSupported = $false } else { $Script:ShlinkVersionIsSupported = $true } } if ($Script:ShlinkVersionIsSupported -eq $false -And $Script:ShlinkVersion) { Write-Warning -Message ("PSShlink supports Shlink {0} or newer, your Shlink server is {1}. Some functions may not work as intended. Consider upgrading your Shlink instance." -f $Script:MinSupportedShlinkVersion, $Script:ShlinkVersion) } } function InvokeShlinkRestMethod { [CmdletBinding()] param ( [Parameter()] [String]$Server = $Script:ShlinkServer, [Parameter()] [SecureString]$ApiKey = $Script:ShlinkApiKey, [Parameter()] [Microsoft.PowerShell.Commands.WebRequestMethod]$Method = 'GET', [Parameter(Mandatory)] [String]$Endpoint, [Parameter()] [String]$Path, # Default value set where no Query parameter is passed because we still need the object for pagination later [Parameter()] [System.Collections.Specialized.NameValueCollection]$Query = [System.Web.HttpUtility]::ParseQueryString(''), [Parameter()] [hashtable]$Body, [Parameter()] [String[]]$PropertyTree, [Parameter()] [Int]$Page, [Parameter()] [String]$PSTypeName ) $Params = @{ Method = $Method Uri = "{0}/rest/v2/{1}" -f $Server, $Endpoint ContentType = "application/json" Headers = @{"X-Api-Key" = [PSCredential]::new("none", $ApiKey).GetNetworkCredential().Password} ErrorAction = "Stop" ErrorVariable = "InvokeRestMethodError" } if ($PSBoundParameters.ContainsKey("Path")) { $Params["Uri"] = "{0}/{1}" -f $Params["Uri"], $Path } # Preserve the URI which does not contain any query parameters for the pagination URI building later $QuerylessUri = $Params["Uri"] if ($PSBoundParameters.ContainsKey("Query")) { $Params["Uri"] = "{0}?{1}" -f $Params["Uri"], $Query.ToString() } if ($PSBoundParameters.ContainsKey("Body")) { $Params["Body"] = $Body | ConvertTo-Json } $Result = do { try { Write-Verbose ("Body: {0}" -f $Params["Body"]) $Data = Invoke-RestMethod @Params } catch { # The web exception class is different for Core vs Windows if ($InvokeRestMethodError.ErrorRecord.Exception.GetType().FullName -match "HttpResponseException|WebException") { $ExceptionMessage = $InvokeRestMethodError.Message | ConvertFrom-Json | Select-Object -ExpandProperty detail $ErrorId = "{0}{1}" -f [Int][System.Net.HttpStatusCode]$InvokeRestMethodError.ErrorRecord.Exception.Response.StatusCode, [String][System.Net.HttpStatusCode]$InvokeRestMethodError.ErrorRecord.Exception.Response.StatusCode switch -Regex ($InvokeRestMethodError.ErrorRecord.Exception.Response.StatusCode) { "Unauthorized" { $Exception = [System.UnauthorizedAccessException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::AuthenticationError, $Params['Uri'] ) } "BadRequest|Conflict" { $Exception = [System.ArgumentException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::InvalidArgument, $Params['Uri'] ) } "NotFound" { $Exception = [System.Management.Automation.ItemNotFoundException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::ObjectNotFound, $Params['Uri'] ) } "ServiceUnavailable" { $Exception = [System.InvalidOperationException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::ResourceUnavailable, $Params['Uri'] ) } default { $Exception = [System.InvalidOperationException]::new($ExceptionMessage) $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( $Exception, $ErrorId, [System.Management.Automation.ErrorCategory]::InvalidOperation, $Params['Uri'] ) } } $PSCmdlet.ThrowTerminatingError($ErrorRecord) } else { $PSCmdlet.ThrowTerminatingError($_) } } $PaginationData = if ($PropertyTree) { Write-Output $Data.($PropertyTree[0]).pagination } else { Write-Output $Data.pagination } if ($PaginationData) { $Query["page"] = $PaginationData.currentPage + 1 $Params["Uri"] = "{0}?{1}" -f $QuerylessUri, $Query.ToString() } Write-Output $Data } while ($PaginationData.currentPage -ne $PaginationData.pagesCount -And $PaginationData.pagesCount -ne 0) # Walk down the object's properties to return the desired property # e.g. sometimes the data is burried in tags.data or shortUrls.data etc foreach ($Property in $PropertyTree) { $Result = $Result.$Property } if ($PSBoundParameters.ContainsKey("PSTypeName")) { foreach ($item in $Result) { $item.PSTypeNames.Insert(0, $PSTypeName) } } Write-Output $Result } #endregion #region Public functions function Get-ShlinkDomains { <# .SYNOPSIS Returns the list of all domains ever used, with a flag that tells if they are the default domain .DESCRIPTION Returns the list of all domains ever used, with a flag that tells if they are the default domain .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Get-ShlinkDomains Returns the list of all domains ever used, with a flag that tells if they are the default domain .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param ( [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $Params = @{ Endpoint = "domains" PropertyTree = "domains", "data" ErrorAction = "Stop" } try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Get-ShlinkServer { <# .SYNOPSIS Checks the healthiness of the service, making sure it can access required resources. .DESCRIPTION Checks the healthiness of the service, making sure it can access required resources. https://api-spec.shlink.io/#/Monitoring/health .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Get-ShlinkServer Returns the healthiness of the service. .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param ( [Parameter()] [String]$ShlinkServer ) try { GetShlinkConnection -Server $ShlinkServer -ServerOnly } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $Uri = "{0}/rest/health" -f $Script:ShlinkServer try { Invoke-RestMethod -Uri $Uri -ErrorAction "Stop" } catch { Write-Error -ErrorRecord $_ } } function Get-ShlinkTags { <# .SYNOPSIS Returns the list of all tags used in any short URL, including stats and ordered by name. .DESCRIPTION Returns the list of all tags used in any short URL, including stats and ordered by name. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Get-ShlinkTags Returns the list of all tags used in any short URL, including stats and ordered by name. .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param ( [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $QueryString = [System.Web.HttpUtility]::ParseQueryString('') $QueryString.Add("withStats", "true") $Params = @{ Endpoint = "tags" PropertyTree = "tags", "stats" } $Params["Query"] = $QueryString try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Get-ShlinkUrl { <# .SYNOPSIS Get details of all short codes, or just one. .DESCRIPTION Get details of all short codes, or just one. Various filtering options are available from the API to ambigiously search for short codes. .PARAMETER ShortCode The name of the short code you wish to search for. For example, if the short URL is "https://example.com/new-url" then the short code is "new-url". .PARAMETER Domain The domain (excluding schema) associated with the short code you wish to search for. For example, "example.com" is an acceptable value. This is useful if your Shlink instance is responding/creating short URLs for multiple domains. .PARAMETER SearchTerm The search term to search for a short code with. .PARAMETER Tags One or more tags can be passed to find short codes using said tag(s). .PARAMETER OrderBy Order the results returned by "longUrl", "shortCode", "dateCreated", or "visits". The default sort order is in ascending order. .PARAMETER StartDate A datetime object to search for short codes where its start date is equal or greater than this value. If a start date is not configured for the short code(s), this filters on the dateCreated property. .PARAMETER EndDate A datetime object to search for short codes where its end date is equal or less than this value. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Get-ShlinkUrl Returns all short codes with no filtering applied. .EXAMPLE PS C:\> Get-ShlinkUrl -ShortCode "profile" Returns the short code "profile". .EXAMPLE PS C:\> Get-ShlinkUrl -ShortCode "profile" -Domain "example.com" Returns the short code "profile" using the domain "example.com". This is useful if your Shlink instance is responding/creating short URLs for multiple domains. .EXAMPLE PS C:\> Get-ShlinkUrl -Tags "oldwebsite", "evenolderwebsite" -OrderBy "dateCreated" Returns short codes which are associated with the tags "oldwebsite" and "evenolderwebsite". Ordered by the dateCreated property in ascending order. .EXAMPLE PS C:\> Get-ShlinkUrl -StartDate (Get-Date "2020-10-25 11:00:00") Returns short codes which have a start date of 25th October 2020 11:00:00 AM or newer. If a start date was not configured for the short code(s), this filters on the dateCreated property. .EXAMPLE PS C:\> Get-ShlinkUrl -SearchTerm "microsoft" Returns the short codes which match the search term "microsoft". .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject Objects have a PSTypeName of 'PSShlink'. #> [CmdletBinding(DefaultParameterSetName="ListShortUrls")] param ( [Parameter(Mandatory, ParameterSetName="ParseShortCode")] [String]$ShortCode, [Parameter(ParameterSetName="ParseShortCode")] [String]$Domain, [Parameter(ParameterSetName="ListShortUrls")] [String]$SearchTerm, [Parameter(ParameterSetName="ListShortUrls")] [String[]]$Tags, [Parameter(ParameterSetName="ListShortUrls")] [ValidateSet("longUrl", "shortCode", "dateCreated", "visits")] [String]$OrderBy, [Parameter(ParameterSetName="ListShortUrls")] [datetime]$StartDate, [Parameter(ParameterSetName="ListShortUrls")] [datetime]$EndDate, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) # Using begin / process / end blocks due to the way PowerShell processes all # begin blocks in the pipeline first before the process blocks. If user passed # -ShlinkServer and -ShlinkApiKey in this function and piped to something else, # e.g. Set-ShlinkUrl, and they omitted those parameters from the piped function, # they will be prompted for -ShlinkServer and -ShlinkApiKey. This is not my intended # user experience. Hence the decision to implement begin/process/end blocks here. begin { try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $QueryString = [System.Web.HttpUtility]::ParseQueryString('') } process { $Params = @{ Endpoint = "short-urls" PSTypeName = "PSShlink" ErrorACtion = "Stop" } switch ($PSCmdlet.ParameterSetName) { "ParseShortCode" { switch ($PSBoundParameters.Keys) { "ShortCode" { $Params["Path"] = $ShortCode } "Domain" { $QueryString.Add("domain", $Domain) } } } "ListShortUrls" { $Params["PropertyTree"] = "shortUrls", "data" switch ($PSBoundParameters.Keys) { "Tags" { foreach ($Tag in $Tags) { $QueryString.Add("tags[]", $Tag) } } "SearchTerm" { $QueryString.Add("searchTerm", $SearchTerm) } "OrderBy" { $QueryString.Add("orderBy", $OrderBy) } "StartDate" { $QueryString.Add("startDate", (Get-Date $StartDate -Format "yyyy-MM-ddTHH:mm:sszzzz")) } "EndDate" { $QueryString.Add("endDate", (Get-Date $EndDate -Format "yyyy-MM-ddTHH:mm:sszzzz")) } } } } $Params["Query"] = $QueryString try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } end { } } function Get-ShlinkVisits { <# .SYNOPSIS Get details of visits for a Shlink server, short codes or tags. .DESCRIPTION Get details of visits for a Shlink server, short codes or tags. .PARAMETER ShortCode The name of the short code you wish to return the visits data for. For example, if the short URL is "https://example.com/new-url" then the short code is "new-url". .PARAMETER Tag The name of the tag you wish to return the visits data for. .PARAMETER Domain The domain (excluding schema) associated with the short code you wish to search for. For example, "example.com" is an acceptable value. This is useful if your Shlink instance is responding/creating short URLs for multiple domains. .PARAMETER StartDate A datetime object to filter the visit data where the start date is equal or greater than this value. .PARAMETER EndDate A datetime object to filter the visit data where its end date is equal or less than this value. .PARAMETER ExcludeBots Exclude visits from bots or crawlers. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Get-ShlinkVists Returns the overall visit count for your Shlink server .EXAMPLE PS C:\> Get-ShlinkVisits -ShortCode "profile" Returns all visit data associated with the short code "profile" .EXAMPLE PS C:\> Get-ShlinkVisits -Tag "oldwebsite" Returns all the visit data for all short codes asociated with the tag "oldwebsite" .EXAMPLE PS C:\> Get-ShlinkVisits -ShortCode "profile" -StartDate (Get-Date "2020-11-01") -EndDate (Get-Date "2020-12-01") Returns all visit data associated with the short code "profile" for the whole of November 2020 .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding(DefaultParameterSetName="Server")] param ( [Parameter(Mandatory, ParameterSetName="ShortCode")] [String]$ShortCode, [Parameter(Mandatory, ParameterSetName="Tag")] [String]$Tag, [Parameter(ParameterSetName="ShortCode")] [Parameter(ParameterSetName="Tag")] [String]$Domain, [Parameter(ParameterSetName="ShortCode")] [Parameter(ParameterSetName="Tag")] [datetime]$StartDate, [Parameter(ParameterSetName="ShortCode")] [Parameter(ParameterSetName="Tag")] [datetime]$EndDate, [Parameter(ParameterSetName="ShortCode")] [Parameter(ParameterSetName="Tag")] [Switch]$ExcludeBots, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $QueryString = [System.Web.HttpUtility]::ParseQueryString('') $Params = @{ PropertyTree = @("visits") } switch -Regex ($PSCmdlet.ParameterSetName) { "Server" { $Params["Endpoint"] = "visits" } "ShortCode|Tag" { $Params["PropertyTree"] += "data" $Params["PSTypeName"] = "PSShlinkVisits" switch ($PSBoundParameters.Keys) { "Domain" { $QueryString.Add("domain", $Domain) } "StartDate" { $QueryString.Add("startDate", (Get-Date $StartDate -Format "yyyy-MM-ddTHH:mm:sszzzz")) } "EndDate" { $QueryString.Add("endDate", (Get-Date $EndDate -Format "yyyy-MM-ddTHH:mm:sszzzz")) } "ExcludeBots" { $QueryString.Add("excludeBots", "true") } } } "ShortCode" { $Params["Endpoint"] = "short-urls/{0}/visits" -f $ShortCode } "Tag" { $Params["Endpoint"] = "tags/{0}/visits" -f $Tag } } $Params["Query"] = $QueryString try { $Result = InvokeShlinkRestMethod @Params # I figured it would be nice to add the Server property so it is immediately clear # the server's view count is returned when no parameters are used if ($PSCmdlet.ParameterSetName -eq "Server") { [PSCustomObject]@{ Server = $Script:ShlinkServer visitsCount = $Result.visitsCount } } else { $Result } } catch { Write-Error -ErrorRecord $_ } } function Get-ShlinkVisitsOrphan { <# .SYNOPSIS Get the list of visits to invalid short URLs, the base URL or any other 404. .DESCRIPTION Get the list of visits to invalid short URLs, the base URL or any other 404. .PARAMETER StartDate A datetime object to filter the visit data where the start date is equal or greater than this value. .PARAMETER EndDate A datetime object to filter the visit data where its end date is equal or less than this value. .PARAMETER ExcludeBots Exclude visits from bots or crawlers. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Get-ShlinkVisitsOrphan Get the list of visits to invalid short URLs, the base URL or any other 404. .EXAMPLE PS C:\> Get-ShlinkVisitsOrphan -StartDate (Get-Date "2020-11-01") -EndDate (Get-Date "2020-12-01") -ExcludeBots Get the list of visits to invalid short URLs, the base URL or any other 404, for the whole of November and excluding bots/crawlers. .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param ( [Parameter()] [datetime]$StartDate, [Parameter()] [datetime]$EndDate, [Parameter()] [Switch]$ExcludeBots, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $QueryString = [System.Web.HttpUtility]::ParseQueryString('') $Params = @{ Endpoint = "visits" Path = "orphan" PropertyTree = "visits", "data" } switch ($PSBoundParameters.Keys) { "StartDate" { $QueryString.Add("startDate", (Get-Date $StartDate -Format "yyyy-MM-ddTHH:mm:sszzzz")) } "EndDate" { $QueryString.Add("endDate", (Get-Date $EndDate -Format "yyyy-MM-ddTHH:mm:sszzzz")) } "ExcludeBots" { $QueryString.Add("excludeBots", "true") } } $Params["Query"] = $QueryString try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function New-ShlinkTag { <# .SYNOPSIS Creates one or more new tags on your Shlink server .DESCRIPTION Creates one or more new tags on your Shlink server .PARAMETER Tags Name(s) for your new tag(s) .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> New-ShlinkTag -Tags "oldwebsite","newwebsite","misc" Creates the following new tags on your Shlink server: "oldwebsite","newwebsite","misc" .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param ( [Parameter(Mandatory)] [String[]]$Tags, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $Params = @{ Endpoint = "tags" Method = "POST" Body = @{ tags = @($Tags) } PropertyTree = "tags", "data" ErrorAction = "Stop" } try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } finally { Write-Warning -Message "As of Shlink 2.4.0, this endpoint is deprecated. New tags are automatically created when you specify them in the -Tags parameter with New-ShlinkUrl. At some point, this function may be removed from PSShlink." } } function New-ShlinkUrl { <# .SYNOPSIS Creates a new Shlink short code on your Shlink server. .DESCRIPTION Creates a new Shlink short code on your Shlink server. .PARAMETER LongUrl Define the long URL for the new short code. .PARAMETER CustomSlug Define a custom slug for the new short code. .PARAMETER Tags Associate tag(s) with the new short code. .PARAMETER ValidSince Define a "valid since" date with the new short code. .PARAMETER ValidUntil Define a "valid until" date with the new short code. .PARAMETER MaxVisits Set the maximum number of visits allowed for the new short code. .PARAMETER Title Define a title with the new short code. .PARAMETER Domain Associate a domain with the new short code to be something other than the default domain. This is useful if your Shlink instance is responding/creating short URLs for multiple domains. .PARAMETER ShortCodeLength Set the length of your new short code other than the default. .PARAMETER FindIfExists Specify this switch to first search and return the data about an existing short code that uses the same long URL if one exists. .PARAMETER ValidateUrl Control long URL validation while creating the short code. .PARAMETER ForwardQuery Forwards UTM query parameters to the long URL if any were passed to the short URL. .PARAMETER Crawlable Set short URLs as crawlable, making them be listed in the robots.txt as Allowed. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> New-ShlinkUrl -LongUrl "https://google.com" Will generate a new short code with the long URL of "https://google.com", using your Shlink server's default for creating new short codes, and return all the information about the new short code. .EXAMPLE PS C:\> New-ShlinkUrl -LongUrl "https://google.com" -CustomSlug "mygoogle" -Tags "search-engine" -ValidSince (Get-Date "2020-11-01") -ValidUntil (Get-Date "2020-11-30") -MaxVisits 99 -FindIfExists Will generate a new short code with the long URL of "https://google.com" using the custom slug "search-engine". The default domain for the Shlink server will be used. The link will only be valid for November 2020. The link will only work for 99 visits. If a duplicate short code is found using the same long URL, another is not made and instead data about the existing short code is returned. .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param ( [Parameter(Mandatory)] [String]$LongUrl, [Parameter()] [String]$CustomSlug, [Parameter()] [String[]]$Tags, [Parameter()] [datetime]$ValidSince, [Parameter()] [datetime]$ValidUntil, [Parameter()] [Int]$MaxVisits, [Parameter()] [String]$Title, [Parameter()] [String]$Domain, [Parameter()] [Int]$ShortCodeLength, [Parameter()] [Bool]$FindIfExists, [Parameter()] [Bool]$ValidateUrl, [Parameter()] [Bool]$ForwardQuery, [Parameter()] [Bool]$Crawlable, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $Params = @{ Endpoint = "short-urls" Method = "POST" Body = @{ longUrl = $LongUrl } ErrorAction = "Stop" } switch ($PSBoundParameters.Keys) { "CustomSlug" { $Params["Body"]["customSlug"] = $CustomSlug } "Tags" { $Params["Body"]["tags"] = @($Tags) } "ValidSince" { $Params["Body"]["validSince"] = (Get-Date $ValidSince -Format "yyyy-MM-ddTHH:mm:sszzzz") } "ValidUntil" { $Params["Body"]["validUntil"] = (Get-Date $ValidUntil -Format "yyyy-MM-ddTHH:mm:sszzzz") } "MaxVisits" { $Params["Body"]["maxVisits"] = $MaxVisits } "Domain" { $Params["Body"]["domain"] = $Domain } "Title" { $Params["Body"]["title"] = $Title } "ShortCodeLength" { $Params["Body"]["shortCodeLength"] = $ShortCodeLength } "FindIfExists" { $Params["Body"]["findIfExists"] = $FindIfExists } "ValidateUrl" { $Params["Body"]["validateUrl"] = $ValidateUrl } "ForwardQuery" { $Params["Body"]["forwardQuery"] = $ForwardQuery } "Crawlable" { $Params["Body"]["crawlable"] = $Crawlable } } try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Remove-ShlinkTag { <# .SYNOPSIS Remove a tag from an existing Shlink server. .DESCRIPTION Remove a tag from an existing Shlink server. .PARAMETER Tags Name(s) of the tag(s) you want to remove. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Remove-ShlinkTag -Tags "oldwebsite" -WhatIf Reports what would happen if the command was invoked, because the -WhatIf parameter is present. .EXAMPLE PS C:\> Remove-ShlinkTag -Tags "oldwebsite", "newwebsite" Removes the following tags from the Shlink server: "oldwebsite", "newwebsite" .EXAMPLE PS C:\> "tag1","tag2" | Remove-ShlinkTag Removes "tag1" and "tag2" from your Shlink instance. .EXAMPLE PS C:\> Get-ShlinkUrl -ShortCode "profile" | Remove-ShlinkTag Removes all the tags which are associated with the short code "profile" from the Shlink instance. .INPUTS System.String[] Used for the -Tags parameter. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [String[]]$Tags, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) begin { try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } # Gather all tags and check if any of the user's desired tag(s) to delete # are currently an existing tag within the process / for loop later. # This is because the REST API does not produce any kind of feedback if the # user attempts to delete a tag which does not exist. $AllTags = Get-ShlinkTags } process { $QueryString = [System.Web.HttpUtility]::ParseQueryString('') foreach ($Tag in $Tags) { if ($AllTags.tag -notcontains $Tag) { $WriteErrorSplat = @{ Message = "Tag '{0}' does not exist on Shlink server '{1}'" -f $Tag, $Script:ShlinkServer Category = "ObjectNotFound" TargetObject = $Tag } Write-Error @WriteErrorSplat continue } else { $QueryString.Add("tags[]", $Tag) } $Params = @{ Endpoint = "tags" Method = "DELETE" Query = $QueryString ErrorAction = "Stop" } if ($PSCmdlet.ShouldProcess( ("Would delete tag '{0}' from Shlink server '{1}'" -f ([String]::Join("', '", $Tags)), $Script:ShlinkServer), "Are you sure you want to continue?", ("Removing tag '{0}' from Shlink server '{1}'" -f ([String]::Join("', '", $Tags)), $Script:ShlinkServer))) { try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } } } end { } } function Remove-ShlinkUrl { <# .SYNOPSIS Removes a short code from the Shlink server .DESCRIPTION Removes a short code from the Shlink server .PARAMETER ShortCode The name of the short code you wish to remove from the Shlink server. .PARAMETER Domain The domain associated with the short code you wish to remove from the Shlink server. This is useful if your Shlink instance is responding/creating short URLs for multiple domains. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Remove-ShlinkUrl -ShortCode "profile" -WhatIf Reports what would happen if the command was invoked, because the -WhatIf parameter is present. .EXAMPLE PS C:\> Remove-ShlinkUrl -ShortCode "profile" -Domain "example.com" Removes the short code "profile" associated with the domain "example.com" from the Shlink server. .EXAMPLE PS C:\> Get-ShlinkUrl -SearchTerm "oldwebsite" | Remove-ShlinkUrl Removes all existing short codes which match the search term "oldwebsite". .EXAMPLE PS C:\> "profile", "house" | Remove-ShlinkUrl Removes the short codes "profile" and "house" from the Shlink instance. .INPUTS System.String[] Used for the -ShortCode parameter. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [String[]]$ShortCode, [Parameter()] [String]$Domain, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) begin { try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } } process { foreach ($Code in $ShortCode) { $Params = @{ Endpoint = "short-urls" Path = $Code Method = "DELETE" ErrorAction = "Stop" } $WouldMessage = "Would delete short code '{0}' from Shlink server '{1}'" -f $Code, $Script:ShlinkServer $RemovingMessage = "Removing short code '{0}' from Shlink server '{1}'" -f $Code, $Script:ShlinkServer if ($PSBoundParameters.ContainsKey("Domain")) { $QueryString = [System.Web.HttpUtility]::ParseQueryString('') $QueryString.Add("domain", $Domain) $Params["Query"] = $QueryString $WouldMessage = $WouldMessage -replace "from Shlink server", ("for domain '{0}'" -f $Domain) $RemovingMessage = $RemovingMessage -replace "from Shlink server", ("for domain '{0}'" -f $Domain) } if ($PSCmdlet.ShouldProcess( $WouldMessage, "Are you sure you want to continue?", $RemovingMessage)) { try { $null = InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } } } end { } } function Save-ShlinkUrlQrCode { <# .SYNOPSIS Save a QR code to disk for a short code. .DESCRIPTION Save a QR code to disk for a short code. The default size of images is 300x300 and the default file type is png. The default folder for files to be saved to is $HOME\Downloads. The naming convention for the saved files is as follows: ShlinkQRCode_<shortCode>_<domain>_<size>.<format> .PARAMETER ShortCode The name of the short code you wish to create a QR code with. .PARAMETER Domain The domain which is associated with the short code you wish to create a QR code with. This is useful if your Shlink instance is responding/creating short URLs for multiple domains. .PARAMETER Path The path where you would like the save the QR code. If omitted, the default is the Downloads directory of the runner user's $Home environment variable. If the directory doesn't exist, it will be created. .PARAMETER Size Specify the pixel width you want for your generated shortcodes. The same value will be applied to the height. If omitted, the default configuration of your Shlink server is used. .PARAMETER Format Specify whether you would like your QR codes to save as .png or .svg files. If omitted, the default configuration of your Shlink server is used. .PARAMETER Margin Specify the margin/whitespace around the QR code image in pixels. If omitted, the default configuration of your Shlink server is used. .PARAMETER ErrorCorrection Specify the level of error correction you would like in the QR code. Choose from L for low, M for medium, Q for quartile, or H for high. If omitted, the default configuration of your Shlink server is used. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Save-ShlinkUrlQrCode -ShortCode "profile" -Domain "example.com" -Size 1000 -Format svg -Path "C:\temp" Saves a QR code to disk in C:\temp named "ShlinkQRCode_profile_example-com_1000.svg". It will be saved as 1000x1000 pixels and of SVG type. .EXAMPLE PS C:\> Get-ShlinkUrl -SearchTerm "someword" | Save-ShlinkUrlQrCode -Path "C:\temp" Saves QR codes for all short URLs returned by the Get-ShlinkUrl call. All files will be saved as the default values for size (300x300) and type (png). All files will be saved in "C:\temp" using the normal naming convention for file names, as detailed in the description. .INPUTS System.Management.Automation.PSObject[] Expects PSObjects with PSTypeName of 'PSTypeName', typically from Get-ShlinkUrl. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName="InputObject")] [PSTypeName('PSShlink')] [PSCustomObject[]]$InputObject, [Parameter(Mandatory, ParameterSetName="SpecifyProperties")] [String]$ShortCode, [Parameter(ParameterSetName="SpecifyProperties")] [String]$Domain, [Parameter()] [String]$Path = "{0}\Downloads" -f $home, [Parameter()] [Int]$Size, [Parameter()] [ValidateSet("png","svg")] [String]$Format, [Parameter()] [Int]$Margin, [Parameter()] [ValidateSet("L", "M", "Q", "H")] [String]$ErrorCorrection, [Parameter(ParameterSetName="SpecifyProperties")] [String]$ShlinkServer, [Parameter(ParameterSetName="SpecifyProperties")] [SecureString]$ShlinkApiKey ) begin { $QueryString = [System.Web.HttpUtility]::ParseQueryString('') switch ($PSBoundParameters.Keys) { "Size" { $QueryString.Add("size", $Size) } "Format" { $QueryString.Add("format", $Format) } "Margin" { $QueryString.Add("margin", $Margin) } "ErrorCorrection" { $QueryString.Add("errorCorrection", $ErrorCorrection) } } if ($PSCmdlet.ParameterSetName -ne "InputObject") { $Params = @{ ShortCode = $ShortCode ShlinkServer = $ShlinkServer ShlinkApiKey = $ShlinkApiKey ErrorAction = "Stop" } if ($PSBoundParameters.ContainsKey("Domain")) { $Params["Domain"] = $Domain } # Force result to be scalar, otherwise it returns as a collection of 1 element. # Thanks to Chris Dent for this being a big "ah-ha!" momemnt for me, especially # when piping stuff to Get-Member try { $Object = Get-ShlinkUrl @Params | ForEach-Object { $_ } } catch { Write-Error -ErrorRecord $_ } if ([String]::IsNullOrWhiteSpace($Object.Domain)) { # We can safely assume the ShlinkServer variable will be set due to the Get-ShlinkUrl call # i.e. if it is not, then Get-ShlinkUrl will prompt the user for it and therefore set the variable $Object.Domain = [Uri]$Script:ShlinkServer | Select-Object -ExpandProperty "Host" } $InputObject = $Object } if (-not (Test-Path $Path)) { $null = New-Item -Path $Path -ItemType Directory -ErrorAction "Stop" } } process { foreach ($Object in $InputObject) { if ([String]::IsNullOrWhiteSpace($Object.Domain)) { $Object.Domain = [Uri]$Script:ShlinkServer | Select-Object -ExpandProperty "Host" } $Params = @{ Uri = "{0}/qr-code?{1}" -f $Object.ShortUrl, $QueryString.ToString() ErrorAction = "Stop" } try { $Result = Invoke-WebRequest @Params } catch { Write-Error -ErrorRecord $_ continue } $FileType = [Regex]::Match($Result.Headers."Content-Type", "^image\/(\w+)").Groups[1].Value $FileName = "{0}\ShlinkQRCode_{1}_{2}.{3}" -f $Path, $Object.ShortCode, ($Object.Domain -replace "\.", "-"), $FileType if ($PSBoundParameters.ContainsKey("Size")) { $FileName = $FileName -replace "\.$FileType", "_$Size.$FileType" } $Params = @{ Path = $FileName Value = $Result.Content ErrorAction = "Stop" } # Non-svg formats are returned from web servers as a byte array # Set-Content also changed to accepting byte array via -Encoding parameters after PS6+, so this is for back compatibility with Windows PS. if ($Result.Content -is [System.Byte[]]) { if ($PSVersionTable.PSVersion -ge [System.Version]"6.0") { $Params["AsByteStream"] = $true } else { $Params["Encoding"] = "Byte" } } try { Set-Content @Params } catch { Write-Error -ErrorRecord $_ continue } } } end { } } function Set-ShlinkDomainRedirects { <# .SYNOPSIS Sets the URLs that you want a visitor to get redirected to for "not found" URLs for a specific domain. .DESCRIPTION Sets the URLs that you want a visitor to get redirected to for "not found" URLs for a specific domain. .PARAMETER Domain The domain (excluding schema) in which you would like to modify the redirects of. For example, "example.com" is an acceptable value. .PARAMETER BaseUrlRedirect Modify the 'BaseUrlRedirect' redirect setting of the domain. .PARAMETER Regular404Redirect Modify the 'Regular404Redirect' redirect setting of the domain. .PARAMETER InvalidShortUrlRedirect Modify the 'InvalidShortUrlRedirect' redirect setting of the domain. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Set-ShlinkDomainRedirects -Domain "example.com" -BaseUrlRedirect "https://someotheraddress.com" Modifies the redirect setting 'BaseUrlRedirect' of example.com to redirect to "https://someotheraddress.com". .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding(DefaultParameterSetName="BaseUrlRedirect")] param ( [Parameter(Mandatory)] [String]$Domain, [Parameter(ParameterSetName="BaseUrlRedirect", Mandatory)] [Parameter(ParameterSetName="Regular404Redirect")] [Parameter(ParameterSetName="InvalidShortUrlRedirect")] [String]$BaseUrlRedirect, [Parameter(ParameterSetName="BaseUrlRedirect")] [Parameter(ParameterSetName="Regular404Redirect", Mandatory)] [Parameter(ParameterSetName="InvalidShortUrlRedirect")] [String]$Regular404Redirect, [Parameter(ParameterSetName="BaseUrlRedirect")] [Parameter(ParameterSetName="Regular404Redirect")] [Parameter(ParameterSetName="InvalidShortUrlRedirect", Mandatory)] [String]$InvalidShortUrlRedirect, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $Body = @{ domain = $Domain } switch ($PSBoundParameters.Keys) { "BaseUrlRedirect" { $Body["baseUrlRedirect"] = $BaseUrlRedirect } "Regular404Redirect" { $Body["regular404Redirect"] = $Regular404Redirect } "InvalidShortUrlRedirect" { $Body["invalidShortUrlRedirect"] = $InvalidShortUrlRedirect } } $Params = @{ Endpoint = "domains" Path = "redirects" Method = "PATCH" Body = $Body ErrorAction = "Stop" } try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Set-ShlinkTag { <# .SYNOPSIS Renames an existing tag to a new value on the Shlink server. .DESCRIPTION Renames an existing tag to a new value on the Shlink server. .PARAMETER OldTagName The name of the old tag you want to change the name of. .PARAMETER NewTagName The name fo the new tag you want to the new name to be. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Set-ShlinkTag -OldTagName "oldwebsite" -NewTagName "veryoldwebsite" Updates the tag with the name "oldwebsite" to have a new name of "veryoldwebsite". .INPUTS This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param ( [Parameter(Mandatory)] [String]$OldTagName, [Parameter(Mandatory)] [String]$NewTagName, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } $Params = @{ Endpoint = "tags" Method = "PUT" Body = @{ oldName = $OldTagName newName = $NewTagName } ErrorAction = "Stop" } try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } function Set-ShlinkUrl { <# .SYNOPSIS Update an existing short code on the Shlink server. .DESCRIPTION Update an existing short code on the Shlink server. .PARAMETER ShortCode The name of the short code you wish to update. .PARAMETER LongUrl The new long URL to associate with the existing short code. .PARAMETER Tags The name of one or more tags to associate with the existing short code. Due to the architecture of Shlink's REST API, this parameter can only be used in its own parameter set. .PARAMETER ValidSince Define a new "valid since" date with the existing short code. .PARAMETER ValidUntil Define a new "valid until" date with the existing short code. .PARAMETER MaxVisits Set a new maximum visits threshold for the existing short code. .PARAMETER Domain The domain which is associated with the short code you wish to update. This is useful if your Shlink instance is responding/creating short URLs for multiple domains. .PARAMETER Title Define a title with the new short code. .PARAMETER DoNotValidateUrl Disables long URL validation while creating the short code. .PARAMETER ForwardQuery Forwards UTM query parameters to the long URL if any were passed to the short URL. .PARAMETER Crawlable Set short URLs as crawlable, making them be listed in the robots.txt as Allowed. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. .EXAMPLE PS C:\> Set-ShlinkUrl -ShortCode "profile" -LongUrl "https://github.com/codaamok" -ValidSince (Get-Date "2020-11-01") -ValidUntil (Get-Date "2020-11-30") -MaxVisits 99 Update the existing short code "profile", associated with the default domain of the Shlink server, to point to URL "https://github.com/codaamok". The link will only be valid for November 2020. The link will only work for 99 visits. .EXAMPLE PS C:\> Set-ShlinkUrl -ShortCode "profile" -Tags "powershell","pwsh" Update the existing short code "profile" to have the tags "powershell" and "pwsh" associated with it. .EXAMPLE PS C:\> Get-ShlinkUrl -SearchTerm "preview" | Set-ShlinkUrl -Tags "preview" Updates all existing short codes which match the search term "preview" to have the tag "preview". .INPUTS System.String[] Used for the -ShortCode parameter. .OUTPUTS System.Management.Automation.PSObject #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [String[]]$ShortCode, [Parameter()] [String]$LongUrl, [Parameter()] [String[]]$Tags, [Parameter()] [datetime]$ValidSince, [Parameter()] [datetime]$ValidUntil, [Parameter()] [Int]$MaxVisits, [Parameter()] [String]$Title, [Parameter(ValueFromPipelineByPropertyName)] [String]$Domain, [Parameter()] [Bool]$ValidateUrl, [Parameter()] [Bool]$ForwardQuery, [Parameter()] [Bool]$Crawlable, [Parameter()] [String]$ShlinkServer, [Parameter()] [SecureString]$ShlinkApiKey ) begin { try { GetShlinkConnection -Server $ShlinkServer -ApiKey $ShlinkApiKey } catch { Write-Error -ErrorRecord $_ -ErrorAction "Stop" } } process { $QueryString = [System.Web.HttpUtility]::ParseQueryString('') $Params = @{ Endpoint = "short-urls" Method = "PATCH" Body = @{} } foreach ($Code in $ShortCode) { $Params["Path"] = $Code switch($PSBoundParameters.Keys) { "LongUrl" { $Params["Body"]["longUrl"] = $LongUrl } "Tags" { $Params["Body"]["tags"] = @($Tags) } "ValidSince" { $Params["Body"]["validSince"] = Get-Date $ValidSince -Format "yyyy-MM-ddTHH:mm:sszzzz" } "ValidUntil" { $Params["Body"]["validUntil"] = Get-Date $ValidUntil -Format "yyyy-MM-ddTHH:mm:sszzzz" } "MaxVisits" { $Params["Body"]["maxVisits"] = $MaxVisits } "Title" { $Params["Body"]["title"] = $Title } "Domain" { # An additional null check here, and not as a validate parameter attribute, because I wanted it to be simple # to pipe to Set-ShlinkUrl where some objects have a populated, or null, domain property. # The domain property is blank for short codes if they were created to use the Shlink instance's default domain. # They are also most commonly blank on Shlink instances where there are no additional domains responding / listening. if (-not [String]::IsNullOrWhiteSpace($Domain)) { $QueryString.Add("domain", $Domain) } } "ValidateUrl" { $Params["Body"]["validateUrl"] = $ValidateUrl } "ForwardQuery" { $Params["Body"]["forwardQuery"] = $ForwardQuery } "Crawlable" { $Params["Body"]["crawlable"] = $Crawlable } } $Params["Query"] = $QueryString try { InvokeShlinkRestMethod @Params } catch { Write-Error -ErrorRecord $_ } } } end { } } #endregion |