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)
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.
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 &nbsp
<jtruher> blast - the HTML renderer changed my &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.
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
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 :
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 ;-)),
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
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,
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
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.
*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
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
PoSH>\WriteExample.ps1 only Host output WARNING: Warning only host C:\PowerShell\WriteExample.ps1 : error, will raise error At line:1char: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:1char: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>
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.
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.
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.
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)
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.
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.
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
$new = $Type.invokemember("userFlags",'GetProperty',$null,$winNT,$null) -bor 0x40 $Type.invokemember("userFlags",'SetProperty',$null,$winNT,$new) # Save the changes
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)
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.
*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.
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
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)
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
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.
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)
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.
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).
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:2char:45 + if ($_.Name) {"CN=$($_.name)"} Else {throw <<<< "Name is Mandatory"}
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)"} }
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 :
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.
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.