Hey everyone!
I recently started trying to get a little bit more 'official' and started learning how to write Advance Functions and I wanted to share 4 functions that I believe are quite useful, and hope that someone might find helpful.
Function #1:
function Enable-AclInheritanceOnFolder
{
<#
.SYNOPSIS
This function will turn on inheritance on the folder
.DESCRIPTION
This function will turn inheritance on the folder provided, currenth path if no path is provided.
.EXAMPLE
Enable-AclInheritanceOnFolder -path 'C:\TestPath'
.PARAMETER Path
The path of the folder where the ACL should be added.
#>
[CmdletBinding()]
[OutputType([int])]
Param
(
#Path
[Parameter(Mandatory=$false,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='What is the path where inheritance should be enabled?')]
[Alias('Location')]
[ValidateLength(3,250)]
[string]$path
)
begin
{
if($path.Length -eq 0)
{
Write-Verbose "No Path provided, assigning current location to path."
$currentPath = (Get-Item .\).FullName
}
else
{
$currentPath = $path
}
}
Process
{
try
{
Write-Verbose "Getting folder IO object for $currentPath"
$folderInfo = New-Object IO.DirectoryInfo($currentPath)
Write-Verbose "Getting the Folder Access Control List."
$folderACLs = $folderInfo.GetAccessControl()
Write-Verbose "Enabling Inheritance in ACL Object."
$folderACLs.SetAccessRuleProtection($false,$false)
Write-Verbose "Attempting to save the changes."
$folderInfo.SetAccessControl($folderACLs)
}
catch
{
Write-Verbose "There was an error adding the permissions."
Write-Error $Error[0]
}
}
}
Function #2
function Disable-AclInheritanceOnFolder
{
<#
.SYNOPSIS
This function will turn off inheritance on the folder
.DESCRIPTION
This function will turn off inheritance on the folder provided, currenth path if no path is provided.
It will also require that you specify if the current rules should be kept and converted or if they
should be removed.
.EXAMPLE
Disable-AclInheritanceOnFolder -path 'C:\TestPath' -ConvertRules $true|$false
.EXAMPLE
Disable-AclInheritanceOnFolder -ConvertRules $true|$false
.PARAMETER Path
The path of the folder where the ACL should be added.
.PARAMETER ConvertRules
Bool to know if the current rules should be converted and kept or removed from the folder.
If you remove the rules you might be loose access to the folder, be sure to take ownership of the
folder first.
#>
[CmdletBinding()]
[OutputType([int])]
Param
(
#Path
[Parameter(Mandatory=$false,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='What is the path where inheritance should be disabled?')]
[Alias('Location')]
[ValidateLength(3,250)]
[string]$path,
#PreserveInheritance
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='Should the current rules be removed or converted?')]
[Alias('PreserveInheritance')]
[bool]$RemoveCurrentRules
)
begin
{
if($path.Length -eq 0)
{
Write-Verbose "No Path provided, assigning current location to path."
$currentPath = (Get-Item .\).FullName
}
else
{
$currentPath = $path
}
}
Process
{
try
{
Write-Verbose "Getting folder IO object for $currentPath"
$folderInfo = New-Object IO.DirectoryInfo($currentPath)
Write-Verbose "Getting the Folder Access Control List."
$folderACLs = $folderInfo.GetAccessControl()
Write-Verbose "Enabling Inheritance in ACL Object."
$folderACLs.SetAccessRuleProtection($true,$true)
Write-Verbose "Attempting to save the changes."
$folderInfo.SetAccessControl($folderACLs)
}
catch
{
Write-Verbose "There was an error adding the permissions."
Write-Error $Error[0]
}
}
}
Function #3
function Add-AclToFolder
{
<#
.SYNOPSIS
This function will add an Group to a folder's ACL.
.DESCRIPTION
This function will add the given SecurityGroup or user name to the specified folder.
.EXAMPLE
Add-ACL -Path 'C:\TestPath' -securityGroup 'DL-SECURITYGROUP-NAME'
.PARAMETER Path
The path of the folder where the ACL should be added.
.PARAMETER securityGroup
The security group name
.PARAMETER AccessLevel
The access level that you wish to give to the group. Choose between ReadAndExecute, Modify, FullControl
#>
[CmdletBinding()]
[OutputType([int])]
Param
(
#Path
[Parameter(Mandatory=$false,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='What is the path where this group should be added?')]
[Alias('Location')]
[ValidateLength(3,250)]
[string]$path,
#securityGroup
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='What is the name of the group?')]
[Alias('group')]
[ValidateLength(3,50)]
[string]$securityGroup,
#AccessLevel
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='Access Level: ReadAndExecute, Modify, FullControl')]
[Alias('acl')]
[ValidateLength(3,15)]
[string]$accessLevel
)
begin
{
Write-Verbose "Starting Add-AclToFolder Function."
if($path.Length -eq 0)
{
Write-Verbose "No path was provided. We are getting the current path."
$currentPath = (Get-Item .\).FullName
}
else
{
$currentPath = $path
}
}
Process
{
#region Add-ACL
Write-Verbose "Process has started."
try
{
Write-Verbose "Getting folder IO object for $currentPath"
$folderInfo = New-Object IO.DirectoryInfo($currentPath)
Write-Verbose "Getting the Folder Access Control List."
$folderACLs = $folderInfo.GetAccessControl()
Write-Verbose "Creating new Security.AccessControl.FileSystemAccessRule."
Write-Verbose "Security Group: $securityGroup"
Write-Verbose "Access Level: $accessLevel"
$newACL = New-Object System.Security.AccessControl.FileSystemAccessRule("$($securityGroup)",$accessLevel,"ContainerInherit, ObjectInherit", "None", "Allow")
Write-Verbose "Adding the new Rule to the ACL object."
$folderACLs.AddAccessRule($newACL)
Write-Verbose "Attempting to save the changes."
$folderInfo.SetAccessControl($folderACLs)
}
catch
{
Write-Verbose "There was an error adding the permissions."
Write-Error $Error[0]
}
}
}
Function #4
function Remove-AclFromFolder
{
<#
.SYNOPSIS
This function will remove an Group or User from a folder's ACL.
.DESCRIPTION
This function will remove an Group or User from a folder's ACL. If no path is provided the script will use the current path.
.EXAMPLE
Remove-AclFromFolder -Path 'C:\TestPath' -securityGroup 'DL-SECURITYGROUP-NAME'
.EXAMPLE
Remove-AclFromFolder -securityGroup 'DL-SECURITYGROUP-NAME'
.PARAMETER Path
The path of the folder where the ACL should be removed.
#>
[CmdletBinding()]
[OutputType([int])]
Param
(
#Path
[Parameter(Mandatory=$false,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='What is the path where th group should be removed from?')]
[Alias('Location')]
[ValidateLength(3,250)]
[string]$path,
#securityGroup
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='What is the name of the group?')]
[Alias('group')]
[ValidateLength(3,50)]
[string]$securityGroup
)
begin
{
Write-Verbose "Starting Add-AclToFolder Function."
if($path.Length -eq 0)
{
Write-Verbose "No path was provided. We are getting the current path."
$currentPath = (Get-Item .\).FullName
}
else
{
$currentPath = $path
}
}
Process
{
#region Add-ACL
Write-Verbose "Process has started."
try
{
Write-Verbose "Getting folder IO object for $currentPath"
$folderInfo = New-Object IO.DirectoryInfo($currentPath)
Write-Verbose "Getting the Folder Access Control List."
$folderACLs = $folderInfo.GetAccessControl()
Write-Verbose "Finding the rule to remove."
$aclToRemove = $folderACLs.Access | ?{$_.IdentityReference -match $securityGroup}
Write-Verbose "Removing $($aclToRemove.identityreference) from the ACL Object"
$folderACLs.RemoveAccessRule($aclToRemove)
Write-Verbose "Attempting to save the changes."
$folderInfo.SetAccessControl($folderACLs)
}
catch
{
Write-Verbose "There was an error modifying the permissions."
Write-Error $Error[0]
}
}
}
I believe that I been able to properly document the functions so there shouldn't be any confusion, but do let me know if you find any issues, comments or complaints.
Kind regards,
Me.
Tuesday, April 28, 2015
Monday, March 16, 2015
PowerShell ISE Cut (Ctrl + X)
Hello reader,
I know its been a while since I posted anything but things have been a little dull and haven't had anything worth posting. Today was a bit different though, I encountered a quirky bug, and thought I could share it.
I saw one of the comments by 'Scott - Oregon' in which he stated that VNC Viewer was causing the issue and closing it would resolve the problem. I don't use VNC Viewer, but I did remember earlier while working with mRemoteNG that I accidentally clicked to remote into a server with VNC instead of RDP, so I closed mRemoteNG and voila it started working.
I made sure to sign up and drop a comment on the Microsoft Connect post as it is still an active post.
I hope this might help someone out there!
Kind regards,
Me
Tuesday, January 6, 2015
Exchange cmdlet: Get-User
Hello!
Its been a bit since I posted last mainly because I was out on vacations. I been back for a few days now but it had been a bit uneventful. Until today when I was tasked with creating a termination script for over 300 users. I was provided with a list and started working on writing a script that followed our procedures. I don't think I can go into details for privacy reasons but we make some changes to the settings on our Exchange side when we terminate the user, and i noticed that some of the users that I was provided were not Mailbox users but MailUsers and they even threw a few MailContacts in there too.
The command Get-Mailbox does not work for MailUsers or MailContact and they each have their own. I did some digging around to try to find the best way to identify the type of mailbox they are before trying to use the 'Get-' or the 'Set-' commands but I wasn't able to find much. I even reviewed all of the properties for Get-ADUser and Get-ADObject hoping to find anything that would identify all three of them without having to write extra code - I don't normally like to try to write extra code unless I believe its completely necessary. Unfortunately none of it brought me the answer I was seeking.
It wasn't until I was going through the cmdlets that are added with the Exchange Add-in that I found the cmdlet 'Get-User'. I been writing PowerShell for over 2 years now, and quite franky I've never had never seen this particular cmdlet, but it really did made everything so much easier.
Syntax:
The regular output is:
PS C:\> get-user Jonatan.Bernal
Name RecipientType
---- -------------
Jonatan Bernal UserMailbox
It can if you format-list you get the full object: Deserialized.Microsoft.Exchange.Data.Directory.Management.User
The information provided is quite thorough and I don't have to bother what type of mailbox it is which quite frankly I find very useful to only use 1 cmdlet and use the other ones only when they are needed.
I hope that this might help someone out there.
Feel free to leave me a comment and let me know if this was helpful or know any other interesting cmdlets!
Kind regards,
Me
Update: well it was pointed out to me by +Lincoln Reedy that the cmdlet that I shared about would unfortunately not work for MailContacts, which again can be a problem. He also shared with me the cmdlet 'Get-Recepient' which would work for any type of object. I haven't quite worked much with this new cmdlet yet, so I'm still doing some research because I saw that you can use an authentication type of federation, so I'm wondering if you can actually get recipients outside of your organization. I'll update when I have more information on this.
Its been a bit since I posted last mainly because I was out on vacations. I been back for a few days now but it had been a bit uneventful. Until today when I was tasked with creating a termination script for over 300 users. I was provided with a list and started working on writing a script that followed our procedures. I don't think I can go into details for privacy reasons but we make some changes to the settings on our Exchange side when we terminate the user, and i noticed that some of the users that I was provided were not Mailbox users but MailUsers and they even threw a few MailContacts in there too.
The command Get-Mailbox does not work for MailUsers or MailContact and they each have their own. I did some digging around to try to find the best way to identify the type of mailbox they are before trying to use the 'Get-' or the 'Set-' commands but I wasn't able to find much. I even reviewed all of the properties for Get-ADUser and Get-ADObject hoping to find anything that would identify all three of them without having to write extra code - I don't normally like to try to write extra code unless I believe its completely necessary. Unfortunately none of it brought me the answer I was seeking.
It wasn't until I was going through the cmdlets that are added with the Exchange Add-in that I found the cmdlet 'Get-User'. I been writing PowerShell for over 2 years now, and quite franky I've never had never seen this particular cmdlet, but it really did made everything so much easier.
Syntax:
Get-User [-Identity <UserIdParameter>] <COMMON PARAMETERS>
Get-User [-Anr <String>] <COMMON PARAMETERS>
SourceCOMMON PARAMETERS: [-AccountPartition <AccountPartitionIdParameter>][-Arbitration <SwitchParameter>][-AuditLog <SwitchParameter>]
[-ConsumerNetID <NetID>]
[-Credential <PSCredential>]
[-DomainController <Fqdn>]
[-Filter <String>]
[-IgnoreDefaultScope <SwitchParameter>]
[-Organization <OrganizationIdParameter>]
[-OrganizationalUnit <OrganizationalUnitIdParameter>]
[-PublicFolder <SwitchParameter>]
[-ReadFromDomainController <SwitchParameter>]
[-RecipientTypeDetails <RecipientTypeDetails[]>]
[-ResultSize <Unlimited>]
[-SoftDeletedUser <SwitchParameter>]
[-SortBy <String>]
The regular output is:
PS C:\> get-user Jonatan.Bernal
Name RecipientType
---- -------------
Jonatan Bernal UserMailbox
It can if you format-list you get the full object: Deserialized.Microsoft.Exchange.Data.Directory.Management.User
The information provided is quite thorough and I don't have to bother what type of mailbox it is which quite frankly I find very useful to only use 1 cmdlet and use the other ones only when they are needed.
I hope that this might help someone out there.
Feel free to leave me a comment and let me know if this was helpful or know any other interesting cmdlets!
Kind regards,
Me
Update: well it was pointed out to me by +Lincoln Reedy that the cmdlet that I shared about would unfortunately not work for MailContacts, which again can be a problem. He also shared with me the cmdlet 'Get-Recepient' which would work for any type of object. I haven't quite worked much with this new cmdlet yet, so I'm still doing some research because I saw that you can use an authentication type of federation, so I'm wondering if you can actually get recipients outside of your organization. I'll update when I have more information on this.
Wednesday, December 17, 2014
Regarding the Long Path
I shared my post regarding the Windows long path dilema on the /r/sysadmin subreddit and on G and quite honestly there was quite a reaction. Thank you everyone who took the time to read my article, and I wanted to share something that /u/elkBBQ shared
Source: http://www.xkcd.com/1459/
I had a good chuckle, but on a more serious note. I learned quite a few things from the /r/sysadmin community but I did want to clarify for those who still believe that this is a limitation in the file system (NTFS), but let me tell you it is not. NTFS actually handles up to 32k characters on paths; however, Windows is not able to use it because of a limitation in its API, and while there is some ways to force it like using Unicode and other ways to get around the issue (see link below) there is no office or really a 'good' solution.
I wanted to share the following Article that explains a lot on this issue. The article is written by Kim Hamilton of the BCL Team and as (s)he explains in an answer to a comment:
Sadly in 2007 Microsoft believed this was an issue, and their team were working on a solution. It has been 7 years and the issue still persists, and from what we know off there is no soon to come fix.
Kind regards to everyone,
Me.
PS. Below I'll post some of the most common work arounds for this issue:
Force Unicode: \\?\PATHHERE (please see the link above for details on how to use this method)
Con: Not widely supported
Map a drive or use subst to map a drive.
Con: Unfortunately the only way to get this to work would be to know the exact path. You'll have to map the drive to a subfolder that is close to the one that breaks the limit. Also doesn't play very well with scripting.
Someone (and I apologize I cannot find the user who posted this on the reddit post) recommended to use VBScript; however, I do not posses this skill, so if someone could show us a way for this that'd be awesome!
Peter Kriegel pointed me to a PowerShell Module called File System Security PowerShell Module 3.2.2 but I'm not familiar with it and haven't had a chance to look deep into it as to try to find out who they manage to make it work. Again if someone has some information that they can provide regarding the inner workings, or if indeed truly works I'd appreciate the information.
Source: http://www.xkcd.com/1459/
I had a good chuckle, but on a more serious note. I learned quite a few things from the /r/sysadmin community but I did want to clarify for those who still believe that this is a limitation in the file system (NTFS), but let me tell you it is not. NTFS actually handles up to 32k characters on paths; however, Windows is not able to use it because of a limitation in its API, and while there is some ways to force it like using Unicode and other ways to get around the issue (see link below) there is no office or really a 'good' solution.
I wanted to share the following Article that explains a lot on this issue. The article is written by Kim Hamilton of the BCL Team and as (s)he explains in an answer to a comment:
Diego,
We do need a solution in the underlying windows API, but this would most likely emerge as new APIs rather than changing the existing ones. We've discussed this at length on the longpath alias at Microsoft (yes, we have a whole alias devoted to the issue!) and there are no plans to change the existing ones, since it would break third party code that depend on MAX_PATH buffers on the stack.
We've discussed migration plans to enable those existing Win32 APIs not to enforce MAX_PATH but there are no clear solutions; this would need to be done very carefully to avoid breaking existing Windows-based apps.
The main reason I think we need new Win32 APIs is that \\?\ lumps together long paths and non-canonical paths, so you have to do tricks to get canonicalization on \\?\ paths. That sort of work shouldn't be pushed onto the callers just to achieve long paths.
Thanks,
Kim
Date: 13 Feb 2007 7:08 PM
Date: 13 Feb 2007 7:08 PM
Kind regards to everyone,
Me.
PS. Below I'll post some of the most common work arounds for this issue:
Force Unicode: \\?\PATHHERE (please see the link above for details on how to use this method)
Con: Not widely supported
Map a drive or use subst to map a drive.
Con: Unfortunately the only way to get this to work would be to know the exact path. You'll have to map the drive to a subfolder that is close to the one that breaks the limit. Also doesn't play very well with scripting.
Someone (and I apologize I cannot find the user who posted this on the reddit post) recommended to use VBScript; however, I do not posses this skill, so if someone could show us a way for this that'd be awesome!
Peter Kriegel pointed me to a PowerShell Module called File System Security PowerShell Module 3.2.2 but I'm not familiar with it and haven't had a chance to look deep into it as to try to find out who they manage to make it work. Again if someone has some information that they can provide regarding the inner workings, or if indeed truly works I'd appreciate the information.
Tuesday, December 16, 2014
Permissions
So I worked on this script for a few hours yesterday
The script is intended to go through every path in a directory and get all of the permissions
of the folders, and export them into a CSV. Surprisingly enough, the script itself
works great too; however, i came to the sad problem of paths being too long and
not getting all of the permissions.
I've looked at this from a long of angles and feel like I've ran out of Google power
here. If someone has an idea of how to get this done, that doesn't include
running a Robocopy first to get the paths. Please share with me.
Kind regards,
Me.
$drive
= "F:\"
$fileName
= "C:\Audits\FDrive_Audit_$((Get-Date -Format d).Replace(" ","_").replace("/","-").Replace(":",'-')).csv"
$ACL_Report
= @()
$Stack
= New-Object
System.Collections.Stack
function
getPermissions($folder)
{
$ACL_Report = @()
foreach($reference
in (Get-ACL
$folder |
%{$_.Access}))
{
$aclObj =
New-Object psobject
$aclObj |
Add-Member -MemberType
NoteProperty -Name
'Path' -Value
$folder
$aclObj |
Add-Member -MemberType
NoteProperty -Name
'Account' -Value
$reference.IdentityReference
$aclObj |
Add-Member -MemberType
NoteProperty -Name
'Access' -Value
$reference.FileSystemRights
$aclObj |
Add-Member -MemberType
NoteProperty -Name
'Inherited' -Value
$reference.IsInherited
$aclObj |
Add-Member -MemberType
NoteProperty -Name
'Inheritance' -Value
$reference.InheritanceFlags
$aclObj |
Add-Member -MemberType
NoteProperty -Name
'Propagation' -Value
$reference.PropagationFlags
$ACL_Report += $aclObj
}
$ACL_Report += " "
$ACL_Report | Export-Csv $fileName -NoTypeInformation
-Append
}
function
create-Queue($currentFolder)
{
foreach($folder
in (Get-ChildItem
$currentFolder -Directory))
{
getPermissions $folder.FullName
$Stack.Push($folder.FullName)
create-Queue $stack.Pop()
}
}
create-Queue
$drive
Path is too long
I believe that I should let everyone know that once again Microsoft has not fixed the path is too long issue in his newest release Windows X.
I been deal with this atrocious issue every now and then when writing scripts that run against our file servers, and I must say its a real pain in the rear. I pray one day this will be fixed.
Good day to all.
Edit 9/16/2015: I'm not sure why the pictures are gone. I'll try to replicate the issue once again and repost them soon.
Edit 1/16/2016: I finally took the time to recreate the error and re-upload the picture.
Thursday, December 4, 2014
Don't learn many, become an expert at one!
Today I was reading this article. It was talking about the main differences between PHP and .NET what one should learn, and let me tell you something I wish I had read it back when I was younger.
I took Programming I and II in High School and then again in College, but unfortunately I don't think I ever learned much. Yeah I learned syntax and learned some of the logic behind programming, but nobody ever told me what was said in this article:
I took Programming I and II in High School and then again in College, but unfortunately I don't think I ever learned much. Yeah I learned syntax and learned some of the logic behind programming, but nobody ever told me what was said in this article:
"There are benefits to learning either PHP or .NET. Should you learn both? If you’re new to programming, the answer, I think, is No: At an early stage in your career, you need to focus your energy on getting very good at one thing, which will translate into higher-paying jobs down the road. If you try to go to broad, you will stretch yourself thin and not master anything. (I made that mistake early in my career, and it started hurting my job prospects—employers tend to distrust resumes that list hundreds of technologies in which the applicant is supposedly an expert.) Pick one thing and be great at it!" - Jeff Coswell
In this quote he specifies something very important that I wish to pass to anyone starting with programming: Don't learn multiple languages, learn one and become an expert at it, then switch to other ones if you want to. I wish very much wish someone had told me this, but because nobody did, I ran around through most of my amateur days learning Ruby, Java, C++, C#, Python, Perl, even looked at Haskell and learned a lot of the syntax but in reality I learned nothing because I was never an expert with any of them.
I felt strongly that this needed to be shared because I don't think its quite common knowledge. For example, in college Programming I was Introduction to Java, Programming II was introduction to C++, and Programming III was cancelled due to not having enough students sign up, but I believe it was introduction to C#.
So hear my advice: If you enjoy programming and would like to pursue a career in this field, you need to pick a language, and preferably once that is common enough that you can make a living with, and become an expert. If you then decide you would like to try something else then your transition will be much easier.
If not you'll probably end up like me learning a lot of syntax and not knowing much.
Good luck if you are starting, and if you are an experience developer please let me know what you think, or if you also went through something like this in your earlier days.
Subscribe to:
Posts (Atom)