Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Drumstick posted:

Is it possible to check folder permissions and then print the folder names if the permissions includes a specific user/group using powershell?

Yes, though it was actually more complicated then I thought. I may have overcomplicated a bit, so if anyone has a more elegant solution, I would love to see it.

Here is the code:
code:
$Identity = "Jelmylicious"
$Path = "c:\users" 

$ACLlist = get-childitem $Path | ? {$_.PSisContainer} | get-acl 
foreach ($ACL in $ACLlist)
    {
    foreach ($access in $ACL.access) 
        {
        if ($access.identityreference -match $identity) 
            {
            Write-host $Identity has the right $access.AccessControlType $access.FileSystemRights to $ACL.path.substring(38)
            }
        }
    }    

Now, lets break this down. First we set who we want to search for and where, in $Identity and $Path. These are strings, so we put them in quotes:
code:
$Identity = "name"
$Path = "c:\users"
Next we do a get-childitem, to get all objects in the given path (add a -recurse if you want to get all subfolders too)
code:
get-childitem $Path 
Then we filter for folders (? is an alias for where)
code:
| ? {$_.PSisContainer}
and get all access control lists
code:
| get-acl 
Next step is to identify each access indentityreference in each ACL, hence the double foreach:
code:
foreach ($ACL in $ACLlist)
    {
    foreach ($access in $item.access) 
And we check the identityreference:
code:
if ($access.identityreference -match $identity) 
Now we finally filtered everything, we can output. But, just stating the folders isn't enough. Are the permissions set to allow, or deny? Is it full control or read only?
[code
Write-host $Identity has the right $access.AccessControlType $access.FileSystemRights to $ACL.path.substring(38)
[/code]
The substring part is to cut off the first 38 characters, because $ACL.path would just output Microsoft.PowerShell.Core\FileSystem:: before the string.
Controltype tells us Allow or Deny, Filesystemrights the actual rights being allowed or denied. Or, if you want to see if the rights are inherited from a parent folder, add $access.isinherited to the output.
You could clean this up, because some of the FileSystemRights are in the form of an integer, such as 268435456, which means full control on all FILES under the specified root directory, according to Here. You could rewrite the integers to human readable strings, if you knew which is which.

Output will look like this:

Jelmylicious has the right Allow 268435456 to C:\users\Jelmylicious
Jelmylicious has the right Allow FullControl to C:\users\Jelmylicious

Jelmylicious fucked around with this message at 10:04 on Oct 28, 2011

Adbot
ADBOT LOVES YOU

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

joe944 posted:

Scripting noob here working on my first powershell script and I'm wondering if anyone would be able to help me out.

What I'm trying to do is periodically check the external IP address of a machine and determine if it has changed or not. Preferably if the IP has changed it would create an event log that will be picked up by another monitoring tool.

Feel free to tell me I'm retarded for what I've got so far. It's not complete and doesn't quite do what I want yet, but I think I'm on the right track. I need to do some serious reading up on powershell in general but I've picked up a little bit working on this script so far.

code:
$filename = "path\iplog.txt"
$time = " "+(Get-Date).ToString()
$source = "http://whatismyip.org"
$client = new-object System.Net.WebClient
$webpage = $client.downloadString($source)
$lines = $webpage.split("'n")
foreach ($line in $lines) {
    $value = "6522 "+$line +$time
    $value >> $filename
}
$oldip = get-eventlog -logname 'IP Check' -newest 1 | select message

Write-EventLog  -logname 'IP Check' -source 'IP Check' -eventid 1000 -entrytype information -message $line

Few questions/Comments:

You put the old IP address into $oldip, but you never do anything with it.
Do you want to log an event every time the IP changes, or (like you currently do) every time the script checks?
You will never clean your iplog.txt (I assume you use a valid path in the path part). This will slowly grow, even though you are also putting this info in the IP check eventlog.
First run fails on the $oldip line, because the first log isn't there. I will go practice my try/catches.

Edit: Here's my take. I simplified it a bit:
  • You don't need the log file (unless you want it).
  • Removed the foreach loop, because your $lines only contained the IP anyway, renamed that to $IP
  • Removed the select message, Because it is only for display purposes
  • Added in a check for $OldIP.message, to compare IP addresses
  • Made sure the custom eventlog is there, if not, make it.
  • Made two different event-id codes: ID 1000 on first run, 1001 on IP changes. You could also have it log every time it runs and differentiate between changes and no changes. Just add an else to the if.
code:
#Get IP Address
$source = "http://whatismyip.org"
$client = new-object System.Net.WebClient
$webpage = $client.downloadString($source)
$IP = $webpage.split("'n")

#Check if the custom eventlog exists, if not: create it
try 
    {
    get-eventlog -logname 'IP Check'
    }
catch 
    {
    write-host Creating Eventlog
    new-eventlog -logname 'IP Check' -source 'IP Check'
    }

#Try to get eventlog, then compare. Write EventID 1001 on IP change, Write EventID 1000 on first run
try 
    {
    $OldIP = get-eventlog -logname 'IP Check' -newest 1 
    If (!($OldIP.message -eq $IP)) 
        {
        Write-EventLog  -logname 'IP Check' -source 'IP Check' -eventid 1001 -entrytype information -message "$IP"
        }
    }
Catch #First entry into Eventlog
    {
    Write-EventLog  -logname 'IP Check' -source 'IP Check' -eventid 1000 -entrytype information -message $IP
    }
Now, we could always make it slicker, by making the eventlogs more readable. That would need some better text-parsing when comparing the IP addresses though. If you could connect an SMTP server reliably from this computer, you could have it e-mail you on changes. But that is dependent on the final goal of this script.

Jelmylicious fucked around with this message at 11:05 on Nov 10, 2011

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Since my script compares IP's when writing to the eventlog, you can have it output to a textfile in the same if-statement. If you want to add an eventlog for every check, we could do something like this (only relevant code shown). Note that, since I added an else statement, I removed the negatement in the if statements, for legibility purposes.

code:
#Try to get eventlog, then compare. Write EventID 1001 on IP change, Write EventID 1000 on first run or no change
try 
    {
    $OldIP = get-eventlog -logname 'IP Check' -newest 1 
    If ($OldIP.message -eq $IP)
        {
        Write-EventLog  -logname 'IP Check' -source 'IP Check' -eventid 1000 -entrytype information -message "$IP"
        }
    Else
    {
    $time = 
    Write-EventLog  -logname 'IP Check' -source 'IP Check' -eventid 1001 -entrytype information -message "$IP"
    $after = (Get-Date).ToString("D")
    $before = $oldIP.timewritten.tostring("D")
    $message = "IP Changed to $IP between $before and $after"
    $message | out-file $filename
    }
    }
Catch #First entry into Eventlog
    {
    Write-EventLog  -logname 'IP Check' -source 'IP Check' -eventid 1000 -entrytype information -message $IP
    }
And, in the same block (or maybe make that a function), you can add code to e-mail $message, using send-mailmessage (Powershell v2 only)

Edit: If you are running this on computers with other languages than english, it could be a good idea to force your tostring to use english, like this (replace "en-US" with whatever you want, like "nl-NL" for Dutch).
code:
$Culture = New-Object System.Globalization.CultureInfo("en-US")
$before = $oldIP.timewritten.tostring("D",$Culture)
Different formats for putting time in a string can be used. I noticed I used "D" up there, which is not that good in this instance (no time of day). "r" might be better, which will give you: Thu, 05 Jun 2008 16:40:37 GMT, or "f", since you don't need seconds. "f" will give you: Thursday, June 05, 2008 4:40 PM. See here for details.

Jelmylicious fucked around with this message at 11:51 on Nov 10, 2011

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

KS posted:

I ran into so many things I need to learn along the way with the format of the data and breaking it up into useful chunks: system objects vs. arrays vs. strings and the tools you can use for each. For instance, you can write "select $a" to a file and it works, but you cannot write "select $a.substring(2,13)"

I know it shouldn't be this hard, and I really want to learn how to do this better (and generalize it for result sets of indeterminate size). I just feel lost without awk and sed.

Well, for the indertimenate size, you could use a foreach loop. Without adjusting your code too much:

code:
$diskpart = diskpart /s diskpartlist.txt | Select-String -pattern "Hidden"
Foreach ($volume in $diskpart)
    {
    $a = $Volume.tostring()
    $a = $a.substring(2,13)
    $a = "select $a"
    write-output $a "att vol clear readonly" "att vol clear hidden" "att vol clear shadowcopy" | out-file diskpart.txt -append
    }
This is quick, dirty and not tested, because it is late. Also, for basic string manipulation, put stuff outside of quotes:
code:
$a = "Select " + $a.substring(2,13)

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
My turn for a question:

I will be giving my companies internal Introduction to Powershell course. It will be a two day course for anyone ranging from "never touched powershell" to seasoned sysadmins that know VB by heart, but want to get to know powershell better. (oh god, what am I getting myself into? :ohdear:)
The current iteration of the course is a bit to dry and doesn't allow for that much input from the students about what they want to learn. I am going to make this course more hands-on and practical. My question to you is:
Is there anything that you ever ran into, while using powershell, where you thought to yourself: "If only I had known this when I started out, this would have saved so much time!"
Help me make this course awesome, so my newly made minions can help answer questions in this thread.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

bluegoon posted:

$a = get-childitem C:\Comps\complist.txt
Get-EventLog system -computername $a -newest 3 > systemout.txt

I want to have a list of machines in complist.txt, and have powershell output their eventlogs (system) to systemout.txt, very very new to logic and programming.

Get-Childitem is enumerating files in a folder, it is the new "dir". What you want is:
code:
$computers = get-content C:\Comps\complist.txt
As for the rest, apart from having to use -logname in front of system, it should work. But I would prefer exporting to a CSV for better manipulation of the data afterwards, like this:
code:
Get-EventLog -computername $computers -newest 3 -logname "System" | Export-CSV systemout.csv
Also, try not to use $a far any variable, it isn't readable

e: Aparently Get-Eventlog System should work too, so your second line should work as intended. Apart from it not showing the full info in your text file. So, at least use the export-csv.

Jelmylicious fucked around with this message at 16:13 on Nov 23, 2011

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
My quick test showed a nice CSV cooming from this, so I figured it would fit here. I always try using it first, if it doesn't work, then I start looking at alternatives/fixing it.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

psylent posted:

I'm incredibly new to Powershell, as a senior helpdesk monkey I can see how it's going to be incredibly helpful.

I've got a shitload of user accounts (100+) of people that have left the company that I need to clean up/archive. My first step is to move them all into a "To be Terminated" OU. I then need to remove them from all Security/Distribution lists (except Domain Users) - this is where Powershell will come in handy as I'd prefer to run a quick script rather than go into each user and remove the groups manually!

It looks like I need a script that will strip group memberships based on the object's OU. With a bit of Googling, I found this one but am having a bit of trouble deciphering it:
code:
Get-ADGroup -SearchBase "OU=YOUROU,DC=DOMAIN,DC=COM" -Filter* | Foreach-Object{ 
$Members = Get-ADGroupMember -Identity $_ | Where-Object {$_.objectClass -ne 'computer'} 
Remove-ADGroupMember -Identity $_ -Members $Members -Confirm:$true 
} 
Can anyone help me out?

Get-AdGroup will return security groups in a given OU. So this command seems to strip all members of the groups you get, except for computer accounts. I will dissect the command for you later.

What you need, is a command that gets all the users in an OU and then strip their memberships.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Moey posted:

Looking for a quick pointer in the right direction. Had some success last night using powershell to do some bulk file renaming at home. Get into work today, now I actually get to try and use some of that!

I would like to try and hack through alot of this on my own, but currently I am trying to take a directory with some files in it, then go through and create a folder in that directory with the name of each file name.

Example would be I currently have c:\ps which contains a.txt b.txt c.txt

I need to then create 3 folders, c:\ps\a, c:\ps\b, c:\ps\d

Once that is created, I need to move the respective txt files into the respective folders.

Currently hung on creating the folders by the file names. I was trying something along the lines of

code:
gci |new-item -name $_.Name -itemtype "directory"
But that is complaining that c:\ps already exists.

Well, when creating a folder manually in explorer with the same name as a file, it asks me if I want to overwrite. You could chop off the extension (or add a .dir extension or something) to differentiate the name.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Wicaeed posted:

Always returns "Drive backup does not exists". Is there some secret I'm missing to using variables and boolean operators in functions?

This is a problem of scope, your Variable gets set inside the function, but doesn't exist outside it. You could try three things: you could move the Write-Host inside the function, you could not use a function, but have it in the main executing code, or you could have the function return it:

code:
Function get2k3DBackupStatus
 {
 If ((Test-Path "C:\dell") -ne $false)
  {return "Drive backed up on $CurrentDate"}
 Else
  {return "Drive backup does not exists"}
 }

$BackupFolder2K3DInfo = get2k3DBackupStatus	
Write-Host $BackupFolder2K3DInfo
You were calling the function, right?

Also, since Test-Path already returns a boolean, you could omit the -ne $False, so, just write: if (Test-Path "C:\dell"). But, that is more of a style choice, just keep it readable for you and your colleagues.

Jelmylicious fucked around with this message at 16:44 on Dec 21, 2011

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Wicaeed posted:

Okay, thanks for that clarification. The reason the vbscript is run like it is, is because that the results are later send via email, and from what you said regarding functions, you can't actually store the results as a variable that exists outside the function?

I was under the impression that Functions functioned (:v:) somewhat like subroutes in vbscript. Is that not the case?

If I DID want to store the results to call back on, but keep it inside the function, is that even possible?

That is a fourth possibility, make it a global variable, by starting your variable with $global:

code:
Function get2k3DBackupStatus
{
If ((Test-Path "C:\dell") -ne $false)
{$global:BackupFolder2K3DInfo = "Drive backed up on $CurrentDate"}
Else
{$global:BackupFolder2K3DInfo = "Drive backup does not exists"}
}	
Write-Host $BackupFolder2K3DInfo

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Moey posted:

Didn't know that name would grab the extension as well. I am still having a problem as $_ isn't pulling files from that directory. This script will just create a folder called .basement, then error out for the rest of the items.

Are you running it from the folder? Otherwise use GCI C:\PS (don't forget to add it to the beginning of your new foldername to).

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Wicaeed posted:

Sweet. HOWEVER, I have to be setting my function incorrectly, or something. Any ideas?

You are not calling you function:

code:
Function Example 
    {
    write-host "Function has been run"
    }
will do nothing, while
code:
Function Example 
    {
    write-host "Function has been run"
    }

Example
will print Function has been run. Powershell only runs a main code block, and ignores functions until called in that block. The functions have to be in the beginning, because it does read it sequentually. So it won't know a function defined afterwards.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
The {} are there, because a function can contain multiple lines, and PS needs a way to delimit this.

code:
Function Example2 { write-host "Function has not been run!" }
works exactly the some as
code:
Function Example2 
   { 
   write-host "Function has not been run!"
   }
With one line, the benefits aren't apparent. But, if you have multiple lines and indent like that, it is easier to see where a function ends. I personally like to put the brackets at the same amount of indentation, some people put the opening bracket right after the function, like so:

code:
Function Example2 { 
   write-host "Function has not been run!"
   }
It gets even more interesting when things are nested
code:
Function Example2 { if ($Statement -eq $true)   {write-host "Function has not been run!"} else {write-host "Error"}   }
is less readable than
code:
Function Example2 
   { 
   if ($Statement -eq $true)   
       {
       write-host "Function has not been run!"
       } 
   else 
       {
       write-host "Error"
       }
   }
e: Can't spell. Also, if you are really interested, read http://en.wikipedia.org/wiki/Indent_style. Pick one that seems clear to you and stick with it.

Jelmylicious fucked around with this message at 17:23 on Jan 5, 2012

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

i barely GNU her! posted:

code:
Where-Object {$_.CreationTime.Month -ne ([System.DateTime]::Now.AddMonths(-1).Month -and $_.PsIsContainer }
Will list all objects that are directories (PS calls those containers for various reasons) whose creation time month is not equal to the current date's month-1. That also wraps around January->December correctly. Remember that Where-Object needs to be a filter, e.g. Get-ChildItem | Where-Object {...} | Format-List

When you run this in januari, it will filter out anything made in december of any year. So this doesn't work as asked, when you span multiple years. Also, it doesnt filter out the current month (which I also needs to be filtered out).
e: And that is exactly what he asked, but I already made this, so here you go, getting to first day of that month:

code:
$Today = get-date
$CleanedDate = $today.addDays(1-$today.day) #1 extra to get to first day of the month, not last day of previous month
$CleanedDate = $CleanedDate.Addmonths(-1)
And, depending on how close to midnight you need to come, you can do:
code:
$CleanedDate = $CleanedDate.Addhours(-$CleanedDate.hour)
$CleanedDate = $CleanedDate.Addminutes(-$CleanedDate.minute)
$CleanedDate = $CleanedDate.Addseconds(-$CleanedDate.second)
$CleanedDate = $CleanedDate.AddMilliseconds(-$CleanedDate.millisecond)
Now you can filter with a -lt $CleanedDate. Want it modular (first of however many months ago)?
code:
function FirstOfMonth
    {
    param ($Months = 0)
    $Today = get-date
    $CleanedDate = $today.addDays(1-$today.day) #1 extra to get to first day of the month, not last day of previous month
    $CleanedDate = $CleanedDate.Addmonths(-$Months)
    $CleanedDate = $CleanedDate.Addhours(-$CleanedDate.hour)
    $CleanedDate = $CleanedDate.Addminutes(-$CleanedDate.minute)
    $CleanedDate = $CleanedDate.Addseconds(-$CleanedDate.second)
    $CleanedDate = $CleanedDate.AddMilliseconds(-$CleanedDate.millisecond)
    return $CleanedDate
    }
This function will return first of the month of the current month if you just invoke FirstOfMonth, and will subtract extra months if you add a parameter, so:
FirstOfMonth(1) will give you: donderdag 1 december 2011 0:00:00 (Locale is set to Dutch, but you get the gist).

If anyone is interested in forcing the locale so you can read the date, no matter what language the server is set to, let me know. (Very important if you work with the currency object, because it would display as 100 dollars, or pounds, or Rupee depending on the locale, without converting the currency)

Jelmylicious fucked around with this message at 17:01 on Jan 6, 2012

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Since you seem interested in finding stuff out for yourself, let met introduce you to get-command, get-help and get-member

Walter_Sobchak posted:

1) Is there a way to specify a file by the Date Modified column? I basically want to have a script that auto-deletes the oldest file in a certain folder when I run it.

You are working with files, command for that is get-childitem (or ls for short). Since you want to know what prperties you get with this, you pipe it to get-member (or gm for short):

code:
ls | gm
Output is a wall of properties and functions for System.IO.FileInfo, one of which is:
CreationTime Property System.DateTime CreationTime {get;set;}
So yes, it is possible.
To get the oldest file, try something like this:
code:
$FileList = LS
$oldest = $FileList[0]
foreach ($file in $filelist)
    {
    if ($file.creationtime -lt $oldest.creationtime) 
        {
        $oldest = $file
        }
    }
$oldest.delete()
So, we take the first entry to have something to compare to, and just keep the oldest file so far in $oldest, then delete it. Now on to your next question:

Walter_Sobchak posted:

2) How do you check to see if a program is already running? I'm trying to write a script that checks to see if a couple different programs are running, and if not, to start them.

For this, you are looking for a process that is running. Let's see if powershell has any functions with process in the name, using get-command (gcm for short)
code:
gcm *process*
one, of the entries returned is get-process, sounds promising. Now, how do i use it? Let's ask powershell:

code:
get-help get-process
Tells me: Gets the process that are running on the local computer or a remote computer. Sounds promising, now, how do we use it?
code:
get-help get-process -examples
Whoa, wall of text! But we see a few examples and can start experimenting:

get-process explorer

returns:
code:
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessNam
-------  ------    -----      ----- -----   ------     -- ----------
   1388      84    85024      48024   442 1,052.94    708 explorer
Yay, explorer is running. Now, let's try get-process Jelmylicious

returns a big red error. So I am not running, it seems. But we don't want that error. So, let's pipe to a filter:

code:
get-process | Where {$_.name -eq "Jelmylicious"}
Returns nothing, while the same, but with explorer it returns a value. Now, to make it conditional so you can do something based on whether the process is running or not:
code:
$ProcessRunning = (get-process | Where {$_.name -eq "Jelmylicious"}) -ne $null
Now we have a boolean $ProcessRunning that will equal $true when process is running and $false when it is not running.

E: The sort option two posts before me is more elegant, I was too hung up on explaining how to find info...

Jelmylicious fucked around with this message at 08:38 on Feb 3, 2012

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Also, if you remove all access rights first, you won't have any rights yourself. Which means you don't have rights to set accessrights.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
You can append strings with a simple + so:

$Destinationfolder + $item.name should work.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Wicaeed posted:

Seriously, I've quite thoroughly enjoyed my time with Powershell thus far, I just wish I had more excuses to use it at work :(

Is there anything you do semi-regularly that could be automated? Go on! Script yourself out of a job!

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Hmmm, that's an interesting one. The padding is harder than I thought, or at least, I couldn't find an option for it. (I tried cropping a negative amount of pixels, but that seems to error out.) Filtering is easy:

code:
$image = New-Object -ComObject Wia.ImageFile
$directory = $pwd   #set directory as current working directory, adjust as needed
$FilteredList = @() #empty array to add the images later
$ImageList = gci $directory/* -Include *.jpg   #add a -recurse for subfolders

ForEach($JPG in $ImageList)
    {
    $image.loadfile($JPG.versioninfo.filename)
    if ($image.width -lt 500)
        {
        $FilteredList += $JPG.versioninfo.Filename
        }
    }
This will return a Array of filenames you can use to manipulate the images later.

Only thing I could think of, is to have the script create an empty image, of $image.height * 500, and use the "stamp" filter to add the image over the blank one. See this MSDN page for an example in .NET.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
On the subject of really using .NET, I am only a noob at that, I think Adaz or someone else will be better suited to answer that. And Angrytech: what properties are you trying to get from the computer object? Have youtried simply calling $_.propertyname or used get-adcomputer with the -property flag? Have you checked the Technet page on get-adcomputer

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Could very well be a UAC error, since server 2008 doesn't like you putting things in the C:\ root. Either put it in a folder or elevate your powershell.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Ooh, I really should try out that Beta! Thanks for the link.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Sorry to double post, but this is just too awesome. The new ISE is way more usable! No more seperated input and output pane is good. But the best thing is the syntax checking. Forgot to close some parenthesis? It will highlight your dangling one:



Woohoo! It does the same thing in the powershell pane in ISE. Try putting in the following (which just feels wrong to leave like this)
code:
$string = "text
The highlighting while typing is a bit distracting, especially since any variable you type starts bright red. Red -eq error, damnit! I do like that you can now fold your script blocks. I only wish it would auto highlight all instances of a variable when you select it, but you can't have everything.

Jelmylicious fucked around with this message at 13:35 on Mar 22, 2012

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
This seems to be in the wrong order:
code:
$destination = ($env:USERPROFILE + "C:\Documents and Settings\" + "\Desktop\mylink.url")
Also, never tried to use an elseif. It should work, but I always prefered switch for those things.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Dreadite posted:

I just made a simpler script to copy the shortcut from a network location applied as a logon script at the group OU level in GP.

Since 2008 you can add shortcuts directly through Group Policy Preferences. And you can put it all in one policy and have it select the right one through Item Level Targeting.

Anyway, don't discount your hour fiddling with this, practice with powershell is always valuable! Even when it won't pan out. Part of the process is knowing when you should or shouldn't use PS.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

stubblyhead posted:

Sorry to quote from so far back, but good lord you are not kidding about this cmdlet. I have a multi-line comma-delimited string, you stupid piece of poo poo! Why won't you let me just pipe it straight in!

If it is already comma delimited, just pipe it to a text file with .csv as its extension.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

NaughtyHusky posted:

I have a quick question, I am using the following in order to create a last written file output, however I want to format the date in UNIX time (e.g. UFormat %s) - any help or direction would be great. Do I need to go down the object route?

> Get-ChildItem c:\ -recurse | Select-Object FullName, LastWriteTime, length

This should work:

code:
$BeginningOfUnixTime = Get-Date –Date '01/01/1970'
Get-ChildItem c:\ -recurse | Select-Object FullName, @{Name="UNIX Time"; Expression = {($_.LastWriteTime.ToUniversalTime() - $BeginningOfUnixTime).TotalSeconds}}, length
Nicer would be to make a function that converts to unixtime, keep everything more readable:

code:
$BeginningOfUnixTime = Get-Date –Date '01/01/1970'

Function ConvertTo-UNIXtime
    {
    param($Timestamp)
    return ($Timestamp - $BeginningOfUnixTime).TotalSeconds    
    }
    
Get-ChildItem c:\ -recurse | Select-Object FullName, @{Name="UnixTime"; Expression={ConvertTo-UNIXtime $_.LastWriteTime}}, length

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Drumstick posted:

The folders are their user names. Could I create an array of the names, then use that to assign ownership of the proper folder? Or can I do a for each, read the name then assign that ownership without the array? I'm way to inexperienced at this.

Yes, Basically you read Foldername, get the user from AD and use Set-ACL to set the new permissions. Be careful not to lock yourself out by overwriting admin permissions. I'll see if I can get you some code.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Drumstick posted:

Fantastic, thank you so much. Im glad i'm at least on the right track. I didnt even consider the possibility of locking myself... Im glad I didnt just wing it.

Couldn't fully test, since I am away from my domain right now. This should work. Shouldn't have to tell you, but try it on a testfolder first:

code:
$Location = "e:\Path\to\Profiles"
$Domain = "YourDomainName"
$FolderList = Get-ChildItem $Location
$ACL = Get-ACL $Location #Get Original Permissions of Folder

foreach ($Folder in $FolderList)
    {
    $User = $Domain +"\"+ ($folder.name)  #Make full username as Domani\User
    $permission = $User,"Modify","Allow"  #Permission Rule
    $AccessRule = new-object System.Security.AccessControl.FileSystemAccessRule $permission
    $UserACL = $ACL                       #Reset userACL
    $UserACL.SetAccessRule($AccessRule)   #Modify This ACL with Userpermissions
    set-acl -Path $Folder.FullName -AclObject $UserACL
    }

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

adaz posted:

You need to set inheritance and propagation flags when you create your new access rule.

Right. It's been a while since I did this and wasn't near my domain. Totally forgot.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Korlac posted:

Here's another diagnostic tool out there for any Exchange Administrators. Sometimes you need to look up Application events across several servers, this will let you determine which server role (including all Exchange Servers), which event logs you wish to parse, the event level, and finally the Event ID. Once all that data is selected through the Powershell menu, it will generate a Text file on your desktop with all the matching results.


That looks really nice, but it doesn't account for fat fingering. You can easily change that by using the default switch and a recursive function:

code:
Function Select-Mailserver
{
    Write-Host -fore green "Which Server would you like to search?"
    Write-Host -fore yellow "M: Mailbox Servers."
    Write-Host -fore yellow "H: Hub Transport Servers."
    Write-Host -fore yellow "C: Client Access Servers."
    Write-Host -fore yellow "A: All Exchange Servers."
    $a = Read-Host "Select M, H, C, or A"
    Write-Host " "
    	Switch ($a)
	{
		M {return Get-MailboxServer}
		H {return Get-TransportServer}
		C {return Get-ClientAccessServer}
		A {return Get-ExchangeServer}
                default {Write-Host -fore red "Wrong Input, try again."; Select-MailServer}
	}
}

$servers = Select-MailServer
Next step for upgrading your script would be to make it into a full cmdlet, having a helpfile and letting it take arguments. I'll see if I can write it out for you, if I have time later.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Alright, I had fun making it into a real cmdlet. I just wanted to explain autohelp and parameters. I just used your script as an excuse to do it. Feel free to ask about any of it of course. First the code, then I will explain what I did. I kept the functionality of your script the same.

code:
<#  
.SYNOPSIS  
    This script reads eventlogs of exchange servers by role or eventlog level. 
.DESCRIPTION  
    This script reads eventlogs of exchange servers. You can filter by serverrole and or event level. Default will output to a file on your desktop.
.NOTES    
    Author:              Korlac & Jelmylicious
    First Creation Date: DATE UNKOWN
    Last Edited Date:    15-May-2012
    Created For:         SomethingAwful

    ChangeLog: 
    DATE UNKOWN: Korlac: Functional Creation of script
    15-May-2012: Jelmylicious: Added parameters, helpfile and basic errorcontrol.
.LINK  
    Sources used to create this script:
    Base Script:
    [url]http://forums.somethingawful.com/showthread.php?threadid=3286440&pagenumber=13#post403624126[/url]
.EXAMPLE  
    Get-ExchangeEventlogs
    
    Description
    
    -----------
    This command prompts for all variables and will save the output to your desktop in a text-file named after the SRX number you provide.
.EXAMPLE  
    Get-ExchangeEventlogs.ps1 -Outfile ./DNSErrors.txt -LogName "System" -EntryType "Warning" -EventID 1014
    
    Description
    
    -----------
    This will prompt for server role, then find all warning events with ID 1014 in the systemlog, which are DNS errors. All entries will be saved in DNSErrors.txt in the current directory.
.PARAMETER Outfile  
    Output filename.txt will output to a file/location of your choice.
.PARAMETER Servertype  
    Select servers by Exchange role. Possible values: Mailbox, Hub, Client or AL
.PARAMETER LogName  
    Select Eventlog to probe. Possible values: Application, Security, Setup, System
.PARAMETER Entrytype
    Select the type of entry you want to collect. Possible values: Error, Information, FailureAudit, SuccessAudit and Warning
.PARAMETER EventID
    Event ID of the event you are looking for. Simple number.    
#> 

param 
    (  
    [string] $Outfile 
    ,
    [parameter(HelpMessage="Possible options: Mailbox, Hub, Client or ALL")]
    [ValidateSet("Mailbox", "Hub", "Client", "All")]
    [string] $Servertype
    ,
    [parameter(HelpMessage="Possible options: Application, Security, Setup, System")]
    [ValidateSet("Application", "Security", "Setup", "System")]
    [string] $LogName
    ,
    [parameter(HelpMessage="Possible options: Error, Information, FailureAudit, SuccessAudit and Warning")]
    [ValidateSet("Error", "Information", "FailureAudit", "SuccessAudit", "Warning")]
    [string] $EntryType
    ,
    [int] $EventID
    )


Function Select-Mailserver
{
    param ($a)
    if ($a -eq $null)
    {
        Write-Host -fore green "Which Server would you like to search?"
	    Write-Host -fore yellow "M: Mailbox Servers."
	    Write-Host -fore yellow "H: Hub Transport Servers."
	    Write-Host -fore yellow "C: Client Access Servers."
	    Write-Host -fore yellow "A: All Exchange Servers."
        $a = Read-Host "Select M, H, C, or A"
	    Write-Host " "
    }
	Switch ($a)
	{
		M {return Get-MailboxServer}
		H {return Get-TransportServer}
		C {return Get-ClientAccessServer}
		A {return Get-ExchangeServer}
        default {Write-Host -fore red "$a is not one of the options, please try again."; Select-Mailserver}
	}
}

Function Select-Eventlog
{
    Write-Host -fore green "Which Event Log would you like to search?"
    Write-Host -fore yellow "A: Application logs."
    Write-Host -fore yellow "S: Security logs."
    Write-Host -fore yellow "E: Setup logs."
    Write-Host -fore yellow "Y: System logs."
	$b = Read-Host "Select A, S, E, or Y"
    Write-Host " "
	Switch ($b)
	{
		A {return "Application"}
		S {return "Security"}
		E {return "Setup"}
		Y {return "System"}
        default {Write-Host -fore red "$b is not one of the options, please try again."; Select-Eventlog}
	}
}
function Select-Entrytype
{
    Write-Host -fore green "Which event level are you searching for?"
    Write-Host -fore yellow "E: Error."
    Write-Host -fore yellow "W: Warning."
    Write-Host -fore yellow "F: FailureAudit."
    Write-Host -fore yellow "S: SuccesAudit."
    Write-Host -fore yellow "I: Information."
	$c = Read-Host "Select C, W, F, S, or I"
	Write-Host " "
	Switch ($c)
	{
		C {return "Critical"}
		W {return "Warning"}
		V {return "Verbose"}
		E {return "Error"}
		I {return "Information"}
        default {Write-Host -fore red "$c is not one of the options, please try again."; Select-Entrytype}
	}
}

######################
# Start of main body #
######################

#Gather missing information
if ($Servertype -eq $NULL) 
    {$Servers = Select-MailServer}
Else
    {$Servers = Select-Mailserver $Servertype} 

if ($LogName -eq $NULL) 
    {$LogName = Select-Eventlog}
if ($EntryType -eq $NULL) 
    {$EntryType = Select-Entrytype}
if ($OutFile -eq $NULL)  
    {$SRnumber = read-host -prompt "What is the SRX number for your incident?"
    $OutFile = ~\desktop\$SRnumber.txt}
if ($EventID -eq $NULL)   
    {$EventID = read-host -prompt "What is the EventID number?"}

foreach ($server in $servers)
	{
	$Content = get-eventlog -LogName $LogName -EntryType $EntryType | Where {$_.EventID -eq $EventID} | FL
	$Content | Out-File $OutFile -append -width 2000
	}
First of, you hade some illegal parameters for get-eventlog -Entrytype. Is we look at it's helpfile, you will see the following:

code:
PS D:\> get-help Get-EventLog -Parameter Entrytype

-EntryType <string[]>
    Gets only events with the specified entry type. Valid values are Error, Information, FailureAudit, SuccessAudit, and Warning. The default is all events.

    Required?                    false
    Position?                    named
    Default value                All events
    Accept pipeline input?       false
    Accept wildcard characters?  false
So, I fixed that part. I also changed the switch for select-mailserver to $a[0], so I could simply pass the full name, and not have to write extra switch options. Since the first letter is the thing being switched on, and a string is just an array of letters, $a[0] was the easiest adjustment for that.

Apart from putting in the errorhandling I talked about in the previous post, I added in the following:

PARAMETERS
To make this script accept parameters, I included a param() block. I will explain using one parameter as an example:

code:
[parameter(HelpMessage="Possible options: Mailbox, Hub, Client or ALL")]
I only know you can add a helpmessage, I never actually called the parameter helpmessage. I just include it always, because I am anal.
code:
[ValidateSet("Mailbox", "Hub", "Client", "All")]
This will declare all your possible parameters, so no illegal options will be passed through. It will error out on anything else. Because of switching on the first letter, we could make this less stringent, for instance by accepting just the first letters as options. But I didn't do this. Note: this isn't case sensitive.
code:
[string] $Servertype
The actual name of your parameter. Just by this name, you can call your scipt using -ServerType as an option. I only explicitly defined the objecttype, so get-help will show the objecttype.

You could do more fun stuff with parameters, like setting default values. A fun example for that is:

code:
[string] $LDAP = ([ADSI]“”).distinguishedname
Which will default to your current domain root, which you can override by adding a calling script with -DC "DC=somethingawful,DC=com". Output of ([ADSI]“”).distinguishedname is in the form of: DC=contoso,DC=com

Other fun you could do with parameters would be to make them required, have them positioned (so you don't have to specify the parameter name) or make sets, because only certain combinations of parameters do something. There is more fun to be had, of course.

Helpfile
And now for the awesome part. get-help works for this script! The only thing you need to get that going, is to include that huge commentblock in the beginning in that syntax. I always use a base commentblock as a start for my scripts, where I just fill in the specifics as I go. I suggest anyone making big scripts to do the same! How awesome is it, if you can just tell your coworkers to RTFM in a windows environment! (NB: Man is an alias for get-help, for added fun)
So, to break it down, this is part of my base script, an adaptation of this example:
code:
<#  
.SYNOPSIS  
    A summary of what this script does  
    In this case, this script documents the auto-help text in PSH CTP 3  
    Appears in all basic, -detailed, -full, -examples  
.DESCRIPTION  
    A more in depth description of the script 
    Should give script developer more things to talk about  
    Hopefully this can help the community too  
    Becomes: "DETAILED DESCRIPTION"  
    Appears in basic, -full and -detailed  
.NOTES    
    Author:              Your name here 
    First Creation Date: not set
    Last Edited Date:    not set
    Created For:         not set
    ChangeLog:
    Date: Author: Changes
    TODO:
    Parts not yet implemented, Ideas for the future.
.LINK  
    The sources I used to create this script:
    [url]http://pshscripts.blogspot.com/2008/12/get-autohelpps1.html[/url]
    This is the source of the help part of my base script
.EXAMPLE  
    The first example - just text documentation  
    You should provide a way of calling the script, plus expected output  
    Appears in -detailed and -full  
.EXAMPLE  
    The second example - more text documentation  
    This would be an example calling the script differently. You can have lots  
    and lots, and lots of examples if this is useful.  
    Appears in -detailed and -full  
.PARAMETER foo  
   The .Parameter area in the script is used to derive the contents of the PARAMETERS in Get-Help output which   
   documents the parameters in the param block. The section takes a value (in this case foo,  
   the name of the first actual parameter), and only appears if there is parameter of that name in the  params block. Having a section for a parameter that does not exist generate no extra output of this section  
   Appears in -det, -full (with more info than in -det) and -Parameter (need to specify the parameter name)  
.PARAMETER bar  
   Example of a parameter definition for a parameter that does not exist.  
   Does not appear at all.  
#> 
I hope that was long enough for people.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
get-acl will do what you want, but you might have to parse the outcome a bit to make it readable.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
I just realized: I did not specify in my helpfile that this script requires the exchange modules. This would be one of the more important things to put in there. Whoops :downs:

And for Walter_Sobchak: indeed, what is the exact thing you want to do. Do you want to check the share permissions also, in case those aren't good? How many levels deep will the permissions be unique? Do you need to inventory who is a member of the security groups as well? Want to output it in a pretty excel file for management to swoon over?

Anyway, a simple option would be:

code:
$List = get-content ListOfComputers.txt
foreach ($computer in $list)
{
   Get-WmiObject win32_share -ComputerName $Computer | get-acl | fl
}
This will error out on the IPC$ share, since it is not a real path. So you'd need to filter it out. Also, you might want to filter out the admin shares, unless you suspect something to be wrong there or want to have a complete list. Note that this doesn't have pretty output, giving special permissions a numeric value that is hard to parse for a human. But, this could get you started in the right direction.

e: Why didn't I look at this earlier. If you convert it to HEX, it gets a lot more readable. 268435456 is GENERIC_ALL. 268435456 in hex is 0x10000000

Jelmylicious fucked around with this message at 11:20 on May 16, 2012

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Walter_Sobchak posted:

So the decree came down from on high- The CEO wants a quarterly report of every single share on our network, along with their permissions. Now maintaining a list of the shares is easy, but not so much with the permissions. Is there a way to write a Powershell script to have it aggregate all this info?

Another big post ahoy! First of, as you can see in the comment notes, there is a lot more you can do with this, that isn't implemented yet. I don't filter out admin shares, I do filter out the shares that are unreachable. I chose to output one access right per line, to keep things flat.
The conversion-table for making shares human readable is definitely incomplete, but that is easy to append. I know this script might seem big and daunting for a firsttimer, but that is because I made it into a full script that includes a helpfile and can be run with parameters. Save it as Get-ShareRights.ps1 and you can run it from commandline, or run it as a scheduled job. Then have something compare previous results and you have a quick and dirty rights auditing! But I'm digressing. First the full script, after that, some explanation.

code:
<#  
.SYNOPSIS  
    Exports all filesystem rights of all shares on given computers into CSV file
.DESCRIPTION  
    Exports all filesystem rights of all shares on given computers into CSV file. This scripts assumes the current account has the right to access these shares, also, this script does not look at sharepermissions, Just NTFS rights.
.NOTES    
    Author:              Jelmylicious  
    First Creation Date: 15-May-2012
    Last Edited Date:    15-May-2012
    Created For:         SomethingAwful
    TODO:
    -Create a better conversion table for accessrights.
    -Make it accept several computernames
    -Make it go one level deeper
    -Make it accept a single share
    -Make it also accept a list of shares, rather than computers
    -Have the option to save output per share or per host.
    -Make it accept a filterlist, to filter out adminshares
.LINK  
    Sources Used:
    For the custom objects:
    adaz' awesome post at [url]http://forums.somethingawful.com/showthread.php?threadid=3286440&userid=148148&perpage=40&pagenumber=2#post394234303[/url]
    For the rights conversion table:
    [url]http://msdn.microsoft.com/en-us/library/windows/desktop/aa369774[/url](v=vs.85).aspx
.EXAMPLE  
    Get-ShareRights

    Description
    
    -----------
    This command gets all shares from localhost and enumerates all rights in csv file.
.EXAMPLE
    Get-ShareRights -Computers "Fileserver" -DontConvert -OutFile ".\output.csv"

    Description
    
    -----------
    Probe server named fileserver, while not converting to human readable strings and outputting to output.csv in current working directory.
.PARAMETER DontConvert  
    When this switch is added, the String that get-acl returns arent converted into human readable format. Default is Convert
.PARAMETER $Computers
    List of Computers that need to be scanned, Default is localhost.
.PARAMETER OutFile  
    Specify where you want the file to be saved to. Default is to prompt for location
.PARAMETER InputFile
    List of computers in textformat, one hostname per line.
#> 

param
    (
    [Switch]$DontConvert
    ,
    [string]$Computers = "Localhost"
    ,
    [string]$OutFile = "NotSet"
    ,
    [String]$InputFile = "NotSet"
    )


Function Convertto-Readable
#This function takes the cryptic strings that Get-ACL returns, and makes them human readable. (Incomplete)
{
    param 
        (
        [string] $Rights
        )
    If ($DontConvert) {return $Rights}
    Else
    {
        switch ($Rights)
        {
            268435456  {return "ReadWriteExecute"}
            536870912  {return "Execute"}
            1073741824 {return "Write"}
            default    {return $Rights}
        }
    }
}


#Collect all necessary info and instantiate variables
$ExportTable = @()
if ($OutFile -eq "NotSet") {$OutFile  = Read-Host "Type path you want to save output to: "}
if ($InputFile -ne "NotSet") {[array]$Computers = Get-Content $InputFile}


#########################
#Actual Code starts here#
#########################

foreach ($Computer in $Computers)
{
    $ShareHost = '\\' + $computer + '\'
    $shares = Get-WmiObject win32_share -ComputerName $Computer  
    foreach ($share in $shares)
    {
        $FullShare = $ShareHost + $share.name
        #Not all paths are valid paths, like the default share IPC$, so test before you try
        if (Test-Path $FullShare)
        {
            $ACL = Get-Acl $FullShare 
            foreach ($Access in $ACL.access)
            {
                #Create an empty custon object with all properties needed and add that to the table.
                $ExportLine = "" | select Computer,Share,Path,Identity,Type,Rights,ParentInheritance,Childinheritance,Propagation
                $ExportLine.Computer = $Computer
                $Exportline.Share = $Fullshare
                $Exportline.Path = $Share.path
                $Exportline.Identity = $Access.IdentityReference
                $Exportline.Type = $Access.AccessControlType
                $ExportLine.Rights = (Convertto-Readable $Access.FileSystemRights)
                $ExportLine.ParentInheritance = $Access.IsInherited
                $Exportline.Childinheritance = $Access.InheritanceFlags
                $ExportLine.Propagation = $Access.PropagationFlags
                $ExportTable += $Exportline
            }
        }
        Else
        {
            Write-Host -fore Red "$FullShare does not exist or isn't active"
        }
    }
}
$ExportTable | Export-Csv  $OutFile
And now for the explaining! I am not going to explain the help or the parameter part anymore. For that, you can scroll up a few posts.
Let me start with the only function in this script. All it does is take a simple string as input, and either return a different string if it knows the conversion, or return the same string again if it doesn't. The global parameter $dontconvert is first polled to see if any conversion has to be done at all.

code:
Function Convertto-Readable
#This function takes the cryptic strings that Get-ACL returns, and makes them human readable. (Incomplete)
{
    param 
        (
        [string] $Rights
        )
    If ($DontConvert) {return $Rights}
    Else
    {
        switch ($Rights)
        {
            268435456  {return "ReadWriteExecute"}
            536870912  {return "Execute"}
            1073741824 {return "Write"}
            default    {return $Rights}
        }
    }
}
Now on to the meat of the sauce, which boils down to this:

code:
foreach ($Computer in $Computers)
{
    $ShareHost = '\\' + $computer + '\'
    $shares = Get-WmiObject win32_share -ComputerName $Computer  
    foreach ($share in $shares)
        {
        Get-Acl $FullShare 
        }
This simply takes each computer in your list, then asks the computer to list all it's shares. Then, for each share, it asks for the access list. But if this part is so short, why did I add all that other crud? First off, get-acl output is ugly! It basically crams all the rights info in one property called access. This property is itself an array of arrays. So I split that out, put it in a different object that is flat (no nested properties) so I could easily export it to CSV. The way to do that, I cheated of this post by adaz and then added it to a table for easy exporting:
code:
$ExportLine = "" | select Computer,Share,Path,Identity,Type,Rights,ParentInheritance,Childinheritance,Propagation
$ExportLine.Computer = $Computer
$Exportline.Share = $Fullshare
#[Redacted for brevity]
$Exportline.Childinheritance = $Access.InheritanceFlags
$ExportLine.Propagation = $Access.PropagationFlags
$ExportTable += $Exportline

I also put in a small test, to see if the share is valid, so it wouldn't error out, but give you a small message saying a share doesn't exist:
code:
if (Test-Path $FullShare)
)

And there you have it. If you need it adjusted, I can do so. I might make this script bigger for auditing purposes in my company.

Jelmylicious fucked around with this message at 20:02 on May 18, 2012

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Phone posted:

Ugh, this is gonna hurt: 800k+ files in 7 directories; delete all files older than 60 days. I know I saw something about Powershell performance tapering off before, but I can't remember where I saw it.

I just hope that my gci -recurse works. :toot:

I think this is what you are looking for:

from: http://blogs.msdn.com/b/powershell/archive/2009/11/04/why-is-get-childitem-so-slow.aspx
Since the sweet point seems to be around 300k files, why not specify the 7 directories, and do a simple gci without the recurse on them? I feel dirty for removing some automation, but sometimes, doing it yourself really is better. Or, to get all the directories automatically, either:
- do a gci -directory (powershell 3 option) or
- filter with gci -filter *. (to specify a native filesystem filter for files with no extension)
Last option has the assumption that directories have no extension, and files do. Or,if you can distinguish by name, you could use a different filter.

e: changed image host to imgur, even though msdn.com can probably handle the load from this thread...

Jelmylicious fucked around with this message at 16:42 on Jun 1, 2012

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Scaramouche posted:

This is a theoretical question I guess, but in cases like the above where performance degrades after x number of files, would something DOS based be faster? e.g. http://stackoverflow.com/questions/51054/batch-file-to-delete-files-older-than-n-days

I had no idea PowerShell crapped out like that after a certain amount of files, though I'm usually working with at least 300k files when I'm using it.

I would keep using PowerShell. The advantage of that, is that it returns objects, not plaintext. So, if you can filter it down with -Filter (native NTFS filter, like dir uses) or if you can break it up in chunks, I would keep using Get-ChildItem for flexibilities sake.
For this example, the batch script would of course work, it's just that you can do so much more with the objects. If you'd ever want to expand on your script, the PowerShell one would be very easy to modify.
Granted, the batch script will probably be a lot faster.

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

Wicaeed posted:

So I saw this posted on Reddit, give a really good overview of Powershell for those trying to learn it, and also gives a lot of good tips on script creation: https://www.youtube.com/watch?v=-Ya1dQ1Igkc

For example, I didn't even know of the show-command cmdlet.

Blew my fuckin' MIND, man.

Just gave the first day of our internal two day powershell course. Most important commands I taught were Get-Command (in conjunction with filters), Get-Help and Get-Member. With those three, you can find out almost all you need to know or at least find specific terms to google.

e: Just watched most of that video, it is really good. I think I am going to change the structure of my course a bit, because of this.

Jelmylicious fucked around with this message at 23:27 on Jun 11, 2012

Adbot
ADBOT LOVES YOU

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Alright, I found something wierd, which is probably just sommething in the datetime format the WMI returns. First, let me lay a little background. To get a the installdate through WMI you can ask it like this:
code:
(gwmi win32_operatingsystem).InstallDate
Which outputs: 20120412025148.000000+120
Well, that was helpful. I think I see a 2012 at the beginning, but yeah.... How long is that thing anyway?
code:
((gwmi win32_operatingsystem).InstallDate).length
Twenty-five characters! Let me google if I can convert that.
code:
$LongNumber = (gwmi win32_operatingsystem).InstallDate
[System.Management.ManagementDateTimeconverter]::todatetime($longNumber)
Hmmm, thursday April 12, sounds about right. Let's check systeminfo for that: Original Install Date: 12-4-2012. It works. Now let's play with that long number, with a random number, 25 digits long, starting with 1999:
code:
$LongNumber = 1999193125501212345678901 
[System.Management.ManagementDateTimeconverter]::todatetime($longNumber)
Exception calling "ToDateTime" with "1" argument(s): "Year, Month, and Day parameters describe an un-representable DateTime." Hey, there does seem to be a pattern here. Ok, fix the month and party like it's 1999:
code:
$LongNumber = 1999123125501212345678901 
[System.Management.ManagementDateTimeconverter]::todatetime($longNumber)
Now I get "Hour, Minute, and Second parameters describe an un-representable DateTime." Ok final try:
code:
$LongNumber = 1999123123501212345678901 
[System.Management.ManagementDateTimeconverter]::todatetime($longNumber)
Saturday 25 december 1999 20:29:12. YAY. Wait! what?
What I would expect to be 1999-12-31 23:50:12.12345678901 yields 1999-12-25 20:29:12. 4 days, 3 hours and 21 minutes difference! Let's timetravel! change the 1999 to 1899, 'cause I'm oldfashioned: monday 25 december 1899 20:29:12. Exact same difference! Anyone know what's up? Or should I ask in the .NET thread, since the datetime class is technically more their domain. I trust the conversion, I am just intrigued by this all.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply