PowerShell Invoke-RestMethod Could not create TLS/SSL secure channel - powershell

I'm calling Invoke-RestMethod in a PowerShell script to upload a zip archive to an Artifactory repository.
I tested the script from my local machine PowerShell ISE; upload complete without issue as expected. However, when I execute the script on the intended target machine, I'm seeing an error returned:
The request was aborted: Could not create SSL/TLS secure channel.
I've read numerous online posts related to this error; consensus appears to be to add the following line immediately before the Invoke-RestMethod call in the script:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Ssl3
I added this line but the issue persists.
Execution of the following PowerShell:
[Net.ServicePointManager]::SecurityProtocol
returns:
Ssl3, Tls, Tls11, Tls12
suggesting that all available protocols are enabled.
My script snippet now looks like this:
$headers = #{"X-JFrog-Art-Api" = $artifactoryApiKey}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Ssl3
$return=Invoke-RestMethod -Uri $uri -InFile $sourceFile -Method Put -Headers $headers
As stated above, this script executes without issue on my local machine.
.Net 4.6.1 installed on server returning error.

I don't offer an explanation, but perhaps a solution for some. I was having the same exact issue: PowerShell invoke-RestMethod worked locally but not when run from the intended (Windows 2012 R2) server. I receive the same TLS/SSL error. I read a lot about the TLS handshake and other technotes, and tested suggestions like:
Setting the security protocol as noted in the original question
Enabling TLS protocol in the registry
Listing and comparing Cipher Suites.
In the end, I couldn't get PowerShell to work. HOWEVER, the rest call works from the server using curl.exe in command line. If you see this, I recommend stop trying to troubleshoot PowerShell and give curl a try. I found this and this helpful in writing my first curl.exe rest calls.

Related

Powershell Invoke-WebRequest SSL/TLS [duplicate]

I'm trying to execute this powershell command
Invoke-WebRequest -Uri https://apod.nasa.gov/apod/
and I get this error. "Invoke-WebRequest : The request was aborted: Could not create SSL/TLS secure channel." https requests appear to work ("https://google.com") but not this one in question. How can I get this to work or use other powershell command to read the page contents?
try using this one
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri https://apod.nasa.gov/apod/
In a shameless attempt to steal some votes, SecurityProtocol is an Enum with the [Flags] attribute. So you can do this:
[Net.ServicePointManager]::SecurityProtocol =
[Net.SecurityProtocolType]::Tls12 -bor `
[Net.SecurityProtocolType]::Tls11 -bor `
[Net.SecurityProtocolType]::Tls
Or since this is PowerShell, you can let it parse a string for you:
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
Then you don't technically need to know the TLS version.
I copied and pasted this from a script I created after reading this answer because I didn't want to cycle through all the available protocols to find one that worked. Of course, you could do that if you wanted to.
Final note - I have the original (minus SO edits) statement in my PowerShell profile so it's in every session I start now. It's not totally foolproof since there are still some sites that just fail but I surely see the message in question much less frequently.
The cause of the error is Powershell by default uses TLS 1.0 to connect to website, but website security requires TLS 1.2. You can change this behavior with running any of the below command to use all protocols. You can also specify single protocol.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls, [Net.SecurityProtocolType]::Tls11, [Net.SecurityProtocolType]::Tls12, [Net.SecurityProtocolType]::Ssl3
[Net.ServicePointManager]::SecurityProtocol = "Tls, Tls11, Tls12, Ssl3"
After running these commands, try running your command:
Invoke-WebRequest -Uri https://apod.nasa.gov/apod/
then it will work.
If, like me, none of the above quite works, it might be worth also specifically trying a lower TLS version alone. I had tried both of the following, but didn't seem to solve my problem:
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls
In the end, it was only when I targetted TLS 1.0 (specifically remove 1.1 and 1.2 in the code) that it worked:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls
The local server (that this was being attempted on) is fine with TLS 1.2, although the remote server (which was previously "confirmed" as fine for TLS 1.2 by a 3rd party) seems not to be.
Hope this helps someone.
It works for me...
if (-not ([System.Management.Automation.PSTypeName]'ServerCertificateValidationCallback').Type)
{
$certCallback = #"
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
public class ServerCertificateValidationCallback
{
public static void Ignore()
{
if(ServicePointManager.ServerCertificateValidationCallback ==null)
{
ServicePointManager.ServerCertificateValidationCallback +=
delegate
(
Object obj,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors errors
)
{
return true;
};
}
}
}
"#
Add-Type $certCallback
}
[ServerCertificateValidationCallback]::Ignore()
Invoke-WebRequest -Uri https://apod.nasa.gov/apod/
Make sure you switch the SHELL first:
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
RUN Invoke-WebRequest -UseBasicParsing -Uri 'https://github.com/git-for-windows/git/releases/download/v2.25.1.windows.1/Git-2.25.1-64-bit.exe' -OutFile 'outfile.exe'
I haven't figure out the reason but reinstalling the .pfx certificate(both in current user and local machine) works for me.

