Sync-NcCifsShares (Automated Synching of CIFS Shares to a DR SVM)

The following is a fairly robust tool for syncing CIFS shares on replicated volumes. SVM-DR is a cool feature of cDOT; the incarnation in 8.3.1 may not fit every requirement though...

Briefly, what the tool does (note that exactly the same code will work in WFA - it has added WFA detection):

Acquisition Stage

Connected to the Source Cluster:

- Checks connection to the cluster
- Checks the Source SVM exists
- Checks there’s a CIFS server on this SVM
- Acquires volume junction paths
- Acquires shares
- Acquires ACLs

Connected to the Remote Cluster (which can be the same as the Source, but the Vservers must be different):

- Checks connection to the cluster
- Checks the Remote SVM exists
- Checks there’s a CIFS server on this SVM
- Acquires volume junction paths
- Acquires shares
- Acquires ACLs
- Acquires SnapMirrors (only those with the specified Source Vserver, SnapMirrored, and not SVM-DR relations)

CIFS Share Synchronization Stage

Still connected to the Remote Cluster:

- Cycles through the SnapMirrored volumes
- Checks the junction-path and fixes if required
- Checks the shares on the volume and adds/sets them as required
- Checks the share’s ACLs and adds/sets them as required
- Checks for additional ACLs on the Remote Share, and removes
- Checks for additional shares on the Remote Volume, and removes

The Script

Note: As always, formatted for blogger by replacing tabs with two spaces.

#######################
## Sync-NcCifsShares ##
#######################

Param(
  [parameter(Mandatory=$true)][String]$Source_Cluster,
  [parameter(Mandatory=$true)][String]$Source_Vserver,
  ## Remote_Cluster CAN be the same as Source_Cluster ##
  ## Remote_Vserver CANNOT be the same as Source_Vserver ##
  [parameter(Mandatory=$true)][String]$Remote_Cluster,
  [parameter(Mandatory=$true)][String]$Remote_Vserver,
  [String]$Title = "Sync-NcCifsShares",
  [Switch]$Log,
  [String]$LogFile = ($Title + "_" + $Source_Vserver + "_to_" + $Remote_Vserver + ".log")
)

###################
## GENERAL STUFF ##
###################

## ===== WFA ENVIRONMENT DETECTION ===== ##
[Boolean]$WFA = $FALSE
If(Get-Command Get-WFALogger -ErrorAction SilentlyContinue){ $WFA = $TRUE }
## ===== OUTPUT FUNCTION ===== ##
Function Wr{
  Param([String]$Echo = "",[String]$Ink = "WHITE",[Switch]$EXIT)
  If($Log){ $Echo >> $LogFile }
  If($WFA -and $Echo){ Get-WFALogger -Info -message $("$Echo") }
  elseif($WFA){ Get-WFALogger -Info -message $(" ") }
  If($EXIT){ Write-Host $Echo -ForegroundColor RED; Write-Host; EXIT }
  Write-Host $Echo -ForegroundColor $Ink
};Wr
## ===== TITLE ===== ##
Wr "+++++ $Title +++++" MAGENTA;Wr
## ===== TRAP DETECTION ===== ##
Trap{
  Wr;Wr "TRAP DETECTED!" RED
  Wr ("Time of Error : " + (get-date).DateTime) RED
  Wr ("Line Number   : " + $_.InvocationInfo.ScriptLineNumber) RED
  Wr ("Offset in Line: " + $_.InvocationInfo.OffsetInLine) RED
  Wr ("Error Message : " + $_.Exception.Message) RED;Wr
}
## ===== MAPPING VSERVER TO CLUSTER ===== ##
[System.Object]$VsToCl = @{}
[String]$VsToCl.$Source_Vserver = $Source_Cluster
[String]$VsToCl.$Remote_Vserver = $Remote_Cluster
If($Source_Vserver -eq $Remote_Vserver){ Wr "Cannot have Source and Remote Vserver with same name!" -EXIT }
## ===== LOAD THE DATA ONTAP PSTK ===== ##
If(!(Get-Module DataONTAP)){ [Void](Import-Module DataONTAP -ErrorAction SilentlyContinue) }
If(!(Get-Module DataONTAP)){ Wr "Failed to load DataONTAP PSTK!" -EXIT }
else{ Wr "Loaded DataONTAP PSTK" GREEN;Wr }

#######################
## ACQUISITION STAGE ##
#######################

