Foreach Nested loops issue in powershell - windows

What I need to do is to compare the Licence attribute associated with each Username from $O365Users with Enabled attribute using the matching Employee ID in $userID (if it exists). With the standard nested ForEach (above) We use this script to help manage our local Active Directory and MSOL (Microsoft Online – Office 365) objects. My question is : I have got an issue related to the foreach loop so same object returns multiple (forever) I want to do it line-by-line for each user
Import-Module ActiveDirectory
Import-Module MSOnline
$password = ConvertTo-SecureString 'PASSWORD' -AsPlainText -Force
$LiveCred = New-Object System.Management.Automation.PSCredential ("username#domain.com", $password)
New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $LiveCred -Authentication Basic -AllowRedirection
Connect-MsolService -Credential $Livecred
$O365Users = Get-MsolUser -All
ForEach ($O365User in $O365Users)
{
$userID = Import-CSV "c:\Export\list.csv"
$ADuser = Get-ADUser -Filter "EmployeeID -eq $($userID.EmployeeID)" -Properties whenCreated, Enabled, SAMAccountName
If (($ADUser.Enabled -eq $True) -and ($O365User.isLicensed = $true))
{
Get-MsolUSer -UserPrincipalName $ADuser.UserPrincipalName
Set-MsolUserLicense -UserPrincipalName $ADuser.UserPrincipalName -RemoveLicenses "company:ENTERPRISEPACK"
}
}
CSV file :
EmployeeID
52576
1234
8599

Here you go this should work
$userID = Import-Csv "c:\export\list.csv"
foreach ($user in $userID){
$ADuser = Get-ADUser -Filter "EmployeeId -eq $($user.EmployeeID)" -Properties whenCreated, Enabled, SAMAccountName
$O365User = Get-MsolUser -UserPrincipalName $ADuser.UserPrincipalName
if(($ADuser.Enabled -eq $true) -and ($O365User.isLicensed -eq $true)){
Get-MsolUSer -UserPrincipalName $ADuser.UserPrincipalName
Set-MsolUserLicense -UserPrincipalName $ADuser.UserPrincipalName -RemoveLicenses "company:ENTERPRISEPACK"
}
}
Regarding the follow-up question on performance (just what i think might increase performance, no warranty and not tested):
$userID = Import-Csv "c:\export\list.csv"
$adusers = Get-ADUser -Filter * -properties EmployeeID,whenCreated,Enabled,SAMAccountname
$msolusers = Get-MsolUser -All
foreach ($user in $userID){
$ADuser = $adusers | where {$_.EmployeeID -eq $user.EmployeeID}
$O365User = $msolusers | where {$_.UserPrincipalName -eq $ADuser.UserPrincipalName}
if(($ADuser.Enabled -eq $true) -and ($O365User.isLicensed -eq $true)){
Set-MsolUserLicense -UserPrincipalName $ADuser.UserPrincipalName -RemoveLicenses "company:ENTERPRISEPACK"
}
}
Depending on how many of the AD / MSOL users there are and you have to match this might de- or increase the execution time, you will have to test since i cant.
I also removed the get-msoluser in your if statement since it´s only function is generating (unnecessary?) output. If there are any problems with my "improvements" let me know and we can see what we can do ;)

Please try:
Import-Module ActiveDirectory
Import-Module MSOnline
$password = ConvertTo-SecureString 'PASSWORD' -AsPlainText -Force
$LiveCred = New-Object System.Management.Automation.PSCredential ("username#domain.com", $password)
New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $LiveCred -Authentication Basic -AllowRedirection
Connect-MsolService -Credential $Livecred
$userIDs = Import-CSV "c:\Export\list.csv"
$O365Users = Get-MsolUser -All
ForEach ($O365User in $O365Users)
{
foreach ($userID in $userIDs)
{
$ADuser = Get-ADUser -Filter "EmployeeID -eq $($userID.EmployeeID)" -Properties whenCreated, Enabled, SAMAccountName,ObjectGUID
$valuetoconvert=$ADuser.ObjectGUID
$guid = [GUID]$valuetoconvert
$bytearray = $guid.tobytearray()
$ImmutableID = [system.convert]::ToBase64String($bytearray)
If (($ADUser.Enabled -eq $True) -and ($O365User.isLicensed = $true) -and ($ImmutableID -eq $O365User.ImmutableID ) )
{
Get-MsolUSer -UserPrincipalName $ADuser.UserPrincipalName
Set-MsolUserLicense -UserPrincipalName $ADuser.UserPrincipalName -RemoveLicenses "company:ENTERPRISEPACK"
}
}
}

Related

How to get the share folder level permission using powershell

