Powershell web request - 400 Bad Request - rest

Im building a powershell script to execute commands on a REST-like server, I am using powershell to POST XML data to the webservice. Everything is working perfectly except when the "application" produces an error. The application replies back with details in xml and also gives me a Web Request Error (in this case 400) which is when i have problems. Whenever i get a Web Request Error that is not 200 the commands throw an error which screws up how i handle the error.
This is an example of the response im receiving (using fiddler):
HTTP/1.1 400 Bad Request
Date: Thu, 29 Nov 2012 04:29:48 GMT
Content-Type: text/xml;charset=ISO-8859-1
Connection: close
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Status>
<operation>Add Network</operation>
<result>ERROR</result>
<resultCode>REASON_210</resultCode>
</Status>
This is the error i receive in powershell:
Exception calling "UploadData" with "3" argument(s): "The remote server returned an error: (400) Bad Request."
At C:\Scripting\test.ps1:47 char:34
+ $response = $query.UploadData <<<< ($url,"POST",$byteArray)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Exception calling "GetString" with "1" argument(s): "Array cannot be null.
Parameter name: bytes"
At C:\Scripting\test.ps1:48 char:62
+ $stringresponse = [System.Text.Encoding]::ASCII.GetString <<<< ($response)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
I dug further into the error and this is as close as i could get to retrieving the content of the response (the XML).
I thought I might be able to pull it out from this..
$error[1].exception.InnerException.Data
But it is empty :(
PS C:\Scripting> $error[1].exception.InnerException | fl -force
Status : ProtocolError
Response : System.Net.HttpWebResponse
Message : The remote server returned an error: (400) Bad Request.
Data : {}
InnerException :
TargetSite : Byte[] UploadDataInternal(System.Uri, System.String, Byte[], System.Net.WebRequest ByRef)
StackTrace : at System.Net.WebClient.UploadDataInternal(Uri address, String method, Byte[] data, WebRequest& request)
at System.Net.WebClient.UploadData(Uri address, String method, Byte[] data)
at System.Net.WebClient.UploadData(String address, String method, Byte[] data)
at UploadData(Object , Object[] )
at System.Management.Automation.MethodInformation.Invoke(Object target, Object[] arguments)
at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[]
originalArguments)
HelpLink :
Source : System
PS C:\Scripting> $error[1].exception.InnerException.response | fl
IsMutuallyAuthenticated : False
Cookies : {}
Headers : {Connection, Transfer-Encoding, Content-Type, Date}
ContentLength : -1
ContentEncoding :
ContentType : text/xml;charset=ISO-8859-1
CharacterSet : ISO-8859-1
Server :
LastModified : 29/11/12 4:04:19 PM
StatusCode : BadRequest
StatusDescription : Bad Request
ProtocolVersion : 1.1
ResponseUri : https://api-au.dimensiondata.com/oec/0.9/cdefb891-1552-4008-8638-7393eda8d7e1/network
Method : POST
IsFromCache : False
Here is my powershell code that I am using:
function send_web ($url,$data) {
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$query = new-object system.net.WebClient
$auth = 'Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($username+":"+$password ))
$query.Headers.Add('Authorization', $auth )
[byte[]]$byteArray = [System.Text.Encoding]::ASCII.GetBytes($data)
$query.Headers.Add("ContentType","application/xml")
$response = $query.UploadData($url,"POST",$byteArray)
$stringresponse = [System.Text.Encoding]::ASCII.GetString($response)
return $stringresponse
}
This seems like it shouldn't be a hard thing to do any help would be much appreciated :)
Richard

Related

Unreliable HTTP status code when using the HEAD method

