halbaradkenafin Posted June 17, 2015 Author Posted June 17, 2015 Here is a script I've recently finished updating to create user accounts, either staff or student with the only difference being the Staff member uses a title and student has intake year. It accepts input through pipeline so you can just import-csv and then pipe it to the script and have it work it's magic on each row in the csv. We use a very basic csv file as most of the values are generated automatically from that data: forename,surname,intakeyear,password,Staffmember,Title john,smith,2014,Password1 Jane,Doe,,Password1,True,Miss <# .Synopsis Script to create single users in AD and assign to correct groups .DESCRIPTION This script will create a single user account in AD, home directory and assign to the groups needed. Input can be specified using the parameters or passed through the Pipeline, such as from Import-Csv. Output will be a pretty printed statement of the new users username when calling the script on it's own or a custom object with the parameters of the newly created account when called as part of a pipeline. The pipeline can then be passed to Export-Csv for easy distribution of account information or to another command. The script will automatically generate a username using the standard format for either staff or students (depending on if -staffmember switch is supplied) and ensure the username is not already in use, if it is then it will increment it until it finds an available username. .EXAMPLE Create-SingleUser.ps1 -Forename John -Surname Smith -IntakeYear 2015 -Password Password1 This will create a new account for the user John Smith, create the home folder and assign them to the AD groups. it will then output the following to the screen: "New User created with Username: J.Smith2 and Pasword specified." .EXAMPLE Create-SingleUser.ps1 -Forename John -Surname Smith -Password Password1 -Title Mr -StaffMember This will create a new account for the staff member John Smith, create the home folder and assign to the ad groups. It will then output the following to the screen: "New User created with Username: Smith.J1 and Pasword specified." .EXAMPLE Import-Csv -Path "C:\User.csv" | Create-SingleUser.ps1 | Export-Csv -Path "C:\NewUsersDetails.csv" This will import a csv of user details and create accounts for each one, then output the new details to the specified csv file. #> [cmdletbinding()] param ( [Parameter(ValueFromPipelineByPropertyName)] [string]$Forename = $(Read-Host "Enter Forename"), [Parameter(ValueFromPipelineByPropertyName)] [string]$Surname = $(Read-Host "Enter Surname"), [Parameter(ValueFromPipelineByPropertyName)] [string]$IntakeYear, [Parameter(ValueFromPipelineByPropertyName)] [string]$Password = $(Read-Host "Enter password for new user"), [parameter(ValueFromPipelineByPropertyname)] [switch]$StaffMember, [parameter(ValueFromPipelineByPropertyName)] [string]$Title ) Process { # This will return if the queried user # does not exist within AD. Function Check-ADUser { [string]$Name = $args[0] $ADUser = Get-ADUser -Filter {SamAccountName -eq $Name} if (!$ADUser) { return 0 } } # Creates the basic details for the user depending # on if they are a member of staff or not. if ($StaffMember) { $ShortName = "$Surname.$($Forename.Substring(0,1))" $IsADUser = Check-ADUser $ShortName $Count = 1 while ($IsADUser -ne 0) { $ShortName = "$Surname.$($Forename.Substring(0,1))$Count" $IsADUser = Check-ADUser $ShortName $Count += 1 } $DisplayName = "$Title $($Forename.Substring(0,1)). $Surname" $Description = "Teacher" $HomeDirectory = "\\Fileserver\Staffwork$\$ShortName" $ProfilePath = "\\fileserver\staffprofiles$\$ShortName" $OU = "OU=Teaching Staff,OU=Users,DC=Domain,DC=local" } else { $ShortName = "$($Forename.Substring(0,1)).$Surname" $IsADUser = Check-ADUser $ShortName $Count = 1 while ($IsADUser -ne 0) { $ShortName = "$($Forename.Substring(0,1)).$Surname$Count" $IsADUser = Check-ADUser $ShortName $Count += 1 } $DisplayName = "$Forename $Surname" $Description = "Student" $HomeDirectory = "\\fileserver\studentwork$\$IntakeYear\$ShortName" $ProfilePath = "\\fileserver\studentprofiles$\$IntakeYear\$ShortName" $OU = "OU=$IntakeYear,OU=Students,OU=Users,DC=domain,DC=local" } $SecureString = ConvertTo-SecureString -String $Password -AsPlainText -Force $UserPrincipalName = "[email protected]" $email = "[email protected]" # Creates the AD Account using the details specified and then pauses to allow replication to the DC New-ADUser -Name $ShortName -Description $Description -DisplayName $DisplayName -GivenName $Forename -HomeDirectory $HomeDirectory -HomeDrive "N:" -ProfilePath $ProfilePath -SamAccountName $ShortName -Surname $Surname -UserPrincipalName $UserPrincipalName -Path $OU -AccountPassword $SecureString -EmailAddress $email -Enabled 1 -ChangePasswordAtLogon 1 Start-Sleep -s 10 # Creates home folders and main group membership if ($Staffmember) { New-Item -Path "\\fileserver\StaffWork$\" -Name $ShortName -ItemType Directory Add-NTFSAccess -Account "domain\$ShortName" -AccessRights FullControl -AccessType Allow -Path $HomeDirectory Add-ADGroupMember -Identity "All Staff" -Members $ShortName } else { New-Item -Path "\\fileserver\StudentWork$\$IntakeYear\" -Name $ShortName -ItemType Directory Add-NTFSAccess -Account "domain\$ShortName" -AccessRights FullControl -AccessType Allow -Path $HomeDirectory Add-ADGroupMember -Identity $IntakeYear -Member $ShortName } Add-ADGroupMember -Identity "Other Users Group" -Member $ShortName # Outputs created account data based on if the script was called on it's own or as part of a pipeline if ($PSCmdlet.MyInvocation.PipelineLength -gt 1) { Write-Output (New-Object -TypeName PSObject -Properties (@{"Forename"=$Forename;"Surname"=$Surname;"Username"=$ShortName;"Password"=$Password})) } else { Write-Output -InputObject "New User created with Username: $ShortName and Pasword specified." } }
howartp Posted June 17, 2015 Posted June 17, 2015 Just finished this one recently and ran it live last night. Imports parents that have registered for our Parent Portal from CSV (that originally comes from SIMS each night) into AD then emails them their password. They have to contact me separately for their username if they haven't been told what it will be when they filled in the paperwork. All accounts expire on the 2nd of $nextmonth - this expiry is re-set again every night. If a parent has left / become unlinked, they won't be in the CSV, so they won't get updated - and will therefore expire. Any live parents will always be updated to next-month by the time we get there. Any clashes or duplicates get emailed to me to sort out. write-host "Importing GetPassword.ps1" . "\\server\share\GetPassword.ps1" $csv = Import-Csv \\server\share\Parents2AD.csv $csv $Expiry = ((Get-Date -day 01).AddMonths(1).AddDays(1)) $Expiry = Get-Date $Expiry -Format D write-host "Account Expiries will be set to: " $Expiry Import-Module ActiveDirectory function trimit($inStr) { return ($inStr -replace(' ','') -replace('-','') -replace('-','')) } write-host "Looping through CSV" $csv | ForEach { $DisplayName = trimit($_.givenName + "." + $_.sn) #$DisplayName if ($_.sAMAccountName -eq " ") { $AcName = trimit($_.givenName + '.' + $_.sn) } else { $AcName = $_.sAMAccountName } if ($AcName.Length -gt 20) { $AcName = $AcName.Substring(0,20) } #$AcName $PassWord = Create-RandomPassword #$PassWord $Email = $_.mail #$Email $testcount = (Get-ADUser -filter {name -eq $AcName}).count if ($testcount -lt 1) { write-host "Creating user:" $AcName " - " $DisplayName New-ADUser -Name $AcName -DisplayName $DisplayName -GivenName $_.givenName -Surname $_.sn -SamAccountName $AcName -UserPrincipalName $AcName -EmailAddress $_.mail -Path "OU=ParentUsers,DC=scraven,DC=internal" -PasswordNeverExpires $True -AccountPassword (ConvertTo-SecureString $PassWord -AsPlainText -force) -Enabled $True -AccountExpirationDate $Expiry Send-MailMessage -SmtpServer "192.168.x.x" -From "[email protected]" -To $Email -Subject "South Craven Parent Account" -Body $("Dear Parent/Carer,`r`n`r`nYour South Craven parent account has been created.`r`n`r`nYour password is: $PassWord`r`n`r`nTo obtain your username, please reply to this email.`r`n`r`nWe recommend you use our Self Service portal to a) change your password to something you know, and b) set some security questions so you can automatically reset your password if you forget it in the future.`r`n`r`nYou can find our Self Service portal at: http://selfservice.southcraven.org.`r`n`r`nPlease login with your username and the password above and follow the prompts to enroll.`r`n`r`nIf you have any problems or queries, please contact the IT Office on [email protected] or 01535 632861.`r`n`r`nIT Support.") } else { $UserMail = (Get-ADUser $AcName -Properties EmailAddress | select EmailAddress).EmailAddress #$UserMail if ($UserMail -eq $Email) { #write-host "Record exists and matches ($Email)" #Therefore just update expiry Set-ADUser $AcName -AccountExpirationDate $Expiry } else { write-host "Username $AcName exists but doesn't match ($UserMail <> $Email)" #Therefore send a warning email to ITSupport Send-MailMessage -SmtpServer "192.168.x.x" -From "[email protected]" -To $("[email protected]") -Subject "Duplicate Parent Account" -Body $("Duplicate parent account found! `r`n`r`nUsername: $AcName`r`nCurrent Email: $UserMail`r`nNew Email: $Email`r`n`r`nPlease check whether this is a changed email address or a duplicate account.`r`n`r`nIT Support.") } } } It makes use of this password script which I nabbed from somewhere. I've just removed I, L, O, 1 and 0's from it. function Create-RandomPassword ( [int] $minLength = 8, [int] $maxLength = 8, [bool] $useSymbols = $false, [bool] $asSecureString = $false ) { [system.Security.Cryptography.RNGCryptoServiceProvider] $random = new-object System.Security.Cryptography.RNGCryptoServiceProvider # Get an array of all characters that can be used in the password [string] $choice = Get-CharacterChoice -useSymbols $useSymbols $randomPassword = $null if (($minLength -le $maxLength) -and ($minLength -ge 6)) { # Allocate a byte array of dimension 1 $randomNumber = new-object byte[] 1 if ($minLength -eq $maxLength) { [int] $length = $minLength } else { # Calculate a random length between minLength and maxLength $random.GetBytes($randomNumber) [int] $length = $minLength + $randomNumber[0] % ($maxLength - $minLength + 1) } # Allocate a byte array of dimension $length $randomSequence = new-object byte[] $length $hasUCase = $hasLCase = $hasNum = $false while(!$hasUCase -or !$hasLCase -or !$hasNum) { # Generate random sequence of bytes $random.GetBytes($randomSequence) # Ensure that there is at least one number, uppercase # character and lowercase character in the sequence. $hasUCase = $hasLCase = $hasNum = $false foreach($b in $randomSequence) { [char]$char = $choice[$b % $choice.Length] if ($char -ge 'A' -and $char -le 'Z') { $hasUCase = $true } if ($char -ge 'a' -and $char -le 'z') { $hasLCase = $true } if ($char -ge '0' -and $char -le '9') { $hasNum = $true } } } if ($asSecureString) { $randomPassword = new-object System.Security.SecureString } else { [string] $randomPassword = '' } # Assign the password from the sequence of random bytes foreach($b in $randomSequence) { [char]$char = $choice[$b % $choice.Length] if ($asSecureString) { $randomPassword.AppendChar($char) } else { $randomPassword += $char } } } return $randomPassword } # Outputs an array of all the characters that the generated password # can be made up of. function Get-CharacterChoice ( [bool] $useSymbols = $true ) { if ($useSymbols) { [string] $choice = '!"#$%&''()*+,-./' } else { [string] $choice = '' } $choice += '23456789' if ($useSymbols) { $choice += ':;<=>?@' } $choice += 'ABCDEFGHJKMNPQRSTUVWXYZ' if ($useSymbols) { $choice += '[\]^_`' } $choice += 'abcdefghjkmnpqrstuvwxyz' if ($useSymbols) { $choice += '{|}~' } return $choice }
themightymrp Posted July 7, 2015 Posted July 7, 2015 To answer @Davit2005 above: The Param() section is simply a block of code where you would define any parameters you want to use in your script/function. For example, if you wanted to run your command and pass it a value for a username, this is where you define your 'Username' parameter. The (cmdletbinding[]) line (which normally goes above the Param()) is a way of saying "take the below code and make it into my very own powershell cmdlet i.e. Get-MyUserInfo The [Parameter(Mandatory=$true)] says that you MUST supply a value for this parameter if you want me to run. If you try running the command without this it will actually prompt you to enter something. [int]$NumOfDays is the actual parameter you will be asking for, a variable if you will. The [int] at the beginning tells powershell that the value to expect is an integer number All of the other lines which start with [switch] are additional values which can only be on or off (hence, switch). By mentioning them here in the code you will turn the values on. By omitting the lines completely they will be in their default OFF state. Generally these bits will correspond to a tick box somewhere in a graphical interface. If you want to learn Powershell then I can't recommend highly enough signing up for free with the Microsoft Virtual Academy. Search for the course "Getting started with Powershell 3.0 Jumpstart". One of the 2 hosts is the inventor of Powershell and they explain things REALLY WELL!
Garacesh Posted July 9, 2015 Posted July 9, 2015 Only some basic ones here.. I have a script that reads new AD users from a CSV and creates home folders and sorts out permissions. If anyone wants it I'll dig it out. Ooh, could I take a look at the permission-settings bit, please, @SteveM555? I have a script that creates a new user, and most of the time this sets full control for the kid whose account has just been created. But every now and again, and I can't find a pattern for it, I just get "failed: cannot find set-acl with 1 argument" (or something like that, I forget the exact error) # Grant user full control of home folder New-Item -ItemType Directory -Path $HomeDirectory $ACL = (Get-ACL -Path $HomeDirectory) $FullControlAccessRule = (New-Object System.Security.AccessControl.FileSystemAccessRule([system.Security.Principal.NTAccount]"Domain\$UserName","FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")) $ACL.AddAccessRule($FullControlAccessRule) Set-ACL -Path $HomeDirectory $ACL
halbaradkenafin Posted July 9, 2015 Author Posted July 9, 2015 Ooh, could I take a look at the permission-settings bit, please, @SteveM555? I have a script that creates a new user, and most of the time this sets full control for the kid whose account has just been created. But every now and again, and I can't find a pattern for it, I just get "failed: cannot find set-acl with 1 argument" (or something like that, I forget the exact error) # Grant user full control of home folder New-Item -ItemType Directory -Path $HomeDirectory $ACL = (Get-ACL -Path $HomeDirectory) $FullControlAccessRule = (New-Object System.Security.AccessControl.FileSystemAccessRule([system.Security.Principal.NTAccount]"Domain\$UserName","FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")) $ACL.AddAccessRule($FullControlAccessRule) Set-ACL -Path $HomeDirectory $ACL I used to do it that way until I discovered the amazing-ness of NTFS Security Module which makes it so much easier. Now it's just a case of doing something like this: Add-NTFSAccess -Path -Account -AccessRights Modify -AccessType Allow
Garacesh Posted July 9, 2015 Posted July 9, 2015 Hm. Not a fan of using external modules. Messes up compatibility, if my machine ever breaks and I have to rebuild it won't work until I invariably spend 6 hours rewriting it only to remember the module. Could add a comment at the top saying requires and a link, I guess..
halbaradkenafin Posted July 9, 2015 Author Posted July 9, 2015 Hm. Not a fan of using external modules. Messes up compatibility, if my machine ever breaks and I have to rebuild it won't work until I invariably spend 6 hours rewriting it only to remember the module. Could add a comment at the top saying requires and a link, I guess.. Documenting it is definitely a good idea. You could also store the module on a server share (probably a sysadmin share that only you and other techs have access to) and then use either Import-Module pointing at that or add into your PowershellProfile to include that location in the places it looks for modules.
themightymrp Posted July 9, 2015 Posted July 9, 2015 If your My Documents redirect to the network then you can store your modules there and they will auto-import on any machine you use (Powershell V3 and up). Just create a folder structure in your Documents like this: "My Documents\WindowsPowerShell\Modules\NTFSSecurity" Extract the above modules files directly into the NTFSSecurity folder and that's it
Davit2005 Posted July 10, 2015 Posted July 10, 2015 Stealing the title of the thread but 'Does anyone have a script which..' I can use to find who owns files in a shared area. Basically I want to specify the owner i.e. Joe.Blogs and then have the script search through the shared area and report file sizes etc. If you could identify what part of the script does what that would really help me out.
halbaradkenafin Posted July 10, 2015 Author Posted July 10, 2015 (edited) Stealing the title of the thread but 'Does anyone have a script which..' I can use to find who owns files in a shared area. Basically I want to specify the owner i.e. Joe.Blogs and then have the script search through the shared area and report file sizes etc. If you could identify what part of the script does what that would really help me out. I'll try to throw something together quickly for you, It will probably end up basically being something like this but prettied up a bit: Get-childitem -path | % { Write-output (Get-NTFSOwner -path $_.Fullname | Where {$_.Owner -eq "Domain\Username"})} Edit: Prettied up code: <# .Synopsis Script to find files owned by a specific user and report details of them. .DESCRIPTION Long description .EXAMPLE Get-FilesByUser -UserName "domain\Joe.Bloggs" -Path "T:\" This will get all files owned by Joe.Bloggs on the T: drive. .EXAMPLE Get-FilesByUser -UserName "domain\Joe.Bloggs" -Path "T:\" -Details This will get all files owned by Joe.Bloggs on the T: drive and report details of them. #> [CmdletBinding()] Param ( # Username of owner. Should include short domain name. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)] $Username, # Path to folder or drive to search [parameter(ValueFromPipelineByPropertyName)] [string]$Path, #Switch to specify whether to include full details of files [parameter(ValueFromPipelineByPropertyName)] [switch]$Details = $False ) Process { $FilesAndFolders = Get-ChildItem -path $Path -Recurse | % { Get-NTFSOwner -path $_.FullName | Where { $_.Owner -eq $Username}} If ($Details) { $FileDetails = @() foreach ($File in $FilesAndFolders) { $IndividualFile = Get-Item -Path $File.Item | Select * $FileDetails += New-Object -TypeName PSObject -Property (@{'FileName'=$IndividualFile.Name;'Location'=$IndividualFile.Directory;'Size (KB)'=(($IndividualFile.length)/1KB);'LastAccessed'=$IndividualFile.LastAccessTime}) } Write-Output $FileDetails } else { Write-Output $FilesAndFolders } } Needs the NTFS Security Module (I may be in love with that module for anything permissions related) and the help comments should tell you the basics of how to run it. I included an option to have just a list of files or the "full" details output. You can also pipe it to Export-CSV and it should come out in a reasonable format, or pipe in a a list of names and paths and it will search them one at a time. As the basic code above proves it's a pretty simple thing to do with something as awesome as the NTFS Security Module (can't recommend it enough). Edited July 10, 2015 by halbaradkenafin 1
halbaradkenafin Posted September 9, 2015 Author Posted September 9, 2015 I was asked recently by someone to be able to find all the groups someone is a member of in O365. It's a pretty easy process through the web interface but involves a lot of clicking so I figured I'd script it and add in a little extra functionality for the inevitable time someone says "Can you tell us which groups these 3/4/5/6 people are members of?". It requires the AD and MSOnline Modules but that's all: <# .Synopsis Gets all the O365 groups that a specific user is a member of from those available. .DESCRIPTION Gets all the O365 groups that a specific user is a member of based on user inpupt. This information can then be piped out to CSV or other formats. Will also accept a list of names and find groups that each is a member of but with the option to also get groups that all users are a member of. The script first connects to the O365 environment and then retrieving a list of groups that exist within the environment. It will then loop through each specified user and find each group that they are a member of and compile that to a list, once all the users have been searched it will return the list to the pipeline to be passed to either an export or format cmdlet. .EXAMPLE Get-MsolGroupMembership -User "J.Bloggs" This will find each group that J.Bloggs is a member of. .EXAMPLE Get-MsolGroupMembership -User "J.Bloggs","J.Smith","P.Davies" This will get a list of groups that each named users is a member of, grouped by user. .EXAMPLE Get-MsolGroupMembership -User "J.Bloggs","J.Smith","P.Davies" -Shared This will get a list of groups that each named user is a member of, grouped by user, but also a seperate section of groups that all of the users are members of together. .EXAMPLE Get-MsolGroupMembership -User "J.Bloggs","J.Smith","P.Davies" -SharedOnly This will get a list of groups that contain all the named users but not groups that they don't share with each other member. .EXAMPLE Get-MsolGroupMembership -User "J.Bloggs","J.Smith","P.Davies" -NotShared This will get a list of groups that contain all named users but that don't contain any of the other users. #> [CmdletBinding()] Param ( #Users to find membership for. [Parameter(Mandatory=$true, Position=0)] [string[]]$User, #Also list groups shared by all users [switch] $Shared, #Only list groups shared by all users [switch] $SharedOnly, #Don't list groups shared by all users [switch] $NotShared ) #Ensure the provided usernames are valid for the enviroment foreach ($IndivUser in $User) { if ($IndivUser -match "@") { $ADUser = Get-ADUser -Identity ($IndivUser.split("@"))[0] } else { $ADUser = Get-ADuser -Identity $IndivUser } If (!$ADUser) { Write-Error "Username $IndivUser doesn't exist. Please check the entered value and try again." Exit } } #Connect to O365 $LiveCred = Get-Credential Connect-MsolService -Credential $LiveCred #Get the list of groups $AllGroups = Get-MsolGroup -All $Domain = Get-MsolDomain | Where {$_.Authentication -eq "Federated"} | Select -ExpandProperty Name #function to get all the groups that a user is a member of and return them function Get-UsersGroups { $UserGroups = @() Foreach ($Group in $AllGroups) { If ((Get-MsolGroupMember -GroupObjectId $Group.ObjectId -all).EmailAddress -contains $args[0]) { $UserGroups += $group.DisplayName } } Return $UserGroups } $CombinedMembership = @() #Loop through each named user and find their groups foreach ($IndivUser in $User) { if ($IndivUser -match "@") { [system.Collections.Arraylist]$UserGroups = Get-UsersGroups $IndivUser } else { [system.Collections.Arraylist]$UserGroups = Get-UsersGroups "$IndivUser@$domain" } $CombinedMembership += New-Object -TypeName PsObject -Property @{'Username' = $IndivUser;'Groups'=$UserGroups} } #if statements for what to output if ($Shared -or $SharedOnly -or $NotShared) { #Find the shared groups [system.Collections.Arraylist]$sharedGroups = $CombinedMembership.Groups | Select -Unique Foreach ($PossibleGroup in $SharedGroups) { $PossibleGroup = $PossibleGroup.Replace(")","\)") $PossibleGroup = $PossibleGroup.Replace("(","\(") if (([regex]::Matches($CombinedMembership.Groups,$PossibleGroup)).Count -ne $CombinedMembership.Count) { $sharedGroups.Remove($PossibleGroup) } } #Create the output objects needed for the shared groups if ($Shared) { $CombinedMembership += New-Object -TypeName Psobject -property @{'Username'='Shared';'Groups'=$SharedGroups} } elseif ($SharedOnly) { $CombinedMembership = New-Object -TypeName Psobject -property @{'Username'='Shared';'Groups'=$SharedGroups} } elseif ($NotShared) { Foreach ($GroupToRemove in $SharedGroups) { foreach ($IndivUser in $CombinedMembership) { if ($IndivUser.Groups -contains $GroupToRemove) { $CombinedMembership[$CombinedMembership.IndexOf($IndivUser)].Groups.Remove($GroupToRemove) } } } } } Write-Output $CombinedMembership It doesn't accept input from the pipeline but the output can be piped to any of the export-* and format-* cmdlets.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now