#Cred= Get-Credential
$FolderPath = dir -Directory -Path "\\abc\dc\da" -Recurse -Force | where {$_.psiscontainer -eq $true}
$Report = #()
Foreach ($Folder in $FolderPath) {
$Acl = Get-Acl -Path $Folder.FullName
# $Name= Get-ChildItem $Folder -Recurse | where {!$_.PSIsContainer} |select-object fullname
foreach ($Access in $acl.Access)
{
$Properties = [ordered]#{'FolderName'=$Folder.FullName;'AD
Group or User'=$Access.IdentityReference;'Permissions'=$Access.FileSystemRights;'Inherited'=$Access.IsInherited}
$Report += New-Object -TypeName PSObject -Property $Properties
}
}
$Report | Export-Csv -path "C:\Output\Test_16-06-2021.csv"
In the above script, I'm able to get all my folder structure with permission but I want to do with a custom parameter like if I want to only folder level 3 it should get me the output as below
\\abc\a\b\c
\\abc\a\c\d
not like
\\abc\a\b\c\d\text.txt
\\abc\a\c\d\e\f\g\demo.pdf
If you're using PowerShell 5.0, you can use the -Recurse and -Depth parameters combined:
[string]$rootFolder = Read-Host -Prompt "Enter root folder path (no trailing '\')"
[int]$recursionDepth = Read-Host -Prompt "Enter recursion depth"
[string]$outputCsv = Read-Host -Prompt "Enter output .csv file (full path)"
$folders = Get-ChildItem -Directory -Path $rootFolder -Recurse -Depth $recursionDepth -Force | Where-Object { $_.PSIsContainer -eq $true }
[array]$report = #()
foreach ($folder in $folders) {
$acl = Get-Acl -Path $folder.FullName
foreach ($access in $acl.access) {
[hashtable]$properties = [ordered]#{
'FolderName' = $folder.FullName;
'AD Group or User' = $access.IdentityReference;
'Permissions' = $access.FileSystemRights;
'Inherited' = $access.IsInherited
}
$report += New-Object -TypeName PSObject -Property $properties
}
}
Write-Host $report
$report | Export-Csv -Path $outputCsv
If that isn't an option, you can try the following:
[string]$rootFolder = Read-Host -Prompt "Enter root folder path (no trailing '\')"
[int]$recursionDepth = Read-Host -Prompt "Enter recursion depth"
[string]$outputCsv = Read-Host -Prompt "Enter output .csv file (full path)"
[string]$recursionStr = if ($recursionDepth -eq 0) { "\*" } else { ("\*" * ($recursionDepth + 1)) }
$folders = Get-ChildItem -Directory -Path "$rootFolder$recursionStr" -Force | Where-Object { $_.PSIsContainer -eq $true }
[array]$report = #()
foreach ($folder in $folders) {
$acl = Get-Acl -Path $folder.FullName
foreach ($access in $acl.access) {
[hashtable]$properties = [ordered]#{
'FolderName' = $folder.FullName;
'AD Group or User' = $access.IdentityReference;
'Permissions' = $access.FileSystemRights;
'Inherited' = $access.IsInherited
}
$report += New-Object -TypeName PSObject -Property $properties
}
}
Write-Host $report
$report | Export-Csv -Path $outputCsv
See Limit Get-ChildItem recursion depth

ERROR New-ADUser :The supplied user buffer is not valid

I made a PowerShell script to create Active Directory users from a CSV file. For one specific user I get this error
As you can see, the user has been successfully created though. All other users have been made as well. Does anyone know what this error means?
Script:
Import-Module ActiveDirectory
$csv_file = Read-Host -Prompt "Path naar CSV bestand?"
$pwd = Read-Host -Prompt "Password for users?" -AsSecureString
$csv_file = $csv_file.Replace('"','')
$csv_list = Import-Csv $csv_file
$domainname = Get-ADDomain
$domainname = $domainname.DNSRoot
$server = hostname
$usernames = (get-aduser -Filter *).name
$i = 0
foreach ($adUser in $csv_list) {
$department = $adUser.Department
if ($adUser.GivenName) {
$givenname = $adUser.GivenName
}
else {
$givenname = $adUser.FirstName
}
if ($adUser.Surname) {
$surname = $adUser.Surname
}
else {
$surname = $adUser.LastName
}
$username = ("${givenname}.${surname}" -replace (' ','')).ToLower()
if ($username -in $usernames -eq $true) {
$number = 1
}
$variableusername = $username
while ($variableusername -in $usernames -eq $true) {
$number += 1
$variableusername = [string]($username + $number)
}
$username = $variableusername
$usernames += $username
$sam = $username
if ($sam.Length -gt 20) {
$sam = $sam.Substring(0,20)
}
$path = (Get-ADOrganizationalUnit -Filter * | Where-Object -Property Name -eq $department).DistinguishedName | Select-Object -First 1
if ($path -eq $False) {
$department = Read-Host -Prompt "Could not find an OU for user $username in department ${department}. Please specify the correct OU"
$path = (Get-ADOrganizationalUnit -Filter * | Where-Object -Property Name -eq $department).DistinguishedName | Select-Object -First 1
}
$password = $pwd
$email = "${username}#${domainname}"
$title = $adUser.Title
$dpn = "${givenname} ${surname}"
New-ADUser -name $username -path $path -UserPrincipalName "${username}#${domainname}" -GivenName $givenname -Surname $surname -Department $department -AccountPassword $password -EmailAddress $email -ChangePasswordAtLogon $False -Title $title -Enabled $True -Server $server -DisplayName $dpn -SamAccountName $sam
Add-ADGroupMember -Identity "G_${department}" -Members $sam
$i++
Write-Progress -activity "Creating users . . ." -status "Created: $i of $($csv_list.Count)" -percentComplete (($i / $csv_list.Count) * 100)
}