I have a PowerShell script to verify the existence of many (half a million) files in a web server. Some of these files are large (gigabytes) and I don't wan't to transfer them, so I've been using the HTTP HEAD method.
The problem is, today I found that the HTTP status code returned from the HEAD method may be different from the status returned by GET (which is always the correct one):
Using HEAD:
> Invoke-WebRequest -Method head http://example.com/82833749.mbtiles
StatusCode : 200
StatusDescription : OK
Content : {}
RawContent : HTTP/1.1 200 OK
Content-MD5: LwIBDR5QRptNAE2Hdpu+aw==
x-ms-request-id: 774da565-101e-0053-7903-38e136000000
x-ms-version: 2009-09-19
x-ms-lease-status: unlocked
x-ms-blob-type: BlockBlob
Connect...
Headers : {[Content-MD5, LwIBDR5QRptNAE2Hdpu+aw==], [x-ms-request-id,
774da565-101e-0053-7903-38e136000000], [x-ms-version, 2009-09-19], [x-ms-lease-status,
unlocked]...}
RawContentLength : 0
With GET:
> Invoke-WebRequest http://example.com/82833749.mbtiles
Invoke-WebRequest : BlobNotFoundThe specified blob does not exist. RequestId:dede785b-b01e-0091-4b08-38a7b0000000
Time:2022-03-15T01:02:36.7883837Z
No linha:1 caractere:1
+ Invoke-WebRequest http://example.com/82833749.mbtiles
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
> $Error[0].Exception.Response.StatusCode.value__
404
Right after running the GET request, the HEAD request will return the correct status code, making it more confusing to understand what is going on.

Changes in System.Net.HttpWebRequest between Powershell versions?

