Monthly Archives: September 2016

Get DSA object creation date

‘Sup PSHomies,

The project manager is on to me! 😛

PM: “PowerShell is awesome!!!”

Me: “You know it!!!”

PM: “Glad you agree! So here’s what I need, We need a weekly update of all users, groups in the source domain based on their creation date…”

Me: “Wait, what just happened?

PM: “You were agreeing that PowerShell is awesome? (Grinning)”

Me: “Well played… I ain’t even mad mad at ya…”

And so begins another PowerShell journey 🙂

The idea here is to be proactive in our migration. Instead of asking for a list of newly created users/groups we’ll gather the information ourselves. All the customer has to do now is validate our list as to who is relevant. Which got me thinking, why stop at user/groups?

Remember I needed to recreate the DFS Structure? The data that I used was from February. What if there were newly created DFS links in the mean time? Turns out my hunch paid off!

Now curiosity got the better of me… “What about accounts that were deleted?” We’re migrating in batches. My first action is always to verify that the accounts in the batch exist. Having a list of deleted objects helps verifying it wasn’t a typo and that this object isn’t relevant anymore…

Here’s what I came up with:

<#
Author: I. Strachan
Version:
Version History:
Purpose: Export DSA Objects
#>
[CmdletBinding()]
param(
$domain = 'pshirwin.local'
)
#region Initiate HashTables & variables
$exportDate = Get-Date -Format ddMMyyyy
$xlsxFile = ".\export\dsa\source\$domain - DSAObjects - $exportDate.xlsx"
$xmlFile = ".\export\dsa\source\$domain - DSAObjects - $exportDate.xml "
$DSAObjects = @{}
$Select = @{}
#endregion
#region Main
$DSAObjects.DFSLinks = Get-ADObject -LDAPFilter '(objectClass=msDFS-LInkv2)'-Properties msDFS-LinkPathv2,msDFS-Propertiesv2,whenChanged,whenCreated |
ForEach-Object{
[PSCustomObject]@{
DFSLink = '\\pshirwin{0}' -f ($_.'msDFS-LinkPathv2').Replace('/','\')
State = $_.'msDFS-Propertiesv2' -join ','
Changed = $_.whenChanged
Created = $_.whenCreated
CreatedDate = Get-Date (Get-Date $_.whenCreated).Date -format yyyyMMdd
}
}
$DSAObjects.PrintQueues = Get-ADObject -LDAPFilter '(objectClass=printQueue)' -Properties printerName,portName,printShareName,uNCName,serverName,whenChanged,whenCreated |
ForEach-Object{
[PSCustomObject]@{
PrinterName = $_.printerName
PortName = $_.portName -join ','
PrintShareName = $_.printShareName -join ','
ServerName = $_.serverName
UNCName = $_.uNCName
Changed = $_.whenChanged
Created = $_.whenCreated
CreatedDate = Get-Date (Get-Date $_.whenCreated).Date -format yyyyMMdd
}
}
$DSAObjects.Contacts = Get-ADObject -LDAPFilter '(objectClass=contact)' -Properties DisplayName,givenName,sn,DistinguishedName,mail,whenChanged,whenCreated |
Foreach-Object {
[PSCustomObject]@{
GivenName = $_.givenName
SurName = $_.sn
DistinguishedName = $_.DistinguishedName
DisplayName = $_.DisplayName
EmailAddress = $_.mail
Changed = $_.whenChanged
Created = $_.whenCreated
CreatedDate = Get-Date (Get-Date $_.whenCreated).Date -format yyyyMMdd
}
}
$DSAObjects.Users = Get-ADUser -LDAPFilter '(objectClass=user)' -Properties accountExpirationDate,LastLogonDate,Initials,Description,EmailAddress,Enabled,DisplayName,OfficePhone,MobilePhone,Department,whenChanged,whenCreated,DistinguishedName,canonicalname |
Foreach-Object {
[PSCustomObject]@{
SamAccountName = $_.SamAccountName
DistinguishedName = $_.DistinguishedName
CanonicalName = $_.CanonicalName
Enabled = $_.Enabled
GivenName = $_.GivenName
Initials = $_.Initials
SurName = $_.SurName
EmailAddress = $_.EmailAddress
Description = $_.Description
Displayname = $_.DisplayName
OfficePhone = $_.OfficePhone
MobilePhone = $_.MobilePhone
Department = $_.Department
LastLogonDate = $_.LastLogonDate
AccountExpiresOn = $_.accountExpirationDate
Changed = $_.whenChanged
Created = $_.whenCreated
CreatedDate = Get-Date (Get-Date $_.whenCreated).Date -format yyyyMMdd
}
}
$DSAObjects.Groups = Get-ADGroup -LDAPFilter '(objectClass=group)' -Properties Member,MemberOf,whenChanged,whenCreated |
Foreach-Object {
[PSCustomObject]@{
DistinguishedName = $_.DistinguishedName
SamAccountName = $_.SamAccountName
Name = $_.Name
Changed = $_.whenChanged
Created = $_.whenCreated
CreatedDate = Get-Date (Get-Date $_.whenCreated).Date -format yyyyMMdd
Member = $_.Member
MemberOf = $_.MemberOf
}
}
$DSAObjects.Deleted = Get-ADObject -Filter * -IncludeDeletedObjects -Properties CN,SamAccountName,LastKnownParent | Where-Object{$_.Deleted -eq $true} |
ForEach-Object{
[PSCustomObject]@{
CN = $(($_.CN -split "`n") -join '; ')
SamAccountName = $_.SamAccountName
Deleted = $_.Deleted
ObjectClass = $_.ObjectClass
LastKnownParent = $_.LastKnownParent
}
}
#endregion
#region Export Object and to Excel
$Select.DFSLinks = '*'
$Select.Users = '*'
$Select.Contacts = '*'
$Select.Groups = @('ObjectClass', 'DistinguishedName', 'Name', 'Changed', 'Created')
$Select.PrintQueue = @('ObjectClass', 'PrinterName','PortName','PrintShareName','ServerName','UNCName','Changed','Created')
$Select.Deleted = @('CN','SamAccountName','Deleted','ObjectClass','LastKnownParent')
#XML File
$DSAObjects |
Export-Clixml -Path $xmlFile -Encoding UTF8
#Excel File
$DSAObjects.Keys |
ForEach-Object{
if($DSAObjects.$_){
$DSAObjects.$_ |
Select-Object $Select.$_ |
Export-Excel -Path $xlsxFile -WorkSheetname $_ -AutoSize -BoldTopRow -FreezeTopRow
}
}
#endregion
view raw Get-DSAObjects.ps1 hosted with ❤ by GitHub

I added Contact and PrintQueues as a bonus 😉

For reporting it’s ImportExcel to the rescue!

dsaobjects

At first I added a extra property in order to lookup the creation date using yyyyMMdd date format. Turns out it wasn’t necessary. You can just a easily use a filter on Created

dsaobjects-filter

The PM is quite pleased… PowerShell is Awesome!!! But you already knew that… 😉

Hope it’s worth something to you

Ttyl,

Urv

 

DFSn links Operational readiness

‘Sup PSHomies,

So here is the follow up on the previous blog.

I recreated the shares, check! Now it’s time to create the DFSn Links & targets!

Here’s the code to create the necessary links and targets:

<#
Author: I. Strachan
Version:
Version History:
Purpose: Create DFS link with specified target
#>
[cmdletbinding()]
Param(
$csvFile = 'DFSnLinksTargets.csv'
)
Import-Module DFSN -Verbose:$false
#region Import
$csvDFSTargets = Import-Csv -Path .\sources\csv\$($csvFile) -Delimiter "`t" -Encoding UTF8
$DfsFolders = Get-DfsnRoot | ForEach-Object {Get-DfsnFolder -Path "$($_.Path)\*"}
#endregion
#region Main
foreach($dfs in $csvDFSTargets){
#Test if target exists first
if(!(Test-Path -LiteralPath $dfs.Target)){
Write-Warning "Target $($dfs.Target) doesn't exists"
Write-Verbose "Creating target $($dfs.Target)"
New-Item -Path $dfs.Target -ItemType Directory -Force
if(!($DfsFolders.Path -contains $($dfs.link))){
Write-Verbose "Creating $($dfs.link) with target $($dfs.Target)"
New-DfsnFolder -Path $dfs.Link -TargetPath $Dfs.Target
}
}
Else{
Write-Warning "Folder '$($dfs.Target)' already exists"
#Check if the DFS Target already exists
$CheckDFSTarget = Get-DfsnFolderTarget -Path $dfs.Link
if($CheckDFSTarget.TargetPath -eq $dfs.Target){
Write-Warning "DFSn Target is already set to $($dfs.Target)"
}
else{
Write-Verbose "Setting $($dfs.link) with target $($dfs.Target)"
Set-DfsnFolderTarget -Path $dfs.Link -TargetPath $dfs.Target
}
}
}
#endregion

Quick run down. The csv is straight forward Link, Target (tab delimited). I created a few checkpoints (again, better safe than sorry).First, I get a list of all actual DFS links & targets for reference. If the target doesn’t exist create it. That’s where the SMB share preparation came in 😉 If the link doesn’t exist create it. If the target exist, verify that it’s set to the specified link.

In my case I only have 1:1 Dfs link/target relationships. So this worked for me. Be sure to test this before trying it out in production.

I needed to recreate 1386 DFS links. This took a few minutes. 10 failed, not bad considering the amount. Now the old Irwin would have panicked at the sight of something going wrong (Whooosahhh). I kept it together and let script ride out. Once it was done I did my OVF to see which ones  failed 😉

<#
Author: I. Strachan
Version:
Version History:
Purpose: OVF DFS links & targets
#>
Param(
$csvFile = 'DFSnLinksTargets.csv'
)
Import-Module DFSN -Verbose:$false
#region Import CSV and saved Credentials
$csvDFSnTargets = Import-Csv -Path .\sources\csv\$($csvFile) -Delimiter "`t" -Encoding UTF8
$DfsnLinkTargets = Get-DfsnRoot |
ForEach-Object{
Get-DfsnFolder -Path "$($_.Path)\*" |
Get-DfsnFolderTarget |
Select-Object Path,TargetPath
}
#endregion
$lookupDFSn = $DfsnLinkTargets | Group-Object -AsHashTable -AsString -Property Path
#region Main
$csvDFSnTargets |
ForEach-Object{
Describe "DFSn Link & Target Operation validation share $($_.Link)" -Tags 'DFSnLinkTargets' {
Context "Verifying DFS Link $($_.Link)" {
It "DFS Link $($_.Link) exists" {
$lookupDFSn.$($_.Link).Path | Should be $_.Link
}
It "DFS Target $($_.Target) exists" {
$lookupDFSn.$($_.Link).TargetPath | Should be $_.Target
}
}
}
}
#endregion

This is one of those rare instances that I created the missing links by hand (don’t judge me I was on a tight schedule). Turns out a few had some form of a special character in the name. Creating them by hand didn’t raise an issue.

Last but not least I wanted to generate a ReportUnit HTML of the DFSn test results. Generating the ReportUnit HTML file didn’t go as I expected. It did succeed in the end., but generating a HTML report for 2772 was a bit too much. Having said that…

There’s a module for Formatting Pester result using PScribo created by Erwan Quélin. Definitely check it out if you want to do everything in PowerShell (Who doesn’t? :-P)

Usage is pretty easy:

dfsformatpesterps1

Here’s a quick screenshot of Format-Pester output:

dfsformatpester

Nice!

Once I resolved the failed test, I ran the test again, nothing but purple and green!

A quick notification in Slack using PSSlack by @pscookiemonster (Even though I’m the only one using Slack at the moment, give it time…)

Awesome!

So I learned that the size of the test when using ReportUnit.exe could be an issue, but if you want to do everything in PowerShell then Format-Pester is a better fit.

Hope it’s worth something to you

Ttyl,

Urv