/\/\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 "," ")

Thursday, August 31, 2006

 


Powershell, Has my Dell a dangerous battery Part 2 ?



Yesterday after I posted  PowerShell, Has my Dell a dangerous battery ? , I did meet James Thruher

on IRC (irc.freenode.net channel  #Powershell ), and while discussing the script he came to the idea to  get the serials direct from the Dell site.

b.t.w. if you want to join us on IRC pleasy check it out, on that IRC channel you can also meet amongs others :

Some other regulars on : irc.freenode.net channel  #Powershell are :

DbmwS Don't Bother me with Spam (aka D2D, Dance to die ) from  monadblog  and http://del.icio.us/powershell and very active on the PowerShell Newsgroup : Windows Powershell

Karl Prosser, the guy behind PowerShell Analyzer : Karl Prosser - Klumsy Geek (note that he has a public Alpha release of PowerShell Analyzer (a great PowerShell editor) out now that you can download from his blog)

Arild : from AnkhSVN and also PowerShell fan see his Blog  (see also (PowerShell, AnkhSVN and Subversion) on Scott Hanselman's blog)

 

So, as I did see the new Item on Jim's blog this morning I did think he posted it  as my last remark was :

<jtruher> it's still doesn't seem to be quite right - it seems to miss a couple of numbers
* mow001 needs some sleep, see it on your blog ;-)

, but better yet, he posted an other real cool script Background "jobs" and PowerShell.

but as I did see also some differences between his (scraped from the Dell site) and my list, see the compare below,  and I think it is a very cool addition I will post it here also.

MSH>compare-object ($AffectedSerials | sort) ($dell | sort)

InputObject SideIndicator
----------- -------------
1K055 =>
5P474 =>
J1524 =>
OR331 =>
59474 <=

Jim did post the solution as a comment 2 comments on last post, but I will post that example here also . and try to keep the &amp;nbsp

 

<jtruher> blast - the HTML renderer changed my &amp;nbsp; to a real space
<mow001> I will add it to my new post thanks

I will post a complete script again to check this I made 2 options, use the current list ( I made a fresh one with the latest list with the trick below for if yyou have no internet), or it can download it from the Dell site.

 

MSH>[string]::join("','",$AffectedSerials)
3K590','59474','6P922','C2603','C5339','C5340','C5446','C6269','C6270','D2961','D5555','D6024','D6025
','F2100','F5132','GD785','H3191','JD616','JD617','KD494','M3006','RD857','TD349','U5867','U5882','W5
915','X5308','X5329','X5332','X5333','X5875','X5877','Y1333','Y4500','Y5466

# changed in notepad to :

 

$affectedSerials = '3K590','59474','6P922','C2603',
'C5339','C5340','C5446','C6269','C6270','D2961','D5555',
'D6024','D6025','F2100','F5132','GD785','H3191','JD616',
'JD617','KD494','M3006','RD857','TD349','U5867','U5882',
'W5915','X5308','X5329','X5332','X5333','X5875','X5877',
'Y1333','Y4500','Y5466'

 

So now it works like this : 

 

MSH>.\CheckBattery.msh
retrieved affected serials 1K055 C5446 F2100 KD494 W5915 Y1333 3K590 C6269 F5132 OR331 X5308 Y4500 5
P474 C6270 GD785 M3006 X5329 Y5466 6P922 D2961 H3191 RD857 X5332 C2603 D5555 J1524 TD349 X5333 C5339
D6024 JD616 U5867 X5875 C5340 D6025 JD617 U5882 X5877
Battery Model : DELL 4M0105
MSH>.\CheckBattery.msh -internal
Battery Model : DELL 4M0105

 

and the script looks like this :

 

# CheckBattery.ps1
#
# Check Dell Battery for Fire Hazard
# Thanks to James Thruher for the Web Part

# /\/\o\/\/ 2006
# http://mow001.blogspot.com

Param ($computer = ".",
        [switch]$internalList)

if ($internalList.IsPresent) { 
  $script:affectedSerials = '3K590','59474','6P922','C2603',
  'C5339','C5340','C5446','C6269','C6270','D2961','D5555',
  'D6024','D6025','F2100','F5132','GD785','H3191','JD616',
  'JD617','KD494','M3006','RD857','TD349','U5867','U5882',
  'W5915','X5308','X5329','X5332','X5333','X5875','X5877',
  'Y1333','Y4500','Y5466'

Else { 
  $wc = new net.webclient
  $bat = $wc.downloadstring("https://www.dellbatteryprogram.com/")
  $r = [regex]"&nbsp;([A-Z0-9][A-Z0-9][0-9][0-9][0-9])"

  $matches = $r.matches($bat)
  $script:AffectedSerials = $matches | foreach { $_.groups[1].captures[0].value }
  "retrieved  affected serials $($script:AffectedSerials)"


get-WmiObject win32_battery | Foreach { 
  $Name = $_.name 
  write-host "Battery Model : $name"
  $script:AffectedSerials | Where {$name -match $_} | Foreach { 
      Write-Warning "Affected : Battery $Name matches : *$_*" 
  } 
}

Note as in the other post (I forgot to mention it but )you can give also a computername to connect to a remote system.

And this time I made it as a script that you can run .

Enjoy,

Greetings, /\/\o\/\/
Tags : Monad msh PowerShell


 0 comments

Wednesday, August 30, 2006

 


PowerShell, Has my Dell a dangerous battery ?



Some Dell Batteries can become to hot  see : Dell Laptop Explodes in Flames - Gizmodo

Dell has started a Recall of Notebook Computer Batteries,CPSC, Dell Announce Recall of Notebook Computer Batteries.

Not all Dell Batteries are Affected, so  I made a PowerShell script to get the Battery Model and check it against the list of batteries so that you can check your battery easy. For more information and pointers to instructions on how to inspect your battery, and how to begin the battery exchange process, see Dell's Battery Return Program page.

I also will use this example, to show again how to start different kinds of examples in PowerShell as there are a couple of different ways to make and start the examples, making it very flexible, but for beginning users in PowerShell it is not always that clear how to start the examples on my blog, I noticed from some comments and questions, I mentioned this before a couple of times before on my blog e.g. in PowerShell and Active Directory Part 8 (ACL's), but I will use this example to show some of the different forms again and how to use them as I think it is an important issue and of course want everyone to be able to start and use the examples I provide on my blog ;-)

*Edit* in the next post Powershell, Has my Dell a dangerous battery Part 2 ?  you find an example that gets te serials from the Dell site directly, also I have a new list of serials with some changes ! so check out the next post also !

The first Example you can just paste onto your Console

 

# Lines of code that can just be pasted onto the PowerShell Console

$AffectedSerials = "3K590","59474","6P922"
  "C2603","C5339","C5340",  "C5446","C6269"
  "C6270",  "D2961","D5555","D6024","D6025"
  "F2100","F5132","GD785","H3191","JD616"
  "JD617","KD494","M3006","RD857","TD349"
  "U5867","U5882","W5915","X5308","X5329"
  "X5332","X5333","X5875","X5877","Y1333"
  "Y4500","Y5466" 

get-WmiObject win32_battery | Foreach { 
  $Name = $_.name 
  write-host "Battery Model : $name"
  $AffectedSerials | Where {$name -match $_} | Foreach { 
      Write-Warning "Affected : Battery $Name matches : *$_*" 
  } 
}
 
 
Ok  Lets see how that does look,Let's past in the first line
 

PoSH>$AffectedSerials = "3K590","59474","6P922",
>> "C2603","C5339","C5340", "C5446","C6269",
>> "C6270", "D2961","D5555","D6024","D6025",
>> "F2100","F5132","GD785","H3191","JD616",
>> "JD617","KD494","M3006","RD857","TD349",
>> "U5867","U5882","W5915","X5308","X5329",
>> "X5332","X5333","X5875","X5877","Y1333",
>> "Y4500","Y5466"
>>

this is also the way I created the list from the Dell website and pasted it in from notepad , next to that it started with this oneliner I created interactively :

Posh>gwmi win32_battery |% {$AffectedSerials |% {$name -match $_}}

 

As you could see from the first line, filling the Affected serials variable, PowerShell detects that a line is not ready when you past it in and will continue the  line (showing >>)

So I copied that line into notepad did format it more lines, after that I cleaned out the code a bit, while pasting to code back into the console to check until I liked the result

I really love to work on the console by pasting in and out of the console like this while testing as its really quick as you use quickedit !! the console works great see also : Commandline editing with MSH.

 (as my VM has not battery and my base OS on my laptop runs still MSH you see the prompt change ;-)),

 

Posh>gwmi win32_battery |% {$AffectedSerials |% {$name -match $_}}

MSH>$AffectedSerials = "3K590","59474","6P922",
>> "C2603","C5339","C5340", "C5446","C6269",
>> "C6270", "D2961","D5555","D6024","D6025",
>> "F2100","F5132","GD785","H3191","JD616",
>> "JD617","KD494","M3006","RD857","TD349",
>> "U5867","U5882","W5915","X5308","X5329",
>> "X5332","X5333","X5875","X5877","Y1333",
>> "Y4500","Y5466"
>>
MSH>get-WmiObject win32_battery | Foreach {
>> $Name = $_.name
>> write-host "Battery Model : $name"
>> $AffectedSerials | Where {$name -match $_} | Foreach {
>> Write-Warning "Affected : Battery $Name matches : *$_*"
>> }
>>
>> }
>>
Battery Model : DELL 4M0105

Note that with this method all is loaded in the $global (interactive) scope, so that I can also interact with the variables in between, in this case I use this to add part of my found serial to the $Affected serials HashTable to test the Warning  :

 

# I add part of my model to the array with serials to check, to show the output with a affected battery

MSH>$affectedSerials += "4M010"
MSH>get-WmiObject win32_battery | Foreach {
>> $Name = $_.name
>> write-host "Battery Model : $name"
>> $AffectedSerials | Where {$name -match $_} | Foreach {
>> Write-Warning "Affected : Battery $Name matches : *$_*"
>> }
>> }
>>
Battery Model : DELL 4M0105
WARNING: Affected : Battery DELL 4M0105 matches : *4M010*

You can see that I do not have to paste the HashTable back in as I have it still loaded in my $global Scope, and I can just change it and past in the second line again, and now it will show the error as it will match against my battery.

This method is very handy while working interactively developing or testing as you can test parts of the script just by pasting them in, as I work like this almost always you can do this with almost every example on my blog.

Another version you will find often on my blog is a function,

# check-Battery function

function Check-Battery ($computer = '.') {

  $AffectedSerials = "3K590","59474","6P922"
    "C2603","C5339","C5340",  "C5446","C6269"
    "C6270",  "D2961","D5555","D6024","D6025"
    "F2100","F5132","GD785","H3191","JD616"
    "JD617","KD494","M3006","RD857","TD349"
    "U5867","U5882","W5915","X5308","X5329"
    "X5332","X5333","X5875","X5877","Y1333"
    "Y4500","Y5466" 

  get-WmiObject win32_battery -computer $computer | Foreach { 
    $Name = $_.name 
    write-host "Battery Model : $name"
    $AffectedSerials | Where {$name -match $_} | Foreach { 
        Write-Warning "Affected : Battery $Name matches : *$_*" 
    } 
  }
}

you can also use those examples just by pasting them onto the Console, but after that you need to start them

MSH># check-Battery function
MSH>
MSH>function Check-Battery ($computer = '.') {
>>
>> $AffectedSerials = "3K590","59474","6P922",
>> "C2603","C5339","C5340", "C5446","C6269",
>> "C6270", "D2961","D5555","D6024","D6025",
>> "F2100","F5132","GD785","H3191","JD616",
>> "JD617","KD494","M3006","RD857","TD349",
>> "U5867","U5882","W5915","X5308","X5329",
>> "X5332","X5333","X5875","X5877","Y1333",
>> "Y4500","Y5466"
>>
>> get-WmiObject win32_battery -computer $computer | Foreach {
>> $Name = $_.name
>> write-host "Battery Model : $name"
>> $AffectedSerials | Where {$name -match $_} | Foreach {
>> Write-Warning "Affected : Battery $Name matches : *$_*"
>> }
>> }
>> }
>>
MSH>Check-Battery
Battery Model : DELL 4M0105
MSH>Check-Battery foo
get-WMIObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At line:10 char:16
+ get-WmiObject <<<< win32_battery -computer $computer | Foreach {

MSH>$AffectedSerials -contains '4M010'
True

Note that as I declare the $AffectedSerials in the function, my modified $AffectedSerials is not used so my computer will not show up as affected, but my $global scope is not changed it still contains the added value as the function runs in his own scope.

A common misunderstanding is that an example like this is copied from my blog into notepad and then saved in a file.

as this is the declaration of a function this function gets loaded not executed, also after you run the script you can not use the function if you want to load it in the interactive ($global) scope to keep it handy you need to start the script with a dot (called Dot Sourcing to be able to use it after running the script) as with the function

some examples of this:

 PowerShell out-DataGrid update and more Dataset utilities 

working with CSV files in MSH (part one)

this way of loading functions is great for use in a profile see also :

Tuning MSH in your Profile

 

I hope this shows a bit how easy it is to test by pasting code into the console, how to work with the different examples and how the different ways of starting code differ. in later posts more about how to work with scripts.

If you still have problems starting one of my examples please let me know.

Enjoy,

Greetings, /\/\o\/\/
Tags : Monad msh PowerShell


posted by /\/\o\/\/
 4 comments

Monday, August 28, 2006

 


PowerShell Adding -verbose switch to functions



*Remark* this is a post to test Live Writer so it will change a couple of times. (*Edit 5 *for now it looks good I can edit my posts without losing formatting, and offline)

*Edit 2*  there will be some double posts for testing, I will remove those later (this link will stay others will be removed)

In the last post you did see that I used the verbose-switch

here I will show an example on how to set this for the function itself, or to use the preference variables to set this:

*Edit* note that all the Write Messages do not get passed to the pipeline so only will display on the host.

*see this examples in normal text look if pipeline chars do stay

PoSH>$test = "pipeline" ; write-host "Host"
Host

PoSH>$test
pipeline

PoSH>1,2 |% {out-host "pipeline $_" ; write-host "Host $_"}
Host 1
Host 2

PoSH>1,2 |% {"pipeline $_" ; write-host "Host $_"}
pipeline 1
Host 1
pipeline 2
Host 2

PoSH>1,2 |% {"pipeline $_" ; write-host "Host $_"} |% {"second foreach : $_"}
second foreach : pipeline 1
Host 1
second foreach : pipeline 2
Host 2

PoSH>

*End Edit*

 

In the former examples you could see what is the difference between outputting to the host and to the pipeline, the last example shows that in the second foreach there is only the output from out-object (out-object is used by default if you just put a string on a line as I did in the example)  

an example how to add a switch to a funtion, so you can override it for a function like you can with a CMDlet :

 

# WriteExample.ps1
# add a verbose switch to a PowerShell Function

param ([switch]$Verbose)

# Check verbose Switch

if ($verbose.IsPresent) {
  $VerbosePreference = 'Continue'
  Write-Verbose "Verbose Mode Enabled"
}
Else {
  $VerbosePreference = 'SilentlyContinue'
}

# Rest of script

write-host "only Host output"
write-verbose "only Host output verbose mode"
write-warning "Warning only host"
write-error "error, will raise error"

now you can change the behavior by setting the preference in the $global scope or overrule it on a Function base this like this :

 *Edit 4 *Note that the -verbose switch will set it only for the script it leaves the global setting alone

# Examples working with the different preferences

PoSH>$VerbosePreference
SilentlyContinue
PoSH>$WarningPreference
Continue
PoSH>$ErrorActionPreference
Continue


PoSH>\WriteExample.ps1
only Host output
WARNING: Warning only host
C:\PowerShell\WriteExample.ps1 : error, will raise error
At line:1 char:19
+ .\WriteExample.ps1  <<<<

# verbose

PoSH>\WriteExample.ps1 -v
VERBOSE: Verbose Mode Enabled
only Host output
VERBOSE: only Host output verbose mode
WARNING: Warning only host
C:\PowerShell\WriteExample.ps1 : error, will raise error
At line:1 char:19
+ .\WriteExample.ps1  <<<< -v
PoSH>

# changing also warning and error preference

PoSH>$ErrorActionPreference = 'SilentlyContinue'
PoSH>$WarningPreference = 'SilentlyContinue'
PoSH>.\WriteExample.ps1
only Host output
PoSH>.\WriteExample.ps1 -v
VERBOSE: Verbose Mode Enabled
only Host output
VERBOSE: only Host output verbose mode
PoSH>

Enjoy,

Greetings, /\/\o\/\/
Tags : Monad msh PowerShell




posted by /\/\o\/\/
 2 comments
 


PowerShell and Active Directory Part 9 (nested groups)



In this entry I will show how you can use a recursive script to get the all members of a group, including the members that are in nested groups.

Note that the script will not handle groups with more as 1000 (W2K) or 1500 (W2K3) users,
as this is the maximum the Members property will enumerate.
If you have groups that are bigger you need to adapt the script to do a paged search for examples how to do this see :Large AD queries in Monad

The script looks like this :

# Function get-NestedMembers
# List the members of a group including all nested members of subgroups
# /\/\o\/\/ 2006

function get-NestedMembers ($group){ 
  if ($group.objectclass[1] -eq 'group') { 
        write-verbose "Group $($group.cn)" 
    $Group.member |% { 
      $de = new-object directoryservices.directoryentry("LDAP://$_"
      if ($de.objectclass[1] -eq 'group') { 
        get-NestedMembers $de 
      } 
      Else { 
        $de.sAMAccountName 
      } 
    } 
  }
  Else {
    Throw "$group is not a group"
  } 


Note that I did add the groupname as a Write-Verbose, so it will only show in verbose mode and in verbose mode will only be displayed not passed on to the pipeline , also it will show users that are member of more groups as often as they get found, as show in the examples below you can use group or sort -unique to get a list of them or to only show the users / computers found in more groups once.

# get-NestedMembers usage examples :
 
# get a group

$group = new-object directoryservices.directoryentry("LDAP://cn=MainGroup,OU=Groups,DC=mow,DC=Local")

# Get all nested members

get-NestedMembers $group

User1
User2
User2
User3

# Show current verbose mode :

MowPS>$VerbosePreference
SilentlyContinue

# Enable Verbose Mode :

$VerbosePreference = 'continue'

get-NestedMembers $group

VERBOSE: Group MainGroup
User1
User2
VERBOSE: Group SubGroup
User2
User3

# Disable Verbose Mode again :

$VerbosePreference = 'SilentlyContinue'

# Group the output to get the doubles
 
get-NestedMembers $group | group

Count Name                      Group
----- ----                      -----
    1 User1                  {User1}
    2 User2                  {User2, User2}
    1 User3                  {User3}

# Use sort -Unique to get every user only once

get-NestedMembers $group | sort -Unique

User1
User2
User3



Enjoy,

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


posted by /\/\o\/\/
 0 comments

Thursday, August 24, 2006

 


Powershell and hey Scripting guy !



one of my most favorite scripting sites is the Script Center on Microsoft teched.

As I said often before on my blog especialy the scripting guys items I like very much, I follow it for a long time and and I did learn a lot from it.

the first powershell examples on the scriptingcenter in the Windows PowerShell Script Repository where a bit dissapointing
see also : First Monad B3 impression and please help the scripting guys

but after some complaining : see also : PowerShell's Script Center problem
the Scripting guy made it all up see :

How Can I Use Windows PowerShell to Start a Service on a Remote Computer?
What Can I Do With Windows PowerShell?

Yesterday the scripting guy answered a Powershell Question again :
How Can I Use Windows PowerShell to Determine the Status of the Guest Account?

For the question of today in powershell see also :
How Can I Add or Subtract Two Hexadecimal Numbers?
for Powershell this looks like this :

# Add some hex numbers :

MSH>0x1C8 + 0xD201
54217

# use the format operator :

MSH>"{0:x}" -f (0x1C8 + 0xD201)
d3c9

# use the tostring overload

MSH>(0x1C8 + 0xD201).tostring('x')
d3c9


need I say more ?

for more examples of formatting on the powershellconsole see also 2006 Winter Scripting Games (part 2)


So you see that its also nice to follow the examples not in Powershell and try to find a PowerShell way, I have a lot of examples of doing that, in the series about the Scripting Games I did compete in on my blog where I did al the questions also in powerShell form (MSH at the time) as all the material and origional answers are still online here : Scripting Games Home.

The PowerShell versions a made for the events you can find here : (from first to last)
2006 Winter Scripting Games
2006 Winter Scripting Games (part 2)
scripting games part 3
MSH Concentration (scripting games part 4)
Scripting Games week 2
The Scripting Games Event 6 in MSH
Scripting Games almost over
scripting games Last Day MSH answers for event 7 and 8

but as you see the "Hey scripting guy !" series is also nice to check fo the VbSScript examples as a lot of the techniques also can be used in powershell,
you can theck the archive here : Hey, Scripting Guy! Archive by Date
and the Script center has also a "what is new" RSS feed that you can use to be triggered when new examples get posted as they get metioned in the feed, hence its one of favorite feeds as well.

I need a bit more time on the next part of the AD series as I still don't like the version as I have it at the moment
As also I had a very busy Weekend doing a big migration at a customer last weekend, (powershell was very usefull there , not to say that it did make/save my weekend, as it enabled me to quickly do some large AD / Exchange changes and other checks of logfiles from robocopy etc .
I made some powershell scripts on the fly to :

checking list of emailadresses agains the adresses in AD etc.
scanning (grep-ping) logfiles etc.
changing Email aliases,
setting primiary aliases
removing double primairy adresses
setting forwarding on mailboxes
setting Permissions on Mailboxes.
list the mailboxes I did disable the mailbox restrictions on
setting some redirections on group mailboxes etc.


As I do think that I could have done the status reports asked, lastminute changes, and fixes needed during the migration that quick and in a situation that was, as Jeffrey Snover would say "With my hair on fire"

I did had to replace someone at the last minute on this project, hence did not know the situation wel, and the large (large) Vbscripts and tooling used for it I did not realy know , so to make these changes/fixes with the origional scripts, as mostly there was only need to do a small part of the complete migration script to run again, or needed some tuning, or I was not completly sure if all siteeffects where covered if I did run the script and also I did some things as setting send-As security on mailboxes that is not that easy,

So I was realy glad I could do a lot on the fly on the powershell commandline, and make small scripts on the fly to do the changes, to do that in VbScripts would be way to much work to even have tried.

So in this situation I realy did feel the "Power" of powershell during big migrations, As is that much more easy to make this kind of changes and checks,

I think a lot of times last weekend about how difficult that would have been in VbScript and how much time I would have taken.

and that I realy could not miss PowerShell anymore.

I will also make a post with some examples I did use for this as I clear them up and sort them out, for a later post in the AD series.

Enjoy,

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


posted by /\/\o\/\/
 0 comments

Monday, August 14, 2006

 


PowerShell Orphan share remover Tool (Update)



I needed to clean some Orphan Shares on our servers,
but when I did lookup the MSH script, I make last year I found that the stats did not work anymore in PowerShell, also it had a boolean parameter, since we have a Switch argument now in PowerShell that would be better,

So I Updated the MSH Orphan share remover Tool To PowerShell,
I changed the $doChange Bool to a [Switch], (new in powerShell RC1
see PowerShell AD site Finder) also the -eq behavour when checking an array changed,
so I needed to change the to -contains (for the example I did leave them in but I switched also to keeping count in the script)

The Update script looks like this :

# OrphanShares.PS1
# Checks / Removes Orphan Shares
# /\/\o\/\/ 2005
# V2 updated to PowerShell RC1

param (
  [string] $Computer = ".",   
  [Switch] $Remove
)

# Connect to registry

$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $Computer)
$key = $Reg.OpenSubKey("SYSTEM\CurrentControlSet\Services\LanmanServer\Shares", $True)

# get shares in Registry :

$Regshares = $Key.GetValueNames()

# get Existing shares :

$shares = @();get-WMIObject -computer $Computer win32_share | foreach {$shares += $_.name}

# Compare the Lists :

$Exist = 0
$Orphan = 0
$deleted = 0

$RegShares | foreach {
  if ($shares -eq $_) {

    "$_ Exists"
    $Exists += 1

  }Else {

    "$_ is an Orphan"
    $Orphan += 1

    # Only if DoChange is True Delete The Orphan Share

    if ($Remove.IsPresent) {$Key.DeleteValue($_);"$_ Deleted !!";$deleted +=1}
  }
}

# show some statistics :

"Existing : $Exists"
"Orphan   : $Orphan"
"Deleted   : $Deleted"

# changed -eq to -contains but not used anymore 

# "Orphan   : ";($RegShares | foreach {"$_ $($shares -contains $_)"} | findstr "False" | measure-object).count
# "Existing : ";($RegShares | foreach {"$_ $($shares -contains $_)"} | findstr "True" | measure-object).count



For more information about Orphan shares and the script see the old post,
Enjoy,

Greetings /\/\o\/\/

Tags :


posted by /\/\o\/\/
 0 comments

Friday, August 11, 2006

 


PowerShell and Active Directory Part 8 (ACL's)



This eighth part of the AD series is the *Edit* change of plans, see rest of blogentry, there will be one more, (one before) last about creating users in Active Directory,
in this post we will transform the filter used in last post to a more robust function, and transform it in a more production quality script.

*Note* Disclaimer, while I discus the posibilities for making a production quality script the example script I provide in this post is only a example on how to start, not complete and tested script, so only use this in a test environment also be sure to have a basic understanding
of how the script works, better yet read the other posts in this series first!

Part 1 to 3 of this series handle the making of a user from the start from the commandline, an overview what you can find where you can find in the beginning of part 4 ,
Part 5 - 7 handle the way to use CSV files or any other way to fill the PSObject with the needed properties, and this function.

PowerShell and Active Directory Part 7
PowerShell and Active Directory Part 6
PowerShell and Active Directory Part 5
PowerShell and Active Directory Part 4 (TypeData)
PowerShell and Active Directory Part 3 (UserProperties)
PowerShell and Active Directory Part 2
PowerShel and Active Directory Part 1

First about the change to a script, the Filter I used is easy to test with as you can just past it to the commandline, this version is a script that you can run directly.

for another example see the changes Mike Hodnick made to an out-zip function I made before MSH out-zip Function in his blogpost here Create zip/compressed folders with Powershell to make it into a scriptas it was "but it wasn't exactly 100% usable out of the box".

The only thing that needed changing is the extension (MSH to PS1) and it works,
only this script was made to create / load the function :

. c:\powershell\fuctions\out-zip.ps1

note the [dot][space] before the scriptpath, that makes that the function is loaded into the $global scope, otherwise it is gone again after the script ends.
It is gone also if you close the PowerShell console but I load this in my profile to keep it handy.
As I mostly test by pasting code into the console most of my examples are like this, so you can just past them in from my blog to test.

So it both handy out-of-the box I think but in an other way and / or a different purpose.
it's good to compare the 2 scripts to see wat I mean.
In this case I also did choose to switch to a scriptfile to run and let it call internal functions.
for some more info and another example see also : PowerShell AD site Finder this script has the same form, also the switch parameter and byRef I discuss there.


For the setting "User can not change password" I did not yet discuss that in Part 3 about setting the special properties, this is actualy a ACL on the directoryentry and you can not set it using ADSI, only with the NT provider.
You can not use the WinNT provider with a DirectoryEntry Object, but there are some workarounds see also : Access ADSI WinNT provider from Monad Part 2

You see there are 2 workarounds,
The First I found was inline VB.NET code MSH access NT provider
The second was the Reflection example in part 2,that way, Importing the VisualBasic namespace , I show here :

# Setting User can not change password with NT provider using the VisualBasic namespace

[System.Reflection.Assembly]::LoadWithPartialName('microsoft.visualbasic')

$winNT = [microsoft.visualbasic.interaction]::GetObject("WinNT://mow/mow",$null)
$Type = $winNT.gettype()
$Type.invokemember("userFlags",'GetProperty',$null,$winNT,$null) -band 0x40

$new = $Type.invokemember("userFlags",'GetProperty',$null,$winNT,$null) -bor 0x40
$Type.invokemember("userFlags",'SetProperty',$null,$winNT,$new)
# Save the changes

$Type.invokemember("setinfo",'invokemethod',$null,$winNT,$null)


But as it is actualy an ACL on the directory object we can also change the security on the User object ourself, see for more information here : MDSN User Cannot Change Password (LDAP Provider) (Be sure to follow the links to the different examples )
for a VbScript see example : HilltopLabs : cannot Change Password

This looks very difficult and big scripts are needed, but we have powershell ;-)
So this looks like this in the script :

  # Set User Cannot Change Password Property

  if ($_.CantChangePassword) {

    $everyOne = [System.Security.Principal.SecurityIdentifier]'S-1-1-0'
    $EveryoneDeny = new-object System.DirectoryServices.ActiveDirectoryAccessRule ($Everyone,'ExtendedRight','Deny','ab721a53-1e2f-11d0-9819-00aa0040529b')
    $self = [System.Security.Principal.SecurityIdentifier]'S-1-5-10'
    $SelfDeny = new-object System.DirectoryServices.ActiveDirectoryAccessRule ($self,'ExtendedRight','Deny','ab721a53-1e2f-11d0-9819-00aa0040529b')

    $newUser.get_ObjectSecurity().AddAccessRule($selfDeny)
    $newUser.get_ObjectSecurity().AddAccessRule($EveryoneDeny)

    $newUser.CommitChanges()

  }


Not to Bad right, but note that the other examples do some more checking and can set it back also, as we create new users here we do not need all this.

*Edit* I decided this subject was important enough to get his own blogentry and found back some old material from when I did not have internet I did not post yet, so I will post that material here and will continue the script in next post.

In the samples below you can also see how to explore the AD security from the commandline and also to check the current ACL's and remove the Access denied rules.
you can see that it is also very easy, also I will discuss the User must change password at next logon property in this examples, setting pwdLastset to -1 or 0
will switch that (it can not be set in combination the the User can not change password)


# Remove Access denied from commandline

$mow.get_ObjectSecurity().GetAccessRules($true,$false, [System.Security.Principal.SecurityIdentifier] )

$mow.get_ObjectSecurity().GetAccessRules($true,$false, [System.Security.Principal.NTAccount]) |? {$_.ObjectType -eq 'ab721a53-1e2f-11d0-9819-00aa0040529b'}
$mr = $mow.get_ObjectSecurity().GetAccessRules($true,$false, [System.Security.Principal.NTAccount]) |? {$_.ObjectType -eq 'ab721a53-1e2f-11d0-9819-00aa0040529b'} |? {$_.AccessControlType -eq 'Deny'}

$mow.get_ObjectSecurity().RemoveAccessRule($mr[0])

$mow.get_ObjectSecurity().RemoveAccessRule($mr[1])

$mow.CommitChanges()

########################################


PoSH>$mr[0].IdentityReference | gm


   TypeName: System.Security.Principal.NTAccount


[System.Security.Principal.SecurityIdentifier].GetConstructors() |% {"$_"}


##############

# Make SID Objects 

$everyOne = [System.Security.Principal.SecurityIdentifier]'S-1-1-0'
$self = [System.Security.Principal.SecurityIdentifier]'S-1-5-10'


# you can use NTAccount but thats localized 

# translate 

PoSH>$self.Translate( [System.Security.Principal.NTAccount])

Value
-----
NT AUTHORITY\SELF

# get the consructors

[System.DirectoryServices.ActiveDirectoryAccessRule].GetConstructors() |% {"$_"}
 

# we are going to use this one :

Void .ctor(System.Security.Principal.IdentityReference, System.DirectoryServices.ActiveDirectoryRights, System.Security
.AccessControl.AccessControlType, System.Guid)

new System.DirectoryServices.ActiveDirectoryAccessRule ($self,'ExtendedRight','Deny','ab721a53-1e2f-11d0-9819-00aa0040529b')

ActiveDirectoryRights : ExtendedRight
InheritanceType       : None
ObjectType            : ab721a53-1e2f-11d0-9819-00aa0040529b
InheritedObjectType   : 00000000-0000-0000-0000-000000000000
ObjectFlags           : ObjectAceTypePresent
AccessControlType     : Deny
IdentityReference     : S-1-5-10
IsInherited           : False
InheritanceFlags      : None
PropagationFlags      : None


$SelfDeny = new-object System.DirectoryServices.ActiveDirectoryAccessRule ($self,'ExtendedRight','Deny','ab721a53-1e2f-11d0-9819-00aa0040529b')
$SelfAllow = new-object System.DirectoryServices.ActiveDirectoryAccessRule ($self,'ExtendedRight','Allow','ab721a53-1e2f-11d0-9819-00aa0040529b')
$EveryoneDeny = new-object System.DirectoryServices.ActiveDirectoryAccessRule ($Everyone,'ExtendedRight','Deny','ab721a53-1e2f-11d0-9819-00aa0040529b')
$EveryOneAllow = new-object System.DirectoryServices.ActiveDirectoryAccessRule ($Everyone,'ExtendedRight','Allow','ab721a53-1e2f-11d0-9819-00aa0040529b')

$mow.get_ObjectSecurity().AddAccessRule($selfDeny)
$mow.get_ObjectSecurity().AddAccessRule($EveryoneDeny)

$mow.CommitChanges()

#########################################

# Setting User must change password at next Logon

$user.pwdLastSet = -1

# Reading password last set value

$ds = New-Object directoryservices.directorysearcher($user)

MowPS>[datetime]::fromfiletime(($ds.findone().properties.pwdlastset)[0])

Monday, July 172006 5:07:05 PM

# Un-setting user must chage password at next logon
$user.pwdLastSet = 0

MowPS>$user.InvokeGet('PasswordLastChanged')

***************************

MowPS>[datetime]::fromfiletime(($ds.findone().properties.pwdlastset)[0])


Next post I will go on with the script, but I'm still a bit thinging about it and as said did think the ACL material was worth a own "extra" part in this series.

Enjoy,

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


posted by /\/\o\/\/
 0 comments

Wednesday, August 09, 2006

 


Signing PowerShell Scripts



Scott Hanselmann, posted an excelent Signing PowerShell Scripts tutorial,

after the bogus "powershell worm" (next is the virusscanner removing xcopy as a virus ;-))

for more info see Windows PowerShell and the “PowerShell Worm”

this is a great tutorial about the security features of powershell.

enjoy,

greetings /\/\o\/\/

Tags :




posted by /\/\o\/\/
 1 comments
 


PowerShell and Active Directory Part 7



*Warning* As you have RC2 Installed all my AD posts will not work anymore, it is verry confusing, but fixable if you read explaination here PowerShell RC2 and Active Directory :


As I did switch back on all my work PC's (I did call all my colleagues not to upgrade as all our AD tools are messed up, I will not update them, as I would not recommend to upgrade to RC2 of you work with AD a lot.


In this seventh part of this AD series, we will expand the filter a little,but first we need to provide the properties,so we need to fill the CSV file a bit more with the fields needed.
You can just start-up excel to edit it but here I will use some more powershell onliners to fill them from the results of the export I did of some of the users I created from the adventureworks sample DB for my demosetup and did export in in PowerShell and Active Directory Part 5 , I did place get content to show what the current content is and will go on from there, using the onliners on the commandline to fill them, but you can also use the Ken Meyer user we created also in that post with the MMC and exported that onle is allready complete(for now). for more about CSV handling see more Monad scripts, and a bit more CSV and the links there for my CSV series.

# use invoke-item (ii) to invoke Excel to edit 

PoSH>ii modUsers.csv

# the file with six test users i have now

PoSH>cat modUsers.csv
Name,displayName,Description,Room,Telephone,FirstName,Initials,LastName,Department,Company,HomeDir,HomeDrive,LogonScript,Accountname,Mai

l,OU
NewUser0003,"Roberto  Tamburello","Engineering - Roberto  Tamburello",,,Roberto,,Tamburello,Engineering,,,,,,,
NewUser0009,"Gail A Erickson","Engineering - Gail A Erickson",,,Gail,,Erickson,Engineering,,,,,,,
NewUser0011,"Jossef H Goldberg","Engineering - Jossef H Goldberg",,,Jossef,,Goldberg,Engineering,,,,,,,
NewUser0012,"Terri Lee Duffy","Engineering - Terri Lee Duffy",,,Terri,,Duffy,Engineering,,,,,,,
NewUser0267,"Michael I Sullivan","Engineering - Michael I Sullivan",,,Michael,,Sullivan,Engineering,,,,,,,
NewUser0270,"Sharon B Salavaria","Engineering - Sharon B 

Salavaria",,,Sharon,,Salavaria,Engineering,,,,,,,"OU=MowOtherOU,DC=mow,DC=local"

# set some properties

PoSH>$newusers |% {$_.HomeDir = "H:"}
PoSH>$newusers |% {$_.Company = "PoSH works"}
PoSH>$newusers |% {$_.LogonScript = "Logon.cmd"}
PoSH>$newusers |% {$_.HomeDrive = "H:"}
PoSH>$newusers |% {$_.HomeDir = "\\mowdc001\home$\$($_.name)"}

# show first user

PoSH>$newusers[0]


Name        : NewUser0003
displayName : Roberto  Tamburello
Description : Engineering - Roberto  Tamburello
Room        :
Telephone   :
FirstName   : Roberto
Initials    :
LastName    : Tamburello
Department  : Engineering
Company     : PoSH works
HomeDir     : H:
HomeDrive   : \\mowdc001\home$\NewUser0003
LogonScript : Logon.cmd
Accountname :
Mail        :
OU          :

# fill some rooms

PoSH>1..3 |% {$newUsers[$_].room = "room $_"}

# try some formating on the mailaddress

PoSH>$newUsers |% {"{0}.{1}@Mowmail.com" -f $_.Firstname,$_.LastName}
Roberto.Tamburello@Mowmail.com
Gail.Erickson@Mowmail.com
Jossef.Goldberg@Mowmail.com
Terri.Duffy@Mowmail.com
Michael.Sullivan@Mowmail.com
Sharon.Salavaria@Mowmail.com

PoSH>$newUsers |% {"{0}.{1}@Mowmail.com" -f $_.Firstname.Substring(0,1),$_.LastName}
R.Tamburello@Mowmail.com
G.Erickson@Mowmail.com
J.Goldberg@Mowmail.com
T.Duffy@Mowmail.com
M.Sullivan@Mowmail.com
S.Salavaria@Mowmail.com

# fill that also

PoSH>$newUsers |% {$_.mail = ("{0}.{1}@Mowmail.com" -f $_.Firstname.Substring(0,1),$_.LastName)}

PoSH>$newUsers | ft name,room,mail -a

Name        Room   Mail
----        ----   ----
NewUser0003        R.Tamburello@Mowmail.com
NewUser0009 room 1 G.Erickson@Mowmail.com
NewUser0011 room 2 J.Goldberg@Mowmail.com
NewUser0012 room 3 T.Duffy@Mowmail.com
NewUser0267        M.Sullivan@Mowmail.com
NewUser0270        S.Salavaria@Mowmail.com


Ok enough CSV handling and looping tricks, lets get back AD that we are here fore and to the script to create.

In the following script I addded the following :

- Displayname
I did work out 3 possiblities for that,
1 it was given in the CSV file, so we just fill it
2 It was not given but we have a firstname and lastname so we generate it (as the MMC does)
3 displayname first and last name or not given set it to the name
- Some other otional properties
- if homeDirectory is given the Creating of homedirectories (as the MMC does)
- setting the initial password
- useraccount control to 512 (default) to setenabled (see former posts)

the filter now looks like this :

filter new-AdUser ([System.DirectoryServices.DirectoryEntry]$importOU){ 

  # check for name field : 

  if ($_.Name) {$CN= $_.name} Else {throw "Name is Mandatory"

  # Use default OU or connect to OU given in CSV file  

  if ($_.OU) { 
      $OU = new-object System.DirectoryServices.DirectoryEntry("LDAP://$($_.OU)"
  }  
  Else { 
      $OU = $ImportOU 
  } 

  # create the new user : 
  Write-host -fore "Yellow" "Creating $($_.name)"

  $NewUser = $ou.get_Children().add("CN=$($_.name)",'user'
  $NewUser.commitChanges() 

  # If accountname is not given make it same as CN 

  if ($_.Accountname) {$newUser.sAMAccountName = $_.Accountname} Else {$newUser.sAMAccountName = $_.name} 
  $NewUser.commitChanges() 

  # Process DisplayName

  if ($_.DisplayName) {
    # displayname given so use that
    $newUser.DisplayName = $_.DisplayName
  }
  ElseIf ($_.firstname -and $_.lastname) {
    # no displayname but lastname and firstname given generate Displayname
    $newUser.DisplayName = "$($_.LastName), $($_.FirstName) $($_.Initials)"
  }
  Else {
    # just use the UserName
    $newUser.DisplayName = $_.Name
  }

  # Fill Other Properties if given :

  if ($_.Firstname) {$newUser.GivenName = $_.Firstname}
  if ($_.Lastname) {$newUser.SN = $_.LastName}
  if ($_.Initials) {$newUser.Initials = $_.Initials}

  if ($_.Description) {$newUser.Description = $_.Description}
  if ($_.Room) {$newUser.physicalDeliveryOfficeName = $_.Room}
  if ($_.Telephone) {$newUser.telephoneNumber = $_.Telephone}
  if ($_.Department) {$newUser.Department = $_.Department}
  if ($_.Mail) {$newUser.Mail = $_.Mail}
  if ($_.HomeDrive) {$newUser.HomeDrive = $_.HomeDrive}
 
  # Fill HomeDir and try to create the folder

  if ($_.HomeDir) {
    $newUser.homeDirectory = $_.HomeDir

    # check if homeDir does exist, and if its possible to create

    if (Test-Path $_.HomeDir) {
      "HomeDir Does Exist"
    }
    Else {
      if (split-Path $_.HomeDir -Parent) {
        "Parent Found, Share $($_.HomeDir) can be created"
        $ud = md $_.HomeDir
        $ar = new-object Security.AccessControl.FileSystemAccessRule($_.name,'Modify, Synchronize','ContainerInherit, ObjectInherit','none','Allow')
        $ac = $ud.GetAccessControl()
        $ac.AddAccessRule($ar)
        Set-Acl $ud $ac
      }
      Else{
        "$(split-Path $_.HomeDir -Parent) not Found, Create share Manual"
      }
    }
  }

  if ($_.LogonScript) {$newUser.scriptPath = $_.LogonScript}

  # set initial password and force user to change it at first logon

  $newUser.Invoke("SetPassword","P@ssW0Rd")
  $newUser.pwdLastSet = 0

  # Enable User
  $newUser.userAccountControl = 512

}


note that as this is a filter not a script or function yet so all the code gets executed for every record,
we will change that in next post as for the other things I want to do I want to add some code that will only run once,
and we will add some more properties and errorhandling you will see the differance and use of this in next post also and work on the output.

the output looks like this :

PoSH>$mowou.get_Children() |? {$_.name -like "NewUser*"} |% {$mowou.get_Children().Remove($_)}
PoSH>$newusers | new-aduser $mowou

Creating NewUser0003
Parent Found, Share \\mowdc001\home$\NewUser0003 can be created
Creating NewUser0009
Parent Found, Share \\mowdc001\home$\NewUser0009 can be created
Creating NewUser0011
Parent Found, Share \\mowdc001\home$\NewUser0011 can be created
Creating NewUser0012
Parent Found, Share \\mowdc001\home$\NewUser0012 can be created
Creating NewUser0267
Parent Found, Share \\mowdc001\home$\NewUser0267 can be created
Creating NewUser0270
Parent Found, Share \\mowdc001\home$\NewUser0270 can be created

# only the basic errorhandling (270 user allready exists)

PoSH>$mowou.get_Children() |? {$_.name -like "NewUser*"} |% {$mowou.get_Children().Remove($_)}
PoSH>$newusers | new-aduser $mowou

Creating NewUser0003
HomeDir Does Exist
Creating NewUser0009
HomeDir Does Exist
Creating NewUser0011
HomeDir Does Exist
Creating NewUser0012
HomeDir Does Exist
Creating NewUser0267
HomeDir Does Exist
Creating NewUser0270
Exception calling "CommitChanges" with "0" argument(s): "The object already exists. (Exception from HRESULT: 0x80071392
)"
At line:14 char:25
+   $NewUser.commitChanges( <<<< )
Exception setting "sAMAccountName""A constraint violation occurred. (Exception from HRESULT: 0x8007202F)"
At line:16 char:81
+   if ($_.Accountname) {$newUser.sAMAccountName = $_.Accountname} Else {$newUser.s <<<< AMAccountName = $_.name}
Exception calling "CommitChanges" with "0" argument(s): "A constraint violation occurred. (Exception from HRESULT: 0x80
07202F)"
At line:17 char:25
+   $NewUser.commitChanges( <<<< )
Exception setting "DisplayName""A constraint violation occurred. (Exception from HRESULT: 0x8007202F)"
At line:21 char:14
+     $newUser.D <<<< isplayName = $_.DisplayName
Exception setting "GivenName""A constraint violation occurred. (Exception from HRESULT: 0x8007202F)"
At line:32 char:31
+   if ($_.Firstname) {$newUser.G <<<< ivenName = $_.Firstname}
Exception setting "SN""A constraint violation occurred. (Exception from HRESULT: 0x8007202F)"
At line:33 char:30
+   if ($_.Lastname) {$newUser.S <<<< N = $_.LastName}

# and the result 

PoSH>$mowou.get_Children() |? {$_.cn -match 'NewUser'} | format-aduser | ft

Name        displayName Description Room        Telephone   FirstName   Initials    LastName    Department  Company
----        ----------- ----------- ----        ---------   ---------   --------    --------    ----------  -------
NewUser0003 Roberto ... Engineer... {}          {}          Roberto     {}          Tamburello  Engineering {}
NewUser0009 Gail A E... Engineer... room 1      {}          Gail        {}          Erickson    Engineering {}
NewUser0011 Jossef H... Engineer... room 2      {}          Jossef      {}          Goldberg    Engineering {}
NewUser0012 Terri Le... Engineer... room 3      {}          Terri       {}          Duffy       Engineering {}
NewUser0267 Michael ... Engineer... {}          {}          Michael     {}          Sullivan    Engineering {}


PoSH>($mowou.get_Children() |? {$_.cn -match 'NewUser'} | format-aduser)[0]

Name        : NewUser0003
displayName : Roberto  Tamburello
Description : Engineering - Roberto  Tamburello
Room        : {}
Telephone   : {}
FirstName   : Roberto
Initials    : {}
LastName    : Tamburello
Department  : Engineering
Company     : {}
HomeDir     : \\mowdc001\home$\NewUser0003
HomeDrive   : H:
LogonScript : Logon.cmd
Accountname : NewUser0003
Mail        : R.Tamburello@Mowmail.com


Note that I made one user in another OU and did not delete that user, every line will give his own error and everything is still triedand will fail we work on that also in next post, as for large numbers this is not handy but for a couple of users this doable.
I think all the AD stuff was covert in former posts, so I won't explain that again here and the rest of the filter is straight forward I think, except for setting the security on the directory I covert this before in this post :Adding a Simple AccesRule to a file ACL in MSH and see the links in there for more info, so this is it for now.

Enjoy,

greetings /\/\o\/\/
Tags :


posted by /\/\o\/\/
 0 comments

Monday, August 07, 2006

 


PowerShell and Active Directory Part 6



In this part sixth part of the AD series we will make a start of the script to import / create users from a CSV file (template generated in part 5 of this series).

# Load the CSV file with users 

$newUsers = import-csv  C:\PowerShell\NewUsers.csv 

# the first user looks like this (see part 5)

PoSH>$newusers[0]


Name        : NewUser0003
displayName : Roberto  Tamburello
Description : Engineering - Roberto  Tamburello
Room        :
Telephone   :
FirstName   : Roberto
Initials    :
LastName    : Tamburello
Department  : Engineering
Company     :
HomeDir     :
HomeDrive   :
LogonScript :
Accountname : $222000-BOCQ2JHU74K1
Mail        : 


I will start by just echo-ing some of the properties to the screen in the first examples.
and I will start using a filter as we provide the objects on the pipeline :

# most Simple form Filter

filter new-AdUser {
  if ($_.Name) {"CN=$($_.name)"Else {throw "Name is Mandatory"}
}

# Check on the Name Property

PoSH>"foo" | new-AdUser
Name is Mandatory
At line:2 char:45
+   if ($_.Name) {"CN=$($_.name)"Else {throw  <<<< "Name is Mandatory"}

# if objects in pipeline have name property

PoSH>$newusers | new-AdUser
CN=NewUser0003
CN=NewUser0009
CN=NewUser0011
CN=NewUser0012
CN=NewUser0267
CN=NewUser0270


we will later switch to a function for this but I will start with a filter to show the difference

in this first filter you can see that $_ contains the current item and we can access the different field from the CSV file from the object, in this case we just echo it to the screen.

as we have seen in the second part of this series, all we really need to create a User is a CN, but we have also seen that the SamAccountname another mandatory property gets generated in that case and that that is not realy handy, as we most of the time want this the same as the CN name lets add a check for the property and if it's not there lets add it.

# added SamAccountname

filter new-AdUser {
  # check for name field :

  if ($_.Name) {"CN=$($_.name)"Else {throw "Name is Mandatory"}

  # If accountname is not given make it same as CN

  if ($_.Accountname) {"SamAccountName = $($_.Accountname)"Else {"SamAccountName = $($_.name)"}
}

# objects in pipeline have accountname property

PoSH>$newusers | new-AdUser

CN=NewUser0003
SamAccountName = $222000-BOCQ2JHU74K1
CN=NewUser0009
SamAccountName = $822000-KK9G22LQPGJV
CN=NewUser0011
SamAccountName = $A22000-RFN1CV2D2U5N
CN=NewUser0012
SamAccountName = $B22000-L10CLJU1NCSO
CN=NewUser0267
SamAccountName = $AA2000-R7OSI41L9LHR
CN=NewUser0270
SamAccountName = $DA2000-7UPIT07U0FPV 

# Remove accountname property 

PoSH>$newUsers |% {$_.accountname = ""}

# Now the SamAccountname will be generated the same as the CN

PoSH>$newusers | new-AdUser

CN=NewUser0003
SamAccountName = NewUser0003
CN=NewUser0009
SamAccountName = NewUser0009
CN=NewUser0011
SamAccountName = NewUser0011
CN=NewUser0012
SamAccountName = NewUser0012
CN=NewUser0267
SamAccountName = NewUser0267
CN=NewUser0270
SamAccountName = NewUser0270


As you can see, you can use this to keep the CSV file simple but extendable (you don't need to provide the samaccountname if you want it to be the same as the CN, but you CAN provide it if this is not the case ).
also as my exported users had generated samaccountname properties, I did clear them all so the samacountnames will be the same as the CN.

now to create the users we also need the the OU where to import the user, I will add this as a parameter to the function, but also make a check for a OU field in the CSV file to overrule this on a per user base.
also I will show some examples of how the OU gets parsed so you can use an OU object or the LDAP string :

# OU to import parameter

PoSH>[System.DirectoryServices.DirectoryEntry]$ImportOu = $mowOu
PoSH>[System.DirectoryServices.DirectoryEntry]$ImportOu = "OU=MowOu,DC=mow,DC=local"
PoSH>$ImportOu
out-lineoutput : Exception retrieving member "ClassId2e4f51ef21dd47e99d3c952918aff9cd": "Unspecified error
"

# LDAP path to import parameter

PoSH>[System.DirectoryServices.DirectoryEntry]$ImportOu = "LDAP://OU=MowOu,DC=mow,DC=local"
PoSH>$ImportOu

distinguishedName
-----------------
{OU=MowOu,DC=mow,DC=local}

#  list possible constuctors

PoSH>[System.DirectoryServices.DirectoryEntry].GetConstructors() |% {"$_"}
Void .ctor()
Void .ctor(System.String)
Void .ctor(System.String, System.String, System.String)
Void .ctor(System.String, System.String, System.String, System.DirectoryServices.AuthenticationTypes)
Void .ctor(System.Object)
PoSH>[System.DirectoryServices.DirectoryEntry]$ImportOu = "LDAP://OU=MowOu,DC=mow,DC=local"
PoSH>$ImportOu

distinguishedName
-----------------
{OU=MowOu,DC=mow,DC=local}

# Take care the LDAP path is CaseSensitive 

PoSH>[System.DirectoryServices.DirectoryEntry]$ImportOu = "LDaP://OU=MowOu,DC=mow,DC=local"
PoSH>$ImportOu
out-lineoutput : Exception retrieving member "ClassId2e4f51ef21dd47e99d3c952918aff9cd""Unknown error (0x80005000)"
PoSH>

# added OU as parameter and as field to the filter

filter new-AdUser ([System.DirectoryServices.DirectoryEntry]$importOU){
  # check for name field :

  if ($_.Name) {"CN=$($_.name)"Else {throw "Name is Mandatory"}

  # If accountname is not given make it same as CN

  if ($_.Accountname) {"SamAccountName = $($_.Accountname)"Else {"SamAccountName = $($_.name)"}

  if ($_.OU) {"OU = $($_.OU)"Else {"OU = $($ImportOU.distinguishedName)"}

}

# output now looks like this :

PoSH>$newusers | new-AdUser $mowou
CN=NewUser0003
SamAccountName = NewUser0003
OU=MowOu,DC=mow,DC=local
CN=NewUser0009
SamAccountName = NewUser0009
OU=MowOu,DC=mow,DC=local
CN=NewUser0011
SamAccountName = NewUser0011
OU=MowOu,DC=mow,DC=local
CN=NewUser0012
SamAccountName = NewUser0012
OU=MowOu,DC=mow,DC=local
CN=NewUser0267
SamAccountName = NewUser0267
OU=MowOu,DC=mow,DC=local
CN=NewUser0270
SamAccountName = NewUser0270
OU=MowOu,DC=mow,DC=local

# Some extra tricks using select like in part 5 of this series

# using select to add the OU property on the fly

PoSH>$newusers | select *,@{e={"OU=MowOtherOU,DC=mow,DC=local"};name='OU'} | new-adUser
CN=NewUser0003
SamAccountName = NewUser0003
OU = OU=MowOtherOU,DC=mow,DC=local
CN=NewUser0009
SamAccountName = NewUser0009
OU = OU=MowOtherOU,DC=mow,DC=local
CN=NewUser0011
SamAccountName = NewUser0011
OU = OU=MowOtherOU,DC=mow,DC=local
CN=NewUser0012
SamAccountName = NewUser0012
OU = OU=MowOtherOU,DC=mow,DC=local
CN=NewUser0267
SamAccountName = NewUser0267
OU = OU=MowOtherOU,DC=mow,DC=local
CN=NewUser0270
SamAccountName = NewUser0270
OU = OU=MowOtherOU,DC=mow,DC=local

# or combine the standard OU with some overruled users


PoSH>$newusers | select *,@{e={if ($_.name -eq 'NewUser0270'){"OU=MowOtherOU,DC=mow,DC=local"else {""}};name='OU'} | new-adUser $mowOU

CN=NewUser0003
SamAccountName = NewUser0003
OU = OU=MowOu,DC=mow,DC=local
CN=NewUser0009
SamAccountName = NewUser0009
OU = OU=MowOu,DC=mow,DC=local
CN=NewUser0011
SamAccountName = NewUser0011
OU = OU=MowOu,DC=mow,DC=local
CN=NewUser0012
SamAccountName = NewUser0012
OU = OU=MowOu,DC=mow,DC=local
CN=NewUser0267
SamAccountName = NewUser0267
OU = OU=MowOu,DC=mow,DC=local
CN=NewUser0270
SamAccountName = NewUser0270
OU = OU=MowOtherOU,DC=mow,DC=local

# update the CSV file 

PoSH>$newusers | select *,@{e={if ($_.name -eq 'NewUser0270'){"OU=MowOtherOU,DC=mow,DC=local"else {""}};name='OU'} | e
xport-csv -not c:\powershell\modUsers.csv
PoSH>import-csv c:\powershell\modUsers.csv | ft name,ou

Name                                                        OU
----                                                        --
NewUser0003
NewUser0009
NewUser0011
NewUser0012
NewUser0267
NewUser0270                                                 OU=MowOtherOU,DC=mow,DC=local


Note that you can just edit the CSV file but I did use select again like in the export examples in part 5 of the AD series,to show that you don't need to use the object and can nest any kind of script in in.

Now we have all the properties to start lets change the script to not only echo the properties but actualy do something.

#
# the first version that is actualy working :
 
filter new-AdUser ([System.DirectoryServices.DirectoryEntry]$importOU){

  # check for name field :

  if ($_.Name) {$CN= $_.name} Else {throw "Name is Mandatory"}

  # Use default OU or connect to OU given in CSV file 

  if ($_.OU) {
      $OU = new-object System.DirectoryServices.DirectoryEntry("LDAP://$($_.OU)")
  } 
  Else {
      $OU = $ImportOU
  }

  # create the new user :

  $NewUser = $ou.get_Children().add("CN=$($_.name)",'user')
  $NewUser.commitChanges()

  # If accountname is not given make it same as CN

  if ($_.Accountname) {$newUser.sAMAccountName = $_.Accountname} Else {$newUser.sAMAccountName = $_.name}
  $NewUser.commitChanges()

}

Now let's create the users in the CSV file, where I will overrule the OU for one account.

# make other OU to test

PoSH>($root.get_Children().Add("OU=MowOtherOU","organizationalUnit")).commitChanges()

# one account on other OU 

PoSH>import-csv c:\powershell\modUsers.csv | new-AdUser  $mowou

distinguishedName
-----------------
{OU=MowOu,DC=mow,DC=local}
SamAccountName = NewUser0003
{OU=MowOu,DC=mow,DC=local}
SamAccountName = NewUser0009
{OU=MowOu,DC=mow,DC=local}
SamAccountName = NewUser0011
{OU=MowOu,DC=mow,DC=local}
SamAccountName = NewUser0012
{OU=MowOu,DC=mow,DC=local}
SamAccountName = NewUser0267
{OU=MowOtherOU,DC=mow,DC=local}
SamAccountName = NewUser0270

# check if the users are there :

PoSH>$mowou.get_Children() |? {$_.name -like "NewUser*"} | ft cn,distinguishedName,sAMAccountName

cn                                      distinguishedName                       sAMAccountName
--                                      -----------------                       --------------
{NewUser0003}                           {CN=NewUser0003,OU=MowOu,DC=mow,DC=l... {NewUser0003}
{NewUser0009}                           {CN=NewUser0009,OU=MowOu,DC=mow,DC=l... {NewUser0009}
{NewUser0011}                           {CN=NewUser0011,OU=MowOu,DC=mow,DC=l... {NewUser0011}
{NewUser0012}                           {CN=NewUser0012,OU=MowOu,DC=mow,DC=l... {NewUser0012}
{NewUser0267}                           {CN=NewUser0267,OU=MowOu,DC=mow,DC=l... {NewUser0267}
{NewUser0270}                           {CN=NewUser0270,OU=MowOu,DC=mow,DC=l... {NewUser0270}

# Clean up created users for next test

PoSH>$mowou.get_Children() |? {$_.name -like "NewUser*"} |% {$mowou.get_Children().Remove($_)}
PoSH>$mowou.get_Children() |? {$_.name -like "NewUser*"} | ft cn,distinguishedName,sAMAccountName
PoSH>


Thats it for now, in the next post we will add some more properties and enable them etc. as in the third part of this series.also we will add some more checking and errorhandling etc, make it into a function on the way, and will add some more functionality as creating the Homedirectory etc. as the MMC also does.

Enjoy,

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


posted by /\/\o\/\/
 3 comments

Archives

October 2005   November 2005   December 2005   January 2006   February 2006   March 2006   April 2006   May 2006   June 2006   July 2006   August 2006   September 2006   October 2006   November 2006   December 2006  

$Links = ("PowerShell RC1 Docs"," PowerShell RC1 X86"," PowerShell RC1 X64"," Monad GettingStarted guide"," Monad Progamming Guide"," Monad SDK"," Monad videos on Channel 9"," MSH Community Workspace"," scripts.readify.net "," MonadSource"," www.reskit.net"," PowerShell Blog"," Under The Stairs"," computerperformance powershell Home"," proudlyserving"," MSH on wikipedia"," MSHWiki Channel 9"," Keith Hill's Blog"," Precision Computing"," PowerShell for fun"," MSH Memo (Japanese)"," monadblog")

find-blog -about "PowerShell","Monad" | out-Technorati.
find-blog -contains "","" | out-Technorati.
Google
 
Web mow001.blogspot.com

This page is powered by Blogger. Isn't yours?