I am having some trouble converting a Powershell script from PS5 to PS7.
My goal is to get information on a remote server cert.
This IP can be anything with a cert:
$url = "https://192.168.1.1"
[Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
$req = [Net.HttpWebRequest]::Create($url)
$req.GetResponse()
[datetime]$expiration =
[System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
Write-Output $expiration
When run in Powershell 5, this script completes successfully with the output below:
IsMutuallyAuthenticated : False
Cookies : {}
Headers : {Accept-Ranges, Content-Length, Content-Type, Date...}
SupportsHeaders : True
ContentLength : 98540
ContentEncoding :
ContentType : text/html
CharacterSet : ISO-8859-1
Server : Microsoft-IIS/10.0
...
Date : 7/7/2020 12:00:00 AM
...
This script errors on $req.GetResponse when run in Powershell 7.
MethodInvocationException: Exception calling "GetResponse" with "0" argument(s): "The SSL connection could not be established, see 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 "
Any ideas?

Trying to get Powershell to work with an API

I am trying to write a Powershell script to work with a Microsoft API, and being new to this, I have come across an error that I cannot seem to get past.
Error:
Cannot find an overload for "UpdateDeviceValues" and the argument count: "3".
At C:\PowerShellScripts\API-UpdateDeviceValues-ICC.ps1:13 char:37
+ $accounts = $pmws.UpdateDeviceValues <<<< ('18B81F1FD790','DisableICC','True');
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
From my searches, it seems that the API is requiring three values to be sent to it, but that is what I am sending.
My code:
$pmws = New-Object PrincipalManagement;
$pmws.Credentials = $cred;
$pmws.url = "http://172.31.9.2/bss/PrincipalManagement.asmx";
$accounts = $pmws.UpdateDeviceValues('18B81F1FD790','DisableICC','True');
$accounts
Any help with this error would be much appreciated. I am rather new to this, getting Powershell to work with APIs.
This is the SOAP 1.1 example for the API.
POST /bss/PrincipalManagement.asmx HTTP/1.1
Host: 172.31.9.2
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://www.microsoft.com/iptv/bss/UpdateDeviceValues"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope">
<soap:Body>
<UpdateDeviceValues xmlns="http://www.microsoft.com/iptv/bss">
<deviceExternalID>string</deviceExternalID>
<deviceValues>
<DeviceValue Key="string" Value="string" />
<DeviceValue Key="string" Value="string" />
</deviceValues>
</UpdateDeviceValues>
</soap:Body>
</soap:Envelope>
I can run this in PowerShell:
> $pmws | Get-Member
I get a long list of the APIs, and in that list is the one I am trying to use...
TypeName: PrincipalManagement
Name - MemberType - Definition
UpdateDeviceValues - Method - System.Void UpdateDeviceValues(string deviceExternalID, DeviceValue[] deviceValues)
Much thanks to Chamele0n! Answer found!
I was missing how to properly format the parameters when calling the method.
This works!
$Account = "18B81F1FD790"
$deviceValue = New-Object ($ns + ".DeviceValue")
$deviceValue.Key = "DisableICC"
$deviceValue.Value = "True"
$accounts = $pmws.UpdateDeviceValues($Account, $deviceValue)
Thanks,
Jason...

Powershell FTP won't list files on server

I want to make a backup script for ZyWall USG 20.
I used this Powershell code to get a list of the content in the directory:
[System.Net.FtpWebRequest]$ftp = [System.Net.WebRequest]::Create("ftp://*server*/*path*")
$ftp.Credentials = New-Object System.Net.NetworkCredential("*user*","*password*")
$ftp.Method = [System.Net.WebRequestMethods+FTP]::ListDirectory
$response = $ftp.getresponse()
echo $response
I expected to get a list of all the files..instead I get this:
PS Z:\> ...\list-files-test.ps1
ContentLength : -1
Headers : {}
SupportsHeaders : True
ResponseUri : ftp://*server*/*path*/
StatusCode : OpeningData
StatusDescription : 150 Opening BINARY mode data connection for file list
LastModified : 01.01.0001 00:00:00
BannerMessage : 220 FTP Server (ZyWALL USG 20) [::ffff:*server*]
WelcomeMessage : 230 User *user* logged in
ExitMessage :
IsFromCache : False
IsMutuallyAuthenticated : False
ContentType :
PS Z:\>
Why don' I get a list of files?
I checked it with filezilla - the folder is not empty.
And I can download specific files using [System.Net.WebRequestMethods+Ftp]::DownloadFile.
WebRequest.GetResponse() makes the remote call and gets the response details, but there's no content within the response itself.
Instead, you need to continue on and read the response stream out of the response with WebResponse.GetResponseStream().

Call Windows Runtime Classes from PowerShell

Is there a way to call Windows Runtime (WinRT) classes (or objects) from a PowerShell script? I know that you can call COM objects, which WinRT classes are supposed to be "exposed" as ... but so far my attempts have failed...
This is my code I'm trying:
$lockscreen = New-Object -comObject Windows.System.UserProfile.LockScreen
Which gives me the following error:
New-Object : Retrieving the COM class factory for component with CLSID {00000000-0000-0000-0000-000000000000} failed
due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
Does anyone know the correct "COM Class" that I should be using for WinRT classes?
just reference the type, to "load" the assembly...
[Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime]
[Windows.System.UserProfile.LockScreen]::OriginalImageFile
if you don't want the type to be returned in your powershell results then
$null = [Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime]
Here is something hacky that seems to work:
PS> new-object "Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime"
new-object : Constructor not found. Cannot find an appropriate constructor for type
Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime.
At line:1 char:1
+ new-object "Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,Con ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : CannotFindAppropriateCtor,Microsoft.PowerShell.Commands.NewObjectCommand
PS> [Windows.System.UserProfile.LockScreen]::OriginalImageFile
AbsolutePath : C:/Windows/Web/Screen/img100.png
AbsoluteUri : file:///C:/Windows/Web/Screen/img100.png
LocalPath : C:\Windows\Web\Screen\img100.png
Authority :
HostNameType : Basic
IsDefaultPort : True
IsFile : True
IsLoopback : True
PathAndQuery : C:/Windows/Web/Screen/img100.png
...
Note that the first call fails because LockScreen has no constructor but that call does something to pull in the WinRT projection/metadata such that you can now call the static methods/properties on the LockScreen class.
DISCLAIMER: there isn't any documentation that I can find on this New-Object syntax so it is entirely possible that Microsoft could change it considering it is essentially a "hidden" and probably not fully developed feature.