i wanna do a script which first checks the internet connection and if the computer has internet downloads a file. If the computer has no internet, the shell should write "You are not connected with the internet". The script does not work with
connection test-netconnection www.hawk.de -CommonTCPPort HTTP{}
My script is:
if() {
$client = new-object System.Net.WebClient
$client.Credentials = Get-Credential
$client.DownloadFile(“https://www.mydomain.de/sites/default/files/styles/xs_12col_16_9_retina/public/DSC_6947.JPG”,“C:\Users\Ole\Downloads\Github\bild.JPG”)
}else {
Write-Host "Could not connect to the Internet."
}
fi
Thanks for your help.
Try this for your if block..
IF (((Test-NetConnection www.site.com -Port 80 -InformationLevel "Detailed").TcpTestSucceeded) -eq $true)
Change the web address to suit obviously...
Windows tracks Internet connection status as part of Network Location Awareness; you can check this status using something like this:
If ((Get-NetConnectionProfile).IPv4Connectivity -contains "Internet" -or (Get-NetConnectionProfile).IPv6Connectivity -contains "Internet") {
# Do something here
}
https://learn.microsoft.com/en-us/windows/win32/winsock/network-location-awareness-service-provider-nla--2
You can use try..catch block for this:
try {
$client = new-object System.Net.WebClient $client.Credentials = Get-Credential $client.DownloadFile(“https://www.mydomain.de/sites/default/files/styles/xs_12col_16_9_retina/public/DSC_6947.JPG”,“C:\Users\Ole\Downloads\Github\bild.JPG”)
}
catch {
write-host "Not connected to the internet"
}
if..fi syntax refers to bash that's not powershell. Try..Catch is used to handle exceptions and errors.
Related
Occasionally I forget to log off from a server or am disconnected through an error and I don't remember the name of the server. And my domain account starts getting periodically locked out, so I have to access logs on DC to find out which server(s) keep locking my account and log off from it/them. So I wanted to write to script in powershell that would log me off from all servers in a domain (with the exception of the server where I run the script on of course) without me needing to search which to log off from. This is what I have:
$ErrorActionPreference = "Silentlycontinue"
$Servers = (Get-ADComputer -Filter *).Name
$ScriptBlock = {
$Sessions = quser | ?{$_ -match $env:USERNAME}
if (($Sessions).Count -ge 1)
{
$SessionIDs = ($Sessions -split ' +')[2]
Write-Host "Found $(($SessionIDs).Count) user login(s) on $Server."
$SessionIDs | ForEach-Object
{
Write-Host "Logging off session [$($_)]..."
logoff $_
}
}
}
foreach ($Server in $Servers)
{
if ($Server -isnot $env:COMPUTERNAME)
{
Invoke-Command -ComputerName $Server -ScriptBlock {$ScriptBlock}
}
}
But when I launch the script, nothing happens. The script doesn't return any errors but doesn't log me off from any server, nor does it write any of the messages from Write-Host cmdlet, obviously. I noticed the $SessionIDs variable definition only returns ID of the first session. Usually this shouldn't be a problem, since it's unlikely I will have more than one session on a server, but I'd like to have this insurance. Can anyone tell me what's wrong in the script?
I notice a few things...
"First, I don't think quser | Where-Object {$_ -match $env:USERNAME} will ever return anything. The output of quser will not contain the hostname."
Try this for getting logon sessions:
$Sessions = (query user /server:$Env:ComputerName) -split "\n" -replace '\s\s+', ';' |
ConvertFrom-Csv -Delimiter ';'
Next, when you reference the $Server variable on the remote machine in your script block, it is out of scope. You would need to use $Using:Server in the script block.
Lastly, the -isnot operator doesn't compare value, it compares type. So in your last foreach, the if statement evaluates to "if type string is not type string" and will not run. Try -ne or -notlike instead.
Working with objects is much easier if you can just parse the output of QUser.exe. Given your scenario, here's my take on it:
$servers = (Get-ADComputer -Filter '*').Name.Where{$_ -ne $env:COMPUTERNAME}
foreach ($server in $servers)
{
if (-not ($quser = ((QUser.exe /server:$server) -replace '\s{20,39}',',,' -replace '\s{2,}',',' 2>&1) | Where-Object -FilterScript { $_ -match $env:USERNAME })) {
Continue
}
Write-Verbose -Message "$($quser.Count) session(s) found on $server." -Verbose
($quser.Trim() | ConvertFrom-Csv -Header 'USERNAME','SESSIONNAME','ID','STATE','IDLE TIME','LOGON TIME').foreach{
Write-Verbose -Message "Logging user [$($_.UserName)] off." -Verbose
LogOff.exe $_.ID /server:$server
}
}
Filtering should always happen before hand meaning, filter out your computer name on your first call to Get-ADComputer. Since you're using QUser.exe and LogOff.exe to begin with, I'd recommend the use of it all the way through since LogOff accepts an ID value that QUser outputs.
Next, placing the call to quser inside your if statement does two things in this case.
Filters for all users matching $ENV:UserName
Returns $true if anything is found, and $false if not found.
So, switching the results using -not will turn $false into $true allowing the execution of the code block which will just continue to the next server.
This in turn doesn't bother with the rest of the code and continues onto the next computer if no matching names were found.
The use of $quser inside the if statement is so you can save the results to it if more than one name is found; (..) allows this as it turns the variable assignment into an expression having the output pass through onto the pipeline where it is either empty, or not.
Finally, referencing the $quser variable we can convert the strings into objects piping to ConvertFrom-Csv. Only step left to do is iterate through each row and passing it over to LogOff to perform the actual logoff.
If you've noticed, the headers are manually-specified because it is filtered out by the Where-Object cmdlet. This is a better approach seeing as there could be "more than one" RDP Session, now you're just left with those sessions matching the name which can be saved to $quser, so no extra filtering is needed down the line.
So I modified the script this way and it works, sort of. It logs off account from servers, which is the main goal. There are still some glitches, like the message it sends from the first Write-Host doesn't give server's name, the message from second one gives a different value than it should (it gives [1] value after -split instead of [2] for some reason; but those are not really that important things, even though I will try to make at least the first message right) and $SessionIDs still gives only the first value, but usually you shouldn't have more than one RDP session per server. I've seen more sessions of one user, but that is very rare. But I'd also like to fix this if possible. Nevertheless, the script basically does the most important thing. But if someone has a suggestion how to fix the glitches I mentioned I would be grateful.
$ErrorActionPreference = "Silentlycontinue"
$Servers = (Get-ADComputer -Filter *).Name
$ScriptBlock = {
$Sessions = quser | ?{$_ -match $env:USERNAME}
if (($Sessions).Count -ge 1)
{
$SessionIDs = , ($Sessions -split ' +')[2]
Write-Host "Found $(($SessionIDs).Count) user login(s) on $Server."
Foreach ($SessionID in $SessionIDs)
{
Write-Host "Logging off session $SessionID..."
logoff $SessionID
}
}
}
foreach ($Server in $Servers)
{
if ($Server -ne $env:COMPUTERNAME)
{
Invoke-Command -ComputerName $Server -ScriptBlock $ScriptBlock
}
}
I am limited to only using native windows tools, and I need to automate opening a browser, going to a website, and then closing the browser. I'm going to use task scheduler to run the script.
I created this PowerShell script which successfully opens a browser, and closes it.
How would I go about having this open a specific webpage? Start Arguments or something else?
$browser = [Diagnostics.Process]::Start("chrome.exe")
$id = $browser.Id
Start-Sleep -Seconds 5
try {
Stop-Process -Id $id -ErrorAction stop
} catch {
Write-Host "Failed to kill"
}
You can start the process with the appropriate Chromium command line argument(s):
$browser = [Diagnostics.Process]::Start("chrome.exe", "https://stackoverflow.com/ --new-window")
$id = $browser.Id
Start-Sleep -Seconds 5
try {
Stop-Process -Id $id -ErrorAction stop
}
catch {
Write-Host "Failed to kill"
}
Process.Start Method
Run Chromium with flags
List of Chromium Command Line Switches
I am using below script to Verify checksum of a remote file against a local file. The server I installed on my machine is freeSSHd.
When I tried to execute the below script using PowerShell ISE I get an error message saying:
Your shell is probably incompatible with the application (BASH is recommended)
I've granted shell access in the FreeSSHd Server User properties:
Script:
param (
# Use Generate URL function to obtain a value for -sessionUrl parameter.
$sessionUrl = "sftp://user:mypassword;fingerprint=ssh-rsa-xx-xx-xx#example.com/",
[Parameter(Mandatory = $True)]
$localPath,
[Parameter(Mandatory = $True)]
$remotePath,
[Switch]
$pause = $False
)
try
{
Write-Host $localPath -foregroundcolor Gray
# Calculate local file checksum
$localChecksum = ((CertUtil -hashfile $localPath SHA1)[1] -replace " ","")
# Write-Host "Local Checksum:"
Write-Host $localChecksum
# Load WinSCP .NET assembly
#Add-Type -Path (Join-Path $PSScriptRoot "WinSCPnet.dll")
[Reflection.Assembly]::LoadFrom("\\c:\Program Files (x86)\WinSCP\WinSCPnet.dll") | Out-Null
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.ParseUrl($sessionUrl)
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
Write-Host $remotePath -foregroundcolor Gray
# Calculate remote file checksum
$sha1Command = "bash sha1sum -b $remotePath | awk '{print `$1}'"
$result = $session.ExecuteCommand($sha1Command)
$result.Check()
$remoteChecksum = $result.Output;
#$remoteChecksum =
[System.BitConverter]::ToString($session.CalculateFileChecksum("sha-1", $remotePath))
# Write-Host "Remote Checksum:"
Write-Host $remoteChecksum
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
# Compare cheksums
if ($localChecksum -eq $remoteChecksum)
{
Write-Host
Write-Host "Match" -foregroundcolor "green"
$result = 0
}
else
{
Write-Host
Write-Host "Does NOT match" -foregroundcolor "red"
$result = 1
}
}
catch [Exception]
{
Write-Host $_.Exception.Message
$result = 1
}
# Pause if -pause switch was used
if ($pause)
{
Write-Host "Press any key to exit..."
[System.Console]::ReadKey() | Out-Null
}
exit $result
FreeSSHd server does not support any "bash". Its "shell" is Windows cmd.exe.
Your code cannot work. Windows cmd.exe is not compatible with WinSCP.
Moreover FreeSSHd is pretty buggy, do not use it.
You should use another Windows SSH server.
You can use Windows build of OpenSSH. It would allow you to execute a PowerShell script on the server to calculate the checksum.
If you install Windows Subsystem for Linux, you may even get the sha1sum (but I'm not sure).
You can use Cygwin, if you need to simulate *nix environment on Windows.
You can use Bitvise SSH Server for personal use for free. Its SFTP server supports checksum calculation on its own, so you would be able to use WinSCP Session.CalculateFileChecksum method directly.
There are lot of other options.
Hoping someone can guide me / help me.
The issue, I have 2 servers one running a Ubuntu which has a website for clients to login and download / view reports. The other is a windows server 2012 R2 which creates / stores the reports. I need to move the files from the windows to the Ubuntu server so clients can view. The data is large currently 7gb and growing at 3 gb a year.
I need a batch file to connect using ftp and then copy the folder to a local folder. However it only needs to copy those files which have modified.
I have only ever written one batch file and I cant seem to find any ftp batch scripts which only copies modifed files.
Your my last resort as I cant seem to find a coder who knows batch script (its a dieing art). I have never used powershell so would not know where to start here.
Any help or advice please let me know.
Thanks
John
You can do it with PowerShell with winscp. Exemple :
try
{
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "example.com"
UserName = "user"
Password = "mypassword"
SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult = $session.PutFiles("d:\toupload\*", "/home/user/", $False, $transferOptions)
# Throw on any error
$transferResult.Check()
# Print results
foreach ($transfer in $transferResult.Transfers)
{
Write-Host ("Upload of {0} succeeded" -f $transfer.FileName)
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}
catch [Exception]
{
Write-Host ("Error: {0}" -f $_.Exception.Message)
exit 1
}
This would be a way to do it in PowerShell. It would take files that are older then 31 days and upload them.
function FTP-Upload {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Source_File,
[Parameter(Mandatory=$true)]
[string]$Target_File,
[Parameter(Mandatory=$true)]
[string]$Target_Server,
[Parameter(Mandatory=$true)]
[string]$Target_Username,
[Parameter(Mandatory=$true)]
[string]$Target_Password
)
$FTP = [System.Net.FTPWebRequest]::Create("ftp://$Target_Server/$Target_File")
$FTP = [System.Net.FTPWebRequest]$FTP
$FTP.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$FTP.Credentials = New-Object System.Net.NetworkCredential($Target_Username,$Target_Password)
$FTP.UseBinary = $true
$FTP.UsePassive = $true
# read in the file to upload as a byte array
$content = [System.IO.File]::ReadAllBytes($Source_File)
$FTP.ContentLength = $content.Length
# get the request stream, and write the bytes into it
$rs = $FTP.GetRequestStream()
$rs.Write($content, 0, $content.Length)
# be sure to clean up after ourselves
$rs.Close()
$rs.Dispose()
}
$Upload_Server = "server.network.tld"
$Upload_Location = "/data/"
$Upload_Username = "ftpuser"
$Upload_Password = "ftppassword"
$Files_To_Upload = Get-ChildItem E:\Path\To\Files -Recurse | Where-Object {($_.CreationTime -le (Get-Date).AddDays(-31)) -and (!$_.PSIsContainer)}
Foreach ($File in $Files_To_Upload) {
FTP-Upload -Source_File $File.FullName -Target_File ($Upload_Location + $File.Name) -Target_Server $Upload_Server -Target_Username $Upload_Username -Target_Password $Upload_Password
}
I'm working on a large script where I run a Foreach loop, define variables in that loop and afterwards check if the $Server variable is pingable and if it is remotely accessible.
For this I use the following functions coming from the PowerShell help:
# Function to check if $Server is online
Function CanPing ($Server) {
$error.clear()
$tmp = Test-Connection $Server -ErrorAction SilentlyContinue
if ($?) {
Write-Host "Ping succeeded: $Server"; Return $true
}
else {
Write-Host "Ping failed: $Server."; Return $false
}
}
# Function to check if $Server is remotely accessible
Function CanRemote ($Server) {
$s = New-PSSession $Server -Authentication Credssp -Credential $Credentials -Name "Test" -ErrorAction SilentlyContinue
if ($s -is [System.Management.Automation.Runspaces.PSSession]) {
Enter-PSSession -Session $s
Exit-PSSession
Write-Host "Remote test succeeded: $Server."; Return $true
}
else {
Write-Host "Remote test failed: $Server."; Return $false
}
}
# Execute functions to check $Server
if ($Server -ne "UNC") {
if (CanPing $Server) {
if (-Not (CanRemote $Server)) {
Write-Host "Exit loop REMOTE" -ForegroundColor Yellow
continue
}
}
else {
Write-Host "Exit loop PING" -ForegroundColor Yellow
continue # 'continue' to the next object and don't execute the rest of the code, 'break' exits the foreach loop completely
}
}
Every time when I run this code, there is a process created on the remote server called wsmprovhost.exe. This process represents the PowerShell session, if the info I found on the web is correct. However, when doing Get-PSSession $Server there are no open sessions displayed in the PowerShell ISE, even though the processes are visible on the remote server and can only be killed with the Task Manager.
When I run this code often the limit of open sessions is reached because every time a new process wsmprovhost.exe is added to the $Server and the command errors out. I've tried to solve this by adding Exit-PSSessionto the code, but it doesn't close the session.
Any help or ideas are more than welcome.
The problem is that Enter-PSSession. Enter-PSSession can only be used interactively, you can't use it in a script. I'd suggest something more like this:
# Function to check if $Server is remotely accessible
Function CanRemote ($Server) {
Try {
$s = New-PSSession $Server -Authentication Credssp -Credential $Credentials -Name "Test" -ErrorAction Stop
Write-Host "Remote test succeeded: $Server."
$true
Remove-PSSession $s
}
Catch {
"Remote test failed: $Server."
$false
}
}
If I have understood correctly, Your remote ps-session are not getting closed.
To my understaning, Get-PSSession will show the session till your local session
is alive (I mean the session you created the remote ps-session) but once your local session
ends Get-PSSession will not show them cause they are no more live on your computer
rather on the remote system (or) they are no more in local session scope.
You can get the session using the command
Get-PSSession -ComputerName server_name
If you want to remove them you can do like
Get-PSSession -ComputerName server_name | Remove-PSSession
Even After executing the below command also, if you are not able to create session
Get-PSSession -ComputerName server_name | Remove-PSSession
Please, Restart the service Windows Remote Management (WS-Management) in the target machine.