# /\/\o\/\/ PowerShelled

This blog has moved to http://ThePowerShellGuy.com Greetings /\/\o\/\/
$AtomFeed = ("Atom.xml")$PreviousItems = (" Moving to ThePowerShellGuy.com "," PowerShell Code formatting test for my new Blog "," PowerShell Code formatting test for my new Blog "," PowerShell Community Extensions V1.0 "," PowerShell : Access remote eventlogs "," Windows PowerShell Scripting Sweepstakes! "," PowerShell : making Custum Enums "," TechNet Webcast: An Overview of Windows PowerShell... "," "Windows PowerShell: TFM" goes "Gold" "," PowerShell : Advanced renaming of files "," ")

## Powershell links and free E-book

DontBotherMeWithSpam, started a PowerShell link collection on delicious, http://del.icio.us/powershell

next to the Powershell book previews already metioned (and on the delicious links), I noticed that I did not yet linked to this one yet :

In this free e-book on realtimepublishers.com about PowerShell you can find a good introduction to PowerShell. An Introduction to Microsoft PowerShell by Don Jones

and on the codeproject a nice article about writing a CMDlet to use Copernic Desktop Search (CDS) application from powershell.

http://www.codeproject.com/useritems/getfromcopernic.asp

Enjoy,

Greetings /\/\o\/\/
Tags :

## PowerShell Tab Completion

PowerShell Tab Completion is since RC1 customizable,

you can look at the current implementation like this :

get-content function:TabExpansion

This is my first customization to this function :

I added Tab-completion for Types to get the static methods (and properties)
I like this especialy becouse normaly you have to remember to use get-member - static to list them.

This also works for Enums :