Getting a file from BitBucket Rest API v2.0

I have a script which grabs a file from GIT using the bitbucket REST API (1.0) however it has recently stopped working. I'm theorizing this may be due to the v1 REST API being depreciated but I'm not sure.
Anyway I am trying to retrieve the file using the new 2.0 REST API but I can't seem to get the syntax right as the request continually fails.
I'm starting out with curl since its easiest to test. This is what I'm trying:
curl -u myusername#mydomain.com "https://api.bitbucket.org/2.0/repositories/MyCompany/myrepo/downloads/Scripts/Environment Setup/test.txt"
Enter host password for user 'myusername#mydomain.com': redacted
{"type": "error", "error": {"message": "Resource not found", "detail": "There is no API hosted at this URL.\n\nFor information about our API's, please refer to the documentation at: https://developer.atlassian.com/bitbucket/api/2/reference/"}}
Here is the reference documentation I am using: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/downloads/%7Bfilename%7D
Maybe I am using the wrong function? I'm not sure.
For posterities sake, you don't want to use the following to download an individual file from bitbucket:
https://api.bitbucket.org/2.0/repositories/MyCompany/myrepo/downloads/path/to/your/file.txt
("Downloads" is to download entire repo files like a .zip file)
Instead you want to do:
curl --user myuser#mydomain.com:password "https://api.bitbucket.org/2.0/repositories/MyCompany/myrepo/src/master/path/to/file.txt"
If you're trying to use Invoke-RestRequest (in powershell) note there are some extra steps. With the old 1.0 API you could do:
$cred = Get-Credential
$uri = "https://api.bitbucket.org/1.0/repositories/MyCompany/$($filepath)"
# Get the files from bitbucket (GIT)
Invoke-RestMethod -Credential $cred -Uri $uri -Proxy $proxyUri -OutFile $destination
With the new 2.0 API that no longer works. Powershell's Invoke-RestMethod waits for a 401 response before sending the credentials, and the new 2.0 bitbucket api never provides one, so credentials never get sent causing a 403 forbidden.
To work around that you have to use the following ugly hack to force Invoke-RestMethod to send the credentials immediately in an Authorization header:
$cred = Get-Credential
$uri = "https://api.bitbucket.org/2.0/repositories/MyCompany/$($filepath)"
$username = ($cred.GetNetworkCredential()).username
$password = ($cred.GetNetworkCredential()).password
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
# Get the files from bitbucket (GIT)
Invoke-RestMethod -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Uri $uri -Proxy $proxyUri -OutFile $destination
Hopefully that helps someone else out in the future!
Thanks #Jim Redmond for the help.
You can also use the PowerShell module BitbucketServerAutomation. There's not a ton of cmdlets, they do have Get-BBServerFile and Get-BBServerFileContent. I have found it is well written, very usable and being updated regularly. The Invoke-BBServerRestMethod cmdlet is available if you need a command it doesn't have.

What is the curl equivalent command in powershell for uploading the apk file?

I am trying to perform CI/CD using Perfecto and hence I am trying to upload a file to perfecto when my Bamboo build is finished.
I was trying with the following cURL command when we have a Linux server.
curl -X POST --upload-file test.apk 'https://****.perfectomobile.com/services/repositories/media/PRIVATE:test.apk?operation=upload&user=<email>&password=<password>&overwrite=true'
Now our server is changed to Windows and hence I want a powershell script which I can use as an Inline Scripts in Bamboo.
Can you please tell me what is an equivalent script in Powershell for windows.
Many thanks in advance.
# Gather your information.
$email = "myEmail#website.com";
$password = "powershellR0cks!";
$subDomain = "****";
$url = "https://$subDomain.perfectomobile.com/services/repositories/media/PRIVATE:test.apk?operation=upload&user=$email&password=$password&overwrite=true";
$filePath = ".\test.apk";
# Make the request.
$response = Invoke-WebRequest -Uri $URL -Method Post -InFile $filePath -ContentType "application/octet-stream";
# Check for success.
if (-not ($response.StatusCode -eq 200)) {
throw "There was an error uploading the APK manifest.";
}
You may want to check the value of -ContentType, but I think that's correct. You don't necessarily need to include the scheme (HTTPS) if you don't want to, and semicolons in PowerShell are optional, but you can include them if you want.
The $response variable is an HtmlWebResponseObject that has the content of the response, the status code, and a bunch of other useful info. You can check out the available properties and methods on the object by running $response | Get-Member.
Finally, the Invoke-WebRequest cmdlet also has other parameters that may be useful to you, such as -Credential, -Headers, and more.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view=powershell-5.1
As a side-note, if you run Get-Alias -Name "curl", you can see that anytime you use curl in PowerShell, you're really just calling Invoke-WebRequest. You can use the curl alias if you want, but it's generally not a good idea to use aliases in automation since they can be modified or deleted.

