RemoteArchiveDSC.psm1

enum Ensure {
    Absent
    Present
}

$checkModFileNameDefault = ".RemoteArchive"

function Get-CheckModFile {
    param(
        [Parameter(Mandatory)]
        [string]$DestinationPath,
        [Parameter(Mandatory)]
        [string]$CheckModFileName
    )

    if ($DestinationPath.EndsWith("\")) {
        $DestinationPath + $CheckModFileName
    }
    else {
        $DestinationPath + "\" + $CheckModFileName
    }
}
function Get-RemoteArchive {
    param(
        [Parameter(Mandatory)]
        [string]$DestinationPath,
        [Parameter(Mandatory)]
        [string]$URL,
        [ensure]$Ensure = "Present",
        [switch]$Force,
        [string]$CheckModFileName = $checkModFileNameDefault,
        [switch]$UseBasicParsing
    )

    if (Test-Path (Get-CheckModFile $DestinationPath $CheckModFileName)) {
        [ensure]$actualEnsure = "Present"
    }
    else {
        [ensure]$actualEnsure = "Absent"
    }

    @{
        DestinationPath = $DestinationPath
        URL             = $URL
        Ensure          = $actualEnsure
        Force           = $Force
    }
}

function Set-RemoteArchive {
    param(
        [Parameter(Mandatory)]
        [string]$DestinationPath,
        [Parameter(Mandatory)]
        [string]$URL,
        [ensure]$Ensure = "Present",
        [switch]$Force,
        [string]$CheckModFileName = $checkModFileNameDefault,
        [switch]$UseBasicParsing
    )

    if (-not (Test-RemoteArchive @PSBoundParameters)) {
        if ($Ensure -eq "Present") {
            $tempFileName = (Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())) + ".zip"
            $resp = Invoke-WebRequest -Method Get -Uri $URL -OutFile $tempFileName -ErrorVariable err -PassThru -UseBasicParsing:$UseBasicParsing
            if ($err) {
                Write-Error "Failed to download file from $URL"
                return
            }

            Expand-Archive -Path $tempFileName -DestinationPath $DestinationPath -Force:$Force -ErrorVariable 'err'
            if ($err) {
                Write-Error "Extracting archive to $DestinationPath failed"
                Remove-Item $tempFileName
                return
            }
            $resp.Headers["Last-Modified"] | Out-File -Encoding utf8 (Get-CheckModFile $DestinationPath $CheckModFileName)
        }
        else {
            Remove-Item $DestinationPath -Recurse -Force:$Force
        }
    }
}

function Test-RemoteArchive {
    param(
        [Parameter(Mandatory)]
        [string]$DestinationPath,
        [Parameter(Mandatory)]
        [string]$URL,
        [ensure]$Ensure = "Present",
        [switch]$Force,
        [string]$CheckModFileName = $checkModFileNameDefault,
        [switch]$UseBasicParsing
    )

    $resource = Get-RemoteArchive @PSBoundParameters

    if ($Ensure -ne $resource.Ensure) {
        $false
        return
    }

    if ($Ensure -eq "Present") {
        $resp = Invoke-WebRequest -Method Head -Uri $URL -ErrorVariable 'err' -UseBasicParsing:$UseBasicParsing
        if ($err) {
            Write-Error "Failed to retrieve headers from $URL"
            return
        }
        $lastModified = $resp.Headers["Last-Modified"]
        if ([System.String]::IsNullOrEmpty($lastModified)) {
            Write-Error "The url did not return a Last-Modified header. This is required"
            return
        }

        $lastModified = [System.Datetime]::ParseExact($lastModified, "R", $null) # RFC-1123
        $lastModified -le (Get-ChildItem (Get-CheckModFile $DestinationPath $CheckModFileName)).LastWriteTimeUtc
        return
    }

    $true
}


[DscResource()]
class RemoteArchive {
    [DscProperty(Key)]
    [string]$Name

    [DscProperty(Mandatory)]
    [string]$DestinationPath

    [DscProperty(Mandatory)]
    [string]$URL

    [DscProperty()]
    [ensure]$Ensure = "Present"

    [DscProperty()]
    [bool]$Force = $false

    [DscProperty()]
    [string]$CheckModFileName = $checkModFileNameDefault

    [DscProperty()]
    [bool]$UseBasicParsing

    [RemoteArchive]Get() {
        $resource = Get-RemoteArchive -DestinationPath $this.DestinationPath `
            -URL $this.URL `
            -Ensure $this.Ensure `
            -Force:$this.Force `
            -CheckModFileName $this.CheckModFileName `
            -UseBasicParsing:$this.UseBasicParsing
        return $resource
    }

    [void]Set() {
        Set-RemoteArchive -DestinationPath $this.DestinationPath `
            -URL $this.URL `
            -Ensure $this.Ensure `
            -Force:$this.Force `
            -CheckModFileName $this.CheckModFileName `
            -UseBasicParsing:$this.UseBasicParsing
    }

    [bool]Test() {
        $ok = Test-RemoteArchive -DestinationPath $this.DestinationPath `
            -URL $this.URL `
            -Ensure $this.Ensure `
            -Force:$this.Force `
            -CheckModFileName $this.CheckModFileName `
            -UseBasicParsing:$this.UseBasicParsing
        return $ok
    }
}