We Can Fix Mixed Security Style Qtrees for You ;-)

Mixed security style qtrees have always been a little bit of a support nightmare, but when they’ve been implemented many moons ago when someone thought they were a good idea, and you’ve now got millions of files on the qtree, how do you get the required knowledge/insight to help you flip the qtree security style?

We have a solution ;-)

Please see the following PowerShell script - copy and paste into a text editor and save as say “ScanForSecurityStyleInfo.ps1” and run - simples (be patient, it may take a while, you can always check the command-history logs on the node it’s pointed at to see where it’s at.)

WARNING: A tool like this generates massive command-history logs on the node it’s pointed at, will generate a lot of additional disk I/O and CPU processing cycles, so never run this against a production system.

The Script

Note: Formatted for blogger by replaced the tabs with <##>

##############################
## ScanForSecurityStyleInfo ##
##############################

Param(
<##>[String]$Cluster,
<##>[String]$Username,
<##>[String]$Password,
<##>[String]$Vserver,
<##>[String]$Volume,
<##>[String]$Qtree
)

"";"+++++ ScanForSecurityStyleInfo (cDOT) +++++";""
"IMPORTANT: If possible, run this against your DR system since it will generate substantial ..."
"IMPORTANT: ... disk, CPU, and logging (command-history.log) load on the destination system.";""
"NOTE: This tool does not use protocol acccess ..."
"NOTE: ... it does not recquire NFS or CIFS access to the file system to be scanned."
"NOTE: You do not need to suspend mirrors if scanning DR.";""
"PERFORMANCE TIP 1: Point this at the node-management LIF of the node that has the volume you're scanning."
"PERFORMANCE TIP 2: The server you're running this script on should ideally be local to the cDOT system ..."
"PERFORMANCE TIP 3: ... and of a reasonable spec (i.e. 4GB RAM minimum, 2 cores or better recommended.)";""

#############################################
## CHECK FOR DATA ONTAP POWERSHELL TOOLKIT ##
#############################################

If(!(Get-Module DataONTAP)){Import-Module DataONTAP -ErrorAction SilentlyContinue}
If(!(Get-Module DataONTAP)){"Cannot find Data ONTAP PowerShell Toolkit - exiting!"; ""; EXIT}

#################################
## 1: ACQUIRE DETAILS FOR SCAN ##
#################################

"<<<<< PART 1/3: ACQUIRE DETAILS FOR SCAN >>>>>";""

## CLUSTER/USERNAME/PASSWORD/VSERVER ##
## ================================= ##

$SecurePass = $null
If($Password){ $SecurePass = $Password | ConvertTo-SecureString -AsPlainText -Force }

">>> Enter Connection Details ";""
If(!$Cluster)    { $Cluster    = Read-Host "Cluster " } else { "Cluster  = $Cluster"}
If(!$Username)   { $Username   = Read-Host "Username" } else { "Username = $Username" }
If(!$SecurePass) { $SecurePass = Read-Host "Password" -AsSecureString}; ("")

$Credential = New-Object System.Management.Automation.PsCredential($Username,$SecurePass)
$Connection = Connect-NcController -Name $Cluster -Credential $Credential -ErrorAction SilentlyContinue
If(!$Connection){"Unable to connect to $Cluster - exiting!"; ""; EXIT}

## SVMs in CLUSTER ##
## =============== ##

">>> List of Data SVMs in $Cluster"; ""
$vsAttrs = Get-NcVserver -Template
$vsQuery = Get-NcVserver -Template
$vsQuery.VserverType = "data"
[System.Array]$DataVservers = (Get-NcVserver -Attributes $vsAttrs -Query $vsQuery).VserverName
If($DataVservers.count -eq 0){ "There are no Data SVMs in $Cluster - exiting!"; ""; EXIT}
$DataVservers | Foreach { $_ }; ""
If(!$Vserver) { $Vserver    = Read-Host "Select Vserver " } else { "Vserver  = $Vserver" }
If(!($DataVservers -Contains $Vserver)){"";"Vserver $Vserver does not exist - exiting!";"";EXIT};""
$Global:CurrentNcController.Vserver = $Vserver ## CONTEXT SET TO $VSERVER

## ACQUIRE VOLUME TO BE SCANNED ##
## ============================ ##

">>> List of Volumes in $Vserver "; ""
$Attrs = Get-NcVol -Template
$Query = Get-NcVol -Template
Initialize-NcObjectProperty -object $Query -name VolumeStateAttributes
$Query.VolumeStateAttributes.IsVserverRoot = ""
[System.Array]$VolumesList = (Get-NcVol -Attributes $Attrs -Query $Query).Name
If($VolumesList.count -eq 0){ "There are no data volumes in SVM $Vserver - exiting!"; ""; EXIT}
$VolumesList | Foreach { $_ }; ""
If(!$Volume){ $Volume = Read-Host "Select Volume" } else { "Volume to check = $Volume" }
If(!($VolumesList -Contains $Volume)){"";"Volume $Volume does not exist - exiting!";"";EXIT};""

Initialize-NcObjectProperty -object $Attrs -name VolumeIdAttributes
$Attrs.VolumeIdAttributes.JunctionPath = ""
[String]$JunctionToVol = (Get-NcVol -Name $Volume -Attributes $Attrs).VolumeIdAttributes.JunctionPath
$JunctionToVol = $JunctionToVol.Substring(0,($JunctionToVol.Length - 1 - $Volume.Length)) # Required for Read-NcDirectory

## ACQUIRE MIXED QTREE TO BE SCANNED ##
## ================================= ##

