Invoke-WebRequest to null if it fails - powershell

I'm running this part of the code in my script but I have small issue.
$response = Invoke-WebRequest -Uri "http://169.254.169.254/metadata/instance/compute?api-version=2019-06-01" -Headers #{Metadata = "true"} -TimeoutSec 1 -ErrorAction SilentlyContinue
if($response.StatusCode -ne 200) {
write-host "$env:computername is not in the cloud. Let's continuing configurations"
configuration code
}else{
write-host "$env:computername is in the cloud. Stop the script"
}
When the invoke fails which in my cases does 99% of the time it outputs the fail as big red error message which I would want to get rid of
Then I tried to suppress to output of the invoke-webrequest using these command but none of them worked
| Out-Null
> $null
$null =
Tried also to play with Try & Catch but did not manage to it work either because I somehow never got to my last if statement
Write-Verbose "Checking if this is an Azure virtual machine"
try {
$response = Invoke-WebRequest -Uri "http://169.254.169.254/metadata/instance/compute?api-version=2019-06-01" -Headers #{Metadata = "true"} -TimeoutSec 1 -ErrorAction SilentlyContinue
}
catch {
Write-Verbose "Error $_ checking if we are in Azure"
return $false
}
if ($null -ne $response -and $response.StatusCode -eq 200) {
Write-Verbose "Azure check indicates that we are in Azure"
return $true
}
return $false
if($false -eq 'False')
{
Write-Host "server is in Azure"
}
else{
Write-host "server is not in Azure"
}
What should I try next? I'm not super good at PowerShell so there might even just be some errors in syntax or misunderstandings.

I've done some tests with Invoke-WebRequest and was able to suppres the error with try & catch. Try something like that:
Write-Verbose "Checking if this is an Azure virtual machine"
try {
$response = Invoke-WebRequest -Uri "http://169.254.169.254/metadata/instance/compute?api-version=2019-06-01" -Headers #{Metadata = "true"} -TimeoutSec 1 -ErrorAction Stop
# Response was successful (200), otherwise script would run in catch at this point
Write-Verbose "$env:computername is in the cloud. Stop the script"
} catch {
# Webrequest failed (not 200)
Write-Verbose "$env:computername is not in the cloud. Let's continuing configurations"
# CONFIGURATION CODE
}

Related

how to run a command x number of times inside a while loop - powershell

so here i have a part of my code where im trying to work out how i can run a command inside a while loop x number of times and end the script.
$timer = New-TimeSpan -Minutes 120
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timer) {
$ReleaseStatus = Invoke-RestMethod -Uri "https://vsrm.dev.azure.com/company/Project/_apis/release/releases/$RId/environments/$EId/?`api-version=6.0" -Method GET -Headers $Header -Verbose
start-sleep -seconds 10
#if release is successful return message and end script.
if ($ReleaseStatus.Status -eq 'Succeeded') {
write-host "Release Succeeded"
return
}
#if the release fails run the command x number of times.
if ($ReleaseStatus.status -eq 'Rejected') {
Write-Host "Release failed, Re-running release for you"
#input command here to run release x number of times and then end whole script.
}
}
I have tried various methods but not getting anywhere, anyone know how i can perform this?
Use a variable to keep count of your retries:
$timer = New-TimeSpan -Minutes 120
# configure number of attempts - this will run up to 5 times (initial attempt + 4 retries)
$retries = 5
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timer -and $retries -gt 0) {
$ReleaseStatus = Invoke-RestMethod -Uri "https://vsrm.dev.azure.com/company/Project/_apis/release/releases/$RId/environments/$EId/?`api-version=6.0" -Method GET -Headers $Header -Verbose
start-sleep -seconds 10
#if release is successful return message and end script.
if ($ReleaseStatus.Status -eq 'Succeeded') {
write-host "Release Succeeded"
return
}
# release didn't succeed, decrement retry counter
$retries--
if($retries -ge 1){
Write-Host "Release failed, Re-running release for you"
}
else {
Write-Host "Release failed, but no more retries for you"
}
}
Now the while() condition only continues if 1) we haven't timed out yet AND 2) we have retries left - if either condition is no longer met it'll stop
okay so here's how i did it. please read the hash notes.
#Check the status of release every y seconds
$timer = New-TimeSpan -Minutes 120
#set the value of I to 0 outside of the while loop.
$i = 0
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timer) {
$ReleaseStatus = Invoke-RestMethod -Uri "https://vsrm.dev.azure.com/$Organisation/$Project/_apis/release/releases/$RId/environments/$EId/?`api-version=6.0" -Method GET -Headers $Header -Verbose
start-sleep -seconds 10
if ($ReleaseStatus.Status -eq 'Succeeded') {
write-host "Release Succeeded"
return
}
if ($ReleaseStatus.status -eq 'Rejected') {
Write-Host "Release failed, Re-running release for you"
$Uri = "https://vsrm.dev.azure.com/$Organisation/$Project/_apis/Release/releases/$RId/environments/$EId/?api-version=6.0-preview.6"
#we've put the value of retries into a variable to be parametrised. If I is greater than number of retries then return(cancel operation)
if ($i -gt $NumberOfRetryAttempts) {
return
}
Invoke-RestMethod -uri $uri -Method PATCH -Headers $Header -Body $body2
#you put the I variable inside the while loop and you increment by 1 each time the condition comes back false.
$i++
}
}