ec2 user-data skips block of Powershell

I have this script baked into an AWS-AMI, running the script logged into an ec2-instance works, but when calling from userdata the Join AD with newname part gets skipped.
# Get Credentials from SSM
echo $new_name
$domain = "xxxx"
$username = (Get-SSMParameterValue -Name ad_domain_user).Parameters[0].Value
$password = (Get-SSMParameterValue -Name ad_domain_password -WithDecryption $True).Parameters[0].Value | ConvertTo-SecureString -asPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
# Get VPC Tags for OU Name
$vpc_id=(Invoke-RestMethod -uri http://169.254.169.254/latest/meta-data/network/interfaces/macs/$mac/vpc-id)
$env_name=(aws ec2 describe-vpcs --vpc-ids $vpc_id --query 'Vpcs[*].[Tags[?Key==`Name`] | [0].Value]' --output text)
echo "Attempting to join AD for env_name:$env_name" | Out-File "C:\Program Files\Amazon\Ec2ConfigService\Logs\pslog.txt" -Append
# Join AD with newname
Try {
if ("$env_name" -match "xxxx*") {
Add-Computer -DomainName $domain -ComputerName $env:computername -newname $new_name -Credential $credential -Force -Restart -ErrorAction 'Stop'
} elseif ("$env_name" -match "xxxx-xxxx-*") {
$oupath = "OU=Member Servers,OU=xxxx-xxxx,OU=xxxx,DC=aws,DC=ABC,DC=ORG"
Add-Computer -DomainName $domain -ComputerName $env:computername -newname $new_name -Credential $credential -Force -Restart -ErrorAction 'Stop' -OUpath$oupath
} elseif ("$env_name" -match "xxxx-xxxx-*") {
$oupath = "OU=Member Servers,OU=xxxx-xxxx,OU=xxxx,DC=aws,DC=ABC,DC=ORG"
Add-Computer -DomainName $domain -ComputerName $env:computername -newname $new_name -Credential $credential -Force -Restart -ErrorAction 'Stop' -OUpath $oupath
}
} Catch {
echo $_.Exception | Out-File "C:\Program Files\Amazon\Ec2ConfigService\Logs\Error-JoinDomain.txt" -Append
}
## If Error File Exists try again
Try{
If (Test-Path "C:\Program Files\Amazon\Ec2ConfigService\Logs\Error-JoinDomain.txt" -PathType Leaf) {
if ("$env_name" -match "xxxx*") {
Add-Computer -DomainName $domain -ComputerName $env:computername -newname $new_name -Credential $credential -Force -Restart -ErrorAction 'Stop'
} else {
Add-Computer -DomainName $domain -ComputerName $env:computername -newname $new_name -Credential $credential -Force -Restart -ErrorAction 'Stop' -OUpath $oupath
}
}
} Catch {
echo $_.Exception | Out-File "C:\Program Files\Amazon\Ec2ConfigService\Logs\Error-JoinDomain.txt" -Append
echo $_.Exception | Out-File "C:\Program Files\Amazon\Ec2ConfigService\Logs\pslog.txt" -Append
}

Error with code "Missing = operator after key in hash literal, hash literal was incomplete"

I have a list of users in a CSV File that I want to find out if/when they last logged in. I can get this info for an individual account but I need to write this to a file for several accounts.
The error I'm getting:
At line:5 char:7
import-module activedirectory
Missing = operator after key in hash literal
hash literal was incomplete
My Code:
$resultList = #()
Import-Csv C:\Users\admin\Desktop\SamAccountName.csv -header("SamAccountName") | Foreach-Object{
$user = ([adsisearcher]"(samAccountName=$($_.SamAccountName))").FindOne()
$resultList += New-Object -TypeName PSObject -Property #{
SamAccountName = $_.SamAccountName
Import-Module ActiveDirectory
function Get-ADUserLastLogon([string]$_.userName)
{
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$time = 0
foreach($dc in $dcs)
{
$hostname = $dc.HostName
$user = Get-ADUser $userName | Get-ADObject -Properties lastLogon
if($user.LastLogon -gt $time)
{
$time = $user.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
Write-Host $username "last logged on at:" $dt }
Get-ADUserLastLogon -UserName $user
}
}
}
$resultList | export-csv -Path c:\users\admin\desktop\SamAccountName_results.csv -NoTypeInfo
Instead of this:
$user = ([adsisearcher]"(samAccountName=$($_.SamAccountName))").FindOne()
$resultList += New-Object -TypeName PSObject -Property #{
SamAccountName = $_.SamAccountName
Please do this:
$user = ([adsisearcher]"(samAccountName=$($_.SamAccountName))").FindOne()
$resultList += New-Object -TypeName PSObject -Property #{
SamAccountName = $_.SamAccountName
}

