powercli remove empty folders from vcenter - powershell

i have been searching for quite some time and i cant seem to find anything close.
i am working on automating our VM for our DEV & QA dept using VCAC.
i have reached the point that during VM creation a folder with the project's name is created under the dept (for exaple DEV\Upgrade1)
the problem starts when the DEV guys decides to delete t the whole project and start over.
i am left with a lot of empty folders throughout the VC server and i was wondering if there is a powercli script i can run daily to check if there are any empty folders (with no vms) inside and delete them if they exist.
its a tricky issue because i found i can use remove-folder but only if i give its name which i dont know.
and i dont want to delete folders with VMS inside.
anyone can help me?
thanks

If your already connected to your server and in powercli run this.
$folders = get-folder
Foreach ($folder in $folders)
{
if((get-folder $folder|get-vm).count -eq 0)
{
remove-folder -folder $folder -confirm $false
}
}
Drop a -location $datacenter onto the first get-folder if you want to isolate.

Related

if then else not seeing else argument

I'm trying to learn myself some PowerShell scripting to automate some tasks at work.
The latest task I tried to automate was to create a copy of user files to a network-folder, so that users can easily relocate their files when swapping computers.
Problem is that my script automatically grabs the first option in the whole shebang, it never picks the "else"-option.
I'll walk you through part of the script. (I translated some words to make it easier to read)
#the script asks whether you want to create a copy, or put a copy back
$question1 = Read-Host "What would you like to do with your backup? make/put back"
if ($question1 -match 'put back')
{Write-Host ''
Write-Host 'Checking for backup'
Write-Host ''
#check for existing backup
if (-Not(Test-Path -Literalpath "G:\backupfolder"))
{Write-Host "no backup has been found"}
Elseif (Test-Path -LiteralPath "G:\backupfolder")
{Write-Host "a backup has been found."
Copy-Item -Path "G:\backupfolder\pictures\" -Destination "C:\Users\$env:USERNAME\ ....}}
Above you see the part where a user would want the user to put a "backup" back.
It checks if a "backup" exists on the G-drive. If the script doesn't see a backup-folder it says so. If the script DOES see the backup it should copy the content from the folders on the G-drive to the similarly named folder you'd find on the user-profile-folder. Problem is: So far it only acts as if there is never a G:\backupfolder to be found. It seems that I'm doing something wrong with if/then/else.
I tried with if-->Else, and with if-->Elseif, but neither works.
I also thought that it could be the Test-Path, so I tried adding -LiteralPath, but to no avail.
There is more to the script but it's just more if/then/else. If I can get it to work on this part I should be able to get the rest working. What am I not seeing/doing wrong?

Checking if a UNC Path/Server Folder Exists

