Export Multi column Excel spreadsheet from Powershell script - arrays

I have been working at this for what seems like way too long now. I am pulling all of the ADgroups names and descriptions in my domain. At this point it doesn't necessarily matter but its eating at me.
$exportList = #()
$ADgroups = Get-ADGroup -Filter * -Properties Description
Foreach ($group in $adgroups){
$GroupObj = New-Object System.Object
$GroupObj | Add-Member -Type NoteProperty -Name Name -Value $group.Name
$GroupObj | Add-Member -Type NoteProperty -Name Description -Value $group.Description
$exportList += $GroupObj
}
$exportList | Out-File C:\test\ADGroups.csv
And the file comes out like this where all of the data is within column A
but I would like for the name to show in column A and the Description to show in column B I have tried making different Arrays and tried calling the properties in different ways but nothing I have tried yet has worked and I am sure that I am missing something very simple.

You're using Out-File Instead of Export-CSV which export a text file instead of a comma separated file
Replace the:
$exportList | Out-File C:\test\ADGroups.csv
To:
$exportList | Export-CSV C:\test\ADGroups.csv
Also, You can cut some lines and simply do:
Get-ADGroup -Filter * -Properties Description |
Select Name,Description | Export-CSV C:\test\ADGroups.csv

Related

Get-ADUser - searching for expired account. Using variables in command

I am currently working on a Powershell GUI script to help my team easier find accounts with expired passwords, disabled accounts etc and to output these to a CSV. It revolves almost entirely around the "Get-ADUser" command. So far almost everything has worked, bar finding accounts with expired passwords.
I've researched this a lot already but there seems to be no easy way of finding expired accounts using Get-ADUser. I know I can use Search-ADAccount instead but it would be very awkward to do so (as I would need to re-write a lot of code).
Get-Aduser -Properties * -Filter {PasswordExpired -eq $true} just draws a blank.
I've found a partial solution over at https://serverfault.com/questions/805526/get-aduser-password-expired-filter-not-working-correctly/805611
For example,
Get-ADUser -Properties * -Filter * | ? {$_.PasswordExpired -eq $True -and $_.Enabled -eq $true} | Select-Object name, enabled | Export-Csv "C:\report.csv" -NoTypeInformation
works just fine but if I try to assign the 'middle' of the command i.e
{$_.PasswordExpired -eq $True -and $_.Enabled -eq $true}
to a variable and then substitute it into the command I either get an error, a list of all those in my AD or nobody at all. The rational for substituting in a variable is to allow for the possible account statuses (that the user can choose from by selecting a radio button).
I've tried the various permutations of double and single quotes, including and not including curly brackets etc but Powershell will not give me a break!
Thanks!
The Get-ADUser cmdlet exposes the PasswordExpired extended property, which is a boolean indicating if the password is expired. It is based on the msDS-User-Account-Control-Computed attribute. However, you cannot filter with this property.
This would mean you can check the UF_PASSWORD_EXPIRED bit on that property:
Get-ADUser -Filter "Enabled -eq 'True'" -Properties 'msDS-User-Account-Control-Computed' |
Where-Object {($_.'msDS-User-Account-Control-Computed' -band 0x800000) -eq 0x800000} | # UF_PASSWORD_EXPIRED --> 0x800000 = 8388608
Select-Object Name, Enabled | Export-Csv "C:\report.csv" -NoTypeInformation
You can speed up the above by extending the filter to rule out users with PasswordNeverExpires and PasswordNotRequired both $false:
$filter = "Enabled -eq 'True' -and PasswordNeverExpires -eq 'False' -and PasswordNotRequired -eq 'False'"
Get-ADUser -Filter $filter -Properties PasswordNeverExpires, PasswordNotRequired, 'msDS-User-Account-Control-Computed' |
Where-Object {($_.'msDS-User-Account-Control-Computed' -band 0x800000) -eq 0x800000} | # UF_PASSWORD_EXPIRED --> 0x800000 = 8388608
Select-Object Name, Enabled | Export-Csv "C:\report.csv" -NoTypeInformation
I reckon I've found a solution over on Stack Exchange.
See https://serverfault.com/questions/723217/find-out-if-password-expired-or-when-it-expires-for-user-in-a-specific-ou
Early tests suggest it works.

