Powershell: Fix My Loop Logic - powershell

I am trying to get the logic straightened out for a loop that I need to continue to prompt a user to enter a valid UNC path. I have it set to test the path and output to the console that the path is invalid. But, after that, it moves back to my prompt for choice. I want it to instead, ask the user to enter another valid path before moving on to the next step. Here is my loop:
do{
Write-Host ""
$pathPrompt = Write-Host "Please enter path to file/folder:" -ForegroundColor Cyan;
$path = Read-Host;
$test = Test-Path $path
if($test -eq $false){
Write-Host "ERROR! Invalid Path!" -ForegroundColor Red
}
}until($test -eq $true){
Write-Host ""
Write-Host "Getting ACL on"$path -ForegroundColor Green
Get-NTFSAccess -Path $path
}
What am I missing or not doing right here?

Sounds like you want to reuse your validation test. You could put it in a function for reuse:
Function Get-ValidPath {
do {
Write-Host "`r`nPlease enter path to file/folder:" -ForegroundColor Cyan
$path = Read-Host
$test = Test-Path $path
if ($test -eq $false) {
Write-Host "ERROR! Invalid Path!" -ForegroundColor Red
}
} until ($test -eq $true)
$path
}
$validatedpath1 = Get-ValidPath
Write-Host "`r`nGetting ACL on $validatedpath1" -ForegroundColor Green
Get-NTFSAccess -Path $validatedpath1
$validatedpath2 = Get-ValidPath

Related

Powershell not reading .txt lines

I cannot get PowerShell to run this .txt file, what am I doing wrong? I tried changing the name of the .txt and checked passed scripts and everything seems to be the same but I keep getting an error saying that the ".txt in invalid"
$POSName = "$PSScriptRoot\Bex.txt"
foreach ($POS in (Get-Content $POSName)) {
$Bex = Get-Service -ComputerName $POSName | Where-Object { $_.name -eq "BexServ" }
}
If ($Bex -eq $null) {
# Service does not exist
Write-Host " doesn't exist." -ForegroundColor Red
}
Else {
# Service does exist
Write-Host "The $($Bex.Name) service found." -ForegroundColor Green
If ($Bex.Status -eq "Running") {
# Stop Service
Set-Service -status stopped -ComputerName $POSName -name $Box.Name -ErrorAction Stop
Write-Host "The $($Bex.Name) successfully stopped." -ForegroundColor Green
}
else {
#service already stopped
If ($Bex.Status -eq "Stopped") {
Write-Host "The $($Bex.Name) service already Stopped." -ForegroundColor Green
}
}
}
As commented, you are using the wrong variable in the loop. The code is reading the text file just fine, it is Get-Service that cannot deal with a path to a file in the -ComputerName parameter.
Also, the placing of the if..else should be inside the loop, not after.
Try
$POSName = "$PSScriptRoot\Bex.txt"
foreach ($POS in (Get-Content $POSName)) {
$Bex = Get-Service -ComputerName $POS | Where-Object { $_.name -eq "BexServ" }
If (!$Bex) {
# Service does not exist
Write-Host " doesn't exist." -ForegroundColor Red
}
Else {
# Service does exist
Write-Host "The $($Bex.Name) service found." -ForegroundColor Green
If ($Bex.Status -eq "Running") {
# Stop Service
Set-Service -status stopped -ComputerName $POSName -name $Box.Name -ErrorAction Stop
Write-Host "The $($Bex.Name) successfully stopped." -ForegroundColor Green
}
else {
#service already stopped
If ($Bex.Status -eq "Stopped") {
Write-Host "The $($Bex.Name) service already Stopped." -ForegroundColor Green
}
}
}
}
It might also be a good idea to output the computername ($POS) in the Write-Host lines too

Get-SPOSite find existing sites