I am working on a project that utilizes a PowerShell script that creates a new login on a remote SQL Server (SSMS) and then checks to see if a particular folder exists on the server. If the folder does not already exist, the script will create that folder.
The issue I am having is that I cannot verify whether or not the folder exists since the path I am testing is a UNC path of the form "\\server\Files\Log". I have tried many different solutions that I have found through a couple hours of searching online, and all solutions return FALSE even though I am testing a server and folder I know already exist.
I am using PowerGUI to write my script and my system is using PowerShell v5. What I have tried so far:
Test-Path $path (where $path has been set to \\server)
Test-Path "filesystem::\\Srv"
[System.IO.Directory]::Exists($path)
I even tried [System.IO.Directory]::Exists('G:\') using all of the
letters I have network servers mapped to to see if I needed to map to the drives to make it work (all returned FALSE)
What am I missing here? Any thoughts on this topic would be greatly appreciated as I have been grinding on this for a while with no progress being made.
EDIT: For anyone who might stumble upon this later, please read the comments, which I found to be super helpful. My main issue was that I was running PowerShell as an administrator which does not have the same permissions as my normal user account. Also note that Test-Path \\server alone does not work, a folder must also be referenced.
You already have the correct answer:
Test-Path $path
or
Test-Path \\server.domain.tld\ShareName
If Test-Path is returning false, I can think of three things that could be wrong:
The share does not exist on that server, or at least with the name you expect
Your user does not have permission to read that share
You are specifying the short name of the server, and you need the FQDN to resolve it. This is common in multidomain environments.
After re-reading your question, it looks like you might be running Test-Path \\server. You cannot check for the existence of a server this way, you have to specify both the server and the share name at a minimum. If you want to know that a server exists and is online, use Test-Connection (assuming you are able to ping this server in the first place). Here is an example of using Test-Connection:
$serverName = 'server.domain.tld'
$sharePath = 'ShareName' # you can append more paths here
if( Test-Connection $serverName 2> $null ){
Test-Path "\\${serverName}\${sharePath}"
}
I used to have an issue where the file existed, but test-path is returning false. I put test-path in a loop that checks a maximum of 10 x, with a one sec pause in between. The script works fine now. If on the first attempt it does not find the file on the second or third it does. Not sure why it returns false on the first attempt.

How to get an environment variable in a Powershell script when it is deployed by SCCM?

I've made a script to automatically change and/or create the default Outlook signature of all the employees in my company.
Technically, it gets the environment variable username where the script is deployed, access to the staff database to get some information regarding this user, then create the 3 different files for the signature by replacing values inside linked docx templates. Quite easy and logical.
After different tests, it is working correctly when you launch the script directly on a computer, either by using Powershell ISE, directly by the CMD or in Visual Studio. But when we tried to deploy it, like it will be, by using SCCM, it can't get any environment variable.
Do any of you have an idea about how to get environment variables in a script when it is deployed by SCCM ?
Here is what I've already tried :
$Name = [Environment]::UserName
$EnvVarUserName = Get-Item Env:\USERNAME
Even stuff like this :
$proc = gwmi win32_process -Filter "Name = 'explorer.exe'"
$report = #()
ForEach ($p in $proc)
{
$temp = "" | Select User
$temp.user = ($p.GetOwner()).User
$report += $temp
}
Thanks in advance and have a nice day y'all !
[EDIT]:
I've found a way of doing this, not the best one, but it works. I get the name of the machine, check the DB where when a laptop is connected to our network it stores the user id and the machine, then get the info in the staff DB.
I will still check for Matt's idea which is pretty interesting and, in a way, more accurate.
Thank you all !
How are you calling the environmental variable? $Env:computernamehas worked for me in scripts pushed out via SCCM before.
Why don't you enumerate the "%SystemDrive%\Users" folder, exclude certain built-in accounts, and handle them all in one batch?
To use the UserName environment variable the script would have to run as the logged-in user, which also implies that all of your users have at least read access to your staff database, which, at least in our environment, would be a big no-no.

Powershell Delete Locked File But Keep In Memory

Until recently, we've been deploying .exe applications by simply copying them manually to the destination folder on the server. Often though, the file was already running at the time of deployment (the file is called from a SQL Server job)--sometimes even multiple instances. We don't want to kill the process while it's running. We also can't wait for it to finish because it keeps on being invoked, sometimes multiple times concurrently.
As a workaround, what we've done is a "cut and paste" via Windows Explorer on the .exe file into another folder. Apparently, what this does is it moves the file (effectively a delete) but keeps it in RAM so that the processes which are using it can continue without issues. Then we'd put the new files there which would get called when any later program would call it.
We've now moved to an automated deploy tool and we need an automated way of doing this.
Stop-Process -name SomeProcess
in PowerShell would kill the process, which I don't want to do.
Is there a way to do this?
(C# would also be OK.)
Thanks,
function moverunningprocess($process,$path)
{
if($path.substring($path.length-1,1) -eq "\") {$path=$path.substring(0,$path.length-1)}
$fullpath=$path+"\"+$process
$movetopath=$path + "--Backups\$(get-date -f MM-dd-yyyy_HH_mm_ss)"
$moveprocess=$false
$runningprocess=Get-WmiObject Win32_Process -Filter "name = '$process'" | select CommandLine
foreach ($tp in $runningprocess)
{
if ($tp.commandline -ne $null){
$p=$tp.commandline.replace('"','').trim()
if ($p -eq $fullpath) {$moveprocess=$true}
}
}
if ($moveprocess -eq $true)
{
New-Item -ItemType Directory -Force -Path $movetopath
Move-Item -path "$path\*.*" -destination "$movetopath\"
}
}
moverunningprocess "processname.exe" "D:\Programs\ServiceFolder"
Since you're utilizing a SQL Sever to call the EXE. Why do you add a table that contains the path to the latest version of the file and modify your code that fires the EXE. That way when a new version is rolled out, you can create a new folder, place the file in it, and update the table pointing to it. That will allow any still active threads to have access to the old version and any new threads will pickup up the new executable. You then can delete the old file after it's no longer needed.

Determine where script is being executed from

I have a script that will send items to the recycle bin (if selected) or delete items permanently. If the script is run locally, the recycle piece works properly.
However, if it's run from a different computer - in this case, my local machine runs the script against a shared folder on a server - the delete is permanent, and doesn't get sent to the recycle bin. The script (in a prior run) makes a decision about WHAT to delete by first setting the Archive bit to TRUE and then (after seeing how many backups it is to retain) un-setting the Archive bit for items to be deleted on the next execution of that same script.
My thought was to alter the main script to mark the files for deletion, but only do the physical action of deleting the file(s) only when the script was being run locally, or to put the Recycle script (by itself) as a Task on the server that would delete & send the item to the Recycle Bin that would run at a set interval.
My questions-
In Powershell (using 2.0) how do you determine the source computer
vs the target computer? In this case, the script is being run from
MyPC, and it's target is Server1.
The script will run whether the target is a mapped drive (Drive Y:),
or if it's targeted by the servername (\Server1). How can you
distinguish the above question in both of these cases?
You can get the local computer name with $env:COMPUTERNAME. Use it to compare the value against the target server name.
For each file, you'd have to check first if the drive is a mapped drive, if it is, get the server name from the wmi instance and compare it to $env:COMPUTERNAME.
You can get a file's Drive qualifier with the Split-Path cmdlet:
PS> $drive = Split-Path Q:\test.txt -Qualifier
PS> $drive
Q:
And then get the server name with WMI:
PS> (gwmi win32_logicaldisk -filter "drivetype=4 and deviceid='$drive'").ProviderName.Split('\')[2]
Server1
The OP wrote:
#Shay - Thanks for your help. I've learned a great deal from many posts by you on various Powershell sites.
I was able to use almost everything you suggested, and only had to add an extra line of code to make it work. I checked the property ([System.Uri]$markedFile).IsUnc to determine if the filename I've read is a UNC name.
It returns False if the drive is mapped, and True if it is UNC. From that, I'm able to get the servername & make a comparison to the environment. Code follows.
$markedFile = "\\Server1\foldername1\Error.log"
#$markedFile = "Y:\foldername1\Error.log"
$TargetComputer = $null
$thisComputer = Get-Content env:computername
if (Test-Path $markedFile) { # if file exists
if (([System.Uri]$markedFile).IsUnc) { # if it's a UNC name & not a mapped drive name
$TargetComputer = ([System.Uri]$markedFile).Host
}
else { #file is not a UNC name, it must be a mapped drive
$drive = Split-Path $markedFile -Qualifier
$TargetComputer = (gwmi win32_logicaldisk -Filter "drivetype=4 and deviceid = '$drive'").Providername.split('\')[2]
}
}
The above code works either way. Thank you again for your help!