Download a file, get the status, then execute the file

I've tried invoke-restmethod, new-object and many other methods to achieve what I'm trying to do. Here are the latest two iterations:
$req = Invoke-WebRequest -uri $scripturl -OutFile "$($scriptpath)\fls.core.ps1"
Write-Host "StatusCode:" $req.StatusCode
$req = Invoke-WebRequest -uri $scripturl -OutFile "$($scriptpath)\fls.core.ps1" | Select-Object -Expand StatusCode
Write-Host "StatusCode:" $req
Basically I'm attempting to download another PowerShell script and execute it. So obviously it needs to be synchronous. I also need the status so I can determine if it updated or not.
Here is pseudo code for what I'm trying to accomplish:
try {
download file
} catch {
output error
if (local copy exists) {
log warning that local copy is being used
} else {
log error could not download and no local copy available
exit script
}
}
run script (only after downloading new one if available)
Here is my current code in full:
$param1=$args[0]
if ($param1 -eq "-d" -or $param1 -eq "-D") {
$isDev = $true
}
#todo: Move to config file
$logpath = "c:\company\logs\loginscript"
$scriptpath = "c:\company\scripts\"
$scripturl = "http://downloads.company.com/fls.core.ps1"
$logfile="$(Get-Date -Format "yyyy-MM-dd hhmmss").log"
Function log($message) {
Write-Output "[$(Get-Date -Format "yyyy-MM-dd hhmmss")] $message" | Out-file "$($logpath)\$($logfile)" -append
if ($isDev) { Write-Host "[$(Get-Date -Format "yyyy-MM-dd hhmmss")] $message" }
}
Function createFolder($path) {
if (-!(Test-Path $path)) { New-Item -Type Directory -Path $path }
}
function updateScripts() {
try {
$req = Invoke-WebRequest -uri $scripturl -OutFile "$($scriptpath)\fls.core.ps1"
Write-Host "StatusCode:" $req.StatusCode
} catch {
Write-Host "StatusCode:" $req.StatusCode
if ($req.StatusCode -eq 404) {
log "WARNING: Script not found at $scripturl"
} else {
log "ERROR: Script download error: $req.StatusCode"
}
if (Test-Path "$($scriptpath)\fls.core.ps1") {
log "WARNING: Using local script"
} else {
log "ERROR: Unable to update script and no local script found. Exiting."
exit
}
}
}
#----------------------------------------------#
#---- MAIN CODE BLOCK -------------------------#
#----------------------------------------------#
createFolder $logpath
createFolder $scriptpath
#update scripts
updateScripts
#execute core loginscript
& $scriptpath/fls.core.ps1
$req.StatusCode appears to be null.
Invoke-WebRequest reports errors as statement-terminating errors, which means that no assignment to variable $req (in statement $req = Invoke-WebRequest ...) takes place in case an error occurs.
Instead, unfortunately, if an error occurs, the response object[1] must be gleaned from the [ErrorRecord] instance representing the error, which is available via $Error[0] after the fact, or via $_ in the catch block of a try { ... } catch { ... } statement (adapted from this answer):
try {
Invoke-WebRequest -Uri $scripturl -OutFile "$scriptpath\fls.core.ps1"
} catch [Microsoft.PowerShell.Commands.HttpResponseException] {
# Get the status code...
$statuscode = $_.Exception.Response.StatusCode
# ... and work with it.
# if ($statusCode -eq 404) { ...
} catch {
# Unexpected error, re-throw
throw
}
Strictly speaking, $_.Exception.Response.StatusCode returns a value from an enumeration type, System.Net.HttpStatusCode, not an [int] value, but you can use it like an integer. To return an integer to begin with, append .Value__ or cast to [int].
Note that Invoke-WebRequest is always synchronous; if you download a file (successfully), the call won't return until the download is completed.
[1] As the linked answer explains, the response object contained in the error record is of a different type than the one that Invoke-WebRequest returns in case of success (which requires -PassThru if -OutFile is also specified): The error record's .Exception.Response property contains a System.Net.Http.HttpResponseMessage instance, whereas Invoke-WebRequest returns an instance (derived from) Microsoft.PowerShell.Commands.WebResponseObject, which incorporates an instance of the former type, in its .BaseResponse property.

Recheck the URL thrice for web exception cases powershell

