Check for File in multiple directories with an IF statement Powershell - powershell

I am facing some problems in powershell. I want to be able to let a powershell command search for multiple directories.
With the name being a variable like "$VM_DISK=VM_DISK.vhdx" and let powershell search in that manor so that if that file exists in a folder such as C:\VM_DISK\ it exit the script.
I have already tried the "Get-Childitem" but it doesn't seem to work when I put my variable in it. Here is an example:
$VM_DISK= "Example.vhdx"
$search=Get-ChildItem -Path C:\VM_DISK\* -Filter $VM_DISK -Recurse
if ($search -eq $VM_DISK) {write-host "Goodbye!" exit} else {write-host "Continue"}
I just cant seem to figure out why this isn't working, hope some can figure it out.

You need to alter your if statement.
if ($search.Name -contains $VM_Disk)
This way you are comparing an Array of names (which is what you want, names of objects, not objects) to a name of particular object (to a string, basically).
This makes little sense in your case, tbh. Since $search would always include $VM_Disk or would be null if nothing was found.
So the proper way to test would be if ($search) (just like Mathias advised). Which would test if anything was returned. Which, basically equals what you are trying to do.

Related

Get all references to a given PowerShell module

Is there a way to find a list of script files that reference a given module (.psm1)? In other words, get all files that, in the script code, use at least 1 of the cmdlets defined in the module.
Obviously because of PowerShell 3.0 and above, most of my script files don't have an explicit Import-Module MODULE_NAME in the code somewhere, so I can't use that text to search on.
I know I can use Get-ChildItem -Path '...' -Recurse | Select-String 'TextToSearchFor' to search for a particular string inside of files, but that's not the same as searching for any reference to any cmdlet of a module. I could do a search for every single cmdlet in my module, but I was wondering if there is a better way.
Clarification: I'm only looking inside of a controlled environment where I have all the scripts in one file location.
Depending on the scenario, the callstack could be interesting to play around with. In that case you need to modify the functions which you want to find out about to gather information about the callstack at runtime and log it somewhere. Over time you might have enough logs to make some good assumptions.
function yourfunction {
$stack = Get-PSCallStack
if ($stack.Count -gt 1) {
$stack[1] # log this to a file or whatever you need
}
}
This might not work at all in your scenario, but I thought I throw it in there as an option.

Powershell folder creation

I have a powershell script that creates folders on our NAS for each student according to their student numbers. The names for the folders comes from a .csv file that I import from the server. This is what I have got:
Set-Location "C:\studentdata"
$StudFolders = import-csv \\servername\datafolder\studfolders.csv
ForEach ($StudFolders in $StudFolders) {
if(Test-Path -Path C:\Studentdata\$StudFolders) {
New-Item $StudFolders.Name -type directory
}else{
"Folders already created"
}
}
This script works great, if I run it only once. If I run it again, I get errors in the console window about the folders already existing. What I want to do is catch the errors with the IF part of the script, but I am not sure if I have the correct usage of the IF for powershell. This will help if I edit the .csv with more student number it will display without errors.
Can someone point me in the right direction?
EDIT:
This is what I have in the studfolders.csv
Name
2003040052
2003060213
2003060310
2003060467
Lets take a look at your logic:
you are using an if statement, which works if something returns true
you are using a test-path cmdlet which returns true if something exists
See the problem? you need to do it vice versa:
if (!(test-path ...)) { ... } # ! - is the operator to invert true to false
or you can switch if and else content, so when if executes it returns "Folders already created", and else creates folders
As a matter of coding style, I would avoid using the same variable name with different semantics, as in ($Studfolders in $Studfolders). Code like this is very hard to read a few months down the road. I generally use the singular for each object in the collection, as in ($Studfolder in $Studfolders). But if your style works for you, OK.
Previous answers have already pointed out that your logic is backwards. You need to reverse it.
Next, it doesn't look to me as though you are accessing the component of each item in the loop. When you do an Import-Csv, several things happen: The first record in the csv file is treated as a header, providing the names for the fields that follow. If there is indeed a header in your csv file, you need to reference it when you retrieve the first field from each item, even if it's the only field.
The result of an import-csv is an array of custom objects. Each custom object looks like a hashtable that contains key, value pairs. Something like this might work
Set-Location "C:\studentdata"
$StudFolders = import-csv \\servername\datafolder\studfolders.csv
ForEach ($StudFolder in $StudFolders) {
if(Test-Path -Path C:\Studentdata\$StudFolder.Name) {
"Folders already created"
}else{
New-Item $StudFolder.Name -type directory
}
}
I have presumed that the first record in the Csv file looks like this:
"Name"
That is why I referenced the field as $Studfolder.Name"
If this isn't the case, you are going to have to do something different.
Having tried everything with the script, I could not get it to catch the errors. I decided to ask my manager, and he came up with the following solution that works quite well and catches the errors.
import-csv '\\servername\datafolder\studfolders.csv'|ForEach-Object -process {
$path =$_.Name
$path='C:\Studentdata\'+$path
if (Test-Path -Path $path){
"Folder already created"
} else {
New-Item $path -type directory
}
}
Thanks to #Walter Mitty and #4c74356b41 for help try to find an answer.