Wr ">>>>> Acquisition Stage <<<<<" MAGENTA;Wr
## ===== DEFINING VARIABLES ===== ##
[System.Object]$Vols    = @{}
[System.Object]$Shares  = @{}
[System.Object]$ACLs    = @{}
## ===== CYCLE THROUGH SOURCE & REMOTE VSERVER ===== ##
Foreach($Vserver in ($Source_Vserver,$Remote_Vserver )){
  [String]$Cluster = $VsToCl.$Vserver
  ## ===== WFA RUNNING / OR NOT ===== ##
  If($WFA){ Connect-WfaCluster $Cluster }
  else{
    ## ===== CREDENTIALS & CONNECT TO CONTROLLER DETECTION ===== ##
    If(!(Get-NcCredential $Cluster)){ Wr "Failed to Get-NcCredential $Cluster!" -EXIT }
    else{ Wr ("Using credential " + ((Get-NcCredential $Cluster).Credential.UserName) + " for $Cluster") GREEN }
    If(!(Connect-NcController $Cluster -ErrorAction SilentlyContinue)){ Wr "Failed to connect to cluster $Cluster!" -EXIT }
    else{ Wr "Successfully tested connection to $Cluster" GREEN }   
  }
  ## ===== RUNNING DATA SVM & CIFS SERVER DETECTION ===== ##
  If( !(Get-NcVserver | where { ($_.State -eq "running") -and ($_.VserverType -eq "data") -and ($_.VserverName -eq $Vserver) }) ){ Wr "Cluster $Cluster has no running data SVM called $Vserver!" -EXIT }
  else{ Wr "Found running data SVM called $Vserver" GREEN }
  If( !(Get-NcCifsServer -VserverContext $Vserver) ){ Wr "$Vserver has no CIFS server!" -EXIT }
  else{ Wr "$Vserver has CIFS server" GREEN }
  ## ===== ACQUIRE VOLS, SHARES & SHARE ACLs ===== ##
  $VolAttrs = Get-NcVol -Template
  Initialize-NcObjectProperty -object $VolAttrs -name VolumeIdAttributes
  $VolAttrs.VolumeIdAttributes.JunctionPath = ""             
  $Vols.$Vserver   = Get-NcVol -Attributes $VolAttrs -VserverContext $Vserver
  Wr "Acquired volume junction path info from $Vserver" GREEN
  $Shares.$Vserver = Get-NcCifsShare -VserverContext $Vserver
  Wr "Acquired CIFS share info from $Vserver" GREEN
  $ACLs.$Vserver   = Get-NcCifsShareAcl -VserverContext $Vserver
  Wr "Acquired CIFS share ACL info from $Vserver" GREEN;Wr
}
## ===== ACQUIRE SNAPMIRRORS ===== ##
$SMattrs = Get-NcSnapMirror -Template
$SMattrs.MirrorState = ""
[System.Object]$GetNcSnapMirror = Get-NcSnapMirror -Attributes $SMattrs -VserverContext $Remote_Vserver | Where {($_.SourceVserver -eq $Source_Vserver) -and ($_.MirrorState -eq "snapmirrored") -and ($_.SourceVolume -ne "")}
Wr "Acquired SnapMirrors from $Source_Vserver to $Remote_Vserver" GREEN;Wr

######################################
## CIFS SHARE SYNCHRONIZATION STAGE ##
######################################

Wr "<<<<< CIFS Share Synchronization Stage >>>>>" MAGENTA;Wr

## ===== FUNCTION TO CHECK/CORRECT JUNCTION PATH ===== ##
Function Sync-NcJunctionPathVolumeDpPair{
  ## NOTE! The following variables exist outside this function: ##
  ## $Shares,$ACLs,$Source_Vserver,$Remote_Vserver,$SourceVol,$SrcVolName,$RemoteVol,$RemVolName ##
  [String]$SrcJPath = $SourceVol.VolumeIdAttributes.JunctionPath
  [String]$RemJPath = $RemoteVol.VolumeIdAttributes.JunctionPath
  ## ===== SYNC JUNCTION PATHS ===== ##
  If($SrcJPath -eq $RemJPath){
    Wr "$SrcVolName has matching junction path with $RemVolName" GREEN
    RETURN $TRUE
  }elseif($SrcJPath -eq $NULL){
    Wr "$SrcVolName on $Source_Vserver has no junction path. Dismounting volume $RemVolName on $Remote_Vserver!" YELLOW
    [Void](Dismount-NcVol -Name $RemVolName -VserverContext $Remote_Vserver)
  }elseif($RemJPath -eq $NULL){
    Wr "$RemVolName on $Remote_Vserver has no junction path - mounting volume!" YELLOW
    [Void](Mount-NcVol -Name $RemVolName -JunctionPath $SrcJPath -VserverContext $Remote_Vserver)
  }else{
    Wr "$RemVolName on $Remote_Vserver has incorrect junction path - re-mounting volume!" YELLOW
    [Void](Dismount-NcVol -Name $RemVolName -VserverContext $Remote_Vserver)
    [Void](Mount-NcVol -Name $RemVolName -JunctionPath $SrcJPath -VserverContext $Remote_Vserver)            
  }
  ## ===== RE-VERIFY JUNCTION PATH ===== ##
  $RemJPath = (Get-NcVol -Name $RemVolName -Attributes $VolAttrs -VserverContext $Remote_Vserver).VolumeIdAttributes.JunctionPath
  If($SrcJPath -ne $RemJPath){
    Wr "Failed to correct junction path!" RED
    RETURN $FALSE
  }
  Wr "Successfully updated junction path!" GREEN
  RETURN $TRUE
}

