## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx #region Function Get-GitRepositoryRelease Function Get-GitRepositoryRelease { <# .SYNOPSIS Download release assets from public and private Git repositories. .DESCRIPTION Leverages the GitHub API (by default) to list and download release assets from public and private Git repositories. Supports filtering by release version (latest, specific tag, or pre-release) and asset name patterns. The function can download assets while maintaining logging through the $WriteLogMessage scriptblock from the toolkit. .PARAMETER AccessToken A personal access token (PAT) for authenticating with the Git API. Required for private repositories. For GitHub, generate tokens at https://github.com/settings/tokens with appropriate repository access scopes. .PARAMETER BaseURI The base URI of the Git hosting API. Defaults to 'https://api.github.com' if not specified. Can be customized for GitHub Enterprise, Gitea, GitLab, or other compatible Git hosting platforms. .PARAMETER RepositoryOwner The username or organization name that owns the repository. This is the first part of the repository URL path. .PARAMETER RepositoryName The name of the repository to access. This is the second part of the repository URL path. .PARAMETER ReleaseTag The specific release tag to download. If not specified, defaults to 'latest'. Use 'latest' for the most recent non-prerelease version, or specify a tag name like 'v1.0.0'. .PARAMETER IncludePreRelease When specified, includes pre-release versions when searching for releases. When combined with ReleaseTag 'latest', returns the most recent release including pre-releases. .PARAMETER AssetInclusionExpression A regular expression pattern to include only matching asset file names. Defaults to '(.*)' to include all assets. Example: '\.exe$' for only .exe files. .PARAMETER AssetExclusionExpression A regular expression pattern to exclude matching asset file names. Defaults to '^$' to exclude nothing (only matches empty strings). Example: '\.sig$|\.sha256$' to exclude signature files. .PARAMETER DestinationDirectory The local directory path where assets will be downloaded. Defaults to '$Env:Windir\Temp\GitHubReleases\$RepositoryName' if not specified. .PARAMETER Download When specified, downloads the assets to the DestinationDirectory. Without this switch, only a listing is returned. .PARAMETER Force When specified, overwrites existing files at the destination. Without this switch, existing files are skipped. .PARAMETER ContinueOnError When specified, continues processing remaining items if an error occurs. Without this switch, errors are terminating. .EXAMPLE Download the latest Win32-OpenSSH MSI release, dynamically filtering for the current OS architecture. # Determine the OS architecture for asset filtering (Win32, Win64, ARM64) $OSArchitectureMap = @{ 'AMD64' = 'Win64' 'x86' = 'Win32' 'ARM64' = 'ARM64' 'ARM' = 'ARM' } $ProcessorArchitecture = [System.Environment]::GetEnvironmentVariable('PROCESSOR_ARCHITECTURE') $OSArchitectureFilter = $OSArchitectureMap[$ProcessorArchitecture] # Build regex pattern to match MSI for the current architecture (e.g., "OpenSSH-Win64.*\.msi$") $AssetPattern = "OpenSSH-$($OSArchitectureFilter).*\.msi$" $GetGitRepositoryReleaseParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $GetGitRepositoryReleaseParameters.BaseURI = 'https://api.github.com' $GetGitRepositoryReleaseParameters.RepositoryOwner = "PowerShell" $GetGitRepositoryReleaseParameters.RepositoryName = "Win32-OpenSSH" $GetGitRepositoryReleaseParameters.ReleaseTag = "latest" $GetGitRepositoryReleaseParameters.AssetInclusionExpression = $AssetPattern $GetGitRepositoryReleaseParameters.DestinationDirectory = "$($Env:Userprofile)\Downloads\OpenSSH-Release" $GetGitRepositoryReleaseParameters.Download = $True $GetGitRepositoryReleaseParameters.Force = $False $GetGitRepositoryReleaseParameters.ContinueOnError = $False $GetGitRepositoryReleaseParameters.Verbose = $True $GetGitRepositoryReleaseResult = Get-GitRepositoryRelease @GetGitRepositoryReleaseParameters Write-Output -InputObject ($GetGitRepositoryReleaseResult) .EXAMPLE Download a specific release version from the microsoft/winget-cli repository. $GetGitRepositoryReleaseParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $GetGitRepositoryReleaseParameters.BaseURI = 'https://api.github.com' $GetGitRepositoryReleaseParameters.RepositoryOwner = "microsoft" $GetGitRepositoryReleaseParameters.RepositoryName = "winget-cli" $GetGitRepositoryReleaseParameters.ReleaseTag = "v1.7.10661" $GetGitRepositoryReleaseParameters.AssetInclusionExpression = '\.msixbundle$' $GetGitRepositoryReleaseParameters.DestinationDirectory = "$($Env:Userprofile)\Downloads\WinGet-Release" $GetGitRepositoryReleaseParameters.Download = $True $GetGitRepositoryReleaseParameters.Force = $False $GetGitRepositoryReleaseParameters.ContinueOnError = $False $GetGitRepositoryReleaseParameters.Verbose = $True $GetGitRepositoryReleaseResult = Get-GitRepositoryRelease @GetGitRepositoryReleaseParameters Write-Output -InputObject ($GetGitRepositoryReleaseResult) .EXAMPLE Download the latest pre-release from a private repository using an access token. $GetGitRepositoryReleaseParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $GetGitRepositoryReleaseParameters.AccessToken = "YourAccessToken" $GetGitRepositoryReleaseParameters.BaseURI = 'https://api.github.com' $GetGitRepositoryReleaseParameters.RepositoryOwner = "YourOrganization" $GetGitRepositoryReleaseParameters.RepositoryName = "YourPrivateRepo" $GetGitRepositoryReleaseParameters.ReleaseTag = "latest" $GetGitRepositoryReleaseParameters.IncludePreRelease = $True $GetGitRepositoryReleaseParameters.DestinationDirectory = "$($Env:Userprofile)\Downloads\PrivateRelease" $GetGitRepositoryReleaseParameters.Download = $True $GetGitRepositoryReleaseParameters.Force = $True $GetGitRepositoryReleaseParameters.ContinueOnError = $False $GetGitRepositoryReleaseParameters.Verbose = $True $GetGitRepositoryReleaseResult = Get-GitRepositoryRelease @GetGitRepositoryReleaseParameters Write-Output -InputObject ($GetGitRepositoryReleaseResult) .NOTES This function uses the $WriteLogMessage scriptblock from the toolkit for logging. Ensure the toolkit is dot-sourced before calling this function for proper logging support. This function could also theoretically support Git repositories outside of GitHub, such as Gitea, or GitLab, provided they have compatible API endpoints. .LINK https://docs.github.com/en/rest/releases/releases .LINK https://docs.github.com/en/rest/releases/assets #> [CmdletBinding(ConfirmImpact = 'Low')] Param ( [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [System.String]$AccessToken, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [System.URI]$BaseURI, [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [System.String]$RepositoryOwner, [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [System.String]$RepositoryName, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [System.String]$ReleaseTag, [Parameter(Mandatory=$False)] [Switch]$IncludePreRelease, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [Regex]$AssetInclusionExpression, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [Regex]$AssetExclusionExpression, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [System.IO.DirectoryInfo]$DestinationDirectory, [Parameter(Mandatory=$False)] [Switch]$Download, [Parameter(Mandatory=$False)] [Switch]$Force, [Parameter(Mandatory=$False)] [Switch]$ContinueOnError ) Begin { Try { $DateTimeLogFormat = 'dddd, MMMM dd, yyyy @ hh:mm:ss.FFF tt' ###Monday, January 01, 2019 @ 10:15:34.000 AM### [ScriptBlock]$GetCurrentDateTimeLogFormat = {(Get-Date).ToString($DateTimeLogFormat)} $DateTimeMessageFormat = 'MM/dd/yyyy HH:mm:ss.FFF' ###03/23/2022 11:12:48.347### [ScriptBlock]$GetCurrentDateTimeMessageFormat = {(Get-Date).ToString($DateTimeMessageFormat)} $DateFileFormat = 'yyyyMMdd' ###20190403### [ScriptBlock]$GetCurrentDateFileFormat = {(Get-Date).ToString($DateFileFormat)} $DateTimeFileFormat = 'yyyyMMdd_HHmmss' ###20190403_115354### [ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)} $TextInfo = (Get-Culture).TextInfo $CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]' $CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters) $CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters) [ScriptBlock]$ErrorHandlingDefinition = { Param ( [Int16]$Severity, [Boolean]$ContinueOnError ) $ExceptionPropertyDictionary = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $ExceptionPropertyDictionary.Message = $_.Exception.Message $ExceptionPropertyDictionary.Category = $_.Exception.ErrorRecord.FullyQualifiedErrorID $ExceptionPropertyDictionary.Script = Try {[System.IO.Path]::GetFileName($_.InvocationInfo.ScriptName)} Catch {$Null} $ExceptionPropertyDictionary.LineNumber = $_.InvocationInfo.ScriptLineNumber $ExceptionPropertyDictionary.LinePosition = $_.InvocationInfo.OffsetInLine $ExceptionPropertyDictionary.Code = $_.InvocationInfo.Line.Trim() $ExceptionMessageList = New-Object -TypeName 'System.Collections.Generic.List[String]' ForEach ($ExceptionProperty In $ExceptionPropertyDictionary.GetEnumerator()) { Switch ($Null -ine $ExceptionProperty.Value) { {($_ -eq $True)} { $ExceptionMessageList.Add("[$($ExceptionProperty.Key): $($ExceptionProperty.Value)]") } } } $LogMessageParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $LogMessageParameters.Message = $ExceptionMessageList -Join ' ' $LogMessageParameters.Verbose = $True Switch ($Severity) { {($_ -in @(1))} {$WriteLogMessage.Invoke(1, @($LogMessageParameters.Message))} {($_ -in @(2))} {$WriteLogMessage.Invoke(2, @($LogMessageParameters.Message))} {($_ -in @(3))} {$WriteLogMessage.Invoke(3, @($LogMessageParameters.Message))} } Switch ($ContinueOnError) { {($_ -eq $False)} { Throw } } } #Determine the date and time we executed the function $FunctionStartTime = (Get-Date) [String]$FunctionName = $MyInvocation.MyCommand [System.IO.FileInfo]$InvokingScriptPath = $MyInvocation.PSCommandPath [System.IO.DirectoryInfo]$InvokingScriptDirectory = $InvokingScriptPath.Directory [System.IO.FileInfo]$FunctionPath = "$($InvokingScriptDirectory)\Functions\$($FunctionName).ps1" [System.IO.DirectoryInfo]$FunctionDirectory = "$($FunctionPath.Directory)" $WriteLogMessage.Invoke(0, @("Function `'$($FunctionName)`' is beginning. Please Wait...")) #Define Default Action Preferences $ErrorActionPreference = 'Stop' [String[]]$AvailableScriptParameters = (Get-Command -Name ($FunctionName)).Parameters.GetEnumerator() | Where-Object {($_.Value.Name -inotin $CommonParameterList)} | ForEach-Object {"-$($_.Value.Name):$($_.Value.ParameterType.Name)"} $WriteLogMessage.Invoke(0, @("Available Function Parameter(s) = $($AvailableScriptParameters -Join ', ')")) [String[]]$SuppliedScriptParameters = $PSBoundParameters.GetEnumerator() | ForEach-Object {Try {"-$($_.Key):$($_.Value.GetType().Name)"} Catch {"-$($_.Key):Unknown"}} $WriteLogMessage.Invoke(0, @("Supplied Function Parameter(s) = $($SuppliedScriptParameters -Join ', ')")) $WriteLogMessage.Invoke(0, @("Execution of $($FunctionName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))")) #region Adjust security protocol type(s) [System.Net.SecurityProtocolType]$DesiredSecurityProtocol = [System.Net.SecurityProtocolType]::TLS12 $WriteLogMessage.Invoke(0, @("Attempting to set the desired security protocol to `"$($DesiredSecurityProtocol.ToString().ToUpper())`". Please Wait...")) $Null = [System.Net.ServicePointManager]::SecurityProtocol = ($DesiredSecurityProtocol) #endregion #Define default parameter values Switch ($True) { {([String]::IsNullOrEmpty($BaseURI) -eq $True) -or ([String]::IsNullOrWhiteSpace($BaseURI) -eq $True)} { [System.URI]$BaseURI = 'https://api.github.com' } {([String]::IsNullOrEmpty($ReleaseTag) -eq $True) -or ([String]::IsNullOrWhiteSpace($ReleaseTag) -eq $True)} { [System.String]$ReleaseTag = 'latest' } {([String]::IsNullOrEmpty($DestinationDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($DestinationDirectory) -eq $True)} { [System.IO.DirectoryInfo]$DestinationDirectory = "$($Env:Windir)\Temp\GitHubReleases\$($RepositoryName)" } {([String]::IsNullOrEmpty($AssetInclusionExpression) -eq $True) -or ([String]::IsNullOrWhiteSpace($AssetInclusionExpression) -eq $True)} { [Regex]$AssetInclusionExpression = '(.*)' } {([String]::IsNullOrEmpty($AssetExclusionExpression) -eq $True) -or ([String]::IsNullOrWhiteSpace($AssetExclusionExpression) -eq $True)} { [Regex]$AssetExclusionExpression = '^$' } } #Define helper function #region Function Convert-FileSize Function Convert-FileSize { [CmdletBinding()] Param ( [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [Alias("Length")] $Size, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [Alias("DP")] [Int]$DecimalPlaces ) Try { Switch ($True) { {([String]::IsNullOrEmpty($DecimalPlaces) -eq $True) -or ([String]::IsNullOrWhiteSpace($DecimalPlaces) -eq $True)} { [Int]$DecimalPlaces = 2 } } $OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $OutputObjectProperties.Size = $Size $OutputObjectProperties.DecimalPlaces = $DecimalPlaces Switch ($Size) { {($_ -lt 1MB)} { $OutputObjectProperties.Divisor = 1KB $OutputObjectProperties.SizeUnit = 'KB' $OutputObjectProperties.SizeUnitAlias = 'Kilobytes' Break } {($_ -lt 1GB)} { $OutputObjectProperties.Divisor = 1MB $OutputObjectProperties.SizeUnit = 'MB' $OutputObjectProperties.SizeUnitAlias = 'Megabytes' Break } {($_ -lt 1TB)} { $OutputObjectProperties.Divisor = 1GB $OutputObjectProperties.SizeUnit = 'GB' $OutputObjectProperties.SizeUnitAlias = 'Gigabytes' Break } {($_ -ge 1TB)} { $OutputObjectProperties.Divisor = 1TB $OutputObjectProperties.SizeUnit = 'TB' $OutputObjectProperties.SizeUnitAlias = 'Terabytes' Break } } $OutputObjectProperties.CalculatedSize = [System.Math]::Round(($Size / $OutputObjectProperties.Divisor), $OutputObjectProperties.DecimalPlaces) $OutputObjectProperties.CalculatedSizeStr = "$($OutputObjectProperties.CalculatedSize) $($OutputObjectProperties.SizeUnit)" } Catch { Write-Error -Exception $_ } Finally { $OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties) Write-Output -InputObject ($OutputObject) } } #endregion #Create an object that will contain the functions output. $OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $OutputObjectProperties.RequestURI = $Null $OutputObjectProperties.ReleaseInfo = $Null $OutputObjectProperties.WebRequestResponseList = New-Object -TypeName 'System.Collections.Generic.List[System.Object]' } Catch { $ErrorHandlingDefinition.Invoke(2, $ContinueOnError.IsPresent) } Finally { } } Process { Try { #region Build the release request URI $RequestURIBuilder = New-Object -TypeName 'System.Text.StringBuilder' $Null = $RequestURIBuilder.Append($BaseURI.Scheme).Append('://') $Null = $RequestURIBuilder.Append($BaseURI.DnsSafeHost) $Null = $RequestURIBuilder.Append(':').Append($BaseURI.Port) $Null = $RequestURIBuilder.Append('/').Append('repos') $Null = $RequestURIBuilder.Append('/').Append($RepositoryOwner) $Null = $RequestURIBuilder.Append('/').Append($RepositoryName) $Null = $RequestURIBuilder.Append('/').Append('releases') Switch ($ReleaseTag.ToLower()) { {($_ -ieq 'latest')} { Switch ($IncludePreRelease.IsPresent) { {($_ -eq $True)} { # Get all releases and pick the first one (most recent) $Null = $RequestURIBuilder.Append('?per_page=1') } Default { # Use the /latest endpoint for non-prerelease $Null = $RequestURIBuilder.Append('/').Append('latest') } } } Default { # Specific tag requested $Null = $RequestURIBuilder.Append('/').Append('tags').Append('/').Append($ReleaseTag) } } [System.URI]$RequestURI = $RequestURIBuilder.ToString() $WriteLogMessage.Invoke(0, @("Request URI: $($RequestURI)")) #endregion $OutputObjectProperties.RequestURI = $RequestURI #region Invoke the web request to get release information $InvokeWebRequestHeaders = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $InvokeWebRequestHeaders.'Accept' = 'application/vnd.github+json' Switch ($True) { {([String]::IsNullOrEmpty($AccessToken) -eq $False) -and ([String]::IsNullOrWhiteSpace($AccessToken) -eq $False)} { $InvokeWebRequestHeaders.Authorization = "Bearer $($AccessToken)" } } $InvokeWebRequestParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $InvokeWebRequestParameters.UseBasicParsing = $True $InvokeWebRequestParameters.Uri = $RequestURI $InvokeWebRequestParameters.UseDefaultCredentials = $True $InvokeWebRequestParameters.DisableKeepAlive = $False $InvokeWebRequestParameters.TimeoutSec = 30 $InvokeWebRequestParameters.Method = 'Get' $InvokeWebRequestParameters.ContentType = 'application/vnd.github+json' $InvokeWebRequestParameters.UserAgent = [Microsoft.PowerShell.Commands.PSUserAgent]::Chrome $InvokeWebRequestParameters.Verbose = $False Switch ($InvokeWebRequestHeaders.Keys.Count -gt 0) { {($_ -eq $True)} { $InvokeWebRequestParameters.Headers = $InvokeWebRequestHeaders } } $WriteLogMessage.Invoke(0, @("Attempting to retrieve release information. Please Wait...")) $InvokeWebRequestResult = Invoke-WebRequest @InvokeWebRequestParameters $WriteLogMessage.Invoke(0, @("Status Code: $($InvokeWebRequestResult.StatusCode)")) $OutputObjectProperties.WebRequestResponseList.Add($InvokeWebRequestResult) #endregion #region Process the release response Switch ($InvokeWebRequestResult.StatusCode -in @(200)) { {($_ -eq $True)} { $WebRequestResponseContent = $InvokeWebRequestResult.Content $ReleaseData = ConvertFrom-JSON -InputObject ($WebRequestResponseContent) # Handle array response (when using per_page for pre-releases) Switch ($ReleaseData -is [System.Array]) { {($_ -eq $True)} { $ReleaseData = $ReleaseData[0] } } $ReleaseInfoProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $ReleaseInfoProperties.TagName = $ReleaseData.tag_name $ReleaseInfoProperties.Name = $ReleaseData.name $ReleaseInfoProperties.IsPreRelease = $ReleaseData.prerelease $ReleaseInfoProperties.IsDraft = $ReleaseData.draft $ReleaseInfoProperties.PublishedAt = $ReleaseData.published_at $ReleaseInfoProperties.HTMLURL = $ReleaseData.html_url $ReleaseInfoProperties.TarballURL = $ReleaseData.tarball_url $ReleaseInfoProperties.ZipballURL = $ReleaseData.zipball_url $ReleaseInfoProperties.Body = $ReleaseData.body $ReleaseInfoProperties.AssetList = New-Object -TypeName 'System.Collections.Generic.List[System.Management.Automation.PSObject]' $OutputObjectProperties.ReleaseInfo = New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($ReleaseInfoProperties) $WriteLogMessage.Invoke(0, @("Release Tag: $($OutputObjectProperties.ReleaseInfo.TagName)")) $WriteLogMessage.Invoke(0, @("Release Name: $($OutputObjectProperties.ReleaseInfo.Name)")) $WriteLogMessage.Invoke(0, @("Is Pre-Release: $($OutputObjectProperties.ReleaseInfo.IsPreRelease)")) $WriteLogMessage.Invoke(0, @("Published At: $($OutputObjectProperties.ReleaseInfo.PublishedAt)")) #region Process release assets $ReleaseAssets = $ReleaseData.assets $ReleaseAssetCount = ($ReleaseAssets | Measure-Object).Count $WriteLogMessage.Invoke(0, @("Found $($ReleaseAssetCount) asset(s) in this release.")) ForEach ($Asset In $ReleaseAssets) { $AssetName = $Asset.name # Apply inclusion/exclusion filters Switch ($AssetName -imatch $AssetInclusionExpression) { {($_ -eq $True)} { Switch ($AssetName -inotmatch $AssetExclusionExpression) { {($_ -eq $True)} { $AssetObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' $AssetObjectProperties.Name = $AssetName $AssetObjectProperties.DownloadURL = $Asset.browser_download_url -As [System.URI] $AssetObjectProperties.Size = $Asset.size -As [System.UInt64] $AssetObjectProperties.ContentType = $Asset.content_type $AssetObjectProperties.DownloadCount = $Asset.download_count $AssetObjectProperties.CreatedAt = $Asset.created_at $AssetObjectProperties.UpdatedAt = $Asset.updated_at $AssetObjectProperties.DestinationPath = [System.IO.FileInfo]"$($DestinationDirectory.FullName)\$($AssetName)" $AssetObjectProperties.DownloadStatus = 'NotAttempted' $AssetObjectProperties.DownloadStartTime = $Null $AssetObjectProperties.DownloadCompletionTime = $Null $AssetObjectProperties.DownloadDuration = $Null $AssetObject = New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($AssetObjectProperties) $OutputObjectProperties.ReleaseInfo.AssetList.Add($AssetObject) $WriteLogMessage.Invoke(0, @("Asset matched filters: $($AssetName) [Size: $((Convert-FileSize -Size $Asset.size).CalculatedSizeStr)]")) } } } } } #endregion $WriteLogMessage.Invoke(0, @("$($OutputObjectProperties.ReleaseInfo.AssetList.Count) asset(s) matched the inclusion/exclusion filters.")) } } #endregion $OutputObject = New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($OutputObjectProperties) # Convert AssetList to array if ReleaseInfo exists, otherwise ensure it's an empty array Switch ($Null -ine $OutputObject.ReleaseInfo) { {($_ -eq $True)} { Try {$OutputObject.ReleaseInfo.AssetList = $OutputObject.ReleaseInfo.AssetList.ToArray()} Catch {$OutputObject.ReleaseInfo.AssetList = @()} } } #region Download assets if requested Switch ($Download.IsPresent) { {($_ -eq $True)} { $AssetList = $OutputObject.ReleaseInfo.AssetList $AssetListCount = ($AssetList | Measure-Object).Count Switch ($AssetListCount -gt 0) { {($_ -eq $True)} { $AssetListCounter = 1 For ($AssetListIndex = 0; $AssetListIndex -lt $AssetListCount; $AssetListIndex++) { Try { $AssetItem = $AssetList[$AssetListIndex] $DownloadAssetItem = { Param ( [System.Management.Automation.PSObject]$AssetItem ) $WriteLogMessage.Invoke(0, @("Attempting to download asset $($AssetListCounter) of $($AssetListCount). Please Wait...")) $WriteLogMessage.Invoke(0, @("Asset Name: $($AssetItem.Name)")) $WriteLogMessage.Invoke(0, @("Download URL: $($AssetItem.DownloadURL)")) $WriteLogMessage.Invoke(0, @("Destination: $($AssetItem.DestinationPath)")) $WriteLogMessage.Invoke(0, @("Size: $((Convert-FileSize -Size $AssetItem.Size).CalculatedSizeStr)")) $WebClient = New-Object -TypeName 'System.Net.WebClient' Switch ([System.IO.Directory]::Exists($AssetItem.DestinationPath.Directory)) { {($_ -eq $False)} { $Null = [System.IO.Directory]::CreateDirectory($AssetItem.DestinationPath.Directory) } } $AssetItem.DownloadStatus = 'Attempted' $AssetItem.DownloadStartTime = Get-Date $Null = $WebClient.DownloadFile($AssetItem.DownloadURL, $AssetItem.DestinationPath.FullName) $AssetItem.DownloadCompletionTime = Get-Date $AssetItem.DownloadDuration = New-TimeSpan -Start ($AssetItem.DownloadStartTime) -End ($AssetItem.DownloadCompletionTime) $AssetItem.DestinationPath = New-Object -TypeName 'System.IO.FileInfo' -ArgumentList @($AssetItem.DestinationPath.FullName) $AssetItem.DownloadStatus = 'Success' $WriteLogMessage.Invoke(0, @("Download completed in $($AssetItem.DownloadDuration.TotalSeconds.ToString('F2')) second(s)")) } Switch ([System.IO.File]::Exists($AssetItem.DestinationPath)) { {($_ -eq $True)} { Switch ($Force.IsPresent) { {($_ -eq $True)} { $Null = $DownloadAssetItem.InvokeReturnAsIs($AssetItem) } Default { $WriteLogMessage.Invoke(0, @("Skipping download of asset $($AssetListCounter) of $($AssetListCount) - file already exists.")) $AssetItem.DownloadStatus = 'Skipped' } } } Default { $Null = $DownloadAssetItem.InvokeReturnAsIs($AssetItem) } } } Catch { $AssetItem.DownloadStatus = 'Failed' $ErrorHandlingDefinition.Invoke(2, $ContinueOnError.IsPresent) } Finally { $AssetListCounter++ } } } } } } #endregion } Catch { $ErrorHandlingDefinition.Invoke(2, $ContinueOnError.IsPresent) } Finally { } } End { Try { #Determine the date and time the function completed execution $FunctionEndTime = (Get-Date) $WriteLogMessage.Invoke(0, @("Execution of $($FunctionName) ended on $($FunctionEndTime.ToString($DateTimeLogFormat))")) #Log the total script execution time $FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime) $WriteLogMessage.Invoke(0, @("Function execution took $($FunctionExecutionTimespan.Hours.ToString()) hour(s), $($FunctionExecutionTimespan.Minutes.ToString()) minute(s), $($FunctionExecutionTimespan.Seconds.ToString()) second(s), and $($FunctionExecutionTimespan.Milliseconds.ToString()) millisecond(s)")) $WriteLogMessage.Invoke(0, @("Function `'$($FunctionName)`' is completed.")) } Catch { $ErrorHandlingDefinition.Invoke(2, $ContinueOnError.IsPresent) } Finally { Write-Output -InputObject ($OutputObject) } } } #endregion