$status_wait =1
Do{
$https_request= invoke_webrequest -uri "https:\\"
$https_status = [int]http_request.statuscode
If($($_.categoryinfo.reason) -eq "webexception") {
Start-sleep -secounds 30
$status_wait+=1
}
Elseif($http_status -eq 200) {
Write-host "URL is up"
}
}Untill (status_wait -eq 3)
You are looking for something like this ?
#Remove-Variable * -ErrorAction SilentlyContinue
$url = "www.yourtargetsite.com"
function try-webrequest($url){
try{
return (Invoke-WebRequest -uri $url -ErrorAction Stop)
}catch{
return $_
}
}
$wr = try-webrequest $url
if ($wr.statuscode -ne 200){
If($wr.categoryinfo.reason -eq "webexception"){
$count = 1
while($count -lt 4){
"Retrying...$count"
$count++
if ((try-webrequest $url).statuscode -eq 200){break}
sleep -seconds 30
}
}else{
#In case you want to handle any other exception
"The link is down NOT due to webexception"
}
}else{
"The link's up"
}
PS: I have just started answering on stackoverflow. I'd love to hear your feedback.

Powershell loop iteration count

The purpose of the scripts is to catch wrong logins to Elasticsearch, and gave the opportunity to retry entering a password. The idea was to count loop iteration by adding i++ after $result = Invoke-RestMethod $uri -Credential $credential step, but it turned out that -ErrorAction SilentlyContinue process is ignored in case it's located in try{} section, so i moved loop counts into finally {$i++} section, but the cons is that the last Attempt 5 does not pass the condition while ($i -lt 6) cuz it is 5+1 in finally {$i++} section.
$elasticsearch_pass = Read-Host "Enter ELASTICSEARCH password"
$uri = "https://${elasticsearch_ip}:9200"
$user = "static_user"
$i = 1
while ($i -lt 6) {
try {
Write-Host -ForegroundColor Cyan `n ("Trying to connect to elasticsearch. Attempt $i")
$secpasswd = ConvertTo-SecureString $elasticsearch_pass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
$result = Invoke-RestMethod $uri -Credential $credential
if ($result.tagline -eq 'You Know, for Search') {
Write-Host -ForegroundColor Green `n ("Connection to elasticsearch {0} established..." -f $elasticsearch_ip)
break
}
}
catch {
Write-Host -ForegroundColor Red $_
if ("$_" -eq "Unauthorized") {
$elasticsearch_pass = Read-Host `n "Wrong elasticsearch password! Pls enter correct one"
}
}
finally {$i++}
}
What is the best approach to set a static loop iteration count and execute the last Attempt?
Why use While, if an interation var is needed the usual solution is a traditional for:
$uri = "https://${elasticsearch_ip}:9200"
$user = "static_user"
$i = 1
$maxTry = 5
For( $i = 1; $i -le $maxTry; ++$i )
{
try {
Write-Host -ForegroundColor Cyan `n ("Trying to connect to elasticsearch. Attempt $i of $maxtry ")
$secpasswd = ConvertTo-SecureString $elasticsearch_pass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
$result = Invoke-RestMethod $uri -Credential $credential
if ($result.tagline -eq 'You Know, for Search') {
Write-Host -ForegroundColor Green `n ("Connection to elasticsearch {0} established..." -f $elasticsearch_ip)
break
}
}
Catch {
Write-Host -ForegroundColor Red $_.Exception.Message
if ($_.Exception.Message -match "Unauthorized") {
}
}
}
In either case I don't think you need to do the ++$i in the finally block, you could just do in the catch and continue. However the for loop allows you to do it at the right point in each iteration without the confusion.
Also added $maxTry gives a slightly better echo...
Since this is interactive I changed some of the catch block to echo a little more cleanly.
Also moved the Read-Host into the loop. and changed the language a little. though there are a couple of ways to do that...

Powershell -UseDefaultCredentials to Authenticate URL

New to the forum and new to Powershell. Hopefully in the right place. I'm trying to authenticate a web url and use the -UseDefaultCredentials within Powershell. I found this code on Stackoverflow - 404 Response:
$URI='http://www.example.com'
try {
$HttpWebResponse = $null;
$HttpWebRequest = [System.Net.HttpWebRequest]::Create("$URI");
$HttpWebResponse = $HttpWebRequest.GetResponse();
if ($HttpWebResponse) {
Write-Host -Object $HttpWebResponse.StatusCode.value__;
Write-Host -Object $HttpWebResponse.GetResponseHeader("X-Detailed-Error");
}
}
catch {
$ErrorMessage = $Error[0].Exception.ErrorRecord.Exception.Message;
$Matched = ($ErrorMessage -match '[0-9]{3}')
if ($Matched) {
Write-Host -Object ('HTTP status code was {0} ({1})' -f $HttpStatusCode, $matches.0);
}
else {
Write-Host -Object $ErrorMessage;
}
$HttpWebResponse = $Error[0].Exception.InnerException.Response;
$HttpWebResponse.GetResponseHeader("X-Detailed-Error");
}
The code above is doing what I want with returning the HTTP code. However, the question is, where do I insert the -UseDefaultCredentials within this block of code? I've tried adding it in to within and outside the try statement but get the error message:
Error Message
I originally used this piece of code:
Invoke-WebRequest -Uri http://www.example.com -UseDefaultCredentials
but my problem I experienced was then trying to write out the HTTP return code compared to the code that is listed above.
Any help would be appreciated.