[regex]::M [tab]
[regex]::Match(

[dateTime]:: [tab]
[datetime]::Compare(

[consolecolor]::B [tab]
[consolecolor]::Black [tab]
[consolecolor]::Blue [tab]
[consolecolor]::Black

the Code I added looks like this :

function tabexpansion {

# (Original Code Here )

switch -regex ($lastWord) { # this Part is added to handle Types # /\/\o\/\/ 2006 # Handle Types '($.*$)::(\w*)' { invoke-expression "$($matches[1]) | gm -static" | where {$_.name -like "$($matches[2])*"} |% {
if ($_.MemberType -band$method) {
"$($matches[1])::$($_.name)" + '('
} Else {
"$($matches[1])::$($_.name)"
}
}
}

# End of added Part

# Handle property and method expansion...

# (Original Code Here )

}

*Note* if you output the original code to a file (gc function:TabExpansion | out-file) you will miss the line "Function {" and the last "}" you need to add it to declare the function again.

then below the Switch statement (I left in in for readability, you add the custom code.

you need to . source the script to overwrite the $global tabExpansion function. and put it in your profile to use it every session. Enjoy, gr /\/\o\/\/ Tags : posted by /\/\o\/\/ 4 comments links to this post ### Friday, May 26, 2006 ## PowerShell out-DataGrid update and more Dataset utilities As I renamed the out-PropertyGrid function entry,as I wrongly titled it as out-DataGrid as Abhiskek did an out-DataGrid function here : Abhishek's PowerShell Blog and I already had one for Monad this did lead to some confusion so I renamed it but the link has also changed, this is the new one powershell-out-propertygrid-msh-view.html as I did not convert my out-DataGrid yet (changing MSH to PS in 2 places) I will also provide my version here, the diffence is that I as a Datagrid and Abhiskek did use a array. my version is a bit bigger, but I like the datatable as Greg did notice in the comments the sort in the Datagrid will not work on an array, but does an the datagrid and also that I can combine the data from all kind of sources :(see working with CSV files in MSH (part one) and some of the links or seach for system.data on my blog to find more info.) and as you can see here : MSH GUI Viewer for Dataset Example in some screenshots, so you can walk the relations you did make (only the datagridpart not the converting part for the rest the same as for the old focus hack) I also have a script using only the convert to dateTable, that I will also post , and the helpers to get a CSV file or a Excel sheet into a dataTable ( some already posted ) so now you have the toolset to get together changed for PowerShell , and you can just past in the whole bunch and get working with CSV file Excel files DataTables and Combining them, and last but not least viewing them in a GUI. a last remark note that the functions are made for commandline usage, so you dot-source the connect funtions (otherwise csv files or excel sheets are listed but go out of scope after running the function and con not be used) : then the functions just assume and take the connection object ($con or $xlsCon) as i explained in former posts. so you work like this : # takes directory name as parameter CSV and TXT files in directory are tables. . connect-csv c:\Csvfiles # takes Excel filename as parameter Sheets are datatables . connect-Excel c:\ExcelFiles\file.xls # after connecting you can just use the get-* functions # get CSV file$file = get-DataTable file#csv

# get XlsSheet (defaults to sheet1$)$file2 = get-xlsSheet

# make new dataset

$ds = new-object System.Data.dataSet # merge the files$ds.merge($file)$ds.Merge($file2) # show in GUI (no parameter just assumes there is a$ds object ;-) )

show-DataSet

#  out-dataTable will convert command output to a dataTable ( you can merge again in the $ds)$dt = gps | out-dataTable

and here are the updates and utility scripts :
(note most parts are formerly posted on my blog and origional entrys contain more information about the workings, so if things are not clear try the links)

# Function out-datagrid
# shows piplineinput in a GUI using a datagrid
# /\/\o\/\/ 2006
# http:mow001.blogspot.com

Function out-dataGrid {

# Make DataTable from Input

$dt = new-Object Data.datatable$First = $true foreach ($item in $input){$DR = $DT.NewRow()$Item.PsObject.get_properties() | foreach {
If ($first) {$Col =  new-object Data.DataColumn
$Col.ColumnName =$_.Name.ToString()
$DT.Columns.Add($Col)       }
if ($_.value -eq$null) {
$DR.Item($_.Name) = "[empty]"
}
ElseIf ($_.IsArray) {$DR.Item($_.Name) =[string]::Join($_.value ,";"
}
Else {
$DR.Item($_.Name) = $_.value } }$DT.Rows.Add($DR)$First = $false } # show Datatable in Form$form = new-object Windows.Forms.form
$form.Size = new-object System.Drawing.Size @(1000,600)$DG = new-object windows.forms.DataGrid
$DG.CaptionText = "Number of Rows$($DT.rows.Count)"$DG.AllowSorting = $True$DG.DataSource = $DT.psObject.baseobject$DG.Dock = [System.Windows.Forms.DockStyle]::Fill
$form.text = "$($myinvocation.line)"$form.Controls.Add($DG)$Form.Add_Shown({$form.Activate()})$form.showdialog()

##########################################################
# Load CSV and Excel ADO helper functions

# connect to Excel File

function connect-Excel {
Param ($Path = "")$ConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=$path;Extended Properties=Excel 8.0;"$XlsConn = new-object System.Data.OleDb.OleDbConnection($connString)$Xlsconn.open()
$Tables =$xlsConn.GetOleDbSchemaTable([System.Data.OleDb.OleDbSchemaGuid]::tables,$null)$Tables |% {$_.TABLE_NAME} } # get a sheet from excel into a datatable function get-xlsSheet { Param ($name = "sheet1$")$cmd = new-object System.Data.OleDb.OleDbCommand("Select * from [$name]",$xlsConn)
$da = new-object System.Data.OleDb.OleDbDataAdapter($cmd)
$dt = new-object System.Data.dataTable [void]$da.fill($dt)$dt.TableName = $name return$dt
}

# connect to a directory with CSV files :

function connect-csv {
Param ($Path = ".")$ConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=$path;Extended properties='text;HDR=Yes;FMT=Delimited'"$Conn = new-object System.Data.OleDb.OleDbConnection($connString)$conn.open()
$Tables =$Conn.GetOleDbSchemaTable([System.Data.OleDb.OleDbSchemaGuid]::tables,$null)$Tables |% {$_.TABLE_NAME} } # get CSV / txt file into a dataTable function get-DataTable { Param ($name)
$cmd = new-object System.Data.OleDb.OleDbCommand("Select * from [$name]",$Conn)$da = new-object System.Data.OleDb.OleDbDataAdapter($cmd)$dt = new-object System.Data.dataTable
[void]$da.fill($dt)
$dt.TableName =$name.split("#")[0]
return $dt } # show the$ds dataSet in a GUI

Function show-DataSet {
$form = new-object Windows.Forms.form$form.Size = new-object System.Drawing.Size @(1000,600)
$DG = new-object windows.forms.DataGrid$DG.CaptionText = "Number of Rows $($Ds.rows.Count)"
$DG.AllowSorting =$True
$DG.DataSource =$Ds.PSobject.baseobject
$DG.Dock = [System.Windows.Forms.DockStyle]::Fill$form.text = "$($myinvocation.line)"
$form.Controls.Add($DG)
$Form.Add_Shown({$form.Activate()})
$form.showdialog() } # convert commandoutput to DataTable : Function out-dataTable { # Make DataTable from Input$dt = new-object Data.datatable
$First =$true
foreach ($item in$input){
$DR =$DT.NewRow()
$Item.PsObject.get_properties() |% { If ($first) {
$Col = new-object Data.DataColumn$Col.ColumnName = $_.Name.ToString()$DT.Columns.Add($Col) } if ($_.value -eq $null) {$DR.Item($_.Name) = "[empty]" } ElseIf ($_.IsArray) {
$DR.Item($_.Name) =[string]::Join($_.value ,";") } Else {$DR.Item($_.Name) =$_.value
}
}
$DT.Rows.Add($DR)
$First =$false
}
return $dt } As most where mentioned before and not all had to be changed I still reposted them all as I hope this combination of scripts helps to see the complete picture of combining data in PowerShell using ADO (the samples are not complete as you can add Access and SQL also to the toolkit as you can see in other entries, but that a leave up to you to make the post not even bigger) but I mean that you can just past all the code into your PowerShell console, pick a directory that contains CSV files ans an Excel sheet and follow the examples above and in the other posts and get experimenting. also be sure the check the select samples in the CSV series to create new combined datasets and working with export-csv to safe them. and follow the post about how to make relations and then use Show-DataSet again and see how you can walk the relations. I use this a lot comparing information from different sources. let me know what you think of this way to work with data in Powershell Enjoy, Greetings /\/\o\/\/ Tags : posted by /\/\o\/\/ 0 comments links to this post ### Thursday, May 25, 2006 ## PowerShell : Getting Subdirectories using WMI part 3 The shortcut to filtering subdirectories, I made in : PowerShell : Getting Subdirectories using WMI part 2 was not special character proof, e.g. "mow's dir" went wrong, So here 2 other solutions using WMI relations : # Get Only SubDirectories (gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated($null,"Win32_SubDirectory",$null,$null,$null,"GroupComponent",$false,$null) # or (new-object Management.ManagementObjectSearcher('associators of {Win32_Directory.Name="c:\\foobar"} where assocclass = Win32_SubDirectory role = GroupComponent')).get() But how did I come to those ? As I did say before I showed the wrong workaround in the first, the Management.RelatedObjectQuery helps with this task so following and using the 2 former posts : Lets Query WMI from MSH PowerShell out-DataGrid (MSH view-Object) but as working with relations is an advanced WMI topic, and the former Service example used another kind of relation, I will show how I came to the query in this case. I came to this using the commandline like this : first get some info using the getrelationships() method : # Kind of RelationShips MowPS>(gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelationships() | fl __class __class : Win32_SubDirectory __class : Win32_SubDirectory __class : Win32_SubDirectory __class : Win32_SecuritySettingOfLogicalFile # Roles : MowPS>(gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelationShips('Win32_SubDirectory') | fl [a-z]* GroupComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar" PartComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar\\bar" GroupComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar" PartComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar\\Foo" GroupComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\" PartComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\foobar" we already used the simple overloads using no filter or only the related class, but the next step is that big overload : # get available overloads : MowPS>(gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated.OverloadDefinitions System.Management.ManagementObjectCollection GetRelated() System.Management.ManagementObjectCollection GetRelated(String relatedClass) System.Management.ManagementObjectCollection GetRelated(String relatedClass, String relationshipClass, String relationshipQualifier, String relatedQualifier, String relatedRole, String thisRole, Boolean classDefinitionsOnly, EnumerationOptions options) how to fill that big overload, we use the data we have from getrelationships : we want only Win32_SubDirectory and we want only the ones where we are the GroupComponent. so we use a RelatedObjectQuery to see how to do that : MowPS>$roq = new-object System.Management.RelatedObjectQuery
MowPS>$roq IsSchemaQuery : False SourceObject : RelatedClass : RelationshipClass : RelatedQualifier : RelationshipQualifier : RelatedRole : ThisRole : ClassDefinitionsOnly : False QueryLanguage : WQL QueryString : # fill in the parts we know now :$roq = new-object System.Management.RelatedObjectQuery
$roq.SourceObject = 'Win32_Directory.Name="c:\\foobar"'$roq.RelationshipClass = 'Win32_SubDirectory'
$roq.ThisRole = 'GroupComponent' # or use ProertyGrid to Edit :$roq = new-object System.Management.RelatedObjectQuery
$roq | out-propertygrid # Result MowPS>$roq

IsSchemaQuery         : False
SourceObject          : Win32_Directory.Name="c:\\foobar"
RelatedClass          :
RelationshipClass     : Win32_SubDirectory
RelatedQualifier      :
RelationshipQualifier :
RelatedRole           :
ThisRole              : GroupComponent
ClassDefinitionsOnly  : False
QueryLanguage         : WQL
QueryString           : associators of {Win32_Directory.Name="c:\\foobar"where assocclass = Win32_SubDirectory role = GroupComponent

# Use with ManagementObjectSearcher

# Use Query Object :

$mos = new-object system.management.ManagementObjectSearcher($roq)
$mos.get() # Or Query (new-object Management.ManagementObjectSearcher('associators of {Win32_Directory.Name="c:\\foobar"} where assocclass = Win32_SubDirectory role = GroupComponent')).get() now that you can see parameters like this the overload in not that hard do, just fill in what you know. You see using the .NET system.management helper classes and out-propertyGrid can make this task a whole bit easier. just copy the properties of$roq or just use it.

Enjoy,

Greetings /\/\o\/\/

Tags :

posted by /\/\o\/\/

## PowerShell : Getting Subdirectories using WMI part 2

In the last post PowerShell : Getting Subdirectories using WMI ,
I did use the WMI relation to get a folders subfolder.

Pete did ask 3 questions in the comments of the last post.
I think they were interesing enough to make a new post,

first about using $name and just the string of the directoryname, as I made commandline examples I did just type in the string, and did$name = 'c:\foo' as you would do that in the Share example (it would be the path of the share there, i give that example in the NG I think.)
I just mixed it up in the examples by acident sorry if that was confusing.

but for the second question how to handle the escapingm if you get back a path without the escaping you can use the replace method.

$_.name.replace('\','\\') you can see how I used it in the recursive sample below and the last question, about recursion, we can make a recursive function in PowerShell to get all the subdirectories, in the sample below the function does just call itself again for each subdirectory found. so that it wil recurse the complete subtree. # recursive directory iterating using wmi Function get-DirTree ($name) {
(gwmi Win32_directory -filter "name = '$name'").getrelated('Win32_directory') | where {$_.name -match $name} |% {$_.name
get-SubDirs $_.name.replace('\','\\') } } # Example MowPS>get-SubDirs c:\\foobar c:\foobar\bar c:\foobar\foo c:\foobar\foo\bar Greetings /\/\o\/\/ Tags : posted by /\/\o\/\/ 9 comments links to this post ### Sunday, May 21, 2006 ## PowerShell : Getting Subdirectories using WMI In the new Microsoft.Public.Windows.PowerShell NewsGroup (we where shareing m.p.w.server.scripting), In the Thread : gwmi to get folder and security info : (the newsgroup is still so new that I can not find it in Google yes, so no link you have to fire up your newsreader and if you not have done yet subscribe to the NG (when done, keep hanging there you can learn a lot from the questions and aswers there .) or use this link (http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.windows.powershell&cat=en_US_3750E87B-4971-4A5C-A537-45F5D7ABBECC&lang=en&cr=US ) for the Webbased version. the question was why this command (changed to use the foobar example dir )took to long :$folders = gwmi Win32_Directory Where {$_.Name -eq "C:\foobar"} reason is first ALL the directorys on the computer (on all disks) get collected and only then is the directory filtered out. the answer is using a filter on the WMI query directly : (gwmi Win32_directory -filter "name = 'c:\\foobar'") but how did I come to this getting subdirectories as in the rest of the post was stated that Pete was working on a WMI translation of my PowerShell Import and Export a DirectoryTree and Security info example. working from this former WMIsample. PowerShell Import Shares and Security info From CSV A good reason for this is that you want to do it remote, another reason he rewrote this was that he did not like use SDDL (no reason for using WMI as it would be also possible using the directorytree example ), but lets focus on the WMI and get subdirectory part in the PowerShell Import and Export a DirectoryTree and Security info example I just use ls - recurse to enumerate to directories, in Share example we have a path to get the directory but how to do this in WMI ? look at this example : MowPS>(gwmi Win32_directory -filter "name = '$name'").getrelated()

Hidden                : False
Archive               : False
EightDotThreeFileName : c:\foobar\bar
FileSize              :
Name                  : c:\foobar\bar
Compressed            : False
Encrypted             : False

Hidden                : False
Archive               : False
EightDotThreeFileName : c:\foobar\foo
FileSize              :
Name                  : c:\foobar\foo
Compressed            : False
Encrypted             : False

Hidden                :
Archive               :
EightDotThreeFileName :
FileSize              :
Name                  : c:\
Compressed            :
Encrypted             :

Caption          : Security settings of c:\foobar
ControlFlags     : 44052
Description      : Security settings of c:\foobar
OwnerPermissions : True
Path             : c:\foobar
SettingID        :
__GENUS          : 2
__CLASS          : Win32_LogicalFileSecuritySetting
__SUPERCLASS     : Win32_SecuritySetting
__DYNASTY        : CIM_Setting
__RELPATH        : Win32_LogicalFileSecuritySetting.Path="c:\\foobar"
__PROPERTY_COUNT : 6
__DERIVATION     : {Win32_SecuritySetting, CIM_Setting}
__SERVER         : Computer
__NAMESPACE      : root\cimv2
__PATH           : \\Computer\root\cimv2:Win32_LogicalFileSecuritySetting.Path="c:\\foobar"

the (gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated()
Method will get all the related classes, you can see that this includes all the directories that are related and also the Win32_LogicalFileSecuritySetting class (very hande as we needed that also.

to get all the relations as a WMI class use the GetRelationships Method
(gwmi Win32_directory -filter "name = '$name'").GetRelationships() but there are 2 problems left first we need to split the security and the subdirectory info that is easy : (gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated('Win32_LogicalFileSecuritySetting') (gwmi Win32_directory -filter "name = 'c:\\foobar'").getrelated('Win32_directory') but we also get the parent directory, a diffence is that in this case the find directory is the GroupComponent part, GroupComponent : \\CP340339-A\root\cimv2:Win32_Directory.Name="c:\\" we can filter on the GroupComponent but its hard to type (with all the escaping) But we can make use of some helperclasses for making the combined query. For more info see : Lets Query WMI from MSH it's from october last year so using Monad V2 but this is not changed much. if you need to make more difficult WMI queries I think it still worth reading. only to make the query in a GUI, I made an updatedversion that also takes pipelineinfo : PowerShell out-DataGrid (MSH view-Object) so to edit the query in a GUI you can do this : new-object System.Management.RelatedObjectQuery | out-PropertyGrid as I did a more detailed explainatin in that post I will not go into that, but I will use a simpler workaround in Monad here : I just pipeline it to a where and check if the Path of the directories found contains the name of the current directory, if not it must be the root. (gwmi Win32_directory -filter "name = '$name'").getrelated('Win32_directory') |? {$_.name -match$name}

so you can see that the WMI relations are very handy in this case to get from a directory to the directorysecurity and also gives a way to iterate subdirectories using WMI.

I hope this commandline examples make clear how this could help makeing the directoryscript using WMI.

Enjoy,

Greetings /\/\o\/\/
Tags :

posted by /\/\o\/\/

## PowerShell basic parameter checking of a function

On my blog as I provide samples, I most of the time keep them as simple as possible,
without errorhandling or parameterchecking etc.I just focus on the target

as in the temperature converter function examples in last post,

http://mow001.blogspot.com/2006/05/powershell-celsius-fahrenheit.html

I did them as one-liners without any checking to keep it simple.

Jeffrey Snover, was so kind to leave a variation in the Comments, that does give usage info if you do not give a parameter (see example 2 below), mine would just convert 0 (see example 1 below)

this makes the script a bit more userfriendly, but what if a string is given ?

FooFoo32 ?

hmm, we should better catch that also then ...(that is why I let them out most of the time, to keep focussed at the "real" example ;-), but ok lets provide 2 other variations now, to make it up)

one way is to give typeinfo data to the parameter, so that this is checked by the function,
(see example 3 below)

so we also did catch wrong parameters now.
but this does not give the usage information,

so I did another veriation, in the last example, I removed the typeinfo to the parameter and used a Trap Statement, to provide the UsageInformation.(see example 4 below)

in the samples below I will show the different version and what does happen with different kinds of (wrong) input :

Examples :

# Example 1
# My old Function :

function ConvertTo-Celsius ($Fahrenheit) {($Fahrenheit - 32) / 1.8}

# example

MowPS>ConvertTo-Celsius 77
25

# if you not specify a parameter $null -> 0 is calculated : MowPS>ConvertTo-Celsius -17.7777777777778 # example 2 : # Jeffrey Snover 's variation with some errorhandling function ConvertTo-Fahrenheit ($Celsius=$(Throw "USAGE: ConvertTo-Fahrenheit -Celsius degrees")) { "{0:N1}" -f ($Celsius * 1.8 + 32)
}

function ConvertTo-Celsius ($Fahrenheit=$(Throw "USAGE: ConvertTo-Celsius -Fahrenheit degrees")) {
"{0:N1}" -f (($Fahrenheit - 32) / 1.8) # output # Usage information as no parameter is given, MowPS>ConvertTo-Fahrenheit USAGE: ConvertTo-Fahrenheit -Celsius degrees At line:1 char:48 function ConvertTo-Fahrenheit ($Celsius=$(Throw <<<< "USAGE: ConvertTo-Fahrenheit -Celsius degrees")) { # but if a string is given : MowPS>ConvertTo-Fahrenheit foo foofoo32 # Example 3 # we can catch this by adding typeinfo to the parameter : function ConvertTo-Celsius ([decimal]$Fahrenheit=$(Throw "USAGE: ConvertTo-Celsius -Fahrenheit degrees")) { "{0:N1}" -f (($Fahrenheit - 32) / 1.8)

# output

MowPS>ConvertTo-Celsius
USAGE: ConvertTo-Celsius -Fahrenheit degrees
At line:1 char:57
function ConvertTo-Celsius ([decimal]$Fahrenheit=$(Throw  <<<< "USAGE: ConvertTo-Celsius -Fahrenheit degrees")) {

MowPS>ConvertTo-Celsius foo
ConvertTo-Celsius : Cannot convert value "foo" to type "System.Decimal". Error: "Input string was not in a correct format."
At line:1 char:18
+ ConvertTo-Celsius  <<<< foo

MowPS>ConvertTo-Celsius 77
25.0

# Example 4 :
# an other variation giving a custom message using TRAP :

function ConvertTo-Fahrenheit ($Celsius=$(Throw "USAGE: ConvertTo-Fahrenheit -Celsius degrees")) {
trap{Throw "USAGE: ConvertTo-Fahrenheit -Celsius degrees"}
$Celsius=[decimal]$Celsius
"{0:N1}" -f ($Celsius * 1.8 + 32) } # output : MowPS>ConvertTo-Fahrenheit foo USAGE: ConvertTo-Fahrenheit -Celsius degrees At line:2 char:13 + trap{Throw <<<< "USAGE: ConvertTo-Fahrenheit -Celsius degrees"} hope this examples help in adapting other examples to scripts with some errorchecking, (so lazy me can leave them out in my samples and say it is for readability ;-)) For more information about errorhandling and debugging in powershell see also this 7 part series on the PowerShell team blog about it : Debugging Monad Scripts, Part 7 (Final): How Traps Work (links to all former parts are in this post) Enjoy, Greetings /\/\o\/\/ Tags : posted by /\/\o\/\/ 0 comments links to this post ### Saturday, May 13, 2006 ## PowerShell Celsius Fahrenheit converting Just a quick one, as I needed to convert some temperatures, I did this 2 functions, no magic here, but still they can come handy ;-) MowPS>function ConvertTo-Fahrenheit ($Celsius) {$Celsius * 1.8 + 32}MowPS>ConvertTo-Fahrenheit 2577MowPS>function ConvertTo-Celsius ($Fahrenheit) {($Fahrenheit - 32) / 1.8}MowPS>ConvertTo-Celsius 7725 Enjoy, Greetings /\/\o\/\/ Tags : posted by /\/\o\/\/ 1 comments links to this post ### Thursday, May 11, 2006 ## PowerShell : How Can I Rename Files Using File Names I've Written to a Text File? and an other Hey Scripting Guy Item translation to PowerShell, How Can I Rename Files Using File Names I've Written to a Text File? import-csv names.csv %| {ren$_.old $_.new} as this line (as remarked by Scott Hansellman), see Upgrading MSH, My first Windows PowerShell Commands is a bit awkward opposed to ren *.msh *.ps1 in the CMD shell. ls . *.msh -rec | rename-item -newname {$_.name -replace ".msh",".ps1"} -WhatIf

you can see, by this example, if you compare it to the VBscript version is not that bad ;-)

Here the commandline code of the sample I made for this :

# make testdir

MowPS>md ren

Directory: Microsoft.PowerShell.Core\FileSystem::C:

Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2006-05-11 5:06 PM ren

# create test files

MowPS>cd ren
MowPS>ls
MowPS>sc 001.jpg 'bla'
MowPS>sc 002.jpg 'bla'

# make List

MowPS>copycon
Old,New
001.jpg,foo.jpg
002.jpg,bar.jpg
^Z

cmdlet out-file at command pipeline position 1
Supply values for the following parameters:
FilePath: Names.csv

# CopyCon is a custom Function I like

MowPS>gc function:copycon
if ($args[0] -eq$null) {[system.console]::in.readtoend() out-file}
Else {[system.console]::in.readtoend() out-file $args[0]} # the "script" itself MowPS>import-csv names.csv %| {ren$_.old $_.new} # Checking MowPS>ls Directory: Microsoft.PowerShell.Core\FileSystem::C:\ren Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2006-05-11 5:07 PM 5 bar.jpg -a--- 2006-05-11 5:06 PM 5 foo.jpg -a--- 2006-05-11 5:10 PM 88 Names.csv Enjoy, Greetings /\/\o\/\/ Tags : posted by /\/\o\/\/ 3 comments links to this post ## PowerShell make a drive of an UNC path I see a lot of post lately about using wscript.Network to make a networkmapping, like this :$net = $(New-Object -Com WScript.Network)$net.MapNetworkDrive("u:", "\\computer\share")

e.g. here :

Can you map a drive with PowerShell?

and here.

http://www.computerperformance.co.uk/ezine/ezine110.htm

but there is a more native way to do this on PowerShell,

*edit* but as Jacques (links to French blog janel) pointed out to me in the comments, this is only valid for the PowerShell so you can not use it to map a drive and use it from the CMD prompt or the GUI (what is a big difference ) but if you use it in the powershell it has a lot of power as naming the drive with more letters, a path, or mapping to a registrykey.

New-PSDrive HKCR Registry -root
HKEY_CLASSES_ROOT

or a registry path e.g. the PowerShell configuration

New-PSDrive PsConfig Registry -root
HKLM:\software\Microsoft\powershell\1\ShellIds

and ofcourse as mentioned an UNC path :

just type new-PsDrive and you can make an PowerShell drive of a Networkshare :

MowPS>new-psdrive

cmdlet new-psdrive at command pipeline position 1
Supply values for the following parameters:

Name: u
PSProvider: filesystem
Root: \\Computer\share

Name Provider Root
---- -------- ----
u FileSystem \\Computer\share

gr /\/\o\/\/
Tags :

posted by /\/\o\/\/

## Some Powershell News

I did see some interesting PowerShell items a wanted to share

first this PowerShell Grammar overview.

then as there is a new PowerShell revision1.0.9567.1 (links still work / work again)
there also came 2 updated tools :

Tony did a new version of PowerShell Remoting

PowerShell Remoting version 0.1.1.7

Also Karl made a new version of PowerShell analyzer
Recompiled for new Windows PowerShell RC1 (Refresh version)

http://www.karlprosser.com/coder/?p=43

also I did see this post, Not bothering with MS newsgroups any more,
as I'm sometimes also a bit wondered why some questions are asked, they make it harder to anwer real questions, but then again they also give sometimes lead to nice and interesting reactions Can someone explain why we should need powerscript. and some humor so sometimes are still nice to follow.

as PowerShell gets his own newsgroup I things will get a bit better, as we also have to think about it that we are still "sharing" the NG with the other Server scripting languages, so not everybody is asking for the PowerShell posts also.

it does stay a difficult question, as I also remember how it was when I started with Monad, l but I still would recommed to follow microsoft.public.windows.server.scripting and later microsoft.public.windows.PowerShell as it still is a great source to get PowerShell info, and support.
b.t.w. I did see a discusion like this before in the comments on Lee Holmes blog before about what to post on his blog,
Question: Do you _not_ read the newsgroup?

what do you think about Newsgroups comparing other forms like the docs, Blogs, Sites, IRC ?

Enjoy,
Greetings /\/\o\/\/
Tags :

posted by /\/\o\/\/

## PowerShell Import and Export a DirectoryTree and Security info

While and after writing the PowerShell Export and Import shares scripts I got questions on how to do this for Directory Security in the NG thread that started me writing those 2 example scripts (Security and monad) ,

PowerShell Export Shares and Security info to CSV

PowerShell Import Shares and Security info From CSV

and I also got a comment from Pete Gomersall on the latter post, about doing this for Directories and recursing.

I already provided some info about win32_directory and the get-acl CDMlet in the Thread.

but I decided to write some example scripts for that also, but this Time I will not use WMI but the get-acl and set-acl commandlets.

Also I save the Security info in SDDL (Security Descriptor String Format) form to make the CSV file smaller, as Directories most of the time have much more and detailed ACL's as a Share does.

for more info about SDDL see : Security Descriptor String Format [Security] or do a Google Search on SDDL for more info.

(Note that SDDL does support "GA" GENERIC_ALL opposed to the .NET Enum see NG thread)

The provided scripts work like this :

MowPS>Export-DirTee c:\foobar

Dir                                               SDDL
---                                               ----
c:\foobar                                         O:S-1-5-2

MowPS>Export-DirTee c:\foobar -r

Dir                                               SDDL
---                                               ----
c:\foobar                                         O:S-1-5-2
C:\foobar\Bar                                     O:S-1-5-2
C:\foobar\Foo                                     O:S-1-5-2

MowPS>Export-DirTee c:\foobar -r | fl

Dir  : c:\foobar
SDDL : O:S-1-5-21-

Dir  : C:\foobar\Bar
SDDL : O:S-1-5-21-

MowPS>Export-DirTee c:\foobar -r dirSec.csv

Exporting to dirSec.csv

MowPS>rd c:\fooBar

Confirm

The item at C:\fooBar has children and the -recurse parameter was not specified. If you continue,
all children will be removed with the item. Are you sure you want to continue?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): a

# all Directories are gone now :

MowPS>Export-DirTee c:\foobar -r

Path "c:\foobar" Not Found

# Let re-Import Them :

MowPS>Import-dirTree dirSec.csv
Creating c:\foobar
Setting Security on : c:\foobar

Creating C:\foobar\Bar
Setting Security on : C:\foobar\Bar

Creating C:\foobar\Foo
Setting Security on : C:\foobar\Foo

# and Yes the Security is back also :

MowPS>Export-DirTee c:\foobar

Dir                                               SDDL
---                                               ----
c:\foobar                                         O:S-1-5-21-

MowPS>Export-DirTee c:\foobar -rec

Dir                                               SDDL
---                                               ----
c:\foobar                                         O:S-1-5-21-
C:\foobar\Bar                                     O:S-1-5-21-
C:\foobar\Foo                                     O:S-1-5-21-

Note that this will not keep any files and does not touch the security on them, the Import script will just create the Directories if they do not exist and set the security on them, any existing files will not be touched, and will keep the current security.

and here the scripts :

# export-dirTree
#
# this Function will Export a directory Tree
# complete with securityInfo to CSV
#
# /\/\o\/\/ 2006
# http://mow001.blogspot.com

Function Export-DirTree ($path,[switch]$recurse,$Outfile) { if (Test-Path($path)) {
$DirInfo = @()$DirInfo += $path | select @{e={$_};n='Dir'},
@{e={(get-acl $_).sddl};n='SDDL'} if ($recurse.IsPresent) {
ls $path |? {$_.PsIsContainer} |% {
$DirInfo +=$_ | select @{e={$_.fullname};n='Dir'}, @{e={(get-acl$_.fullname).sddl};n='SDDL'}
}
}

if ($outFile){ write-host "Exporting to$outFile"
$DirInfo | export-csv$outFile
}Else{
$DirInfo } }Else{ write-host "Path "$path" Not Found"
}

}

# Import-DirTree

# This Function will Import the directories from a CSV file
# made by Export-DirTree function complete with securityInfo
#
# /\/\o\/\/ 2006
# http://mow001.blogspot.com

Function Import-DirTree ($file) {$DirList = Import-Csv $file$DirList |% {
Write-Host "Creating $($_.dir)"
New-Item -type directory -path $_.dir | out-null Write-Host "Setting Security on :$($_.dir)"$acl = (get-acl $_.dir)$acl.SetSecurityDescriptorSddlForm($_.sddl) set-acl$_.dir $acl Write-Host "" } } For more info about the PowerShell ACL handling : My Blog : Adding a Simple AccesRule to a file ACL in MSH More ACL and MSH on MSH for Fun PowerShell for Fun blog : http://mshforfun.blogspot.com/2005/12/play-with-acl-in-msh.html MSH For Fun: Play with ACL in MSH (continued) PowerShell For Fun: Combination Rights, Inheritance and ... And this exelent article about the .NET 2.0 Security Classes Enjoy, Greetings /\/\o\/\/ Tags : posted by /\/\o\/\/ 3 comments links to this post ### Thursday, May 04, 2006 ## PowerShell How Can I Query a Text File and Retrieve Records That Occurred on a Specific Date? When I did see todays Hey, Scripting Guy! question (and more answer) Hey, Scripting Guy! How Can I Query a Text File and Retrieve Records That Occurred on a Specific Date? I could not resist : Hey, PowerShelled Scripting Guy! import-csv employees.txt |? {$_.HireDate -eq '3/1/2006'}

but to be honest I also use ADO a lot with CSV files from PowerShell, but mostly with a Dataset and a DataAdapter,

# with import-csv function

MowPS>import-csv employees.txt |? {$_.HireDate -eq '3/1/2006'} LastName FirstName Department HireDate -------- --------- ---------- -------- Myer Ken Finance 3/1/2006 Ackerman Pilar Finance 3/1/2006 # With ADO (and 2 helper functions) MowPS>. connect-csv (pwd) TABLE_NAME ---------- Employees#txt MowPS>(get-DataTable "Employees#txt") |? {$_.HireDate -eq '3/1/2006'}

LastName               FirstName              Department            HireDate
--------               ---------              ----------            --------
Myer                   Ken                    Finance               3/1/2006 12:00:00 AM
Ackerman               Pilar                  Finance               3/1/2006 12:00:00 AM

in this case you see that it does not matter much, and for the ADO example I need 2 helper functions and it is outputting more or less doing the same, but in the second example I have the power of the .NET dataset and can make relations etc

for more info and the helper functions see :

working with CSV files in MSH (part one)

working with CSV files in MSH (part two)

And without the dataAdapter and dataset :

more Monad scripts, and a bit more CSV

enjoy,

gr /\/\o\/\/
Tags :

posted by /\/\o\/\/

## PowerShell Import Shares and Security info From CSV

this Powershell scipt will import the shares from the CSV file created in the first part of this serie : PowerShell Export Shares and Security info to CSV .

For this example let's asume that we have 2 shares : Foo and Bar

so listing the shares will give this output :

MowPS>gwmi Win32_Share -filter 'type=0'

Name Path Description
---- ---- -----------
Bar C:\Bar
Foo C:\Foo

on both we have everyone read and on the Share Bar I added MOW with Full Control

and did we run the script in the first post in this serie and exported them to a CSV file, the file would look like this :

MowPS>gc ShareInfo.csv

Bar,C:\Bar,,Everyone,Computer,S-1-1-0,1179817,0,0
Bar,C:\Bar,,Mow,Computer,S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx,2032127,0,0
Foo,C:\Foo,,Everyone,Computer,S-1-1-0,1179817,0,0

we can check the rights by using the FileSystemRights Enum :

MowPS>[System.Security.AccessControl.FileSystemRights]1179817

MowPS>[System.Security.AccessControl.FileSystemRights]2032127

FullControl

(note share security is a bit different from filesystemsecurity so the reading is a bit different Replace Security on existing share using MSH)

now we want to re-create the shares

I will first show some parts of it from the CommandLine and then show the complete script (the compete script can be pasted to the commandline also) :

first we get the CSV file into an PSObject again

MowPS>$ShareList = Import-Csv ShareInfo.csv MowPS>$sharelist

Name : Bar
Path : C:\Bar
Description :
User : Everyone
Domain : Computer
SID : S-1-1-0
AceFlags : 0
AceType : 0

Name : Bar
Path : C:\Bar
Description :
User : Mow
Domain : Computer
SID : S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx
AceFlags : 0
AceType : 0

Name : Foo
Path : C:\Foo
Description :
User : Everyone
Domain : Computer
SID : S-1-1-0
AceFlags : 0
AceType : 0

as we only can create the share one time but there could me more entries for a share in the script I loop only trough the shares on the commandline it looks like this :

MowPS>$sharelist | select -unique name,Path,Description Name Path Description ---- ---- ----------- Bar C:\Bar Foo C:\Foo you can see how get a list with every share only one time, so I can loop trough it to make the shares. for each share in the loop I find only the rows of the second share and I do another loop (on the commandline Example I hardCoded Bar) MowPS>$sharelist |? {$_.name -eq 'bar'} Name : Bar Path : C:\Bar Description : User : Everyone Domain : Computer SID : S-1-1-0 AccessMask : 1179817 AceFlags : 0 AceType : 0 Name : Bar Path : C:\Bar Description : User : Mow Domain : Computer SID : S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx AccessMask : 2032127 AceFlags : 0 AceType : 0 Next I need to convert the textform SID into a Binary SID again, therefore we can use the securityidentifier .NET class ( for more info see : Get Binary SID in MSH (Share Security Update)) an commandline example for one SID : MowPS>$sharelist[1].sid

S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx

MowPS>$sid = new-object security.principal.securityidentifier(($sharelist[1].sid))
MowPS>[byte[]]$ba = ,0 *$sid.BinaryLength
MowPS>$sid.GetBinaryForm($ba,0)
MowPS>$sid fl BinaryLength : 28 AccountDomainSid : S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx Value : S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx MowPS>$ba
1
5
0
0
0
0
0
5
21
...
...

(there are also examples on my blog to translate the SID object to a user, try a search for SID on my blog for this)

the making of the trustee and ACE'es are the same as in the change share security sample so I will not further go into it here,

only you may note that I do this for the name property only :

$Trustee.get_Properties()['name'].set_Value($_.domain)

this is becouse there is a AliasProperty Name Defined that overrules the Original Name property of the win32_trustee Class in the typedata of PowerShell RC1, I will file this as a Bug on Connect.

the calling of the create method I did from an template I did create with the get-WmiMethodHelp script from this post : MSH get-WmiMethodHelp function Update 3 and did fill in.

note the use of $sd.PsObject.BaseObject (if you forget this PowerShell will give a confusing error) The Complete script looks like this : # ImportShares.ps1 # This script will Import the Shares from a CSV file # made by ExportShares.ps1 complete with securityInfo # # /\/\o\/\/ 2006 # http://mow001.blogspot.com # Import the CSV file$ShareList = Import-Csv ShareInfo.csv

# get the Unique Shares

$sharelist | select -unique name,Path,Description |% {$name = $_.name$path = $_.Path$description = $_.Description "Processing :$name $path$description"

$sd = new-object system.management.managementclass Win32_SecurityDescriptor$sd.DACL = @()
$ace = new-object system.management.managementclass Win32_ace$Trustee = new-object system.management.managementclass win32_trustee

# Get all records in CSV file for the current share

$sharelist |? {$_.name -eq $name} |% { # Convert SID to Binary version :$sid = new-object security.principal.securityidentifier($_.sid) [byte[]]$ba = ,0 * $sid.BinaryLength$sid.GetBinaryForm($ba,0) # Make Trustee Object$Trustee.Domain = $_.Domain$Trustee.get_Properties()['name'].set_Value($_.domain)$Trustee.SID = $ba # Make ACE Object$ace.AccessMask = $_.AccessMask$ace.AceType = $_.AceType$ace.AceFlags = $_.AceFlags$ace.trustee = $trustee.psobject.baseobject # Add ACE to the DACL$sd.DACL += $ACE.psObject.baseobject }$MC = new-object system.management.ManagementClass win32_share
$InParams =$mc.GetMethodParameters('Create')

# fill Parameters

$InParams["Access"] =$sd.PsObject.BaseObject
$InParams["Description"] =$_.Description
$InParams["Name"] =$_.name
$InParams["Password"] = [string]$InParams["Path"] = $_.Path$InParams["Type"] = 0

$R =$mc.InvokeMethod('Create', $inParams,$Null)
"Result : $($R.ReturnValue)"

}

And we can use it like this :

MowPS>gwmi win32_share -filter "type = 0"

Name Path Description
---- ---- -----------
Bar C:\Bar
Foo C:\Foo

#delete shares

MowPS> ( gwmi win32_share -filter "name = 'foo'").delete()
MowPS> ( gwmi win32_share -filter "name = 'bar'").delete()

# shares gone

MowPS>gwmi win32_share -filter "type = 0"

#import shares

MowPS>.\ImportShares.ps1
Processing : Bar C:\Bar
Result : 0
Processing : Foo C:\Foo
Result : 0

# shares back

MowPS>gwmi win32_share -filter "type = 0"

Name Path Description
---- ---- -----------
Bar C:\Bar
Foo C:\Foo

# if shares are there we get an error 22

MowPS>.\ImportShares.ps1
Processing : Bar C:\Bar
Result : 22
Processing : Foo C:\Foo
Result : 22

# check the security :

MowPS>$shareSec = gwmi Win32_LogicalShareSecuritySetting -filter "name='bar'" MowPS>$shareSec.invokeMethod('GetSecurityDescriptor',$null,$null).descriptor.dacl % {
>> $_ select @{e={$share.name};n='Name'},
>> @{e={$share.Path};n='Path'}, >> @{e={$share.Description};n='Description'},
>> @{e={$_.trustee.Name};n='User'}, >> @{e={$_.trustee.Domain};n='Domain'},