Getting Permissions From Remote Share (PowerShell)

I am writing a script to get permissions and some other info on shares throughout the a network and I am having trouble getting the shares permissions. I read online that one can use "GetAccessMask" but I thought it was just for the current user.
#loop for each computer in computers file
ForEach ($computer in $allComputers)
{
Write-Host "Checking - $computer"
If($computer -ne '')
{
#check if online, this uses WMI so if you don't have access to the machine it will show as offline
If(Test-Connection -Computername $computer -ErrorAction SilentlyContinue)
{
$shares = Get-WmiObject -Class Win32_share -ComputerName $computer -Credential $uCredentials
ForEach($share in $shares)
{
$sName = $share.Name
$sPath = $share.Path
$sDesc = $share.Description
$objShare = New-Object -TypeName PSObject
$objShare = Add-Member -PassThru -InputObject $objShare -NotePropertyName Server -NotePropertyValue $computer
$objShare = Add-Member -PassThru -InputObject $objShare -NotePropertyName Online -NotePropertyValue $True
$objShare = Add-Member -PassThru -InputObject $objShare -NotePropertyName Share -NotePropertyValue $sName
$objShare = Add-Member -PassThru -InputObject $objShare -NotePropertyName Path -NotePropertyValue $sPath
$objShare = Add-Member -PassThru -InputObject $objShare -NotePropertyName Description -NotePropertyValue $sDesc
$objShare | Export-CSV -Path $fOutfile -Append -NoClobber -NoTypeInformation
}
}
}
}
Above is a snippet of my script (as a whole it reads server list from a file, lists the share name,path, description) and I am wondering if anyone knows how I could get permissions on a given share for all users/groups with rights to the share.
Thanks in advance!
After you get the Shares from win32_share Class, Get the Share Permissions from the Win32_LogicalShareSecuritySetting Class, like this:
$Shares = Get-WmiObject Win32_Share -ComputerName $computer -Credential $uCredentials |
? {$_.Type -eq 0} ## 0 for Disk Drive shares only see: https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-share
$Array = #()
$ACL = #()
Foreach ($Share in $Shares)
{
$ShareName = $share.name
$SharePermissions = Get-WmiObject Win32_LogicalShareSecuritySetting -Filter "name='$ShareName'" -ComputerName $computer
Foreach ($SP in $SharePermissions)
{
$SecDesc = $SP.GetSecurityDescriptor().Descriptor
foreach($ace in $SecDesc.DACL){
$UserName = $ace.Trustee.Name
If ($ace.Trustee.Domain -ne $Null) {$UserName = "$($ace.Trustee.Domain)\$UserName"}
If ($ace.Trustee.Name -eq $Null) {$UserName = $ace.Trustee.SIDString }
$ACL += New-Object Security.AccessControl.FileSystemAccessRule($UserName, $ace.AccessMask, $ace.AceType)
}
}
$Results = "" | Select Server,Name,Status,Path,Description, ID, Rights
$Results.Server = $Share.__Server
$Results.Name = $Share.Name
$Results.Status = $Share.Status
$Results.Path = $Share.Path
$Results.Description = $Share.Description
$Results.ID = $ACL | % {$_.IdentityReference}
$Results.Rights = $ACL | % {$_.FileSystemRights}
$Results = $Results | ? {$_.id -ne $null}
$Array += $Results
}
$Array

Resources