This is the code that will check if any of the sites listed already exists.
$sites = get-content -Path C:\code\CheckSPSites.txt
foreach($site in $sites){
$url = (Get-SPOSite -Filter{url -like $site} -ErrorAction SilentlyContinue) -ne $null
if ($url -eq $true){
Write-Host "$site already created" -BackgroundColor Red
}else{
Write-Host "$site not created" -BackgroundColor Green
}
}
It doesn't find the sites when I use the variable $site to filter the search.
I've tried putting the variable in quotes (Get-SPOSite -Filter{url -like "$site") and it doesn't work either.
Any help would be appreciated.
Many thanks
Thank you Theo and UBK. Posting your suggestion as an answer to help other community members.
You can use -Identity parameter along with the site collection URL to find out the existence.
For example: Get-SPOSite -Identity https://contoso.sharepoint.com
# Verify if site of same name already exists in SharePoint Online
$siteAlreadyExists = Get-SPOSite -Identity $url
# If it does, stop the script
if ($null -ne $siteAlreadyExists) {
Write-Host "Site already exists" -ForegroundColor Red
...
}
If your code fails when site is not present, then you can try following try-catch block:
try {
$siteAlreadyExists = Get-SPOSite -Identity $url
if ($null -ne $siteAlreadyExists) {
Write-Host "Site already exists" -ForegroundColor Red
}
}
catch {
Write-Host "Site already exists" -ForegroundColor Red
}
You can refer to Number of SPO sites causing issue with Get-SPOSite

Subexpression printing out same strings? Powershell