## ===== FUNCTION TO SYNC SHARES FOR A VOLUME DP PAIR ===== ##
Function Sync-NcSharesOnVolumeDpPair{
  ## NOTE! The following variables exist outside this function: ##
  ## $Source_Vserver,$Remote_Vserver,$SourceVol,$SrcVolName,$RemoteVol,$RemVolName,$VolAttrs ##
  If( !(Sync-NcJunctionPathVolumeDpPair) ){ RETURN }
  ## ===== GET SHARES ON SOURCE AND REMOTE (DP) VOLUME ===== ##
  $SrcSharesOnVol = $Shares.$Source_Vserver | Where { $_.Volume -eq $SrcVolName }
  $RemSharesOnVol = $Shares.$Remote_Vserver | Where { $_.Volume -eq $RemVolName }       
  ## ===== CYCLE THROUGH SHARES ON THE SOURCE VOLUME ===== ##
  Foreach($Share in $SrcSharesOnVol){
    [String]$ShareName  = $Share.ShareName
    [String]$Comment    = $Share.Comment
    $Share.NcController = $NULL
    $Share.Vserver      = $NULL
    $Share.Volume       = $RemVolName
    ## ===== CHECK IF THE SHARE EXISTS; IF NOT ADD, OTHERWISE SET ===== ##
    [Boolean]$NewShareCreated = $FALSE
    If( !($RemSharesOnVol | Where { $_.ShareName -eq $ShareName }) ){
      Wr "Share $ShareName does not exist on $RemVolName - creating!" YELLOW
      [Void]($Share | Add-NcCifsShare -Name $ShareName -VserverContext $Remote_Vserver -Comment $Comment)
      $NewShareCreated = $TRUE
    }else{
      Wr "Share $ShareName does exist on $RemVolName - verifying settings ..." GREEN
      [Void]($Share | Set-NcCifsShare -VserverContext $Remote_Vserver -Comment $Comment)
    }
    ## ===== CYCLE THROUGH THE ACLs ON THE SHARE ===== ##           
    Foreach($ACL in ($ACLs.$Source_Vserver | Where { $_.Share -eq $ShareName }) ){
      [String]$UserOrGroup = $ACL.UserOrGroup
      $ACL.NcController = $NULL
      $ACL.Vserver = $NULL
      ## ===== CHECK IF THE ACL EXISTS; IF NOT ADD (UNLESS NewShareCreated & Everyone), OTHERWISE SET ===== ##
      If($NewShareCreated -and ($UserOrGroup -eq "Everyone")){
        Wr "Setting ACL for $UserOrGroup on share $ShareName ..." GREEN
        [Void]($ACL | Set-NcCifsShareAcl -VserverContext $Remote_Vserver)               
      }elseif( !($ACLs.$Remote_Vserver | Where { ($_.Share -eq $ShareName) -and ($_.UserOrGroup -eq $UserOrGroup) }) ){
        Wr "Adding ACL for $UserOrGroup on share $ShareName ..." YELLOW
        [Void]($ACL | Add-NcCifsShareAcl -VserverContext $Remote_Vserver)                             
      }else{
        Wr "Setting ACL for $UserOrGroup on share $ShareName ..." GREEN
        [Void]($ACL | Set-NcCifsShareAcl -VserverContext $Remote_Vserver)
      }
    }
    ## ===== REMOVE ADDITIONAL ACLs ON THE REMOTE SHARE ===== ##
    Foreach($ACL in ($ACLs.$Remote_Vserver | Where { $_.Share -eq $ShareName }) ){
      [String]$UserOrGroup = $ACL.UserOrGroup
      If( !($ACLs.$Source_Vserver | Where { ($_.Share -eq $ShareName) -and ($_.UserOrGroup -eq $UserOrGroup) } ) ){
        Wr "ACL for $UserOrGroup on share $ShareName does not exist on source - removing!" RED
        [Void]($ACL | Remove-NcCifsShareAcl)
      }      
    }        
  }
  ## ===== REMOVE ADDITIONAL SHARES ON THE REMOTE VOLUME ===== ##
  Foreach($Share in $RemSharesOnVol){
    [String]$ShareName  = $Share.ShareName
    If( !($SrcSharesOnVol | Where { $_.ShareName -eq $ShareName }) ){
      Wr "Share $ShareName does not exist on the $Source_Vserver - removing!" RED
      [Void](Remove-NcCifsShare -Name $ShareName -VserverContext $Remote_Vserver -Confirm:$FALSE)
    }
  }
}

## ===== MAIN PROGRAM: CYCLE THROUGH THE SNAPMIRRORED VOLUMES ===== ##
Foreach($SM in $GetNcSnapMirror){
  $SourceVol  = $Vols.$Source_Vserver | where { $_.Name -eq $SM.SourceVolume }
  $RemoteVol  = $Vols.$Remote_Vserver | where { $_.Name -eq $SM.DestinationVolume }
  [String]$SrcVolName = $SourceVol.Name
  [String]$RemVolName = $RemoteVol.Name
  [Void](Sync-NcSharesOnVolumeDpPair);Wr
}

Comments