powershell confused about array loop, foreach method, string?

I'm trying to export all CA certificates to a directory in Base64 format, I'm new to powershell, since I'm used to doing scripts with bash. Somehow I'm not seeing something that feels like it should be obvious.
This is my line so far,
#(Get-ChildItem -path Cert:\Localmachine\ca).ForEach({Export-Certificate -Type CERT -FilePath "C:\ssl\certs.d\$_.Thumbprint" -Cert "Cert:\LocalMachine\ca\$_.Thumbprint"})
I appreciate any help, as I'm trying to learn how to be idiomatic in PS4.
This line of code contains 3 issues:
First. String interpolation with object property. PS parser doesn't understand "$var.Property", it only understands $expression within "string". But since it's expression, and not just variable name, you can make PS evaluate your line with "$(something to evaluate)". In other words, your -FilePath should be:
-FilePath "C:\ssl\certs.d\$($_.Thumbprint)"
Second. Working with objects. PS underneath is full-blown .Net Framework. Even though many objects are represented in output in a simple, predefined way, actually they are |ed to output as complete live objects. According to MSDN, the -Cert parameter is a <Certificate>, not a string pointing to a certificate, so your -Cert should be simply
-Cert $_
Third. Arrays. Get-ChildItem underneath is nothing more than DirectoryInfo.GetFileSystemInfos() which returns an array of objects. So ideally, you don't need to wrap it with anything and it's possible to simply pipe it further (Get-ChildItem | Foreach-Object{...}). But many people have different tastes on PS syntax, so the form of (gci).ForEach({...}) (without #) has the right to live as well. But what you are doing in form of #(...) is creating a new array of one item being the array returned to you by gci. So technically, it just shouldn't work. It will though, because PS saves you from such mistakes automatically: in PS you can work with array of 1 item in the same way as with this item directly (unless explicitly specified opposite). To illustrate,
#(4).Length # returns 1
#(#(2,3)).Length # returns 2
#(,#(2,3)).Length #returns 1
Thus, your current syntax for Get-ChildItem is error-prone and relies on automatic PS error-handling sugar. I recommend to either remove # in the beginning, or to rewrite in form of
Get-ChildItem -...... | Foreach-Object {...}

Finding the index of a key and value within an array using a wildcard in Powershell

I have been working on an issue and I was able to get done what I need to get done in a relatively decent and acceptable way but I am curious as to why I ran into some of the problems I did.
I'm not too savvy with PowerShell or C# but I have some experience with Java, C++, and a few others. So, if I overlook something really simple, you'll have to forgive me. I'm not looking for a critique of my solution, just some insight into some of the blockades I came across.
What I needed to do was use Powershell to query an LDAP setting. I needed to know MaxConnIdleTime and I needed that either assigned to a variable or accessible through a subroutine [sic] (ex $ldapPolicies.MaxConnIdleTime) so that I could run it through a conditional statement.
Here is how I accomplished it:
$ldap = Get-ADObject -SearchBase "CN=Query-Policies,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=$ENV:COMPUTERNAME,DC=$dc" -Filter 'ObjectClass -like "queryPolicy"' -Properties ldapadminlimits
$ldap = #($ldap.ldapadminlimits)
$ldap | %{
if($_.startswith("MaxConnIdleTime")) {
$match = $_
}
}
I tried NTDSUtil.exe, however, I couldn't redirect the "Show Values" to a text file to read from later and I couldn't write it to a variable in PowerShell.
I tried Start-Transcript and then ran NTDSUtil but it only recorded what occurred within PowerShell and not what happened in NTDSUtil.
Also, I tried giving all of the commands to NTDSUtil at once (NTDSUtil "ldap policies" "connections" "connect to server $ENV:COMPUTERNAME" q "Show Values") but PowerShell doesn't show anything in the console and I have press the exit sequence to return back to PS>.
I know that I could use LDP but I'm not too familiar with ADSI. Research appeared to say that going about attempting to get an LDPdump is a bit antiquated and I pretty much abandoned that attempt.
One of the issues that I had that caused me a small bit of frustration (and the reason I am asking this question) is why can I not search an array and find the index of an item using a wildcard? I tried doing this:
$ldap.IndexOf("MaxConnIdleTime*")
AND
$ldap.IndexOf($ldap -like "MaxConnIdleTime*")
but it always returned -1.
It would work correctly if I tried:
$ldap.IndexOf("MaxConnIdleTime=100")
given that the value was indeed 100. But I am validating that the value was correct.
I know that I could just do something like this:
if($ldap -contains "MaxConnIdleTime=100") {
DO SOMETHING...
} else {
DO SOMETHING ELSE...
}
Why is it that I can't search an array using a wildcard operator? There was no ambiguity, so, it should have worked, right?
I'm not looking for a critique of how I accomplished this, I'm just wanting to understand why it behaved like it did.
Thanks.
I don't think there's a straightforward "search an array by wildcard and return an index" cmdlet, method, statement, etc. in PowerShell.
.IndexOf is not designed to work with a wildcard.
When you used the -like operator on the array, you likely found only a single matching object, but -like returns an array of matches when used on an array.
Passing the array into .IndexOf() then looks for an array element that is itself an array, even if that array only has one object.
This would work:
$ldap.IndexOf(($ldap -like "MaxConnIdleTime*")[0])
As long as you always wanted to find the first one.

Check if an item exists without an error if it doesn't exist

I'd like to use PowerShell to check whether an IIS Web Application exists (or potentially some other kind of item). I can do this with Get-Item, but that reports an error if the item doesn't exist, which is misleading to the user running the script - it looks like something went wrong when actually everything is fine.
How do I do this without an error?
The cmdlet Test-Path is specifically designed for this, it determines whether items of a path exist. It returns a Boolean value and does not generate an error.
The cmdlet Get-Item (and similar) can be used, too, but not directly. One way is already proposed: use -ErrorAction SilentlyContinue. It might be important to know that in fact it still generates an error; it just does not show it. Check the error collection $Error after the command, the error is there.
Just for information
There is a funny way to avoid this error (it also works with some other cmdlets like Get-Process that do not have a Test-Path alternative). Suppose we are about to check existence of an item "MyApp.exe" (or a process "MyProcess"). Then these commands return nothing on missing targets and at the same time they generate no errors:
Get-Item "[M]yApp.exe"
Get-Process "[M]yProcess"
These cmdlets do not generate errors for wildcard targets. And we use these funny wildcards that actually match single items.
Use the command ... get-item blah -ErrorAction SilentlyContinue