">>> List of Mixed Security Style Qtrees in $Vserver "; ""
$qQuery = Get-ncQtree -Template
$qQuery.SecurityStyle = "mixed"
$qQuery.Volume = $Volume
[System.Array]$QtreesList = (Get-NcQtree -Query $qQuery).Qtree ## -VserverContext $Vserver
If($QtreesList.Count -eq 0){"No mixed qtrees in this volume - exiting!"; ""; EXIT}
$QtreesList | Foreach { $_ }; ""
If(!$Qtree){ $Qtree = Read-Host "Select Qtree" } else { "Qtree to check = $Qtree" }
If(!($QtreesList -Contains $Qtree)){"";"This is not a mixed mode qtree - exiting!";"";EXIT};""
$StartReadNcDirPath = "/vol/$Volume/$Qtree"
"Read-NcDirectory uses /vol/VOLNAME/QTREE = $StartReadNcDirPath"

#################
## 2: SCANNING ##
#################

"";"<<<<< PART 2/3: SCANNING >>>>>";""

$StartDate = date
">>> START TIME = $StartDate";""

[System.Array]$Global:DiscoveredFiles   = "STYLE,UID,GID,BITS,CREATED,MODIFIED,ACCESSED,FILE @ FOLDER"
[System.Array]$Global:DiscoveredFolders = "STYLE,UID,GID,BITS,CREATED,MODIFIED,ACCESSED,FOLDER"
[int64       ]$Global:FolderCount       = 0
[int64       ]$Global:FileCount         = 0

$Attributes                   = Read-NcDirectory -Template
$Attributes.Empty             = ""
$Attributes.FileType          = ""
$Attributes.Name              = ""
$Attributes.AccessedTimestamp = ""
$Attributes.ModifiedTimestamp = ""
$Attributes.CreationTImestamp = ""

## Get-NcFileDirSecInfo FUNCTION ##
## ============================= ##

Function Get-NcFileDirSecInfo {
<##>Param([String]$Path)
<##>$GetNcFileDirSec         = Get-NcFileDirectorySecurity -Path ($JunctionToVol + $Path)
<##>[String]$EffectiveStyle  = $GetNcFileDirSec.EffectiveStyle
<##>[String]$UnixUserId      = $GetNcFileDirSec.UnixUserId
<##>[String]$UnixGroupId     = $GetNcFileDirSec.UnixGroupId
<##>[String]$UnixModeBits    = $GetNcFileDirSec.UnixModeBits                     
<##>[Stirng]$OutputString    = ($EffectiveStyle + "," + $UnixUserId + "," + $UnixGroupId + "," + $UnixModeBits + ",")
<##>RETURN $OutputString
}

## RECURSIVE SCANNING FUNCTION ##
## =========================== ##

Function GetDirInfoRecursive {
<##>Param([String]$PathToReadRecursive) 
<##>Write-Host "." -ForegroundColor Cyan -NoNewLine
<##>$GetDirInfo = Read-NcDirectory $PathToReadRecursive -Attributes $Attributes
<##>Foreach ($line in $GetDirInfo){
<##><##>[String]$TimeInfo = [String]$line.CreationTimestampDT + "," + [String]$line.ModifiedTimestampDT + "," + [String]$line.AccessedTimestampDT + ","
<##><##>If($line.FileType -eq "directory"){
<##><##><##>If(($line.Name -ne ".") -and ($line.Name -ne "..") -and ($line.Name -ne ".snapshot")) {
<##><##><##><##>$Global:FolderCount ++
<##><##><##><##>[String]$FileDirSecInfo = Get-NcFileDirSecInfo ($PathToReadRecursive.SubString(4) + "/" + $line.Name)
<##><##><##><##>$Global:DiscoveredFolders += ($FileDirSecInfo + $TimeInfo + $PathToReadRecursive + "/" + $line.Name)
<##><##><##><##>If ($line.Empty -ne "False"){
<##><##><##><##><##>$NewPathToRead = $PathToReadRecursive + "/" + $line.Name
<##><##><##><##><##>GetDirInfoRecursive $NewPathToRead              
<##><##><##><##>}
<##><##><##>}             
<##><##>} elseif($line.FileType -eq "file"){
<##><##><##>$Global:FileCount ++
<##><##><##>[String]$FileDirSecInfo = Get-NcFileDirSecInfo ($PathToReadRecursive.SubString(4) + "/" + $line.Name)
<##><##><##>If($FileDirSecInfo){ $Global:DiscoveredFiles += ($FileDirSecInfo + $TimeInfo + ($line.Name) + " @ " + $PathToReadRecursive) }
<##><##>}
<##>}
}

GetDirInfoRecursive $StartReadNcDirPath

$FinishDate = date
"";"";">>> FINISH TIME = $FinishDate";""

###############
## 3: OUTPUT ##
###############

"<<<<< PART 3/3: OUTPUT >>>>>"
"";">>> SEARCH STATISTICS:";""
"Processed " + $Global:FileCount + " files and " + $Global:FolderCount + " folders.";""
"Started at $StartDate"
"Finished at $FinishDate";""

## OUTPUT TO TEXTFILE ##
## ================== ##

$Date = Get-Date -uformat "%Y%m%d%H%M"
$OutputFileName = "ScanResults_" + $Vserver + "_" + $Volume + "_" + $Qtree + "_" + $date
[System.Array]$StatsOut = "","##### SEARCH STATISTICS #####","",("Processed " + $Global:FileCount + " files and " + $Global:FolderCount + " folders."),"","Started at $StartDate","Finished at $FinishDate",""
$Global:DiscoveredFiles   | Out-File ($OutputFileName + "_FILES.csv") -Encoding Default -Force
$Global:DiscoveredFolders | Out-File ($OutputFileName + "_FOLDERS.csv") -Encoding Default -Force
$StatsOut                 | Out-File ($OutputFileName + "_STATS.txt") -Encoding Default -Force
"OUTPUT FILES = $OutputFileName";""


Comments