I really really need your help. I got a task to create/import users from csv to AD.
But I have two problems
not all the user have all the attributes filled = there are empty "cells" in CSV
Two custom attributes contains dash - and PSH is returning error that this is not allowed.
I tried to search, there are some solutions but I am not sure how to implement it to my file (| ForEach-Object {$CSV = $_ ?????. Cant make it work.
Here is what I have so far
Import-Module ActiveDirectory
$ErrorActionPreference = 'SilentlyContinue'
$ScriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$log = "$ScriptDir\Error-Import.log"
$log1 = "$ScriptDir\Success-Import.log"
$date = Get-Date
$inputFile = Import-CSV $ScriptDir\allusers.csv
Function cUser
{
"ERROR LOG from (" + $date + ") :" | Out-File $log -Append
"————————————————-" | Out-File $log -Append
"Following accounts has been successfully created (" + $date + ") :" | Out-File $log1 - Append
"————————————————-" | Out-File $log1 -Append
foreach($line in $inputFile)
{
$sam = $line.samaccoutnname
#check if user already exists
$exists = Get-ADUser -LDAPFilter "(sAMAccountName=$sam)"
if (!$exists)
{
# when does not exists - load data from CSV and set as variable
$gn = $line.givenName
$sn = $line.sn
$title = $line.title
$stAd=$line.streetAddress
$upn = $line.sAMAccountName + "`#targetrange.local"
$city=$line.l
$st=$line.st
$postc=$line.postalCode
$ehrd=$line.award-hireDate
$comp=$line.company
$dvs=$line.division
$edvs=$line.award-subdivision
$dept=$line.department
$st=$line.street
$tarA=$line.targetAddress
$dn=$line.displayName
$ext2=$line.extensionAttribute2
$ext4=$line.extensionAttribute4
$ext5=$line.extensionAttribute5
$alias = $line.sAMAccountName
$cn=$line.cn
$hdir=$line.unixHomeDirectory
$desc=$line.description
$pat = $line.OU
$pwd = $line.pass
$spass =ConvertTo-SecureString -AsPlainText -Force -String $pwd
$aexp=$line.accountExpires
$Expconv=(Get-Date $aexp ).ToFileTime()
New-ADUser -SamAccountName $alias -UserPrincipalName $upn -GivenName $gn -Surname $sn -title $title -StreetAddress $stAd -l $city -State $st -PostalCode $postc -award-hireDate $ehrd -Company $comp -Division $dvs -award-subdivision $edvs -Department $dept -street $st -targetAddress $tarA -extensionAttribute2 $ext2 -extensionAttribute4 $ext4 -extensionAttribute5 $ext5 -DisplayName $dn -Name $cn -unixHomeDirectory $hdir -Description $Desc -path $pat -AccountExpirationDate $Expconv -AccountPassword $spass -PassThru | Enable-ADAccount
$exists1 = Get-ADUser -LDAPFilter "(sAMAccountName=$sam)"
if ($exists1)
{
"User successfully created : " + $sam | Out-File $log1 -Append
}
Else
{
"SKIPPED – MISSING OR INCORRECT VALUES : " + $sam | Out-File $log -Append
}
}
# if user already exists - skip and write to logfile
Else
{
"SKIPPED – ALREADY EXISTS OR DUPLICATE ALIAS : " + $sam | Out-File $log -Append
}
}
"————————————————-" + "`n" | Out-File $log -Append
}
createUsers
# finito
}
If you can help, it would save my life :-) How to skip empty cells??? And to make attributes with - working - apostrophe is enough? 'award-hiredate' ?
You mentioned that you wanted to skip empty cells. I am not sure how far you wanted to take that but here is something I think does the trick. You have the line.
$inputFile = Import-CSV $ScriptDir\allusers.csv
I would propose it gets updated as follows
$inputFile = Import-CSV $ScriptDir\allusers.csv | Where-Object{$_.psobject.properties.value -notcontains ""}
What this will do is break each entry into an array of its values. If any of the values are empty or "" then they will not be matched into the $inputFile.
Count EventID EventType
----- ------- ---------
14 Information
2 7040 Information
2 0
1 8224 Information
The above data was put into the filter and the output was
Count EventID EventType
----- ------- ---------
2 7040 Information
1 8224 Information
As you can see the lines with empty data are filtered out and the data structure is still preserved.
The Hyphen
As for the hyphen - issue I'm assuming the error you are getting is Unexpected token '-Type' in expression or statement. Guessing it is trying to treat the -hireData in $line.award-hireDate. What you are trying to do is the best in this case. Just use quotes around the parameter name. You would do the same if the parameter had a space in it.
$line."award-hireDate"
$line."award hireDate"
Related
I'm creating a script for adding multiple users in Active Directory. I stumbled upon this link, when I couldn't get the guide described in the question to work either. I then tried one of the solutions in the comments
Import-Module ActiveDirectory
# this defaults to csv fields delimited by a comma. If your CSV file uses a different
# character, then add parameter '-Delimiter' followed by the actual character
$ADUsers = Import-Csv -Path 'C:\Users\Desktop\Powershell files\EM-mis-new-AD.csv'
# the Where-Object clause is just a precaution to omit records that have no username value
$ADUsers | Where-Object { $_.username -match '\S'} | ForEach-Object {
$Username = $_.username
if (Get-ADUser -Filter "SamAccountName -eq '$Username'" -ErrorAction SilentlyContinue) {
Write-Warning "A user account with SamAccountName '$Username' already exist in Active Directory."
}
else {
$Firstname = $_.firstname
$Lastname = $_.lastname
# use splatting on cmdlets that use a lot of parameters
$userParams = #{
SamAccountName = $Username
UserPrincipalName = "$Username#Mydomain.com"
Name = "$Firstname $Lastname"
GivenName = $Firstname
Surname = $Lastname
Enabled = $true
DisplayName = "$Firstname, $Lastname"
Path = $_.ou
AccountPassword = (ConvertTo-SecureString $_.Password -AsPlainText -Force)
ChangePasswordAtLogon = $true
}
# create the user and report back
New-ADUser #userParams
Write-Host "Created new user '$Username' with initial password: $($_.Password)"
}
}
Here is my CSV file
firstname;lastname;username;password;ou
Mette;Frederiksen;MeFr;Password1;OU=Salg,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Sussi;Hart;SuHa;Password1;OU=Salg,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Ove;Tylstrup;OvTy;Password1;OU=Salg,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Karlos;Mondolez;KaMo;Password1;OU=Lager,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Anne;Otto;AnOt;Password1;OU=Lager,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Dennis;Ågard;DeÅg;Password1;OU=Lager,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Helena;Riss;HeRi;Password1;OU=Okonomi,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Risa;Lamende;RiLa;Password1;OU=Okonomi,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
However, when I run the above code nothing happens
PS C:\Users\RGDAdmin> C:\Users\RGDAdmin\Documents\ADUser.ps1
PS C:\Users\RGDAdmin>
When I add the Delimiter parameter, I get this
Created new user 'KaMo' with initial password: Password1
New-ADUser : The directory service was unable to allocate a relative identifier
At C:\Users\RGDAdmin\Documents\ADUser.ps1:31 char:9
+ New-ADUser #userParams
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (CN=Anne Otto,OU...DC=rgd,DC=local:String) [New-ADUser], ADException
+ FullyQualifiedErrorId :
ActiveDirectoryServer:8208,Microsoft.ActiveDirectory.Management.Commands.NewADUser
PS. I know the password is bad practice in terms of passwords
Your file is delimited by semicolons, so you will definitely need to specify the -Delimiter parameter. But the documentation has a caveat:
To specify a semicolon (;) enclose it in single quotation marks.
So it should look like this:
$ADUsers = Import-Csv -Delimiter ';' -Path 'C:\Users\Desktop\Powershell files\EM-mis-new-AD.csv'
If that still results in that RID error, then there's possibly something wrong on the server. Can you create users manually using AD Users and Computers?
Try reviewing this. I don't have access to ActiveDirectory to test it myself.
#helpers
function usernameIsNotBlank {
[CmdletBinding()]
param(
$Username
)
[regex]$rx = "\S"
return $rx.match($Username)
}
function usernameDoesNotAlreadyExist {
[CmdletBinding()]
param(
$Username
)
$UserDoesNotExist = $true
$UserObject = $(
try {
Get-ADUser $Username
}
catch {
$null
}
)
if ($null -ne $UserObject) {
$UserDoesNotExist = $false
Write-Verbose "$Username already exists"
}
else {
$UserDoesNotExist = $true
}
return $UserDoesNotExist
}
function suppliedUsernameIsAvailable {
[CmdletBinding()]
param(
$Username
)
return ((usernameIsNotBlank -Username $Username) -and (usernameDoesNotAlreadyExist -Username $Username))
}
#script
$OriginalVerbose = $VerbosePreference
$VerbosePreference = "Continue"
Import-Module ActiveDirectory
$CSV = "C:\Users\Desktop\Powershell file\EM-mis-new-AD.csv"
$Data = Import-CSV $CSV
foreach ($Line in $Data) {
if (suppliedUsernameIsAvailable($Line.username)) {
New-ADUser -Name "$Line.firstname $Line.lastname" -GivenName "$Line.firstname" -Surname "$Line.lastname" -SamAccoutnname "$(Line.username)#mydomain.com" -AccountPassword (ConvertTo-SecureString $Line.password -AsPlainText -Force) -ChangePasswordAtLogon $true -Path "$Line.ou"
}
}
$VerbosePreference = $OriginalVerbose
I have good script that export 365 Users Mailboxes Size to csv file.
The results looks like that:
Today it display the Total Size with the Unit (KB/MB/GB) inside the same column.
As for the Total Size (Bytes)- i dont really need that column.
So, i want to separate the Total Size column into 2 separate columns of Size and Unit columns.
Like that:
So at the end, the results will be:
Here's the code:
Param(
[Parameter(Mandatory = $false)]
[switch]$MFA,
[switch]$SharedMBOnly,
[switch]$UserMBOnly,
[string]$MBNamesFile,
[string]$UserName,
[string]$Password
)
Function Get_MailboxSize{
$Stats=Get-MailboxStatistics -Identity $UPN
$IsArchieved=$Stats.IsArchiveMailbox
$ItemCount=$Stats.ItemCount
$TotalItemSize=$Stats.TotalItemSize
$TotalItemSizeinBytes= $TotalItemSize –replace “(.*\()|,| [a-z]*\)”, “”
$TotalSize=$stats.TotalItemSize.value -replace "\(.*",""
$DeletedItemCount=$Stats.DeletedItemCount
$TotalDeletedItemSize=$Stats.TotalDeletedItemSize
#Export result to csv
$Result=#{'Display Name'=$DisplayName;'User Principal Name'=$upn;'Mailbox Type'=$MailboxType;'Primary SMTP Address'=$PrimarySMTPAddress;'IsArchieved'=$IsArchieved;'Item Count'=$ItemCount;'Total Size'=$TotalSize;'Total Size (Bytes)'=$TotalItemSizeinBytes;'Deleted Item Count'=$DeletedItemCount;'Deleted Item Size'=$TotalDeletedItemSize;'Issue Warning Quota'=$IssueWarningQuota;'Prohibit Send Quota'=$ProhibitSendQuota;'Prohibit send Receive Quota'=$ProhibitSendReceiveQuota}
$Results= New-Object PSObject -Property $Result
$Results | Select-Object 'Display Name','User Principal Name','Mailbox Type','Primary SMTP Address','Item Count','Total Size','Total Size (Bytes)','IsArchieved','Deleted Item Count','Deleted Item Size','Issue Warning Quota','Prohibit Send Quota','Prohibit Send Receive Quota' | Export-Csv -Path $ExportCSV -Notype -Append
}
Function main(){
#Check for EXO v2 module inatallation
$Module = Get-Module ExchangeOnlineManagement -ListAvailable
if($Module.count -eq 0){
Write-Host Exchange Online PowerShell V2 module is not available -ForegroundColor yellow
$Confirm= Read-Host Are you sure you want to install module? [Y] Yes [N] No
if($Confirm -match "[yY]") {
Write-host "Installing Exchange Online PowerShell module"
Install-Module ExchangeOnlineManagement -Repository PSGallery -AllowClobber -Force
}
else {
Write-Host EXO V2 module is required to connect Exchange Online.Please install module using Install-Module ExchangeOnlineManagement cmdlet.
Exit
}
}
#Connect Exchange Online with MFA
if($MFA.IsPresent){
Connect-ExchangeOnline
}
#Authentication using non-MFA
else{
#Storing credential in script for scheduling purpose/ Passing credential as parameter
if(($UserName -ne "") -and ($Password -ne "")){
$SecuredPassword = ConvertTo-SecureString -AsPlainText $Password -Force
$Credential = New-Object System.Management.Automation.PSCredential $UserName,$SecuredPassword
}
else{
$Credential=Get-Credential -Credential $null
}
Connect-ExchangeOnline -Credential $Credential
}
#Output file declaration
$ExportCSV=".\MailboxSizeReport_$((Get-Date -format yyyy-MMM-dd-ddd` hh-mm` tt).ToString()).csv"
$Result=""
$Results=#()
$MBCount=0
$PrintedMBCount=0
Write-Host Generating mailbox size report...
#Check for input file
if([string]$MBNamesFile -ne "") {
#We have an input file, read it into memory
$Mailboxes=#()
$Mailboxes=Import-Csv -Header "MBIdentity" $MBNamesFile
foreach($item in $Mailboxes){
$MBDetails=Get-Mailbox -Identity $item.MBIdentity
$UPN=$MBDetails.UserPrincipalName
$MailboxType=$MBDetails.RecipientTypeDetails
$DisplayName=$MBDetails.DisplayName
$PrimarySMTPAddress=$MBDetails.PrimarySMTPAddress
$IssueWarningQuota=$MBDetails.IssueWarningQuota -replace "\(.*",""
$ProhibitSendQuota=$MBDetails.ProhibitSendQuota -replace "\(.*",""
$ProhibitSendReceiveQuota=$MBDetails.ProhibitSendReceiveQuota -replace "\(.*",""
$MBCount++
Write-Progress -Activity "`n Processed mailbox count: $MBCount "`n" Currently Processing: $DisplayName"
Get_MailboxSize
$PrintedMBCount++
}
}
#Get all mailboxes from Office 365
else{
Get-Mailbox -ResultSize Unlimited | foreach {
$UPN=$_.UserPrincipalName
$Mailboxtype=$_.RecipientTypeDetails
$DisplayName=$_.DisplayName
$PrimarySMTPAddress=$_.PrimarySMTPAddress
$IssueWarningQuota=$_.IssueWarningQuota -replace "\(.*",""
$ProhibitSendQuota=$_.ProhibitSendQuota -replace "\(.*",""
$ProhibitSendReceiveQuota=$_.ProhibitSendReceiveQuota -replace "\(.*",""
$MBCount++
Write-Progress -Activity "`n Processed mailbox count: $MBCount "`n" Currently Processing: $DisplayName"
if($SharedMBOnly.IsPresent -and ($Mailboxtype -ne "SharedMailbox")){
return
}
if($UserMBOnly.IsPresent -and ($MailboxType -ne "UserMailbox")){
return
}
Get_MailboxSize
$PrintedMBCount++
}
}
#Open output file after execution
If($PrintedMBCount -eq 0){
Write-Host No mailbox found
}
else{
Write-Host `nThe output file contains $PrintedMBCount mailboxes.
if((Test-Path -Path $ExportCSV) -eq "True"){
Write-Host `nThe Output file available in $ExportCSV -ForegroundColor Green
$Prompt = New-Object -ComObject wscript.shell
$UserInput = $Prompt.popup("Do you want to open output file?",0,"Open Output File",4)
If ($UserInput -eq 6){
Invoke-Item "$ExportCSV"
}
}
}
#Disconnect Exchange Online session
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
}
. main
Replace line:
$Results | Select-Object 'Display Name','User Principal Name','Mailbox Type','Primary SMTP Address','Item Count','Total Size','Total Size (Bytes)','IsArchieved','Deleted Item Count','Deleted Item Size','Issue Warning Quota','Prohibit Send Quota','Prohibit Send Receive Quota' | Export-Csv -Path $ExportCSV -Notype -Append
With:
$Results | Select-Object 'Display Name','User Principal Name','Mailbox Type','Primary SMTP Address','Item Count',#{Name = 'Total Size'; Expression = {($_."Total Size").Split(" ")[0]}},#{Name = 'Unit'; Expression = {($_."Total Size").Split(" ")[1]}},'Total Size (Bytes)','IsArchieved','Deleted Item Count','Deleted Item Size','Issue Warning Quota','Prohibit Send Quota','Prohibit Send Receive Quota' | Export-Csv -Path $ExportCSV -Notype -Append
I have replaced 'total size' with the 2 following calculated properties:
#{Name = 'Total Size'; Expression = {($_."Total Size").Split(" ")[0]}}
#{Name = 'Unit'; Expression = {($_."Total Size").Split(" ")[1]}}
This splits the value into an array and will put them into the specified properties of "Total Size" & "Unit"
You can use -csplit (case-sensitive -split) to split the TotalItemSize value by the unit. This will create an array of three items. The third item will be the (# bytes) value that you are replacing anyway.
$Stats=Get-MailboxStatistics -Identity $UPN
$TotalItemSize,$Unit = $Stats.TotalItemSize.Value -csplit '(\b[A-Z]+\b)').Trim()[0,1]
$Unit will now have the unit and $TotalItemSize will contain the size without the unit. Trim() is to remove surrounding spaces from the formatting.
You could technically do all of your size manipulations with one line:
$TotalItemSize,$Unit,$TotalSizeInBytes = ($Stats.TotalItemSize.Value -split ' ' -replace '\(|,')[0,1,2]
I'm attempting to script the creation of accounts in active directory using a csv file. Unfortunately I'm new to PowerShell and scripting in general and am facing difficulty managing a foreach() loop which is intended to handle each of the columns in the csv document I import.
I suspect that I cannot define my variables in one block after foreach() like I have, but am unsure and would appreciate some guidance. Specifically, the purpose of the code is read a csv document and assign each item to a variable that it then uses to create a service account with those values.
The Ideal situation is one where I can create a csv with several hundred rows, execute the script and end with several hundred matching service accounts.
$SVC = (import-csv C:\users\me\desktop\Test.csv -header("Name", "Pass", "WhatDo", "Location", "Domain")) `
foreach($variable in %SVC) {
$name = $SVC.Name
$Pass = $SVC.Pass
$WhatDo = $SVC.WhatDo
$Location = $SVC.Location
$Domain = $SVC.Domain
New-ADuser `
-Name $name `
-AccountPassword (Convertto-SecureString $Pass -AsPlainText -Force) `
-CannotChangePassword $true `
-Description $WhatDo `
-DisplayName $name `
-Enabled $true `
-GivenName $name `
-PasswordNeverExpires $True `
-Office $Location `
-Path "OU=Service-Accounts, Ou=Accunts, OU=_CORP, DC=$Domain, DC=net" `
-SamAccountName $name `
-UserPrincipleName $name + "#" + $domain + ".net" `
Start-Sleep -Seconds 15
Get-ADUser `
-Identity $name | Add-ADPrincipalGroupMembership `
-MemberOf "Group1","Group2","Group3"
}
There are quite a few things wrong in your code, not just the foreach.
But let's start with that: foreach ($variable in $SVC) means that $variable will have the the current item inside your loop, yet you are accessing $SVC in your loop which is still referring to the original collection. $variable is not a good name either, so you should change that to something more meaningful. Also, you wrote %SVC instead of $SVC.
You are also using backtick (`) a lot, sometimes incorrectly. You should only use it when your cmdlet invokation spans multiple lines. In the case of Import-Csv it's not, yet there's backtick at the end. There's also one on the last line of New-ADUser. Some prefer to use Parameter Splatting instead of backticks, but's thats a matter of taste.
Considering you are creating service accounts, I would write the first part like this:
$serviceAccounts = Import-Csv C:\users\me\desktop\Test.csv -Header Name,Pass,WhatDo,Location,Domain
foreach($serviceAccount in $serviceAccounts) {
Then inside your loop you can access the indivdual properties through $serviceAccount:
$name = $serviceAccount.Name
Also, PowerShell expands variables when using double quotes, so -UserPrincipleName can be written like this: -UserPrincipleName "$name#$domain.net"
I prefer using ForEach-Object rather than foreach.
It would be something like:
$SVC = (Import-CSV C:\users\me\desktop\Test.csv -header("Name", "Pass", "WhatDo", "Location", "Domain"))
$SVC | ForEach-Object {
New-ADuser `
-Name $_.Name `
-AccountPassword (Convertto-SecureString $_.Pass -AsPlainText -Force) `
-CannotChangePassword $true `
-Description $_.WhatDo `
-DisplayName $_.Name `
-Enabled $true `
-GivenName $_.Name `
-PasswordNeverExpires $True `
-Office $_.Location `
-Path "OU=Service-Accounts, Ou=Accunts, OU=_CORP, DC=$Domain, DC=net" `
-SamAccountName $_.Name `
-UserPrincipleName $_.Name + "#" + $_.Domain + ".net"
Start-Sleep -Seconds 15
Get-ADUser `
-Identity $_.Name | Add-ADPrincipalGroupMembership `
-MemberOf "Group1","Group2","Group3"
}
$_ represents the current item in the pipeline. ($SVC in your case, which was the wrong variable anyways.) It's less code and I think it's a cleaner way of doing things!
Rename $SVC to $SVCs then foreach ($SVC in $SVCs)
Here is an example how you can do it.(Don't forget the delimiter)
$csv = Import-Csv -Path C:\temp\csv.csv -Header "a","b","c" -Delimiter ";"
foreach($row in $csv){
$row.a
$row.b
$row.c
}
Here are some more examples how foreach works:
Take a look https://ss64.com/ps/foreach.html
Examples
Loop through an array of strings:
$trees = #("Alder","Ash","Birch","Cedar","Chestnut","Elm")
foreach ($tree in $trees) {
"$tree = " + $tree.length
}
Loop through a collection of the numbers, echo each number unless the number is 2:
foreach ($num in 1,2,3,4,5) {
if ($num -eq 2) { continue } ; $num
}
Loop through a collection of .txt files:
foreach ($file in get-ChildItem *.txt) {
Echo $file.name
}
For a small project, I want to create AD users from a small CSV file. I made jobs (PowerShell scripts) that run every few minutes, to pick-up the user creation based on the status of the user I define in the script. Per example; when a user is created in the AD, I change the status to addmailbox. Then another job will come and add the mailbox.
Everything is going well, but I noticed a bug. The way I am editing the status in the CSV file is wrong, because I change the status for all users in the CSV when I only want to change one. But I can't seem to get any other method working.
#When encountering any error, stop the script.
$ErrorActionPreference = "Stop";
#Check if there is a data file.
$FileExists = Test-Path C:\inetpub\wwwroot\melle\data.csv
if($FileExists -eq $true) {
$Users = Import-Csv -Path "C:\inetpub\wwwroot\melle\data.csv"
foreach ($user in $Users) {
$Status = $User.Status;
$userPrincipalName = $User.userPrincipalName;
$SAMAccountName = $User.SAMAccountName;
$userInstance = $User.userInstance;
$Name = $User.Name;
$displayName = $User.DisplayName;
$Path = '"' + $User.Path + '"';
$GivenName = $User.GivenName;
$Surname = $User.Surname;
$SIP = $userPrincipalName;
if ($Status -eq "CreateUser") {
try {
#create user
Import-Module ActiveDirectory;
New-ADUser -SAMAccountName $SAMAccountName -Instance $userInstance -Name $name -DisplayName $displayName -Path "correct.path.com" -GivenName $givenname -Surname $surname -userPrincipalName $userprincipalname -AccountPassword (ConvertTo-SecureString -String "bla" -AsPlainText -Force) -PassThru | Enable-ADAccount;
#change status
(Get-Content C:\inetpub\wwwroot\melle\data.csv) | Foreach-Object {$_ -replace 'CreateUser','AddMailbox'} | Out-File C:\inetpub\wwwroot\melle\data.csv
#exit on completion
Exit(Write-Host 'User was created in AD.');
} Catch {
#write any errors to error log.
$_ | Out-File C:\inetpub\wwwroot\melle\errors.log -Append;
}
} else {
Exit(Write-Host 'No user with status CreateUser was found.');
}
}
}else {
Exit(Write-Host 'No data.csv file was found.');
}
The way I do it now is (Get-Content C:\inetpub\wwwroot\melle\data.csv) | Foreach-Object {$_ -replace 'CreateUser','AddMailbox'} | Out-File C:\inetpub\wwwroot\melle\data.csv but I want to define it for only the row that the script is talking to in the foreach.
I tried searching for a solution on here and different sites, but I wasn't able to get the solutions others are using to work on my script. Some scripts were too advanced for me to understand.
What would be the correct approach to only change the status for the line I'm working on in the loop?
UPDATE: Solved
Working script:
#Check if there is a data file.
$FileExists = Test-Path C:\inetpub\wwwroot\melle\data.csv
if($FileExists -eq $true) {
$Users = Import-Csv -Path "C:\inetpub\wwwroot\melle\data.csv"
$UsersThatNeedToBeCreated = $Users | Where-Object {$_.Status -eq 'CreateUser'};
# Check that we have users that need to be created, might need fine-tuning.
if($UsersThatNeedToBeCreated -eq $null -or $UsersThatNeedToBeCreated.Count -eq 0){
# Write-Host doesn't have a meaningful return code, so you can separate those lines.
Exit(Write-Host 'No user with status CreateUser was found.');
}else{
# This way it's only run once
Import-Module ActiveDirectory;
}
$Users | ForEach-Object {
if($_.Status -eq 'CreateUser'){
try {
#create user
New-ADUser -SAMAccountName $_.SAMAccountName -Instance "'" + $_.userInstance + "'" -Name $_.name -DisplayName $_.displayName -Path "working.path.here" -GivenName $_.givenname -Surname $_.surname -userPrincipalName $_.userprincipalname -AccountPassword (ConvertTo-SecureString -String "Welcome01" -AsPlainText -Force) -PassThru | Enable-ADAccount;
#change status
$_.Status = 'AddMailbox';
} Catch {
#write any errors to error log.
$_ | Out-File C:\inetpub\wwwroot\melle\errors.log -Append;
}
}
}
$Users | ConvertTo-Csv | Out-File 'C:\inetpub\wwwroot\melle\data.csv'
Exit(Write-Host 'User was created in AD.');
}else {
Exit(Write-Host 'No data.csv file was found.');
}
As you already noticed, your current approach doesn't work. You're grabbing the whole content of the CSV and replace every instance. That Import-Csv statement actually gives you a data structure that allows you to change values. You just need to write it back afterwards.
This approach still is going to be error prone and you will run into issues with it. If I'm understanding you correctly, you want to keep track of the state and have multiple scripts that do different things. Sooner or later you will encounter situations where they overwrite each others changes and/or lock due to competing access requests. If you want to do it this way you should consider using a database that supports row based locking (most probably do). Otherwise you will need to find a way to make them run sequentially or implement row based locking yourself.
That said, one possible solution could look like the following. I haven't run it but the basic structure should be right. An example with this for overwriting changes would be the long time it can take to create a mailbox. As the file is only read at the start of the script and written at the end, it might change in-between.
#When encountering any error, stop the script.
$ErrorActionPreference = "Stop";
#Check if there is a data file.
$FileExists = Test-Path C:\inetpub\wwwroot\melle\data.csv
if($FileExists -eq $true) {
$Users = Import-Csv -Path "C:\inetpub\wwwroot\melle\data.csv"
$UsersThatNeedToBeCreated = $Users | Where-Object {$_.Status -eq 'CreateUser'};
# Check that we have users that need to be created, might need fine-tuning.
if($$UsersThatNeedToBeCreated -eq $null -or $UsersThatNeedToBeCreated.Count -eq 0){
# Write-Host doesn't have a meaningful return code, so you can separate those lines.
Write-Host 'No user with status CreateUser was found.'
Exit;
}else{
# This way it's only run once
Import-Module ActiveDirectory;
}
$Users | ForEach-Object {
$Status = $User.Status;
$userPrincipalName = $User.userPrincipalName;
$SAMAccountName = $User.SAMAccountName;
$userInstance = $User.userInstance;
$Name = $User.Name;
$displayName = $User.DisplayName;
$Path = '"' + $User.Path + '"';
$GivenName = $User.GivenName;
$Surname = $User.Surname;
$SIP = $userPrincipalName;
if($_.Status -eq 'CreateUser'){
try {
#create user
New-ADUser -SAMAccountName $SAMAccountName -Instance $userInstance -Name $name -DisplayName $displayName -Path "correct.path.com" -GivenName $givenname -Surname $surname -userPrincipalName $userprincipalname -AccountPassword (ConvertTo-SecureString -String "bla" -AsPlainText -Force) -PassThru | Enable-ADAccount;
# change status
$_.Status = 'AddMailbox';
} Catch {
# write any errors to error log.
$_ | Out-File C:\inetpub\wwwroot\melle\errors.log -Append;
}
}
}
$Users | ConvertTo-Csv -NoTypeInformation | Out-File 'C:\inetpub\wwwroot\melle\data.csv'
Write-Host 'User was created in AD.'
Exit
}else {
Write-Host 'No data.csv file was found.'
Exit
}
I am trying to pull a list of all users in my O365 Tenant and if they are licensed, the list of sublicenses they have been granted. The following code works great to list out my sublicenses:
$userlicensetest = get-msoluser -userprincipalname "steve.dorr#merrillcorp.com"
$userlicensetest.licenses[0].servicestatus
ServicePlan :: ProvisioningStatus
----------- :: ------------------
INTUNE_O365 :: PendingActivation
YAMMER_ENTERPRISE :: PendingInput
OFFICESUBSCRIPTION :: Success
So I tried to modify code I found online to include the sublicense information. Here is what I have built so far:
$ReportPath = "c:\users\userlist.csv"
Add-Content -value ("UserPrincipalName"+","+"IsLicensed"+","+ "Licenses”"+","+ "SubLicenses") -Path $ReportPath
$AllUsers = Get-MsolUser -All
foreach ($User in $AllUsers)
{
$UserPrincipalName = $User.UserPrincipalName
$IsLicensed = $User.IsLicensed
$Licenses = $User.Licenses.AccountSkuId
$SubLicenses = $User.Licenses[0].servicestatus
Add-Content -value ($UserPrincipalName+","+$IsLicensed+","+$Licenses+","+$SubLicenses) -Path $ReportPath
}
The problem is it is only pulling the header line from the sublicense query and not all the lines of detail. So the line for myself in the CSV looks like:
Steve.Dorr#MerrillCorp.com TRUE mymerrillcorp:ENTERPRISEPACK Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus Microsoft.Online.Administration.ServiceStatus
Which does not give me the detail lines I needed.
How do I pull all the lines that Licenses[0].servicestatus generates into the CSV file? I don't care whether it flattens it out and goes across more columns, or takes up multiple lines in Excel.
Thanks.
So since I posted this question I have worked a little on it. I do not have a perfect solution that puts this into a nice neat CSV file, but I do have a routine which now drops all this information into a text file. Below is my code.
$MyCredentials = Get-Credential -Message "Enter Office 365 Email & Password"
Connect-MsolService -Credential $MyCredentials
$ReportFile = "C:\temp\O365Data.txt"
" " | Out-File $ReportFile #erases the file if it exists
$AllUsers = Get-MsolUser -All
foreach ($User in $AllUsers)
{
$UserPrincipalName = $User.UserPrincipalName
$IsLicensed = $User.IsLicensed
$Licenses = $User.Licenses.AccountSkuId
$SubLicenses = $User.Licenses[0].servicestatus
$OneLine = $UserPrincipalName + " " + $IsLicensed
$OneLine| Out-File $ReportFile -Append
if($User.Licenses[0].servicestatus) {$User.Licenses[0].servicestatus | Out-File $ReportFile -Append}
}
To create the report you'll need to create custom objects to hold the properties you are interested in.
The following will take into account all the different licenses that could be applied to a user and then generate a csv with one user listed per line.
# Connect to o365
$MyCredentials = Get-Credential -Message "Enter Office 365 Email & Password"
Connect-MsolService -Credential $MyCredentials
# Prepare result file
$ExportFile = ".\o365Output.csv"
Remove-Item $ExportFile
$Result = #()
# Query all Msol Users
$AllUsers = Get-MsolUser -All
foreach ($User in $AllUsers)
{
# Generate a new object for each user
$ReturnObject = [pscustomobject]#{
UserPrincipalName = $User.UserPrincipalName
IsLicensed = $User.IsLicensed
Licenses = [string]$User.Licenses.AccountSkuId
}
# In the event multiple licenses are found append properties for each license
foreach ($License in $User.Licenses)
{
if($($License.ServiceStatus.ServicePlan.ServiceName).count -eq 1)
{
$ReturnObject | Add-Member -MemberType NoteProperty -Name $License.ServiceStatus.ServicePlan.ServiceName -Value $License.ServiceStatus.ProvisioningStatus
}
else
{
for($i = 0; $i -lt $($License.ServiceStatus.ServicePlan.ServiceName).count; $i++)
{
$ReturnObject | Add-Member -MemberType NoteProperty -Name $License.ServiceStatus.ServicePlan.ServiceName[$i] -Value $License.ServiceStatus.ProvisioningStatus[$i]
}
}
}
$Result += $ReturnObject
}
# Combine properties from all returned objects
$Properties = $Result | ForEach-Object { Get-Member -InputObject $_ -MemberType NoteProperty | Select-Object -ExpandProperty Name } | Select-Object -Unique | Sort-Object
$Headers = #("UserPrincipalName")
$Headers += $Properties -notlike "UserPrincipalName"
# Export to csv
$Result | Select-Object $Headers | Export-Csv -NoTypeInformation $ExportFile
# Open csv
Invoke-Item $ExportFile