Is there a way to sort within DistinguishedName

I am trying to get a list of users and Id like to sort based on the last OU= in DistinguishedName. The syntax I'm using isn't quite right and I need some pointers. Thank You
get-aduser -Filter {Enabled -eq $true} -Properties * | where {($_.EmployeeNumber -eq $null) -and ($_.PrimaryGroup -eq 'CN=Domain Users,CN=Users,DC=OURDOMAIN,DC=net')} | FT SamAccountName,Name,EmployeeNumber,DistinguishedName,Created | export-csv Users.csv
You can ask for the msDS-parentdistname attribute in AD. It's a relatively new attribute, so it's possible it's not available on the version of Windows Server that you're running in your environment. But you can see if it's there.
It's a constructed attribute, which means it's calculated at the time it's asked for. But that also means you have to explicitly ask for it (you can't use -Properties *).
The use Sort-Object to sort your list.
get-aduser -Filter {Enabled -eq $true} -Properties SamAccountName,Name,EmployeeNumber,DistinguishedName,Created,msDS-parentdistname | where {($_.EmployeeNumber -eq $null) -and ($_.PrimaryGroup -eq 'CN=Domain Users,CN=Users,DC=OURDOMAIN,DC=net')} | Sort-Object msDS-parentdistname | FT SamAccountName,Name,EmployeeNumber,DistinguishedName,Created,msDS-parentdistname | export-csv Users.csv

Grabbing a list of MemberOf based off a list of users

I'm currently running into a problem where I am failing to get a list of security groups based off a list of users. The trouble is mainly within grabbing their "MemberOf"; since that is an array within itself. Any suggestions would be helpful!
Code below:
$companycount = Get-ADUser -Properties * -Filter{(company -Like "companyname.")}
$users=#()
foreach($user in $companycount)
{
if($user.Enabled)
{
$ScriptObject = New-Object PSOBJECT
Add-Member -InputObject $ScriptObject -MemberType NoteProperty -Name Sam -Value ""
Add-Member -InputObject $ScriptObject -MemberType NoteProperty -Name Description -Value ""
Add-Member -InputObject $ScriptObject -MemberType NoteProperty -Name MemberOf -Value ""
$ScriptObject.Sam = $user.SamAccountName
$ScriptObject.Description =$user.Description
$ScriptObject.MemberOf = $user.MemberOf
$users+=$ScriptObject
}
}
$users|export-csv
You'll have to treat it like an array, much like you're looping through $companycount.
Keep in mind that MemberOf is an array of the distinguisedName attributes of each group. But the name of the group is part of the distinguisedName (the CN portion), so you can just use Substring() to extract that.
You could use something like this:
$ScriptObject.MemberOf = ($u.MemberOf | ForEach { $_.Substring(3, $_.IndexOf(",") - 3) }) -Join ", "
That will give you a comma-delimited list of names of the groups.
Keep in mind that the name of the group (the cn attribute) can be different than the displayName (for distribution lists, it's the displayName that is shown in Outlook), which may or may not be relevant to you. Also, my code there will cause problems if any of your group names have commas in them.

Filtering packages by their name from list out of file

I am trying to read program names from a file to filter out the installed ones.
For reading the file I got:
$file = Get-Content "C:\Users\testuser\Desktop\Test.txt"
Then I try to filter packages by their name which isnt working, what I tried:
Get-AppxPackage | Where-Object -Property Name -notin $file | select Name, IsFramework
or
Get-AppxPackage | Where-Object { $_.Name -notin $file } | select Name, IsFramework
I cannot use any .Net statements, how can I solve this?
Edit: Here is my file's content:
Microsoft.BioEnrollment Microsoft.AAD.BrokerPlugin Microsoft.Windows.CloudExperienceHost Microsoft.Windows.ShellExperienceHost windows.immersivecontrolpanel Microsoft.Windows.Cortana Microsoft.AccountsControl Microsoft.LockApp
Just to recap from David Brabant, the following worked for askingQuestions:
Get-AppxPackage | ?{ $file.Contains($_.Name) } | select Name, IsFramework
This works great and is probably one of the best ways to do this.
This didn't work for me for all applications, so here's another take at filtering the list. :)
Matching Name:
Get-WmiObject -Class Win32_Product | Where-Object {$_.name -Like "appName"}
Not Matching Name:
Get-WmiObject -Class Win32_Product | Where-Object {$_.name -NotMatch "appName"}
Note that using Where-Object is not case sensitive but looks for exact match. On the flip side, you can also use wildcards.
but to filter by file, you could do something like:
[string[]]$List = (Get-Content -Path 'C:\test.txt') -replace ' ',"`r`n"
foreach ($ProgramName in $List)
{
Get-WmiObject -Class Win32_Product | Where-Object {$_.name -Like $ProgramName}
}
Note that -replace ' ',"rn" is only necessary for spaces between application names. You can just remove this and have each application on a new line. Also note that this could possibly produce duplicates.
Wildcard Example:
Get-WmiObject -Class Win32_Product | Where-Object {$_.name -Like "*adobe*"}
Cheers!