I have this code which deletes User Profiles off a remote machine. The removal of profiles work just fine but, the Aesthetic of doing so doesn't. What do i mean?
I'm passing the user display names to an index and making a selection out of it, and that works fine in regards to assigning the proper names to the appropriate Index Number its associated to in C:\users.
The next line of code is it grabbing the selections i made, and running through them displaying the same name i did for the index, and then it goes off to delete the CIM instance.
So my question is, why is it not passing the subexpression $userinfo1 that is already made and not putting it into the next block of code, for example, the following works as in grabbing the proper Display Name and assigning it to the proper Number:
$menu = (get-childitem "\\$cn\c$\users" | sort LastWriteTime -Descending).Name
$userinfo1 = foreach ($user in $menu) {
Start-Sleep -Milliseconds 2
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) {
"$user - NO DISPLAY NAME in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 2) {
"$user - account not in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 0){
$userinfo # output
}
}
}
}
Write-Warning "Ensure user profiles are no longer active and/or, have profiles be backed-up!"
Write-Host "RESULTS:" -BackgroundColor Black -ForegroundColor White
for ($i=0; $i -lt $userinfo1.Count; $i++) {
Write-Host "$($i): $($userinfo1[$i])"
} #END LIST OF POSSIBLE NAMES
Write-Host ""
Write-Host "For multiple users, seperate using a SPACE(1 2 3)"
$selection = Read-Host "ENTER THE NUMBER of the user(s) or Q to quit"
$selection = $selection -split " "
but, the next block doesn't associate the display name (that was captured in $userinfo1) with the number i select and it just continues to display the first display name with the rest of the profiles its reiterating through:
foreach($Profile in $menu[$selection]){
Write-Host "Deleting user: $(,$userinfo1[$selection]) `
ID:$Profile "}
Hopefully this makes sense, and if anyone can point me in the right direction id greatly appreciate it!
Heres the rest of the script, please feel free to use it as it does work for deleting the actual profile off the system and not just the files.
#Deletes a profile properly off remote machine. WARNING: DOES NOT BACK UP DATA! Use at your own peril. Delprofile
$cn = Read-Host -Prompt "Enter Computer Name"
$ping = Test-Connection -ComputerName $cn -Count 1 -Quiet
If($ping -eq $false){ Write-Host "Computer seems to be offline, please check name spelling." -ForegroundColor DarkYellow; Write-Host ""; &PFL-Delete } else {
$menu = (get-childitem "\\$cn\c$\users" | sort LastWriteTime -Descending).Name
$userinfo1 = foreach ($user in $menu) {
Start-Sleep -Milliseconds 2
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) {
"$user - NO DISPLAY NAME in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 2) {
"$user - account not in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 0){
$userinfo # output
}
}
}
}
Write-Warning "Ensure user profiles are no longer active and/or, have profiles be backed-up!"
Write-Host "RESULTS:" -BackgroundColor Black -ForegroundColor White
for ($i=0; $i -lt $userinfo1.Count; $i++) {
Write-Host "$($i): $($userinfo1[$i])"
} #END LIST OF POSSIBLE NAMES
Write-Host ""
Write-Host "For multiple users, seperate using a SPACE(1 2 3)"
$selection = Read-Host "ENTER THE NUMBER of the user(s) or Q to quit"
$selection = $selection -split " "
foreach($Profile in $menu[$selection]){
Write-Host "Deleting user: $(,$userinfo1[$selection]) `
ID:$Profile "
$del = Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile }
If($del -eq $null){Write-Warning "No CIM instance found on system, profile has been deleted but files persist. Delete manually!"} else{
Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile } | Remove-CimInstance -WhatIf
Write-Host "user profile has been deleted" -ForegroundColor Red
Write-Host ""}
}
}
#CountPs $cn
12/31/2020 - EDIT:
Here is the finished result:
Function Delete-PFL{
#Deletes a profile properly off remote machine. WARNING: DOES NOT BACK UP DATA! Use at your own peril. Delprofile
$cn = Read-Host -Prompt "Enter Computer Name"
$ping = Test-Connection -ComputerName $cn -Count 1 -Quiet
If($ping -eq $false){ Write-Host "Computer seems to be offline, please check name spelling." -ForegroundColor DarkYellow; Write-Host ""; &Delete-PFL } else {
$menu = (get-childitem "\\$cn\c$\users" | sort LastWriteTime -Descending).Name
$userinfo1 = foreach ($user in $menu) {
Start-Sleep -Milliseconds 2
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) {
"$user - NO DISPLAY NAME in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 2) {
"$user - ACCOUNT NOT in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 0){
$userinfo # output
}
}
}
}
Write-Warning "Ensure user profiles are no longer active and/or, have profiles be backed-up!"
Write-Host "RESULTS:" -BackgroundColor Black -ForegroundColor White
for ($i=0; $i -lt $userinfo1.Count; $i++) {
Write-Host "$($i): $($userinfo1[$i])"
} #END LIST OF POSSIBLE NAMES
Write-Host ""
Write-Host "For multiple users, seperate using a SPACE(1 2 3)"
$selection = Read-Host "ENTER THE NUMBER of the user(s) or Q to quit"
$selection = $selection -split " "
foreach($index in $selection) {
$Profile = $menu[$index]
Write-Host "Deleting user: $($userinfo1[$index]) `
ID:$Profile "
$del = Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile }
If($del -eq $null){Write-Warning "No CIM instance found on system, profile has been deleted but files persist."
Write-Host "Attempting to delete files, please wait. . ."
Remove-Item -Path "\\$cn\c$\users\$Profile" -Force -WhatIf
Write-Host ""
Start-Sleep -Seconds 2
Write-Host "Checking if Files are still there. . ."
$TestPath = Test-Path -Path "\\$cn\c$\users\$Profile"
If($TestPath -eq $false){ Write-Host "Profile Files have been deleted. `
Continuing. . . ." -ForegroundColor Green
}
} else{
Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile } | Remove-CimInstance -WhatIf
Write-Host "user profile has been deleted" -ForegroundColor Red
Write-Host ""
}
}
}
#CountPs $cn
}
Remember to remove the -whatif parameter. Enjoy!
$selection is an array of indices, so in your foreach loop you must refer to the single index at hand, not to $selection as a whole, to get the desired display output.
The conceptually clearest approach is probably to iterate over the indices contained in $selection:
foreach($index in $selection) {
$Profile = $menu[$index]
Write-Host "Deleting user: $($userinfo1[$index]) `
EDIPI:$Profile "
# ...
}

