|  | 
|  | 1 | +function Expand-ShortURL { | 
|  | 2 | +    <# | 
|  | 3 | +    .SYNOPSIS | 
|  | 4 | +    Expand shortened URLs to reveal their destination. | 
|  | 5 | +
 | 
|  | 6 | +    .DESCRIPTION | 
|  | 7 | +    Expands shortened URLs by revealing the Location header of the destination address. This function handles | 
|  | 8 | +    single-level redirects only and does not automatically resolve nested shorteners. | 
|  | 9 | +
 | 
|  | 10 | +    .PARAMETER URL | 
|  | 11 | +    The shortened URL to expand. Can be provided as a string or URI object. | 
|  | 12 | +
 | 
|  | 13 | +    .OUTPUTS | 
|  | 14 | +    System.String | 
|  | 15 | +    Returns the expanded URL as a string, or the original URL if no redirect is found. | 
|  | 16 | +
 | 
|  | 17 | +    .EXAMPLE | 
|  | 18 | +    Expand-ShortURL -URL "https://bit.ly/3example" | 
|  | 19 | +
 | 
|  | 20 | +    Expands a bit.ly shortened URL to its full destination. | 
|  | 21 | +
 | 
|  | 22 | +    .EXAMPLE | 
|  | 23 | +    "https://tinyurl.com/example", "https://bit.ly/another" | Expand-ShortURL | 
|  | 24 | +
 | 
|  | 25 | +    Demonstrates pipeline usage to expand multiple URLs. | 
|  | 26 | +
 | 
|  | 27 | +    .NOTES | 
|  | 28 | +    Inspired by @md's concept at https://xkln.net/blog/expanding-shortened-urls-with-powershell/ | 
|  | 29 | +
 | 
|  | 30 | +    This function uses HTTP HEAD requests with zero redirects to capture the Location header. | 
|  | 31 | +    Some URL shorteners may require different approaches or may block automated requests. | 
|  | 32 | +
 | 
|  | 33 | +    .LINK | 
|  | 34 | +    https://xkln.net/blog/expanding-shortened-urls-with-powershell/ | 
|  | 35 | +    #> | 
|  | 36 | +    [CmdletBinding()] | 
|  | 37 | +    [Alias('ExpandUrl')] | 
|  | 38 | +    param ( | 
|  | 39 | +        # The shortened URL to expand | 
|  | 40 | +        [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] | 
|  | 41 | +        [ValidateNotNullOrEmpty()] | 
|  | 42 | +        [Alias('URI', 'Link')] | 
|  | 43 | +        [string]$URL | 
|  | 44 | +    ) | 
|  | 45 | + | 
|  | 46 | +    process { | 
|  | 47 | +        Write-Verbose "Attempting to expand URL: $URL" | 
|  | 48 | + | 
|  | 49 | +        try { | 
|  | 50 | +            # Ensure URL has a scheme | 
|  | 51 | +            if ($URL -notmatch '^https?://' -and $URL -notmatch '^http?://') { | 
|  | 52 | +                $URL = "https://$URL" | 
|  | 53 | +                Write-Verbose "Added HTTPS scheme to URL: $URL" | 
|  | 54 | +            } | 
|  | 55 | + | 
|  | 56 | +            # Try to get redirect location using HEAD request with zero redirects | 
|  | 57 | +            $Response = Invoke-WebRequest -Uri $URL -Method Head -MaximumRedirection 0 -ErrorAction Stop | 
|  | 58 | + | 
|  | 59 | +            # If we get here, there was no redirect | 
|  | 60 | +            Write-Verbose "No redirect found for: $URL" | 
|  | 61 | +            return $URL | 
|  | 62 | + | 
|  | 63 | +        } catch [System.Net.WebException] { | 
|  | 64 | +            # Check if this is a redirect response (3xx status codes) | 
|  | 65 | +            if ($_.Exception.Response -and $_.Exception.Response.StatusCode -match '^3\d\d$') { | 
|  | 66 | +                $ExpandedUrl = $_.Exception.Response.Headers.Location | 
|  | 67 | +                if ($ExpandedUrl) { | 
|  | 68 | +                    Write-Verbose "Expanded URL: $URL -> $ExpandedUrl" | 
|  | 69 | +                    return $ExpandedUrl.ToString() | 
|  | 70 | +                } | 
|  | 71 | +            } | 
|  | 72 | + | 
|  | 73 | +            Write-Warning "Failed to expand URL '$URL': $($_.Exception.Message)" | 
|  | 74 | +            return $URL | 
|  | 75 | + | 
|  | 76 | +        } catch { | 
|  | 77 | +            Write-Warning "Unexpected error expanding URL '$URL': $($_.Exception.Message)" | 
|  | 78 | +            return $URL | 
|  | 79 | +        } | 
|  | 80 | +    } | 
|  | 81 | +} | 
0 commit comments