@ -1,33 +1,126 @@
#!powershell
# This file is part of Ansible.
#
# (c)) 2015, Paul Durivage <paul.durivage@rackspace.com>, Tal Auslander <tal@cloudshare.com>
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Copyright: (c) 2015, Paul Durivage <paul.durivage@rackspace.com>, Tal Auslander <tal@cloudshare.com>
# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# WANT_JSON
# POWERSHELL_COMMON
$ErrorActionPreference = 'Stop'
Function CheckModified-File($url , $dest , $headers , $credentials , $timeout , $use_proxy , $proxy ) {
$fileLastMod = ( [ System.IO.FileInfo ] $dest ) . LastWriteTimeUtc
$webLastMod = $null
$webRequest = [ System.Net.HttpWebRequest ] :: Create ( $url )
foreach ( $header in $headers . GetEnumerator ( ) ) {
$webRequest . Headers . Add ( $header . Name , $header . Value )
}
if ( $timeout ) {
$webRequest . Timeout = $timeout * 1000
}
if ( -not $use_proxy ) {
# Ignore the system proxy settings
$webRequest . Proxy = $null
} elseif ( $proxy ) {
$webRequest . Proxy = $proxy
}
if ( $credentials ) {
$webRequest . Credentials = $credentials
}
$webRequest . Method = " HEAD "
Try {
[ System.Net.HttpWebResponse ] $webResponse = $webRequest . GetResponse ( )
$webLastMod = $webResponse . GetResponseHeader ( " Last-Modified " )
} Catch [ System.Net.WebException ] {
$result . status_code = $_ . Exception . Response . StatusCode
Fail-Json -obj $result -message " Error requesting ' $url '. $( $_ . Exception . Message ) "
} Catch {
Fail-Json -obj $result -message " Error when requesting 'Last-Modified' date from ' $url '. $( $_ . Exception . Message ) "
}
$result . status_code = $webResponse . StatusCode
$result . msg = $webResponse . StatusDescription
$webResponse . Close ( )
if ( $webLastMod -and ( ( Get-Date -Date $webLastMod ) -lt $fileLastMod ) ) {
return $false
} else {
return $true
}
}
Function Download-File($result , $url , $dest , $headers , $credentials , $timeout , $use_proxy , $proxy , $whatif ) {
# Check $dest parent folder exists before attempting download, which avoids unhelpful generic error message.
$dest_parent = Split-Path -LiteralPath $dest
if ( -not ( Test-Path -LiteralPath $dest_parent -PathType Container ) ) {
Fail-Json -obj $result -message " The path ' $dest_parent ' does not exist for destination ' $dest ', or is not visible to the current user. Ensure download destination folder exists (perhaps using win_file state=directory) before win_get_url runs. "
}
# TODO: Replace this with WebRequest
$webClient = New-Object System . Net . WebClient
foreach ( $header in $headers . GetEnumerator ( ) ) {
$webClient . Headers . Add ( $header . Name , $header . Value )
}
# FIXME: WebClient has no Timeout property ? Should be replaced with WebRequest
# if ($timeout) {
# $webClient.Timeout = $timeout * 1000
# }
if ( -not $use_proxy ) {
# Ignore the system proxy settings
$webClient . Proxy = $null
} elseif ( $proxy ) {
$webClient . Proxy = $proxy
}
if ( $credentials ) {
$webClient . Credentials = $credentials
}
Try {
if ( -not $whatif ) {
$webClient . DownloadFile ( $url , $dest )
}
$result . changed = $true
} Catch [ System.Net.WebException ] {
$result . status_code = $_ . Exception . Response . StatusCode
Fail-Json -obj $result -message " Error downloading ' $url ' to ' $dest '. $( $_ . Exception . Message ) "
} Catch {
Fail-Json -obj $result -message " Unknown error downloading ' $url ' to ' $dest '. $( $_ . Exception . Message ) "
}
# FIXME: Reimplement DownloadFile() using WebRequest so we get the real information
$result . status_code = 200
$result . msg = 'OK'
$result . dest = $dest
}
$params = Parse-Args $args -supports_check_mode $true
$check_mode = Get-AnsibleParam -obj $params -name " _ansible_check_mode " -type " bool " -default $false
$url = Get-AnsibleParam -obj $params -name " url " -type " str " -failifempty $true
$dest = Get-AnsibleParam -obj $params -name " dest " -type " path " -failifempty $true
$timeout = Get-AnsibleParam -obj $params -name " timeout " -type " int " -default 10
$headers = Get-AnsibleParam -obj $params -name " headers " -type " dict " -default @ { }
$skip_certificate_validation = Get-AnsibleParam -obj $params -name " skip_certificate_validation " -type " bool "
$validate_certs = Get-AnsibleParam -obj $params -name " validate_certs " -type " bool " -default $true
$username = Get-AnsibleParam -obj $params -name " username " -type " str "
$password = Get-AnsibleParam -obj $params -name " password " -type " str "
$url_username = Get-AnsibleParam -obj $params -name " url_username " -type " str " -aliases " username "
$url_password = Get-AnsibleParam -obj $params -name " url_password " -type " str " -aliases " password "
$use_proxy = Get-AnsibleParam -obj $params -name " use_proxy " -type " bool " -default $true
$proxy_url = Get-AnsibleParam -obj $params -name " proxy_url " -type " str "
$proxy_username = Get-AnsibleParam -obj $params -name " proxy_username " -type " str "
$proxy_password = Get-AnsibleParam -obj $params -name " proxy_password " -type " str "
@ -35,12 +128,33 @@ $force = Get-AnsibleParam -obj $params -name "force" -type "bool" -default $true
$result = @ {
changed = $false
dest = $dest
url = $url
# This is deprecated as of v2.4, remove in v2.8
win_get_url = @ {
dest = $dest
url = $url
}
}
if ( -not $use_proxy -and ( $proxy_url -or $proxy_username -or $proxy_password ) ) {
Add-Warning -obj $result -msg " Not using a proxy on request, however a 'proxy_url', 'proxy_username' or 'proxy_password' was defined. "
}
$proxy = $null
if ( $proxy_url ) {
$proxy = New-Object System . Net . WebProxy ( $proxy_url , $true )
if ( $proxy_username -and $proxy_password ) {
$proxy_credential = New-Object System . Net . NetworkCredential ( $proxy_username , $proxy_password )
$proxy . Credentials = $proxy_credential
}
}
$credentials = $null
if ( $url_username -and $url_password ) {
$credentials = New-Object System . Net . NetworkCredential ( $url_username , $url_password )
}
# If skip_certificate_validation was specified, use validate_certs
if ( $skip_certificate_validation -ne $null ) {
Add-DeprecationWarning -obj $result -message " The parameter 'skip_certificate_validation' is being replaced with 'validate_certs' " -version 2.8
@ -48,50 +162,25 @@ if ($skip_certificate_validation -ne $null) {
}
if ( -not $validate_certs ) {
[ System.Net.ServicePointManager ] :: ServerCertificateValidationCallback = { $true }
[ System.Net.ServicePointManager ] :: ServerCertificateValidationCallback = { $true }
}
Function Download-File($result , $url , $dest , $username , $password , $proxy_url , $proxy_username , $proxy_password ) {
# use last part of url for dest file name if a directory is supplied for $dest
If ( Test-Path -PathType Container $dest ) {
$url_basename = Split-Path -leaf $url
If ( $url_basename . Length -gt 0 ) {
$dest = Join-Path -Path $dest -ChildPath $url_basename
$result . win_get_url . actual_dest = $dest
}
}
# check $dest parent folder exists before attempting download, which avoids unhelpful generic error message.
$dest_parent = Split-Path -Path $dest
$result . win_get_url . dest_parent = $dest_parent
If ( -not ( Test-Path -Path $dest_parent -PathType Container ) ) {
$result . changed = $false
Fail-Json $result " The path ' $dest_parent ' does not exist for destination ' $dest ', or is not visible to the current user. Ensure download destination folder exists (perhaps using win_file state=directory) before win_get_url runs. "
}
$webClient = New-Object System . Net . WebClient
if ( $proxy_url ) {
$proxy_server = New-Object System . Net . WebProxy ( $proxy_url , $true )
if ( $proxy_username -and $proxy_password ) {
$proxy_credential = New-Object System . Net . NetworkCredential ( $proxy_username , $proxy_password )
$proxy_server . Credentials = $proxy_credential
}
$webClient . Proxy = $proxy_server
}
if ( $username -and $password ) {
$webClient . Credentials = New-Object System . Net . NetworkCredential ( $username , $password )
}
Try {
if ( -not $check_mode ) {
$webClient . DownloadFile ( $url , $dest )
}
$result . changed = $true
}
Catch {
Fail-Json $result " Error downloading $url to $dest $( $_ . Exception . Message ) "
# Use last part of url for dest file name if a directory is supplied for $dest
if ( Test-Path -LiteralPath $dest -PathType Container ) {
$uri = [ System.Uri ] $url
$basename = Split-Path -Path $uri . LocalPath -Leaf
if ( $uri . LocalPath -and $uri . LocalPath -ne '/' -and $basename ) {
$url_basename = Split-Path -Path $uri . LocalPath -Leaf
$dest = Join-Path -Path $dest -ChildPath $url_basename
} else {
$dest = Join-Path -Path $dest -ChildPath $uri . Host
}
} elseif ( ( [ System.IO.Path ] :: GetFileName ( $dest ) ) -eq '' ) {
# We have a trailing path separator
Fail-Json -obj $result -message " The destination path ' $dest ' does not exist, or is not visible to the current user. Ensure download destination folder exists (perhaps using win_file state=directory) before win_get_url runs. "
}
$result . dest = $dest
$result . win_get_url . dest = $dest
# Enable TLS1.1/TLS1.2 if they're available but disabled (eg. .NET 4.5)
$security_protcols = [ Net.ServicePointManager ] :: SecurityProtocol -bor [ Net.SecurityProtocolType ] :: SystemDefault
@ -103,45 +192,24 @@ if ([Net.SecurityProtocolType].GetMember("Tls12").Count -gt 0) {
}
[ Net.ServicePointManager ] :: SecurityProtocol = $security_protcols
If ( $force -or -not ( Test-Path -Path $dest ) ) {
Download-File -result $result -url $url -dest $dest -username $username -password $password -proxy_url $proxy_url -proxy_username $proxy_username -proxy_password $proxy_password
}
Else {
$fileLastMod = ( [ System.IO.FileInfo ] $dest ) . LastWriteTimeUtc
$webLastMod = $null
if ( $force -or -not ( Test-Path -LiteralPath $dest ) ) {
Try {
$webRequest = [ System.Net.HttpWebRequest ] :: Create ( $url )
if ( $proxy_url ) {
$proxy_server = New-Object System . Net . WebProxy ( $proxy_url , $true )
if ( $proxy_username -and $proxy_password ) {
$proxy_credential = New-Object System . Net . NetworkCredential ( $proxy_username , $proxy_password )
$proxy_server . Credentials = $proxy_credential
}
$webRequest . Proxy = $proxy_server
}
Download-File -result $result -url $url -dest $dest -credentials $credentials `
-headers $headers -timeout $timeout -use_proxy $use_proxy -proxy $proxy `
-whatif $check_mode
if ( $username -and $password ) {
$webRequest . Credentials = New-Object System . Net . NetworkCredential ( $username , $password )
}
} else {
$webRequest . Method = " HEAD "
[ System.Net.HttpWebResponse ] $webResponse = $webRequest . GetResponse ( )
$is_modified = CheckModified-File -result $result -url $url -dest $dest -credentials $credentials `
-headers $headers -timeout $timeout -use_proxy $use_proxy -proxy $proxy
$webLastMod = $webResponse . GetResponseHeader ( " Last-Modified " )
$webResponse . Close ( )
}
Catch {
Fail-Json $result " Error when requesting Last-Modified date from $url $( $_ . Exception . Message ) "
}
if ( $is_modified ) {
If ( ( $webLastMod ) -and ( ( Get-Date -Date $webLastMod ) -lt $fileLastMod ) ) {
$result . changed = $false
} Else {
Download-File -result $result -url $url -dest $dest -username $username -password $password -proxy_url $proxy_url -proxy_username $proxy_username -proxy_password $proxy_password
}
Download-File -result $result -url $url -dest $dest -credentials $credentials `
-headers $headers -timeout $timeout -use_proxy $use_proxy -proxy $proxy `
-whatif $check_mode
}
}
Exit-Json $result