Invoke-WebRequest doesn't work until I call WebClient.DownloadString

I'm using powershell v5 to call an internal API using TLS1.2 with a self-signed cert. When I call the api I always get Invoke-WebRequest : The underlying connection was closed: An unexpected error occurred on a send.
E.g.:
PS> [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
PS> [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
PS> $url = "https://someinternalserver/blah"
PS> $response = Invoke-WebRequest $url
Invoke-WebRequest : The underlying connection was closed: An unexpected error occurred on a send.
At line:1 char:1
(I've looked in the error object but I don't see anything helpful.)
However if I call the same URL using an instance of WebClient, then the call using WebClient AND all subsequent powershell calls works fine:
PS> $webClient = New-Object System.Net.WebClient
PS> $str = $webClient.DownloadString($url)
PS> Write-Host $str
body of request
PS> $response = Invoke-WebRequest $url
PS> Write-Host $response.Content
body or request
I'm not sure what's going on, but I suspect it has something to do with the self-signed cert, or the crypto. Here's what chrome says about the crypto:
I've used powershell to call APIs with self-signed certs before but never had these kind of issues.
Resolution: I'd like to call the API without first using WebClient.
Thanks.
So dug into the error more and found this in an inner exception:
There is no Runspace available to run scripts in this thread.
You can provide one in the DefaultRunspace property of the System.Management.Automation.Runspaces.Runspace type.
The script block you attempted to invoke was: $true
Which led me here:
Powershell 3.0 Invoke-WebRequest HTTPS Fails on All Requests
Which led me here:
https://stackoverflow.com/a/15841856/6311875
Using that code instead of the {$true} did the trick.
So, this further reinforces the idea that all questions are already answered on SO, you just have to look hard enough.

Powershell and NITRO API for Citrix NetScaler error on GET method

I am using a PowerShell module provided by Citrix to invoke the Nitro REST API. Calling the function I can successfully add and remove load balanced services from the load. However when I try to do a GET method to get the status of a service I get the error:
Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
I have tried running Invoke-RestMethod without using the module but get the same error
Invoke-RestMethod -WebSession $myNSSession.WebSession -Method GET -Uri https://<NetScaler IP/nitro/v1/config/service/<Service Name>
When googling this error everything seems to point to certificate issues. I had this initially even on POST method until i added the below to my script
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
So since this works for doing POST i cant see why it wouldn't for a GET!!
another weird thing is, if I put the URL directly into the browser then enter my credentials i get a response in raw text! so it looks like this is an issue with the way i am calling it in PowerShell rather than the NetScaler or the NITRO API!
Someone please help as this is driving me crazy!!
Admitedly i am new to Invoke-RestMethod commands, but try this:
$creds = Get-Credential
$service = Invoke-RestMethod -Uri https://<NetScaler IP/nitro/v1/config/service/<Service Name> -Credential $creds
What you will get is something similar to this:
*errorcode* *message* *serverity* *service*
* 0 Done NONE {#{name=<service name; n..
then type $service.service and you will see more information. whatever attributes are availible will be listed. then just follow the pattern:
$service.service.
I had the same problem with Nitro API (specifically v10.5), and found that setting certificate policies, TLS versions and trust settings had no effect. POST works, GET fails.
The solution for me was to not use the cmdlets and instead drop back to a native .Net method. Below I am still using HTTPS with an internal certificate, hence still setting the callback.
$NSProtocol = "https://"
$NSHostname = "netscaler"
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$WebRequest = [System.Net.WebRequest]::Create("$NsProtocol$NsHostname/nitro/v1/config/hanode")
$WebRequest.Method = "GET"
$WebRequest.ContentType = "application/json; charset=utf-8";
$WebRequest.Headers.Add("AUTHORIZATION","Basic $([System.Convert]::ToBase64String([System.Text.Encoding]::GetEncoding("ISO-8859-1").GetBytes($nsuser+":"+$nspass)))")
$Response = $WebRequest.GetResponse()
$ReadStream = New-Object System.IO.StreamReader $Response.GetResponseStream()
$HaState = ConvertFrom-Json $ReadStream.ReadToEnd()
Hope that helps.