System.Security.Principal.IdentityNotMappedException Catching all errors

I am having issues with a try/catch block where the catch block for System.Security.Principal.IdentityNotMappedException is catching all errors regardless if they're of type System.Security.Principal.IdentityNotMappedException or not. The problem is when I move the try/catch block outside of the script into it's own for testing, the try/catch works as intended.
The try/catch is inside a for/each so they only thing I can think of is that the loop is somehow tripping up the try/catch block? The one error it loves catching is System.Management.Automation.RuntimeException.
Below is the test script I made that works as intended, but placed within the foreach doesn't:
I actually got the error to manifest with this:
foreach($x in Get-ChildItem){
Get-Acl "C:\fake"
try {
$Acl = Get-Acl
Set-Acl LiteralPath "C:\temp" $Acl -ErrorAction Stop
Write-Host "Access Rule Added" -ForegroundColor Cyan
}
catch [System.UnauthorizedAccessException] {
Write-Host "Insufficient Priviliege. Owner: $($Acl.Owner) ($_)" -ForegroundColor Red
}
catch [System.Security.Principal.IdentityNotMappedException] {
Write-Host "Invalid Prinicpal! ($_)" -ForegroundColor Red
$abort = Read-Host -Prompt "Abort? (Y)"
if($abort -ieq "Y"){ Exit }
}
catch {
Write-Host "ALL: $_" -ForegroundColor Red
}
}
This isn't exactly an answer to my question (as I don't fully understand why it was happening and as such it might snag on something else) but adding -ErrorAction SilentlyContinue to the Get-Acl command that was throwing the first error helps prevent the errant catching:
foreach($x in Get-ChildItem){
Get-Acl "C:\fake" -ErrorAction SilentlyContinue
try {
$Acl = Get-Acl
Set-Acl LiteralPath "C:\temp" $Acl -ErrorAction Stop
Write-Host "Access Rule Added" -ForegroundColor Cyan
}
catch [System.UnauthorizedAccessException] {
Write-Host "Insufficient Priviliege. Owner: $($Acl.Owner) ($_)" -ForegroundColor Red
}
catch [System.Security.Principal.IdentityNotMappedException] {
Write-Host "Invalid Prinicpal! ($_)" -ForegroundColor Red
$abort = Read-Host -Prompt "Abort? (Y)"
if($abort -ieq "Y"){ Exit }
}
catch {
Write-Host "ALL: $_" -ForegroundColor Red
}
}
Though it's strange how $_ doesn't display the actual error but just AclObject in this instance.

IF/ELSE Get-SPWeb

I am trying to quickly check whether a web site exists. I seem to have an error in my IF statement, but I'm not sure of the correct syntax. Here's my code:
$URLis = "https://ourdevsite.dev.com/sites/flibidyboots"
add-pssnapin microsoft.sharepoint.powershell -ea 0
IF ((Get-SPWeb $URLis) -ne 0){
Write-Host "Site does not exist, so we can proceed with building it" -foregroundcolor green
}
Else {
Write-Host "Site does exist, so we need to pick another URL" -foregroundcolor red
}
What did I get wrong?
Ok, first of all its $null, not 0. And second, if it is not $null it exists, so your cases are mixed up.
Here is some code that will work.
Add-PSSnapin "Microsoft.SharePoint.Powershell" -ErrorAction SilentlyContinue
$url = "http://teams"
$w = Get-SPWeb -Identity $url -ErrorAction SilentlyContinue
if ($w) {
Write-Host "Site Exists" -ForegroundColor Green
} else {
Write-Host "No Site" -ForegroundColor Red
}