I am sure this topic has been covered many times before on other forums and blog posts, but here is my contribution to this activity. With the push for advanced functions during the 2011 Scripting Games, I think I would be remiss to not use that functionality on this script.
To start an advanced function, I need to declare the CmdletBinding() attribute at the beginning. This allows for parameter attributes that can make this script work just like a normal compiled cmdlet. I also need to define what types of parameters I can accept as well.
[CmdletBinding(DefaultParameterSetName="Bulk")] Param( [parameter(Mandatory=$true, ParameterSetName="Single")] [string]$ComputerName=$env:ComputerName, [parameter(Mandatory=$true)] [string]$localGroup, [parameter(ParameterSetName="Single")] [string]$domainUser, [parameter(ParameterSetName="Single")] [string]$domainGroup, [parameter(Mandatory=$true, ParameterSetName="Bulk")] [string]$csvFile )
I have used two types of parameter decorations to specify the types of information required to process this script. I have two different parameter set names: “Single” and “Bulk.” I have also specified that some of the parameters are required. In a minute, I will show you how to process which parameter set was given. The CmdletBinding() attribute declares which parameter set I expect to get.
Now to determine what we have to work with…Is it a file name or a username and group name? First, I will use the Switch statement to determine which parameter set was used. However, with Powershell, if the parameter give is ambiguous (all of ours are string parameters, so how does it know?), it will go with what we defined above as the DefaultParameterSetName or the “Bulk” option.
switch ($PSCmdlet.ParameterSetName) { "Single" {<Single user code block>} "Bulk" {<Bulk user code block>} }
I intentionally left out all the code for each type of parameter set to make the snippets more readable here. The full code listing at the end will bring that all together. Since the basic idea of this script is to add a domain object (user or group) to a local group, I will set up a function inside this script to handle only that operation. By design, this allows the script to accept input from different types of sources and still use the same basic code to do the real operation I am trying to accomplish.
Function Add-DomainObjectToLocal { Param($computer,$localgroup,$domain,$object) try { Write-Host "Adding $domain\$object to $localgroup on $computer." ([ADSI]"WinNT://$computer/$localgroup,group").Add("WinNT://$domain/$object") } catch { Write-Host "An error occurred: $_." } }
This function will be inside the script and can be used no matter what input is given, as you will see in a moment. I have also provided a method to give feedback to the user in case there is a problem with any of the items trying to be processed. If, for instance, the domain group is already a member of the local group, an exception will be generated. The CATCH block here will intercept the error and provide a message to the user saying so.
For now, let’s look at processing the “Single” parameter set. This code block would go in the first switch code block (Where <Single User Code Block> is). All we do here is to call the function I just created with the appropriate parameters.
if ($domainGroup){$domainObject = $domainGroup}else{$domainObject=$domainUser} Add-DomainObjectToLocal -computer $Computername -localGroup $localGroup -domain $env:USERDOMAIN -object $domainObject
I took a little bit of a shortcut here. I am making an assumption on my script that a given group will take precedence over a given user assuming that both were provided simulatneously. In real life, I would probably want to check to make sure that at least one of these parameters was populated before trying to execute the function. Instead, I am assuming that if I don’t have a group, that the user supplied a domain user object.
Now we can focus attention on the file input. This assumes that the CSV file to read has two columns: Server and Group. A sample CSV file might look like this:
Server,Group MyServer1,ApplicationGroup1 MyServer1,ApplicationGroup2
Once we get the file loaded, we can recurs through each line to get each group added to the proper server.
$groupInformation = Import-CSV -Path $csvFile Foreach ($item in $groupInformation) { Add-DomainObjectToLocal -computer $item.Server -localGroup $localGroup -domain $env:USERDOMAIN -object $item.Group }
And that pretty much finishes the script. There are some additional things that could be done here. For instance, we could set the $ComputerName, $domainUser, or $domainGroup to accept pipeline input. In the full script source below, however, I have added the comment-based help that allows users to use the built-in help system to see how to use the script.
Please leave me a comment about your thoughts on this script.
<# .SYNOPSIS Adds domain users or groups to the specified local computer group. .DESCRIPTION Using either a CSV file or specific objects, the user can add a domain user or group to a local computer group. If a domain user and domain group are both specified, the domain group will get priority and the domain user will be ignored. This script operates off of the executing user's domain. .PARAMETER ComputerName Specifies the computer to add the domain user or group to. .PARAMETER localGroup Specifies the local security group to add the domain user or group to. .PARAMETER domainUser Specifies a domain user to add to a local security group. .PARAMETER domainGroup Specifies a domain group to add to a local security group. If both a user and group are specified, the group will take precedence and the user will be ignored. .EXAMPLE C:\PS> .\Add-DomainObjectLocal.ps1 -csvFile groups.csv -localGroup Administrators Description ----------- This command reads the Server and Group information from the groups.csv file and adds them to the Administrators group on each named server in the file. .EXAMPLE C:\PS> .\Add-DomainObjectLocal.ps1 -localGroup "Backup Operators" -domainGroup "Domain Backup Operators" Description ----------- This command adds the domain group "Domain Backup Operators" to the local "Backup Operators" security group on the local machine. #> [CmdletBinding(DefaultParameterSetName="Bulk")] Param( [parameter(ParameterSetName="Single")] [string]$ComputerName=$env:ComputerName, [parameter(Mandatory=$true)] [string]$localGroup, [parameter(ParameterSetName="Single")] [string]$domainUser, [parameter(ParameterSetName="Single")] [string]$domainGroup, [parameter(Mandatory=$true, ParameterSetName="Bulk")] [string]$csvFile ) Function Add-DomainObjectToLocal { Param($computer,$localgroup,$domain,$object) try { Write-Host "Adding $domain\$object to $localgroup on $computer." ([ADSI]"WinNT://$computer/$localgroup,group").Add("WinNT://$domain/$object") } catch { Write-Host "An error occurred: $_." } } switch ($PSCmdlet.ParameterSetName) { "Single" { if ($domainGroup) { $domainObject = $domainGroup } else { $domainObject=$domainUser } Add-DomainObjectToLocal -computer $Computername -localGroup $localGroup -domain $env:USERDOMAIN -object $domainObject } "Bulk" { $groupInformation = Import-CSV -Path $csvFile Foreach ($item in $groupInformation) { Add-DomainObjectToLocal -computer $item.Server -localGroup $localGroup -domain $env:USERDOMAIN -object $item.Group } } } <pre>