PowerShell Discussions
PowerShell Discussions
We are running 9.16.1p9 and recently turned off HTTP and HTTPS access to our SVM (recently turned only HTTPS back on) where we host some of our homedrives. This is where the powershell toolkit script is pointed. The SVM has a role that only allows certain behaviors
API Access level
| DEFAULT | None |
| version | Read-only |
| volume create | Read-only |
| volume modify | Read-only |
| volume show | Read-only |
| vserver cifs share access-control create | Read/write |
| vserver cifs share access-control delete | Read/write |
| vserver cifs share access-control modify | Read/write |
| vserver cifs share access-control show | Read/write |
| vserver cifs share create | Read/write |
| vserver cifs share delete | Read/write |
| vserver cifs share modify | Read/write |
| vserver cifs share show | Read/write |
When we have allowed VSAdmin the script will run but cant tell why the role causes it to fail
the script itself (sanitized for posting purposes) is
#F U N C T I O N S
function Get_NetApp1_Folder_Array()
{
$Folders = @()
$Folders = "\NetApp01$\Users"
Return $Folders
Return $Folders
}
function Get_NetApp2_Folder_Array()
{
$Folders = @()
$Folders = "\netapp01$\Users", "\netapp02$\Users", `
"\netapp03$\Users"
Return $Folders
}
function Get_NetApp3_Folder_Array()
{
$Folders = @()
$Folders = "\netapp01$\Users", "\netapp02$\Users", `
"\netapp03$\Users", "\netapp04$\Users", `
"\netapp05$\Users"
Return $Folders
}
function Get_NetApp4_Folder_Array()
{
$Folders = @()
$Folders = "\netapp01$\Users","\netapp02$\Users", `
"\netapp03$\Users", "\netapp04$\Users", `
"\netapp05$\Users", "\netapp06$\Users", `
"\netapp07$\Users"
Return $Folders
}
function Get_Device_Volumes ($Device)
{
$User = "DOMAIN\SERVICEACCOUNT"
$Password = "SERVICEACCOUNT-PASSWORD"
$SecurePass = ConvertTo-SecureString -String $Password -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential $User, $SecurePass
$nacontroller = Connect-NcController $Device -HTTPS -Credential $Credential
$VolumeNameArray = Get-NcVol -Controller $nacontroller
Return $VolumeNameArray
}
function Test_For_User_Folder ($FolderPath2Use)
{
$FolderMessage = Test-Path $FolderPath2Use
Return $FolderMessage
}
function Create_User_Folder ($FolderPath2Use)
{
Try
{
New-Item -ItemType directory -Path $FolderPath2Use -ea Stop
$ReturnMessage = "The folder was created successfully."
}
Catch
{
$ErrorMessage = $_.Exception.Message
$ReturnMessage = "ERROR " + $ErrorMessage
Write-Host $ErrorMessage
}
Return $ReturnMessage
}
function Set_Folder_Permissions_NetApp ($FolderPath2Use, $FQSNUser2Use)
{
$ACL = Get-ACL $FolderPath2Use # Get the existing security members
$permission = $UserFQDN,"Modify","ContainerInherit,ObjectInherit","None","Allow"
$accessRule = new-object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($accessRule)
Try
{
$acl | Set-Acl $FolderPath2Use -ea Stop
$ReturnMessage = "Permissions set for user " + $FQSNUser2Use + " on " + $FolderPath2Use
}
Catch
{
$ErrorMessage = $_.Exception.Message
$ReturnMessage = "ERROR " + $ErrorMessage
}
Return $ReturnMessage
}
function Share_Folder_NetApp ($Volume2Use, $Share2Use, $Controller2Use)
{
$ShareMessage = ""
$FailedToConnect = "False"
Try
{
$User = "DOMAIN\SERVICEACCOUNT"
$Password = "SERVICEACCOUNT-PASSWORD"
$SecurePass = ConvertTo-SecureString -String $Password -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential $User, $SecurePass
$Controller = Connect-NcController $Controller2Use -Credential $Credential -Transient:$true -ea Stop
$FailedToConnect = "False"
}
Catch
{
$ErrorMessage = $_.Exception.Message
$FailedToConnect = "True"
$ReturnMessage = "ERROR " + $ErrorMessage
}
if ($FailedToConnect -eq "False")
{
Try
{
Add-NcCifsShare -name $Share2Use -Path $Volume2Use -Controller $Controller -Comment "Home Share"
}
Catch
{
$ErrorMessage = $_.Exception.Message
$ShareMessage = "ERROR " + $ErrorMessage
}
Try
{
Add-NcCifsShareAcl -Share $Share2Use -UserOrGroup "Authenticated Users" -Permission full_control -Controller $Controller -ea Stop
}
Catch
{
$ErrorMessage = $_.Exception.Message
$ShareMessage = "ERROR " + $ErrorMessage
}
Try
{
Set-NcCifsShareAcl -Share $Share2Use -UserOrGroup "Authenticated Users" -Permission full_control -Controller $Controller -ea Stop
}
Catch
{
$ErrorMessage = $_.Exception.Message
$ShareMessage = "ERROR " + $ErrorMessage
}
Try
{
Remove-NcCifsShareAcl -Share $Share2Use -UserOrGroup "Everyone" -Controller $Controller -ea SilentlyContinue
}
Catch
{
$ErrorMessage = $_.Exception.Message
$ShareMessage = "ERROR " + $ErrorMessage
}
}
$ShareMessage = "Share successfully created"
Return $ShareMessage
}
function RemoveNTFSPermissions($path, $object, $permission)
{
$FileSystemRights = [System.Security.AccessControl.FileSystemRights]$permission
$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]"ContainerInherit, ObjectInherit"
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]"None"
$AccessControlType =[System.Security.AccessControl.AccessControlType]::Allow
$Account = New-Object System.Security.Principal.NTAccount($object)
$FileSystemAccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($Account, $FileSystemRights, $InheritanceFlag, $PropagationFlag, $AccessControlType)
$DirectorySecurity = Get-ACL $path
$DirectorySecurity.RemoveAccessRuleAll($FileSystemAccessRule)
Try
{
Set-ACL $path -AclObject $DirectorySecurity -ErrorAction SilentlyContinue
}
Catch
{
$ErrorMessage = $_.Exception.Message
}
}
function RemoveInheritance($path)
{
$isProtected = $true
$preserveInheritance = $true
$DirectorySecurity = Get-ACL $path
$DirectorySecurity.SetAccessRuleProtection($isProtected, $preserveInheritance)
Try
{
Set-ACL $path -AclObject $DirectorySecurity -ErrorAction SilentlyContinue
}
Catch
{
$ErrorMessage = $_.Exception.Message
}
}
#MAIN PROCESSING THREAD
#Add values for variables from previous operation
$HomeDrive = "\\NetApp1\JUser$"
$HomeDrive = $HomeDrive.Trim()
$SplitForUser = $HomeDrive.Replace("\\","")
$SplitForUser = $SplitForUser.Split("\")
$UserName = $SplitForUser[1]
$UserName = $UserName.Replace("$","")
$Server = $SplitForUser[0]
$UserShare = $UserName + "$"
$UserFQDN = "Domain\" + $UserName
$NetAppDevice = $Server
Write-Host $UserName
#Check to see if the share already exists
#$NetAppShareExists = Test_For_User_Folder $HomeDrive
$NetAppShareExists = $False
if ($NetAppShareExists -eq $False)
{
$bOpStatus = "OK"
$bOpMessage = "Share Good"
if ($NetAppDevice -eq "NetApp1")
{
$FolderArray = Get_NetApp1_Folder_Array
}
if ($NetAppDevice -eq "NetApp2")
{
$FolderArray = Get_NetApp2_Folder_Array
}
if ($NetAppDevice -eq "NetApp3")
{
$FolderArray = Get_NetApp3_Folder_Array
}
if ($NetAppDevice -eq "NetApp4")
{
$FolderArray = Get_NetApp4_Folder_Array
}
$controller = $NetAppDevice
$NetAppVolumes = Get_Device_Volumes $controller
foreach ($Volume in $NetAppVolumes)
{
$Check = "*" + $Volume + "*"
foreach ($Folder in $FolderArray)
{
if ($Folder -like $Check)
{
$TotalSpace = $TotalSpace + $Volume.Available
$Volumes += $Volume
}
}
}
$Volumes = $Volumes | sort-object Available -descending
foreach ($Folder in $FolderArray)
{
$Check = "*" + $Volumes[0] + "*"
if ($Folder -like $Check)
{
$FolderPath = $Folder + "\"
$UseVolume = $Volumes[0]
Break
}
}
$PhysicalPath = "\\" + $NetAppDevice + $FolderPath + $UserName
Write-Host $PhysicalPath
$FolderTest = Test_For_User_Folder $PhysicalPath
#Write-Host $PhysicalPath
$FolderCreateMessage = ""
if ($FolderTest -eq $False)
{
$FolderCreateMessage = Create_User_Folder $PhysicalPath
}
if ($FolderCreateMessage -like '*ERROR*')
{
$FolderCreated = "False"
$bOpStatus = "Failed"
$bOpMessage = "Could not create the user folder."
}
Else
{
$FolderCreated = "True"
}
if ($FolderCreated -eq "True")
{
$PermissionSetMessage = Set_Folder_Permissions_NetApp $PhysicalPath $UserFQDN
RemoveInheritance $PhysicalPath
RemoveNTFSPermissions $PhysicalPath "Authenticated Users" "Modify, ChangePermissions"
RemoveNTFSPermissions $PhysicalPath "Users" "Modify, ChangePermissions"
RemoveNTFSPermissions $PhysicalPath "Creator Owner" "Modify, ChangePermissions"
RemoveNTFSPermissions $PhysicalPath "SYSTEM" "Modify, ChangePermissions"
if ($PermissionSetMessage -like '*ERROR*')
{
#$PermissionSet = "True"
$PermissionSet = "False"
$bOpStatus = "Failed"
$bOpMessage = "Unable to set folder permissions."
}
Else
{
$PermissionSet = "True"
}
If ($PermissionSet -eq "True")
{
$NetAppVolumeSharePath = "/vol/" + $UseVolume + "/Users/" + $UserName
$NetAppShare = $UserShare
$ShareCreateMessage = Share_Folder_NetApp $NetAppVolumeSharePath $NetAppShare $NetAppDevice
if ($ShareCreateMessage -like '*ERROR*')
{
$ShareSet = "False"
$bOpStatus = "Failed"
$bOpMessage = "Unable to share the folder."
}
Else
{
$ShareSet = "True"
$bOpStatus = "Completed"
$bOpMessage = "User Share Created."
}
}
}
}
Else
{
$bOpStatus = "Failed"
$bOpMessage = "User Share Already Exists."
}
new-Object pscustomobject -property @{
StatusMsg = $bOpMessage
Status = $bOpStatus
}
Any help figuring out where we have gone wrong would be amazing. This script has worked for years and since we have started locking down more and more features we are discovering some issues.
Thank you in advance
Hey Brendan,
What's the actual error message you get when running the code? Does your code work with admin privileges (IE is it a problem with your code or the RBAC role you've create)
/Matt
Hey Brendan,
Which version of the PSTK are you using? You can check the version using:
Get-NaToolKitVersion
I noticed in several of your functions you are using the "-HTTPS" parameter with the "Connect-NcController" CmdLet. Please note that this parameter has been deprecated and is no longer a valid parameter for newer builds of the PSTK so the attempt to connect to the cluster would fail as the parameter name is invalid. Valid parameter names are:
PS C:\> Get-Help Connect-NcController
NAME
Connect-NcController
SYNOPSIS
Connect to a clustered Data ONTAP controller.
SYNTAX
Connect-NcController [-Name] <String[]> [-Port <UInt16>] [-Credential <PSCredential>] [-Transient] [-Vserver
<String>] [-Timeout <Int32>] [-Add] [-ApplicationName <String>] [-SSLVersion <String>] [-InformationAction
<ActionPreference>] [-InformationVariable <String>] [-PipelineVariable <String>] [<CommonParameters>]
Also from a security perspective, I would strongly recommend you don't include user names and passwords in your code. Instead i would recommend using the "Add-NcCredential" to first cache the credentials for your RBAC user that will connect to the cluster and then from within your script use the "Get-NcCredential" to retreive the cached credential and pass the $Credential variable as an input parameter into your Function. Note that the credential can only be decrypted as the user who encrypted it so when you cache the credential you must first open a PowerShell session as your RBAC user to run the "Add-NcCredential" CmdLet and your main script must be run as the RBAC user that encrypted the credentials. Just a suggestion to avoid using user names and passwords in clear text within your code.
Hope that gives you a few tips towards getting it working. Let me know what the error message is
/Matt
Hey Brendan,
Just as an example to improve the security of your script, rather than have user names and passwords within your code and then creating a credential object to connect to the cluster. EG:
Function Get_Device_Volumes($Device){
$User = "DOMAIN\SERVICEACCOUNT"
$Password = "SERVICEACCOUNT-PASSWORD"
$SecurePass = ConvertTo-SecureString -String $Password -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential $User, $SecurePass
$nacontroller = Connect-NcController $Device -HTTPS -Credential $Credential
$VolumeNameArray = Get-NcVol -Controller $nacontroller
Return $VolumeNameArray
}
You can cache the Credential as the user who you will schedule the script to runas. EG:
$credential = Get-Credential
Add-NcCredential -Name 192.168.100.100 -Credential $credential
Then from within your script you can retrieve the cached credential and pass them as an input parameter into your function. EG:
Param(
[Parameter(Mandatory = $True, HelpMessage="The Cluster name or IP Address")]
[String]$Cluster,
[Parameter(Mandatory = $True, HelpMessage="The Vserver Name")]
[String]$Vserver,
[Parameter(Mandatory = $False, HelpMessage = "The Credentials to authenticate to MySQL")]
[System.Management.Automation.PSCredential]$Credential
)
#'------------------------------------------------------------------------------
Function GetVolumes{
Param(
[Parameter(Mandatory = $True, HelpMessage="The Cluster name or IP Address")]
[String]$Cluster,
[Parameter(Mandatory = $True, HelpMessage="The Vserver Name")]
[String]$Vserver,
[Parameter(Mandatory = $True, HelpMessage = "The Credentials to authenticate to MySQL")]
[System.Management.Automation.PSCredential]$Credential
)
#'---------------------------------------------------------------------------
#'Connect to the cluster if not already connected.
#'---------------------------------------------------------------------------
If((($global:CurrentNcController).Name -ne $Cluster)){
Try{
Connect-NcController -Name $Cluster -Credential $Credential -ErrorAction Stop | Out-Null
Write-Host $("Connected to cluster ""$Cluster"" as user """ + $Credential.UserName)
}Catch{
Write-Warning -Message $("Failed connecting to cluster ""$Cluster"". Error " + $_.Exception.Message)
Return $Null
}
}
#'---------------------------------------------------------------------------
#'Enumerate the volumes on the vserver.
#'---------------------------------------------------------------------------
Try{
$volumes = Get-NcVol -Vserver $Vserver -ErrorAction Stop
Write-Host "Enumerating Volumes on Vserver ""$Vserver"" on Cluster ""$Cluster"""
}Catch{
Write-Warning -Message $("Failed enumerating volumes on vserver ""$Vserver"" on cluster ""$Cluster"". Error " + $_.Exception.Message)
Return $Null
}
Return $volumes
}#'End Function GetVolumes.
#'------------------------------------------------------------------------------
#'Enumerate Cached Credential for the cluster if not provided.
#'------------------------------------------------------------------------------
If(-Not($Credential)){
Write-Host "Enumerating cached credentials for Cluster ""$Cluster"""
$Credential = (Get-NcCredential -Name $Cluster).Credential
}
If($Null -eq $Credential){
Write-Warning -Message "Failed enumerating Credential for Cluster ""$Cluster"""
Exit
}
#'------------------------------------------------------------------------------
#'Enumerate the vserver volumes.
#'------------------------------------------------------------------------------
$volumes = GetVolumes -Cluster $Cluster -Vserver $Vserver -Credential $Credential
$volumes
Example Output:
PS C:\Scripts\> .\GetVolumes.ps1 -Cluster 192.168.100.100 -Vserver vserver1
Enumerating cached credentials for Cluster "192.168.100.100"
Enumerating Volumes on Vserver "vserver1" on Cluster "192.168.100.100"
Name State TotalSize Used Available Dedupe Aggregate Vserver
---- ----- --------- ---- --------- ------ --------- -------
volume1 online 10.0 GB 5.0 GB aggr1 vserver1
volume2 online 10.0 GB 8.0 GB aggr1 vserver1
volume3 online 10.0 GB 9.0 GB aggr1 vserver1
vserver1_root online 1.0 GB 961.6 MB aggr1 vserver1
Hope that provides a useful example for you.
/Matt