Powershell to SQL database

My initial post was how to pass data directly from the pipeline to a (remote) SQL database. Writing to a CSV then a database did not work for the amount of data I am working with - over 2 million files I am querying.
Below is the final script I came up with. It scans a file server, filters in the pipeline, creates a PSObject with the file attributes and then stores the attributes of the Object to variables. Those variables are then passed to the SQL query string. It's a bit cumbersome, but I could not see another way to get the file attributes read directly into the query string.
I also needed a way to run this remotely.
With the help of Invoke-SQLCmd2 you can write and read data from an SQL server database. More info here.
I'm not sure but I think this CmdLet does not accept pipeline input. So your best bet would be to transform your code to a Foreach structure and invoke the Cmdlet Invoke-SQLcmd2 every time you want to do an INSERT or something else.
Something like this:
$CSV = Get-Content -Path "D:\FS01-USER-Files\$name.csv"
Foreach ($Line in $CSV) {
Invoke-Sqlcmd2 #SQLParams -Query "
INSERT INTO $SQLTable
(FileName, Data)
VALUES('$Line.FileName', '$($Line.Data)')"
}
How can this be run with another account that has domain privileges?
You can set up a Scheduled Task that runs as another user with the password stored. This task can then be triggered by other users who have RDP access to the server where the Scheduled Task has been created.
$ErrorActionPreference = "SilentlyContinue"
$cutOffDate = (Get-Date).addYears(-1)
$exclusions = #(".lnk",".url",".ini",".odc",".ctx",".upd",".ica")
$connectionString = "Server=db01;Database=Files;Integrated Security=True;"
$count = 0
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
gci "D:\USERS" -Recurse | ? {
$_.PSIsContainer -eq $False -and
$_.LastAccessTime -le $cutOffDate -and
$exclusions -notcontains $_.Extension -and
$_.length -gt "0" -and
$_.Directory -notmatch ".*USERS\\.*\\Personal\\sysdata\\cookies"
} | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Directory $_.DirectoryName
$obj | Add-Member NoteProperty Name $_.Name
$obj | Add-Member NoteProperty MB ("{0:N3}" -f ($_.Length/1MB))
$obj | Add-Member NoteProperty LastAccessed $_.LastAccessTime
$obj | Add-Member NoteProperty LastMofified $_.LastWriteTime
$obj | Add-Member NoteProperty Created $_.creationtime
$obj | Add-Member NoteProperty Extension $_.Extension
$v1 = $obj.Directory
$v2 = $obj.Name
$v3 = $obj.MB
$v4 = $obj.LastAccessed
$v5 = $obj.LastMofified
$v6 = $obj.Created
$v7 = $obj.Extension
$query = "INSERT INTO dbo.fs01 (directoryPath,fName,fileSize,lastAccessed,lastModified,createdDate,extension) VALUES ('$v1','$v2','$v3','$v4','$v5','$v6','$v7');"
$command = $connection.CreateCommand()
$command.CommandText = $query
$command.ExecuteNonQuery()
}
$connection.close()

Resources